mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-03-29 18:48:57 +00:00
Merge branch 'haveno-dex:master' into install_whonix_qubes
This commit is contained in:
commit
281264990b
52 changed files with 383 additions and 234 deletions
build.gradle
common/src/main/java/haveno/common/app
core/src/main/java/haveno/core
api
app
offer
support/dispute
trade
Trade.javaTradeManager.java
protocol
xmr
daemon/src/main/java/haveno/daemon/grpc
desktop
docs
gradle
proto/src/main/proto
seednode/src/main/java/haveno/seednode
|
@ -71,7 +71,7 @@ configure(subprojects) {
|
|||
loggingVersion = '1.2'
|
||||
lombokVersion = '1.18.30'
|
||||
mockitoVersion = '5.10.0'
|
||||
netlayerVersion = '700ec94f0f' // Tor browser version 14.0.3 and tor binary version: 0.4.8.13
|
||||
netlayerVersion = 'd4f9d0ce24' // Tor browser version 14.0.7 and tor binary version: 0.4.8.14
|
||||
protobufVersion = '3.19.1'
|
||||
protocVersion = protobufVersion
|
||||
pushyVersion = '0.13.2'
|
||||
|
@ -610,7 +610,7 @@ configure(project(':desktop')) {
|
|||
apply plugin: 'com.github.johnrengelman.shadow'
|
||||
apply from: 'package/package.gradle'
|
||||
|
||||
version = '1.0.18-SNAPSHOT'
|
||||
version = '1.0.19-SNAPSHOT'
|
||||
|
||||
jar.manifest.attributes(
|
||||
"Implementation-Title": project.name,
|
||||
|
|
|
@ -28,7 +28,7 @@ import static com.google.common.base.Preconditions.checkArgument;
|
|||
public class Version {
|
||||
// The application versions
|
||||
// We use semantic versioning with major, minor and patch
|
||||
public static final String VERSION = "1.0.18";
|
||||
public static final String VERSION = "1.0.19";
|
||||
|
||||
/**
|
||||
* Holds a list of the tagged resource files for optimizing the getData requests.
|
||||
|
|
|
@ -159,7 +159,7 @@ public class CoreOffersService {
|
|||
}
|
||||
|
||||
OpenOffer getMyOffer(String id) {
|
||||
return openOfferManager.getOpenOfferById(id)
|
||||
return openOfferManager.getOpenOffer(id)
|
||||
.filter(open -> open.getOffer().isMyOffer(keyRing))
|
||||
.orElseThrow(() ->
|
||||
new IllegalStateException(format("openoffer with id '%s' not found", id)));
|
||||
|
|
|
@ -86,7 +86,7 @@ public class HavenoHeadlessApp implements HeadlessApp {
|
|||
havenoSetup.setDisplaySecurityRecommendationHandler(key -> log.info("onDisplaySecurityRecommendationHandler"));
|
||||
havenoSetup.setWrongOSArchitectureHandler(msg -> log.error("onWrongOSArchitectureHandler. msg={}", msg));
|
||||
havenoSetup.setRejectedTxErrorMessageHandler(errorMessage -> log.warn("setRejectedTxErrorMessageHandler. errorMessage={}", errorMessage));
|
||||
havenoSetup.setShowPopupIfInvalidBtcConfigHandler(() -> log.error("onShowPopupIfInvalidBtcConfigHandler"));
|
||||
havenoSetup.setShowPopupIfInvalidXmrConfigHandler(() -> log.error("onShowPopupIfInvalidXmrConfigHandler"));
|
||||
havenoSetup.setRevolutAccountsUpdateHandler(revolutAccountList -> log.info("setRevolutAccountsUpdateHandler: revolutAccountList={}", revolutAccountList));
|
||||
havenoSetup.setOsxKeyLoggerWarningHandler(() -> log.info("setOsxKeyLoggerWarningHandler"));
|
||||
havenoSetup.setQubesOSInfoHandler(() -> log.info("setQubesOSInfoHandler"));
|
||||
|
|
|
@ -176,7 +176,7 @@ public class HavenoSetup {
|
|||
private Consumer<PrivateNotificationPayload> displayPrivateNotificationHandler;
|
||||
@Setter
|
||||
@Nullable
|
||||
private Runnable showPopupIfInvalidBtcConfigHandler;
|
||||
private Runnable showPopupIfInvalidXmrConfigHandler;
|
||||
@Setter
|
||||
@Nullable
|
||||
private Consumer<List<RevolutAccount>> revolutAccountsUpdateHandler;
|
||||
|
@ -461,7 +461,7 @@ public class HavenoSetup {
|
|||
havenoSetupListeners.forEach(HavenoSetupListener::onInitWallet);
|
||||
walletAppSetup.init(chainFileLockedExceptionHandler,
|
||||
showFirstPopupIfResyncSPVRequestedHandler,
|
||||
showPopupIfInvalidBtcConfigHandler,
|
||||
showPopupIfInvalidXmrConfigHandler,
|
||||
() -> {},
|
||||
() -> {});
|
||||
}
|
||||
|
|
|
@ -117,7 +117,7 @@ public class WalletAppSetup {
|
|||
|
||||
void init(@Nullable Consumer<String> chainFileLockedExceptionHandler,
|
||||
@Nullable Runnable showFirstPopupIfResyncSPVRequestedHandler,
|
||||
@Nullable Runnable showPopupIfInvalidBtcConfigHandler,
|
||||
@Nullable Runnable showPopupIfInvalidXmrConfigHandler,
|
||||
Runnable downloadCompleteHandler,
|
||||
Runnable walletInitializedHandler) {
|
||||
log.info("Initialize WalletAppSetup with monero-java v{}", MoneroUtils.getVersion());
|
||||
|
@ -199,8 +199,8 @@ public class WalletAppSetup {
|
|||
walletInitializedHandler.run();
|
||||
},
|
||||
exception -> {
|
||||
if (exception instanceof InvalidHostException && showPopupIfInvalidBtcConfigHandler != null) {
|
||||
showPopupIfInvalidBtcConfigHandler.run();
|
||||
if (exception instanceof InvalidHostException && showPopupIfInvalidXmrConfigHandler != null) {
|
||||
showPopupIfInvalidXmrConfigHandler.run();
|
||||
} else {
|
||||
walletServiceException.set(exception);
|
||||
}
|
||||
|
|
|
@ -236,7 +236,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
public void onAdded(Offer offer) {
|
||||
|
||||
// cancel offer if reserved funds spent
|
||||
Optional<OpenOffer> openOfferOptional = getOpenOfferById(offer.getId());
|
||||
Optional<OpenOffer> openOfferOptional = getOpenOffer(offer.getId());
|
||||
if (openOfferOptional.isPresent() && openOfferOptional.get().getState() != OpenOffer.State.RESERVED && offer.isReservedFundsSpent()) {
|
||||
log.warn("Canceling open offer because reserved funds have been spent, offerId={}, state={}", offer.getId(), openOfferOptional.get().getState());
|
||||
cancelOpenOffer(openOfferOptional.get(), null, null);
|
||||
|
@ -573,7 +573,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
|
||||
// Remove from offerbook
|
||||
public void removeOffer(Offer offer, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
Optional<OpenOffer> openOfferOptional = getOpenOfferById(offer.getId());
|
||||
Optional<OpenOffer> openOfferOptional = getOpenOffer(offer.getId());
|
||||
if (openOfferOptional.isPresent()) {
|
||||
cancelOpenOffer(openOfferOptional.get(), resultHandler, errorMessageHandler);
|
||||
} else {
|
||||
|
@ -686,7 +686,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
OpenOffer.State originalState,
|
||||
ResultHandler resultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
Optional<OpenOffer> openOfferOptional = getOpenOfferById(editedOffer.getId());
|
||||
Optional<OpenOffer> openOfferOptional = getOpenOffer(editedOffer.getId());
|
||||
|
||||
if (openOfferOptional.isPresent()) {
|
||||
OpenOffer openOffer = openOfferOptional.get();
|
||||
|
@ -750,7 +750,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
|
||||
// close open offer after key images spent
|
||||
public void closeOpenOffer(Offer offer) {
|
||||
getOpenOfferById(offer.getId()).ifPresent(openOffer -> {
|
||||
getOpenOffer(offer.getId()).ifPresent(openOffer -> {
|
||||
removeOpenOffer(openOffer);
|
||||
openOffer.setState(OpenOffer.State.CLOSED);
|
||||
xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
||||
|
@ -813,14 +813,14 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
return openOffers.getObservableList();
|
||||
}
|
||||
|
||||
public Optional<OpenOffer> getOpenOfferById(String offerId) {
|
||||
public Optional<OpenOffer> getOpenOffer(String offerId) {
|
||||
synchronized (openOffers) {
|
||||
return openOffers.stream().filter(e -> e.getId().equals(offerId)).findFirst();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasOpenOffer(String offerId) {
|
||||
return getOpenOfferById(offerId).isPresent();
|
||||
return getOpenOffer(offerId).isPresent();
|
||||
}
|
||||
|
||||
public Optional<SignedOffer> getSignedOfferById(String offerId) {
|
||||
|
@ -987,26 +987,16 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
setSplitOutputTx(openOffer, splitOutputTx);
|
||||
}
|
||||
|
||||
// if not found, create tx to split exact output
|
||||
if (splitOutputTx == null) {
|
||||
if (openOffer.getSplitOutputTxHash() != null) {
|
||||
log.warn("Split output tx unexpectedly unavailable for offer, offerId={}, split output tx={}", openOffer.getId(), openOffer.getSplitOutputTxHash());
|
||||
setSplitOutputTx(openOffer, null);
|
||||
}
|
||||
try {
|
||||
splitOrSchedule(openOffers, openOffer, amountNeeded);
|
||||
} catch (Exception e) {
|
||||
log.warn("Unable to split or schedule funds for offer {}: {}", openOffer.getId(), e.getMessage());
|
||||
openOffer.getOffer().setState(Offer.State.INVALID);
|
||||
errorMessageHandler.handleErrorMessage(e.getMessage());
|
||||
return;
|
||||
}
|
||||
} else if (!splitOutputTx.isLocked()) {
|
||||
|
||||
// otherwise sign and post offer if split output available
|
||||
signAndPostOffer(openOffer, true, resultHandler, errorMessageHandler);
|
||||
// if wallet has exact available balance, try to sign and post directly
|
||||
if (xmrWalletService.getAvailableBalance().equals(amountNeeded)) {
|
||||
signAndPostOffer(openOffer, true, resultHandler, (errorMessage) -> {
|
||||
splitOrSchedule(splitOutputTx, openOffers, openOffer, amountNeeded, resultHandler, errorMessageHandler);
|
||||
});
|
||||
return;
|
||||
} else {
|
||||
splitOrSchedule(splitOutputTx, openOffers, openOffer, amountNeeded, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
// sign and post offer if enough funds
|
||||
|
@ -1017,11 +1007,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
return;
|
||||
} else if (openOffer.getScheduledTxHashes() == null) {
|
||||
scheduleWithEarliestTxs(openOffers, openOffer);
|
||||
resultHandler.handleResult(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// handle result
|
||||
resultHandler.handleResult(null);
|
||||
} catch (Exception e) {
|
||||
if (!openOffer.isCanceled()) log.error("Error processing pending offer: {}\n", e.getMessage(), e);
|
||||
errorMessageHandler.handleErrorMessage(e.getMessage());
|
||||
|
@ -1087,13 +1076,13 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
if (output.isSpent() || output.isFrozen()) removeTxs.add(tx);
|
||||
}
|
||||
}
|
||||
if (!hasExactAmount(tx, reserveAmount, preferredSubaddressIndex)) removeTxs.add(tx);
|
||||
if (!hasExactOutput(tx, reserveAmount, preferredSubaddressIndex)) removeTxs.add(tx);
|
||||
}
|
||||
splitOutputTxs.removeAll(removeTxs);
|
||||
return splitOutputTxs;
|
||||
}
|
||||
|
||||
private boolean hasExactAmount(MoneroTxWallet tx, BigInteger amount, Integer preferredSubaddressIndex) {
|
||||
private boolean hasExactOutput(MoneroTxWallet tx, BigInteger amount, Integer preferredSubaddressIndex) {
|
||||
boolean hasExactOutput = (tx.getOutputsWallet(new MoneroOutputQuery()
|
||||
.setAccountIndex(0)
|
||||
.setSubaddressIndex(preferredSubaddressIndex)
|
||||
|
@ -1115,7 +1104,35 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
return earliestUnscheduledTx;
|
||||
}
|
||||
|
||||
private void splitOrSchedule(List<OpenOffer> openOffers, OpenOffer openOffer, BigInteger offerReserveAmount) {
|
||||
// if split tx not found and cannot reserve exact amount directly, create tx to split or reserve exact output
|
||||
private void splitOrSchedule(MoneroTxWallet splitOutputTx, List<OpenOffer> openOffers, OpenOffer openOffer, BigInteger amountNeeded, TransactionResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
if (splitOutputTx == null) {
|
||||
if (openOffer.getSplitOutputTxHash() != null) {
|
||||
log.warn("Split output tx unexpectedly unavailable for offer, offerId={}, split output tx={}", openOffer.getId(), openOffer.getSplitOutputTxHash());
|
||||
setSplitOutputTx(openOffer, null);
|
||||
}
|
||||
try {
|
||||
splitOrScheduleAux(openOffers, openOffer, amountNeeded);
|
||||
resultHandler.handleResult(null);
|
||||
return;
|
||||
} catch (Exception e) {
|
||||
log.warn("Unable to split or schedule funds for offer {}: {}", openOffer.getId(), e.getMessage());
|
||||
openOffer.getOffer().setState(Offer.State.INVALID);
|
||||
errorMessageHandler.handleErrorMessage(e.getMessage());
|
||||
return;
|
||||
}
|
||||
} else if (!splitOutputTx.isLocked()) {
|
||||
|
||||
// otherwise sign and post offer if split output available
|
||||
signAndPostOffer(openOffer, true, resultHandler, errorMessageHandler);
|
||||
return;
|
||||
} else {
|
||||
resultHandler.handleResult(null);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private void splitOrScheduleAux(List<OpenOffer> openOffers, OpenOffer openOffer, BigInteger offerReserveAmount) {
|
||||
|
||||
// handle sufficient available balance to split output
|
||||
boolean sufficientAvailableBalance = xmrWalletService.getAvailableBalance().compareTo(offerReserveAmount) >= 0;
|
||||
|
@ -1299,13 +1316,13 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
openOffer.setScheduledAmount(null);
|
||||
requestPersistence();
|
||||
|
||||
resultHandler.handleResult(transaction);
|
||||
if (!stopped) {
|
||||
startPeriodicRepublishOffersTimer();
|
||||
startPeriodicRefreshOffersTimer();
|
||||
} else {
|
||||
log.debug("We have stopped already. We ignore that placeOfferProtocol.placeOffer.onResult call.");
|
||||
}
|
||||
resultHandler.handleResult(transaction);
|
||||
},
|
||||
errorMessageHandler);
|
||||
|
||||
|
@ -1557,6 +1574,14 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
return;
|
||||
}
|
||||
|
||||
// Don't allow trade start if not connected to Monero node
|
||||
if (!Boolean.TRUE.equals(xmrConnectionService.isConnected())) {
|
||||
errorMessage = "We got a handleOfferAvailabilityRequest but we are not connected to a Monero node.";
|
||||
log.info(errorMessage);
|
||||
sendAckMessage(request.getClass(), peer, request.getPubKeyRing(), request.getOfferId(), request.getUid(), false, errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
if (stopped) {
|
||||
errorMessage = "We have stopped already. We ignore that handleOfferAvailabilityRequest call.";
|
||||
log.debug(errorMessage);
|
||||
|
@ -1575,7 +1600,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
}
|
||||
|
||||
try {
|
||||
Optional<OpenOffer> openOfferOptional = getOpenOfferById(request.offerId);
|
||||
Optional<OpenOffer> openOfferOptional = getOpenOffer(request.offerId);
|
||||
AvailabilityResult availabilityResult;
|
||||
byte[] makerSignature = null;
|
||||
if (openOfferOptional.isPresent()) {
|
||||
|
@ -1961,6 +1986,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
}
|
||||
|
||||
private boolean preventedFromPublishing(OpenOffer openOffer) {
|
||||
if (!Boolean.TRUE.equals(xmrConnectionService.isConnected())) return true;
|
||||
return openOffer.isDeactivated() || openOffer.isCanceled() || openOffer.getOffer().getOfferPayload().getArbitratorSigner() == null;
|
||||
}
|
||||
|
||||
|
@ -1983,25 +2009,27 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
if (periodicRefreshOffersTimer == null)
|
||||
periodicRefreshOffersTimer = UserThread.runPeriodically(() -> {
|
||||
if (!stopped) {
|
||||
int size = openOffers.size();
|
||||
//we clone our list as openOffers might change during our delayed call
|
||||
final ArrayList<OpenOffer> openOffersList = new ArrayList<>(openOffers.getList());
|
||||
for (int i = 0; i < size; i++) {
|
||||
// we delay to avoid reaching throttle limits
|
||||
// roughly 4 offers per second
|
||||
|
||||
long delay = 300;
|
||||
final long minDelay = (i + 1) * delay;
|
||||
final long maxDelay = (i + 2) * delay;
|
||||
final OpenOffer openOffer = openOffersList.get(i);
|
||||
UserThread.runAfterRandomDelay(() -> {
|
||||
// we need to check if in the meantime the offer has been removed
|
||||
boolean contained = false;
|
||||
synchronized (openOffers) {
|
||||
contained = openOffers.contains(openOffer);
|
||||
}
|
||||
if (contained) maybeRefreshOffer(openOffer, 0, 1);
|
||||
}, minDelay, maxDelay, TimeUnit.MILLISECONDS);
|
||||
synchronized (openOffers) {
|
||||
int size = openOffers.size();
|
||||
//we clone our list as openOffers might change during our delayed call
|
||||
final ArrayList<OpenOffer> openOffersList = new ArrayList<>(openOffers.getList());
|
||||
for (int i = 0; i < size; i++) {
|
||||
// we delay to avoid reaching throttle limits
|
||||
// roughly 4 offers per second
|
||||
|
||||
long delay = 300;
|
||||
final long minDelay = (i + 1) * delay;
|
||||
final long maxDelay = (i + 2) * delay;
|
||||
final OpenOffer openOffer = openOffersList.get(i);
|
||||
UserThread.runAfterRandomDelay(() -> {
|
||||
// we need to check if in the meantime the offer has been removed
|
||||
boolean contained = false;
|
||||
synchronized (openOffers) {
|
||||
contained = openOffers.contains(openOffer);
|
||||
}
|
||||
if (contained) maybeRefreshOffer(openOffer, 0, 1);
|
||||
}, minDelay, maxDelay, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.debug("We have stopped already. We ignore that periodicRefreshOffersTimer.run call.");
|
||||
|
|
|
@ -87,6 +87,9 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||
try {
|
||||
//if (true) throw new RuntimeException("Pretend error");
|
||||
reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, makerFee, sendAmount, securityDeposit, returnAddress, openOffer.isReserveExactAmount(), preferredSubaddressIndex);
|
||||
} catch (IllegalStateException e) {
|
||||
log.warn("Illegal state creating reserve tx, offerId={}, error={}", openOffer.getShortId(), i + 1, e.getMessage());
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.warn("Error creating reserve tx, offerId={}, attempt={}/{}, error={}", openOffer.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
|
||||
model.getXmrWalletService().handleWalletError(e, sourceConnection);
|
||||
|
|
|
@ -77,7 +77,7 @@ public class MakerSendSignOfferRequest extends Task<PlaceOfferModel> {
|
|||
offer.getOfferPayload().getReserveTxKeyImages(),
|
||||
returnAddress);
|
||||
|
||||
// send request to least used arbitrators until success
|
||||
// send request to random arbitrators until success
|
||||
sendSignOfferRequests(request, () -> {
|
||||
complete();
|
||||
}, (errorMessage) -> {
|
||||
|
|
|
@ -196,7 +196,7 @@ public final class MediationManager extends DisputeManager<MediationDisputeList>
|
|||
tradeManager.requestPersistence();
|
||||
}
|
||||
} else {
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(tradeId);
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOffer(tradeId);
|
||||
openOfferOptional.ifPresent(openOffer -> openOfferManager.closeOpenOffer(openOffer.getOffer()));
|
||||
}
|
||||
sendAckMessage(chatMessage, dispute.getAgentPubKeyRing(), true, null);
|
||||
|
|
|
@ -196,7 +196,7 @@ public final class RefundManager extends DisputeManager<RefundDisputeList> {
|
|||
tradeManager.requestPersistence();
|
||||
}
|
||||
} else {
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(tradeId);
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOffer(tradeId);
|
||||
openOfferOptional.ifPresent(openOffer -> openOfferManager.closeOpenOffer(openOffer.getOffer()));
|
||||
}
|
||||
sendAckMessage(chatMessage, dispute.getAgentPubKeyRing(), true, null);
|
||||
|
@ -205,7 +205,7 @@ public final class RefundManager extends DisputeManager<RefundDisputeList> {
|
|||
if (tradeManager.getOpenTrade(tradeId).isPresent()) {
|
||||
tradeManager.closeDisputedTrade(tradeId, Trade.DisputeState.REFUND_REQUEST_CLOSED);
|
||||
} else {
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(tradeId);
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOffer(tradeId);
|
||||
openOfferOptional.ifPresent(openOffer -> openOfferManager.closeOpenOffer(openOffer.getOffer()));
|
||||
}
|
||||
|
||||
|
|
|
@ -143,6 +143,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
private static final long DELETE_AFTER_NUM_BLOCKS = 2; // if deposit requested but not published
|
||||
private static final long EXTENDED_RPC_TIMEOUT = 600000; // 10 minutes
|
||||
private static final long DELETE_AFTER_MS = TradeProtocol.TRADE_STEP_TIMEOUT_SECONDS;
|
||||
private static final int NUM_CONFIRMATIONS_FOR_SCHEDULED_IMPORT = 10;
|
||||
protected final Object pollLock = new Object();
|
||||
protected static final Object importMultisigLock = new Object();
|
||||
private boolean pollInProgress;
|
||||
|
@ -735,12 +736,17 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
// TODO: buyer's payment sent message state property became unsynced if shut down while awaiting ack from seller. fixed in v1.0.19 so this check can be removed?
|
||||
if (isBuyer()) {
|
||||
MessageState expectedState = getPaymentSentMessageState();
|
||||
if (expectedState != null && expectedState != processModel.getPaymentSentMessageStateProperty().get()) {
|
||||
log.warn("Updating unexpected payment sent message state for {} {}, expected={}, actual={}", getClass().getSimpleName(), getId(), expectedState, processModel.getPaymentSentMessageStateProperty().get());
|
||||
processModel.getPaymentSentMessageStateProperty().set(expectedState);
|
||||
if (expectedState != null && expectedState != processModel.getPaymentSentMessageStatePropertySeller().get()) {
|
||||
log.warn("Updating unexpected payment sent message state for {} {}, expected={}, actual={}", getClass().getSimpleName(), getId(), expectedState, processModel.getPaymentSentMessageStatePropertySeller().get());
|
||||
processModel.getPaymentSentMessageStatePropertySeller().set(expectedState);
|
||||
}
|
||||
}
|
||||
|
||||
// handle confirmations
|
||||
walletHeight.addListener((observable, oldValue, newValue) -> {
|
||||
importMultisigHexIfScheduled();
|
||||
});
|
||||
|
||||
// trade is initialized
|
||||
isInitialized = true;
|
||||
|
||||
|
@ -1077,6 +1083,26 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
}
|
||||
}
|
||||
|
||||
public void scheduleImportMultisigHex() {
|
||||
processModel.setImportMultisigHexScheduled(true);
|
||||
requestPersistence();
|
||||
}
|
||||
|
||||
private void importMultisigHexIfScheduled() {
|
||||
if (!isInitialized || isShutDownStarted) return;
|
||||
if (!isDepositsConfirmed() || getMaker().getDepositTx() == null) return;
|
||||
if (walletHeight.get() - getMaker().getDepositTx().getHeight() < NUM_CONFIRMATIONS_FOR_SCHEDULED_IMPORT) return;
|
||||
ThreadUtils.execute(() -> {
|
||||
if (!isInitialized || isShutDownStarted) return;
|
||||
synchronized (getLock()) {
|
||||
if (processModel.isImportMultisigHexScheduled()) {
|
||||
processModel.setImportMultisigHexScheduled(false);
|
||||
ThreadUtils.submitToPool(() -> importMultisigHex());
|
||||
}
|
||||
}
|
||||
}, getId());
|
||||
}
|
||||
|
||||
public void importMultisigHex() {
|
||||
synchronized (walletLock) {
|
||||
synchronized (HavenoUtils.getDaemonLock()) { // lock on daemon because import calls full refresh
|
||||
|
@ -1089,10 +1115,10 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
} catch (IllegalArgumentException | IllegalStateException e) {
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.warn("Failed to import multisig hex, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
|
||||
handleWalletError(e, sourceConnection);
|
||||
doPollWallet();
|
||||
if (isPayoutPublished()) break;
|
||||
log.warn("Failed to import multisig hex, tradeId={}, attempt={}/{}, error={}", getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
|
||||
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
|
||||
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||
}
|
||||
|
@ -1141,6 +1167,9 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
if (removed) wallet.importMultisigHex(multisigHexes.toArray(new String[0]));
|
||||
if (wallet.isMultisigImportNeeded()) throw new IllegalStateException(errorMessage);
|
||||
}
|
||||
|
||||
// remove scheduled import
|
||||
processModel.setImportMultisigHexScheduled(false);
|
||||
} catch (MoneroError e) {
|
||||
|
||||
// import multisig hex individually if one is invalid
|
||||
|
@ -1604,7 +1633,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
}
|
||||
|
||||
// unreserve maker's open offer
|
||||
Optional<OpenOffer> openOffer = processModel.getOpenOfferManager().getOpenOfferById(this.getId());
|
||||
Optional<OpenOffer> openOffer = processModel.getOpenOfferManager().getOpenOffer(this.getId());
|
||||
if (this instanceof MakerTrade && openOffer.isPresent()) {
|
||||
processModel.getOpenOfferManager().unreserveOpenOffer(openOffer.get());
|
||||
}
|
||||
|
@ -1618,15 +1647,16 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
// done if wallet already deleted
|
||||
if (!walletExists()) return;
|
||||
|
||||
// move to failed trades
|
||||
processModel.getTradeManager().onMoveInvalidTradeToFailedTrades(this);
|
||||
|
||||
// set error height
|
||||
if (processModel.getTradeProtocolErrorHeight() == 0) {
|
||||
log.warn("Scheduling to remove trade if unfunded for {} {} from height {}", getClass().getSimpleName(), getId(), xmrConnectionService.getLastInfo().getHeight());
|
||||
processModel.setTradeProtocolErrorHeight(xmrConnectionService.getLastInfo().getHeight());
|
||||
processModel.setTradeProtocolErrorHeight(xmrConnectionService.getLastInfo().getHeight()); // height denotes scheduled error handling
|
||||
}
|
||||
|
||||
// move to failed trades
|
||||
processModel.getTradeManager().onMoveInvalidTradeToFailedTrades(this);
|
||||
requestPersistence();
|
||||
|
||||
// listen for deposits published to restore trade
|
||||
protocolErrorStateSubscription = EasyBind.subscribe(stateProperty(), state -> {
|
||||
if (isDepositsPublished()) {
|
||||
|
@ -1680,10 +1710,14 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
});
|
||||
}
|
||||
|
||||
public boolean isProtocolErrorHandlingScheduled() {
|
||||
return processModel.getTradeProtocolErrorHeight() > 0;
|
||||
}
|
||||
|
||||
private void restoreDepositsPublishedTrade() {
|
||||
|
||||
// close open offer
|
||||
if (this instanceof MakerTrade && processModel.getOpenOfferManager().getOpenOfferById(getId()).isPresent()) {
|
||||
if (this instanceof MakerTrade && processModel.getOpenOfferManager().getOpenOffer(getId()).isPresent()) {
|
||||
log.info("Closing open offer because {} {} was restored after protocol error", getClass().getSimpleName(), getShortId());
|
||||
processModel.getOpenOfferManager().closeOpenOffer(checkNotNull(getOffer()));
|
||||
}
|
||||
|
@ -2017,7 +2051,7 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
|
||||
public MessageState getPaymentSentMessageState() {
|
||||
if (isPaymentReceived()) return MessageState.ACKNOWLEDGED;
|
||||
if (processModel.getPaymentSentMessageStateProperty().get() == MessageState.ACKNOWLEDGED) return MessageState.ACKNOWLEDGED;
|
||||
if (processModel.getPaymentSentMessageStatePropertySeller().get() == MessageState.ACKNOWLEDGED) return MessageState.ACKNOWLEDGED;
|
||||
switch (state) {
|
||||
case BUYER_SENT_PAYMENT_SENT_MSG:
|
||||
return MessageState.SENT;
|
||||
|
@ -2345,7 +2379,12 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
return tradeAmountTransferred();
|
||||
}
|
||||
|
||||
public boolean tradeAmountTransferred() {
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private boolean tradeAmountTransferred() {
|
||||
return isPaymentReceived() || (getDisputeResult() != null && getDisputeResult().getWinner() == DisputeResult.Winner.SELLER);
|
||||
}
|
||||
|
||||
|
@ -2361,11 +2400,6 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// lazy initialization
|
||||
private ObjectProperty<BigInteger> getAmountProperty() {
|
||||
if (tradeAmountProperty == null)
|
||||
|
@ -2436,8 +2470,9 @@ public abstract class Trade extends XmrWalletBase implements Tradable, Model {
|
|||
if (!wasWalletSynced) trySyncWallet(true);
|
||||
updatePollPeriod();
|
||||
|
||||
// reprocess pending payout messages
|
||||
this.getProtocol().maybeReprocessPaymentReceivedMessage(false);
|
||||
// reprocess pending messages
|
||||
getProtocol().maybeReprocessPaymentSentMessage(false);
|
||||
getProtocol().maybeReprocessPaymentReceivedMessage(false);
|
||||
HavenoUtils.arbitrationManager.maybeReprocessDisputeClosedMessage(this, false);
|
||||
|
||||
startPolling();
|
||||
|
|
|
@ -450,8 +450,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
return;
|
||||
}
|
||||
|
||||
// skip if marked as failed
|
||||
if (failedTradesManager.getObservableList().contains(trade)) {
|
||||
// skip if failed and error handling not scheduled
|
||||
if (failedTradesManager.getObservableList().contains(trade) && !trade.isProtocolErrorHandlingScheduled()) {
|
||||
log.warn("Skipping initialization of failed trade {} {}", trade.getClass().getSimpleName(), trade.getId());
|
||||
tradesToSkip.add(trade);
|
||||
return;
|
||||
|
@ -460,8 +460,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
// initialize trade
|
||||
initPersistedTrade(trade);
|
||||
|
||||
// remove trade if protocol didn't initialize
|
||||
if (getOpenTradeByUid(trade.getUid()).isPresent() && !trade.isDepositsPublished()) {
|
||||
// record if protocol didn't initialize
|
||||
if (!trade.isDepositsPublished()) {
|
||||
uninitializedTrades.add(trade);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -556,7 +556,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
if (request.getMakerNodeAddress().equals(p2PService.getNetworkNode().getNodeAddress())) {
|
||||
|
||||
// get open offer
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOfferById(request.getOfferId());
|
||||
Optional<OpenOffer> openOfferOptional = openOfferManager.getOpenOffer(request.getOfferId());
|
||||
if (!openOfferOptional.isPresent()) return;
|
||||
OpenOffer openOffer = openOfferOptional.get();
|
||||
if (openOffer.getState() != OpenOffer.State.AVAILABLE) return;
|
||||
|
@ -747,7 +747,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
}
|
||||
|
||||
private void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
|
||||
log.info("TradeManager handling InitMultisigRequest for tradeId={}, sender={}, uid={}", request.getOfferId(), sender, request.getUid());
|
||||
log.info("TradeManager handling InitMultisigRequest for tradeId={}, sender={}, uid={}", request.getOfferId(), sender, request.getUid());
|
||||
|
||||
try {
|
||||
Validator.nonEmptyStringOf(request.getOfferId());
|
||||
|
@ -766,7 +766,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
}
|
||||
|
||||
private void handleSignContractRequest(SignContractRequest request, NodeAddress sender) {
|
||||
log.info("TradeManager handling SignContractRequest for tradeId={}, sender={}, uid={}", request.getOfferId(), sender, request.getUid());
|
||||
log.info("TradeManager handling SignContractRequest for tradeId={}, sender={}, uid={}", request.getOfferId(), sender, request.getUid());
|
||||
|
||||
try {
|
||||
Validator.nonEmptyStringOf(request.getOfferId());
|
||||
|
@ -923,8 +923,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
requestPersistence();
|
||||
}, errorMessage -> {
|
||||
log.warn("Taker error during trade initialization: " + errorMessage);
|
||||
xmrWalletService.resetAddressEntriesForOpenOffer(trade.getId()); // TODO: move to maybe remove on error
|
||||
trade.onProtocolError();
|
||||
xmrWalletService.resetAddressEntriesForOpenOffer(trade.getId()); // TODO: move this into protocol error handling
|
||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
||||
});
|
||||
|
||||
|
@ -1285,6 +1285,12 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
}
|
||||
}
|
||||
|
||||
public boolean hasFailedScheduledTrade(String offerId) {
|
||||
synchronized (failedTradesManager) {
|
||||
return failedTradesManager.getTradeById(offerId).isPresent() && failedTradesManager.getTradeById(offerId).get().isProtocolErrorHandlingScheduled();
|
||||
}
|
||||
}
|
||||
|
||||
public Optional<Trade> getOpenTradeByUid(String tradeUid) {
|
||||
synchronized (tradableList) {
|
||||
return tradableList.stream().filter(e -> e.getUid().equals(tradeUid)).findFirst();
|
||||
|
|
|
@ -43,7 +43,7 @@ public class ArbitratorProtocol extends DisputeProtocol {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void handleInitTradeRequest(InitTradeRequest message, NodeAddress peer, ErrorMessageHandler errorMessageHandler) {
|
||||
System.out.println("ArbitratorProtocol.handleInitTradeRequest()");
|
||||
log.info(TradeProtocol.LOG_HIGHLIGHT + "handleInitTradeRequest() for {} {}", trade.getClass().getSimpleName(), trade.getShortId());
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
latchTrade();
|
||||
|
@ -78,7 +78,7 @@ public class ArbitratorProtocol extends DisputeProtocol {
|
|||
}
|
||||
|
||||
public void handleDepositRequest(DepositRequest request, NodeAddress sender) {
|
||||
System.out.println("ArbitratorProtocol.handleDepositRequest() " + trade.getId());
|
||||
log.info(TradeProtocol.LOG_HIGHLIGHT + "handleDepositRequest() for {} {}", trade.getClass().getSimpleName(), trade.getShortId());
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
latchTrade();
|
||||
|
|
|
@ -60,8 +60,8 @@ public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol
|
|||
public void handleInitTradeRequest(InitTradeRequest message,
|
||||
NodeAddress peer,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
||||
ThreadUtils.execute(() -> {
|
||||
log.info(TradeProtocol.LOG_HIGHLIGHT + "handleInitTradeRequest() for {} {} from {}", trade.getClass().getSimpleName(), trade.getShortId(), peer);
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
latchTrade();
|
||||
this.errorMessageHandler = errorMessageHandler;
|
||||
|
|
|
@ -68,7 +68,7 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
|
|||
@Override
|
||||
public void onTakeOffer(TradeResultHandler tradeResultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
System.out.println(getClass().getSimpleName() + ".onTakeOffer()");
|
||||
log.info(TradeProtocol.LOG_HIGHLIGHT + "onTakerOffer for {} {}", getClass().getSimpleName(), trade.getShortId());
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
latchTrade();
|
||||
|
@ -99,7 +99,7 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
|
|||
@Override
|
||||
public void handleInitTradeRequest(InitTradeRequest message,
|
||||
NodeAddress peer) {
|
||||
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
||||
log.info(TradeProtocol.LOG_HIGHLIGHT + "handleInitTradeRequest() for {} {} from {}", trade.getClass().getSimpleName(), trade.getShortId(), peer);
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
latchTrade();
|
||||
|
|
|
@ -119,7 +119,7 @@ public class BuyerProtocol extends DisputeProtocol {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onPaymentSent(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
System.out.println("BuyerProtocol.onPaymentSent()");
|
||||
log.info(TradeProtocol.LOG_HIGHLIGHT + "BuyerProtocol.onPaymentSent() for {} {}", trade.getClass().getSimpleName(), trade.getShortId());
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
latchTrade();
|
||||
|
|
|
@ -158,12 +158,15 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||
@Getter
|
||||
@Setter
|
||||
private long tradeProtocolErrorHeight;
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean importMultisigHexScheduled;
|
||||
|
||||
// We want to indicate the user the state of the message delivery of the
|
||||
// PaymentSentMessage. As well we do an automatic re-send in case it was not ACKed yet.
|
||||
// To enable that even after restart we persist the state.
|
||||
@Setter
|
||||
private ObjectProperty<MessageState> paymentSentMessageStateProperty = new SimpleObjectProperty<>(MessageState.UNDEFINED);
|
||||
private ObjectProperty<MessageState> paymentSentMessageStatePropertySeller = new SimpleObjectProperty<>(MessageState.UNDEFINED);
|
||||
@Setter
|
||||
private ObjectProperty<MessageState> paymentSentMessageStatePropertyArbitrator = new SimpleObjectProperty<>(MessageState.UNDEFINED);
|
||||
private ObjectProperty<Boolean> paymentAccountDecryptedProperty = new SimpleObjectProperty<>(false);
|
||||
|
@ -203,11 +206,12 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||
.setPubKeyRing(pubKeyRing.toProtoMessage())
|
||||
.setUseSavingsWallet(useSavingsWallet)
|
||||
.setFundsNeededForTrade(fundsNeededForTrade)
|
||||
.setPaymentSentMessageState(paymentSentMessageStateProperty.get().name())
|
||||
.setPaymentSentMessageStateSeller(paymentSentMessageStatePropertySeller.get().name())
|
||||
.setPaymentSentMessageStateArbitrator(paymentSentMessageStatePropertyArbitrator.get().name())
|
||||
.setBuyerPayoutAmountFromMediation(buyerPayoutAmountFromMediation)
|
||||
.setSellerPayoutAmountFromMediation(sellerPayoutAmountFromMediation)
|
||||
.setTradeProtocolErrorHeight(tradeProtocolErrorHeight);
|
||||
.setTradeProtocolErrorHeight(tradeProtocolErrorHeight)
|
||||
.setImportMultisigHexScheduled(importMultisigHexScheduled);
|
||||
Optional.ofNullable(maker).ifPresent(e -> builder.setMaker((protobuf.TradePeer) maker.toProtoMessage()));
|
||||
Optional.ofNullable(taker).ifPresent(e -> builder.setTaker((protobuf.TradePeer) taker.toProtoMessage()));
|
||||
Optional.ofNullable(arbitrator).ifPresent(e -> builder.setArbitrator((protobuf.TradePeer) arbitrator.toProtoMessage()));
|
||||
|
@ -231,6 +235,7 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||
processModel.setBuyerPayoutAmountFromMediation(proto.getBuyerPayoutAmountFromMediation());
|
||||
processModel.setSellerPayoutAmountFromMediation(proto.getSellerPayoutAmountFromMediation());
|
||||
processModel.setTradeProtocolErrorHeight(proto.getTradeProtocolErrorHeight());
|
||||
processModel.setImportMultisigHexScheduled(proto.getImportMultisigHexScheduled());
|
||||
|
||||
// nullable
|
||||
processModel.setPayoutTxSignature(ProtoUtil.byteArrayOrNullFromProto(proto.getPayoutTxSignature()));
|
||||
|
@ -240,9 +245,9 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||
processModel.setTradeFeeAddress(ProtoUtil.stringOrNullFromProto(proto.getTradeFeeAddress()));
|
||||
processModel.setMultisigAddress(ProtoUtil.stringOrNullFromProto(proto.getMultisigAddress()));
|
||||
|
||||
String paymentSentMessageStateString = ProtoUtil.stringOrNullFromProto(proto.getPaymentSentMessageState());
|
||||
MessageState paymentSentMessageState = ProtoUtil.enumFromProto(MessageState.class, paymentSentMessageStateString);
|
||||
processModel.setPaymentSentMessageState(paymentSentMessageState);
|
||||
String paymentSentMessageStateSellerString = ProtoUtil.stringOrNullFromProto(proto.getPaymentSentMessageStateSeller());
|
||||
MessageState paymentSentMessageStateSeller = ProtoUtil.enumFromProto(MessageState.class, paymentSentMessageStateSellerString);
|
||||
processModel.setPaymentSentMessageStateSeller(paymentSentMessageStateSeller);
|
||||
|
||||
String paymentSentMessageStateArbitratorString = ProtoUtil.stringOrNullFromProto(proto.getPaymentSentMessageStateArbitrator());
|
||||
MessageState paymentSentMessageStateArbitrator = ProtoUtil.enumFromProto(MessageState.class, paymentSentMessageStateArbitratorString);
|
||||
|
@ -274,11 +279,11 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||
return getP2PService().getAddress();
|
||||
}
|
||||
|
||||
void setPaymentSentAckMessage(AckMessage ackMessage) {
|
||||
void setPaymentSentAckMessageSeller(AckMessage ackMessage) {
|
||||
MessageState messageState = ackMessage.isSuccess() ?
|
||||
MessageState.ACKNOWLEDGED :
|
||||
MessageState.FAILED;
|
||||
setPaymentSentMessageState(messageState);
|
||||
setPaymentSentMessageStateSeller(messageState);
|
||||
}
|
||||
|
||||
void setPaymentSentAckMessageArbitrator(AckMessage ackMessage) {
|
||||
|
@ -288,8 +293,8 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||
setPaymentSentMessageStateArbitrator(messageState);
|
||||
}
|
||||
|
||||
public void setPaymentSentMessageState(MessageState paymentSentMessageStateProperty) {
|
||||
this.paymentSentMessageStateProperty.set(paymentSentMessageStateProperty);
|
||||
public void setPaymentSentMessageStateSeller(MessageState paymentSentMessageStateProperty) {
|
||||
this.paymentSentMessageStatePropertySeller.set(paymentSentMessageStateProperty);
|
||||
if (tradeManager != null) {
|
||||
tradeManager.requestPersistence();
|
||||
}
|
||||
|
@ -302,6 +307,14 @@ public class ProcessModel implements Model, PersistablePayload {
|
|||
}
|
||||
}
|
||||
|
||||
public boolean isPaymentSentMessageAckedBySeller() {
|
||||
return paymentSentMessageStatePropertySeller.get() == MessageState.ACKNOWLEDGED;
|
||||
}
|
||||
|
||||
public boolean isPaymentSentMessageAckedByArbitrator() {
|
||||
return paymentSentMessageStatePropertyArbitrator.get() == MessageState.ACKNOWLEDGED;
|
||||
}
|
||||
|
||||
void setDepositTxSentAckMessage(AckMessage ackMessage) {
|
||||
MessageState messageState = ackMessage.isSuccess() ?
|
||||
MessageState.ACKNOWLEDGED :
|
||||
|
|
|
@ -65,7 +65,7 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
|
|||
public void handleInitTradeRequest(InitTradeRequest message,
|
||||
NodeAddress peer,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
||||
log.info(TradeProtocol.LOG_HIGHLIGHT + "handleInitTradeRequest() for {} {} from {}", trade.getClass().getSimpleName(), trade.getShortId(), peer);
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
latchTrade();
|
||||
|
|
|
@ -68,7 +68,7 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
|
|||
@Override
|
||||
public void onTakeOffer(TradeResultHandler tradeResultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
System.out.println(getClass().getSimpleName() + ".onTakeOffer()");
|
||||
log.info(TradeProtocol.LOG_HIGHLIGHT + "onTakerOffer for {} {}", getClass().getSimpleName(), trade.getShortId());
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
latchTrade();
|
||||
|
@ -99,7 +99,7 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
|
|||
@Override
|
||||
public void handleInitTradeRequest(InitTradeRequest message,
|
||||
NodeAddress peer) {
|
||||
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
||||
log.info(TradeProtocol.LOG_HIGHLIGHT + "handleInitTradeRequest() for {} {} from {}", trade.getClass().getSimpleName(), trade.getShortId(), peer);
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
latchTrade();
|
||||
|
|
|
@ -115,7 +115,7 @@ public class SellerProtocol extends DisputeProtocol {
|
|||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void onPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
log.info("SellerProtocol.onPaymentReceived()");
|
||||
log.info(TradeProtocol.LOG_HIGHLIGHT + "SellerProtocol.onPaymentReceived() for {} {}", trade.getClass().getSimpleName(), trade.getShortId());
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
latchTrade();
|
||||
|
|
|
@ -96,6 +96,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
private static final String TIMEOUT_REACHED = "Timeout reached.";
|
||||
public static final int MAX_ATTEMPTS = 5; // max attempts to create txs and other wallet functions
|
||||
public static final long REPROCESS_DELAY_MS = 5000;
|
||||
public static final String LOG_HIGHLIGHT = "\u001B[0m"; // terminal default
|
||||
|
||||
protected final ProcessModel processModel;
|
||||
protected final Trade trade;
|
||||
|
@ -106,6 +107,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
protected ErrorMessageHandler errorMessageHandler;
|
||||
|
||||
private boolean depositsConfirmedTasksCalled;
|
||||
private int reprocessPaymentSentMessageCount;
|
||||
private int reprocessPaymentReceivedMessageCount;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -124,12 +126,12 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
|
||||
protected void onTradeMessage(TradeMessage message, NodeAddress peerNodeAddress) {
|
||||
log.info("Received {} as TradeMessage from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peerNodeAddress, message.getOfferId(), message.getUid());
|
||||
ThreadUtils.execute(() -> handle(message, peerNodeAddress), trade.getId());
|
||||
handle(message, peerNodeAddress);
|
||||
}
|
||||
|
||||
protected void onMailboxMessage(TradeMessage message, NodeAddress peerNodeAddress) {
|
||||
log.info("Received {} as MailboxMessage from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peerNodeAddress, message.getOfferId(), message.getUid());
|
||||
ThreadUtils.execute(() -> handle(message, peerNodeAddress), trade.getId());
|
||||
handle(message, peerNodeAddress);
|
||||
}
|
||||
|
||||
private void handle(TradeMessage message, NodeAddress peerNodeAddress) {
|
||||
|
@ -279,6 +281,22 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
}, trade.getId());
|
||||
}
|
||||
|
||||
public void maybeReprocessPaymentSentMessage(boolean reprocessOnError) {
|
||||
if (trade.isShutDownStarted()) return;
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
|
||||
// skip if no need to reprocess
|
||||
if (trade.isBuyer() || trade.getBuyer().getPaymentSentMessage() == null || trade.getState().ordinal() >= Trade.State.BUYER_SENT_PAYMENT_SENT_MSG.ordinal()) {
|
||||
return;
|
||||
}
|
||||
|
||||
log.warn("Reprocessing payment sent message for {} {}", trade.getClass().getSimpleName(), trade.getId());
|
||||
handle(trade.getBuyer().getPaymentSentMessage(), trade.getBuyer().getPaymentSentMessage().getSenderNodeAddress(), reprocessOnError);
|
||||
}
|
||||
}, trade.getId());
|
||||
}
|
||||
|
||||
public void maybeReprocessPaymentReceivedMessage(boolean reprocessOnError) {
|
||||
if (trade.isShutDownStarted()) return;
|
||||
ThreadUtils.execute(() -> {
|
||||
|
@ -296,7 +314,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
}
|
||||
|
||||
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
|
||||
System.out.println(getClass().getSimpleName() + ".handleInitMultisigRequest() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||
log.info(LOG_HIGHLIGHT + "handleInitMultisigRequest() for " + trade.getClass().getSimpleName() + " " + trade.getShortId() + " from " + sender);
|
||||
trade.addInitProgressStep();
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
|
@ -333,7 +351,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
}
|
||||
|
||||
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
|
||||
System.out.println(getClass().getSimpleName() + ".handleSignContractRequest() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||
log.info(LOG_HIGHLIGHT + "handleSignContractRequest() for " + trade.getClass().getSimpleName() + " " + trade.getShortId() + " from " + sender);
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
|
||||
|
@ -376,7 +394,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
}
|
||||
|
||||
public void handleSignContractResponse(SignContractResponse message, NodeAddress sender) {
|
||||
System.out.println(getClass().getSimpleName() + ".handleSignContractResponse() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||
log.info(LOG_HIGHLIGHT + "handleSignContractResponse() for " + trade.getClass().getSimpleName() + " " + trade.getShortId() + " from " + sender);
|
||||
trade.addInitProgressStep();
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
|
@ -422,7 +440,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
}
|
||||
|
||||
public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
|
||||
System.out.println(getClass().getSimpleName() + ".handleDepositResponse() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||
log.info(LOG_HIGHLIGHT + "handleDepositResponse() for " + trade.getClass().getSimpleName() + " " + trade.getShortId() + " from " + sender);
|
||||
trade.addInitProgressStep();
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
|
@ -452,7 +470,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
}
|
||||
|
||||
public void handle(DepositsConfirmedMessage message, NodeAddress sender) {
|
||||
System.out.println(getClass().getSimpleName() + ".handle(DepositsConfirmedMessage) from " + sender + " for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||
log.info(LOG_HIGHLIGHT + "handle(DepositsConfirmedMessage) for " + trade.getClass().getSimpleName() + " " + trade.getShortId() + " from " + sender);
|
||||
if (!trade.isInitialized() || trade.isShutDown()) return;
|
||||
ThreadUtils.execute(() -> {
|
||||
synchronized (trade.getLock()) {
|
||||
|
@ -481,7 +499,25 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
|
||||
// received by seller and arbitrator
|
||||
protected void handle(PaymentSentMessage message, NodeAddress peer) {
|
||||
System.out.println(getClass().getSimpleName() + ".handle(PaymentSentMessage) for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||
handle(message, peer, true);
|
||||
}
|
||||
|
||||
// received by seller and arbitrator
|
||||
protected void handle(PaymentSentMessage message, NodeAddress peer, boolean reprocessOnError) {
|
||||
log.info(LOG_HIGHLIGHT + "handle(PaymentSentMessage) for " + trade.getClass().getSimpleName() + " " + trade.getShortId() + " from " + peer);
|
||||
|
||||
// validate signature
|
||||
try {
|
||||
HavenoUtils.verifyPaymentSentMessage(trade, message);
|
||||
} catch (Throwable t) {
|
||||
log.warn("Ignoring PaymentSentMessage with invalid signature for {} {}, error={}", trade.getClass().getSimpleName(), trade.getId(), t.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
// save message for reprocessing
|
||||
trade.getBuyer().setPaymentSentMessage(message);
|
||||
trade.requestPersistence();
|
||||
|
||||
if (!trade.isInitialized() || trade.isShutDown()) return;
|
||||
if (!(trade instanceof SellerTrade || trade instanceof ArbitratorTrade)) {
|
||||
log.warn("Ignoring PaymentSentMessage since not seller or arbitrator");
|
||||
|
@ -521,7 +557,19 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
handleTaskRunnerSuccess(peer, message);
|
||||
},
|
||||
(errorMessage) -> {
|
||||
handleTaskRunnerFault(peer, message, errorMessage);
|
||||
log.warn("Error processing payment sent message: " + errorMessage);
|
||||
processModel.getTradeManager().requestPersistence();
|
||||
|
||||
// schedule to reprocess message unless deleted
|
||||
if (trade.getBuyer().getPaymentSentMessage() != null) {
|
||||
UserThread.runAfter(() -> {
|
||||
reprocessPaymentSentMessageCount++;
|
||||
maybeReprocessPaymentSentMessage(reprocessOnError);
|
||||
}, trade.getReprocessDelayInSeconds(reprocessPaymentSentMessageCount));
|
||||
} else {
|
||||
handleTaskRunnerFault(peer, message, errorMessage); // otherwise send nack
|
||||
}
|
||||
unlatchTrade();
|
||||
})))
|
||||
.executeTasks(true);
|
||||
awaitTradeLatch();
|
||||
|
@ -535,7 +583,20 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
}
|
||||
|
||||
private void handle(PaymentReceivedMessage message, NodeAddress peer, boolean reprocessOnError) {
|
||||
System.out.println(getClass().getSimpleName() + ".handle(PaymentReceivedMessage) for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||
log.info(LOG_HIGHLIGHT + "handle(PaymentReceivedMessage) for " + trade.getClass().getSimpleName() + " " + trade.getShortId() + " from " + peer);
|
||||
|
||||
// validate signature
|
||||
try {
|
||||
HavenoUtils.verifyPaymentReceivedMessage(trade, message);
|
||||
} catch (Throwable t) {
|
||||
log.warn("Ignoring PaymentReceivedMessage with invalid signature for {} {}, error={}", trade.getClass().getSimpleName(), trade.getId(), t.getMessage());
|
||||
return;
|
||||
}
|
||||
|
||||
// save message for reprocessing
|
||||
trade.getSeller().setPaymentReceivedMessage(message);
|
||||
trade.requestPersistence();
|
||||
|
||||
if (!trade.isInitialized() || trade.isShutDown()) return;
|
||||
ThreadUtils.execute(() -> {
|
||||
if (!(trade instanceof BuyerTrade || trade instanceof ArbitratorTrade)) {
|
||||
|
@ -652,11 +713,12 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
// handle ack for PaymentSentMessage, which automatically re-sends if not ACKed in a certain time
|
||||
if (ackMessage.getSourceMsgClassName().equals(PaymentSentMessage.class.getSimpleName())) {
|
||||
if (trade.getTradePeer(sender) == trade.getSeller()) {
|
||||
processModel.setPaymentSentAckMessage(ackMessage);
|
||||
processModel.setPaymentSentAckMessageSeller(ackMessage);
|
||||
trade.setStateIfValidTransitionTo(Trade.State.SELLER_RECEIVED_PAYMENT_SENT_MSG);
|
||||
processModel.getTradeManager().requestPersistence();
|
||||
} else if (trade.getTradePeer(sender) == trade.getArbitrator()) {
|
||||
processModel.setPaymentSentAckMessageArbitrator(ackMessage);
|
||||
processModel.getTradeManager().requestPersistence();
|
||||
} else if (!ackMessage.isSuccess()) {
|
||||
String err = "Received AckMessage with error state for " + ackMessage.getSourceMsgClassName() + " from "+ sender + " with tradeId " + trade.getId() + " and errorMessage=" + ackMessage.getErrorMessage();
|
||||
log.warn(err);
|
||||
|
|
|
@ -170,7 +170,7 @@ public abstract class BuyerSendPaymentSentMessage extends SendMailboxMessageTask
|
|||
timer.stop();
|
||||
}
|
||||
if (listener != null) {
|
||||
processModel.getPaymentSentMessageStateProperty().removeListener(listener);
|
||||
processModel.getPaymentSentMessageStatePropertySeller().removeListener(listener);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,8 +194,8 @@ public abstract class BuyerSendPaymentSentMessage extends SendMailboxMessageTask
|
|||
|
||||
if (resendCounter == 0) {
|
||||
listener = (observable, oldValue, newValue) -> onMessageStateChange(newValue);
|
||||
processModel.getPaymentSentMessageStateProperty().addListener(listener);
|
||||
onMessageStateChange(processModel.getPaymentSentMessageStateProperty().get());
|
||||
processModel.getPaymentSentMessageStatePropertySeller().addListener(listener);
|
||||
onMessageStateChange(processModel.getPaymentSentMessageStatePropertySeller().get());
|
||||
}
|
||||
|
||||
// first re-send is after 2 minutes, then increase the delay exponentially
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package haveno.core.trade.protocol.tasks;
|
||||
|
||||
import haveno.common.taskrunner.TaskRunner;
|
||||
import haveno.core.network.MessageState;
|
||||
import haveno.core.trade.Trade;
|
||||
import haveno.core.trade.protocol.TradePeer;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
@ -59,6 +58,6 @@ public class BuyerSendPaymentSentMessageToArbitrator extends BuyerSendPaymentSen
|
|||
|
||||
@Override
|
||||
protected boolean isAckedByReceiver() {
|
||||
return trade.getProcessModel().getPaymentSentMessageStatePropertyArbitrator().get() == MessageState.ACKNOWLEDGED;
|
||||
return trade.getProcessModel().isPaymentSentMessageAckedByArbitrator();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,25 +40,25 @@ public class BuyerSendPaymentSentMessageToSeller extends BuyerSendPaymentSentMes
|
|||
|
||||
@Override
|
||||
protected void setStateSent() {
|
||||
trade.getProcessModel().setPaymentSentMessageState(MessageState.SENT);
|
||||
trade.getProcessModel().setPaymentSentMessageStateSeller(MessageState.SENT);
|
||||
super.setStateSent();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setStateArrived() {
|
||||
trade.getProcessModel().setPaymentSentMessageState(MessageState.ARRIVED);
|
||||
trade.getProcessModel().setPaymentSentMessageStateSeller(MessageState.ARRIVED);
|
||||
super.setStateArrived();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setStateStoredInMailbox() {
|
||||
trade.getProcessModel().setPaymentSentMessageState(MessageState.STORED_IN_MAILBOX);
|
||||
trade.getProcessModel().setPaymentSentMessageStateSeller(MessageState.STORED_IN_MAILBOX);
|
||||
super.setStateStoredInMailbox();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setStateFault() {
|
||||
trade.getProcessModel().setPaymentSentMessageState(MessageState.FAILED);
|
||||
trade.getProcessModel().setPaymentSentMessageStateSeller(MessageState.FAILED);
|
||||
super.setStateFault();
|
||||
}
|
||||
|
||||
|
@ -72,6 +72,6 @@ public class BuyerSendPaymentSentMessageToSeller extends BuyerSendPaymentSentMes
|
|||
|
||||
@Override
|
||||
protected boolean isAckedByReceiver() {
|
||||
return trade.getState().ordinal() >= Trade.State.SELLER_RECEIVED_PAYMENT_SENT_MSG.ordinal();
|
||||
return trade.getProcessModel().isPaymentSentMessageAckedBySeller();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,7 +87,7 @@ public class MaybeSendSignContractRequest extends TradeTask {
|
|||
Integer subaddressIndex = null;
|
||||
boolean reserveExactAmount = false;
|
||||
if (trade instanceof MakerTrade) {
|
||||
reserveExactAmount = processModel.getOpenOfferManager().getOpenOfferById(trade.getId()).get().isReserveExactAmount();
|
||||
reserveExactAmount = processModel.getOpenOfferManager().getOpenOffer(trade.getId()).get().isReserveExactAmount();
|
||||
if (reserveExactAmount) subaddressIndex = model.getXmrWalletService().getAddressEntry(trade.getId(), XmrAddressEntry.Context.OFFER_FUNDING).get().getSubaddressIndex();
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@
|
|||
package haveno.core.trade.protocol.tasks;
|
||||
|
||||
|
||||
import haveno.common.ThreadUtils;
|
||||
import haveno.common.taskrunner.TaskRunner;
|
||||
import haveno.core.trade.Trade;
|
||||
import haveno.core.trade.messages.DepositsConfirmedMessage;
|
||||
|
@ -63,17 +62,7 @@ public class ProcessDepositsConfirmedMessage extends TradeTask {
|
|||
// update multisig hex
|
||||
if (sender.getUpdatedMultisigHex() == null) {
|
||||
sender.setUpdatedMultisigHex(request.getUpdatedMultisigHex());
|
||||
|
||||
// try to import multisig hex (retry later)
|
||||
if (!trade.isPayoutPublished()) {
|
||||
ThreadUtils.submitToPool(() -> {
|
||||
try {
|
||||
trade.importMultisigHex();
|
||||
} catch (Exception e) {
|
||||
log.warn("Error importing multisig hex on deposits confirmed for trade " + trade.getId() + ": " + e.getMessage() + "\n", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
trade.scheduleImportMultisigHex();
|
||||
}
|
||||
|
||||
// persist
|
||||
|
|
|
@ -80,9 +80,6 @@ public class ProcessPaymentReceivedMessage extends TradeTask {
|
|||
return;
|
||||
}
|
||||
|
||||
// save message for reprocessing
|
||||
trade.getSeller().setPaymentReceivedMessage(message);
|
||||
|
||||
// set state
|
||||
trade.getSeller().setUpdatedMultisigHex(message.getUpdatedMultisigHex());
|
||||
trade.getBuyer().setAccountAgeWitness(message.getBuyerAccountAgeWitness());
|
||||
|
|
|
@ -48,7 +48,6 @@ public class ProcessPaymentSentMessage extends TradeTask {
|
|||
trade.getBuyer().setNodeAddress(processModel.getTempTradePeerNodeAddress());
|
||||
|
||||
// update state from message
|
||||
trade.getBuyer().setPaymentSentMessage(message);
|
||||
trade.getBuyer().setUpdatedMultisigHex(message.getUpdatedMultisigHex());
|
||||
trade.getSeller().setAccountAgeWitness(message.getSellerAccountAgeWitness());
|
||||
String counterCurrencyTxId = message.getCounterCurrencyTxId();
|
||||
|
|
|
@ -70,6 +70,9 @@ public class TakerReserveTradeFunds extends TradeTask {
|
|||
MoneroRpcConnection sourceConnection = trade.getXmrConnectionService().getConnection();
|
||||
try {
|
||||
reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, takerFee, sendAmount, securityDeposit, returnAddress, false, null);
|
||||
} catch (IllegalStateException e) {
|
||||
log.warn("Illegal state creating reserve tx, offerId={}, error={}", trade.getShortId(), i + 1, e.getMessage());
|
||||
throw e;
|
||||
} catch (Exception e) {
|
||||
log.warn("Error creating reserve tx, tradeId={}, attempt={}/{}, error={}", trade.getShortId(), i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
|
||||
trade.getXmrWalletService().handleWalletError(e, sourceConnection);
|
||||
|
|
|
@ -135,7 +135,7 @@ public class MoneroWalletRpcManager {
|
|||
|
||||
// stop process
|
||||
String pid = walletRpc.getProcess() == null ? null : String.valueOf(walletRpc.getProcess().pid());
|
||||
log.info("Stopping MoneroWalletRpc path={}, port={}, pid={}", path, port, pid);
|
||||
log.info("Stopping MoneroWalletRpc path={}, port={}, pid={}, force={}", path, port, pid, force);
|
||||
walletRpc.stopProcess(force);
|
||||
}
|
||||
|
||||
|
|
|
@ -1016,6 +1016,13 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
|
||||
public synchronized void resetAddressEntriesForOpenOffer(String offerId) {
|
||||
log.info("resetAddressEntriesForOpenOffer offerId={}", offerId);
|
||||
|
||||
// skip if failed trade is scheduled for processing // TODO: do not call this function in this case?
|
||||
if (tradeManager.hasFailedScheduledTrade(offerId)) {
|
||||
log.warn("Refusing to reset address entries because trade is scheduled for deletion with offerId={}", offerId);
|
||||
return;
|
||||
}
|
||||
|
||||
swapAddressEntryToAvailable(offerId, XmrAddressEntry.Context.OFFER_FUNDING);
|
||||
|
||||
// swap trade payout to available if applicable
|
||||
|
@ -1164,7 +1171,7 @@ public class XmrWalletService extends XmrWalletBase {
|
|||
public Stream<XmrAddressEntry> getAddressEntriesForAvailableBalanceStream() {
|
||||
Stream<XmrAddressEntry> available = getFundedAvailableAddressEntries().stream();
|
||||
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.ARBITRATOR).stream());
|
||||
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.OFFER_FUNDING).stream().filter(entry -> !tradeManager.getOpenOfferManager().getOpenOfferById(entry.getOfferId()).isPresent()));
|
||||
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.OFFER_FUNDING).stream().filter(entry -> !tradeManager.getOpenOfferManager().getOpenOffer(entry.getOfferId()).isPresent()));
|
||||
available = Stream.concat(available, getAddressEntries(XmrAddressEntry.Context.TRADE_PAYOUT).stream().filter(entry -> tradeManager.getTrade(entry.getOfferId()) == null || tradeManager.getTrade(entry.getOfferId()).isPayoutUnlocked()));
|
||||
return available.filter(addressEntry -> getBalanceForSubaddress(addressEntry.getSubaddressIndex()).compareTo(BigInteger.ZERO) > 0);
|
||||
}
|
||||
|
|
|
@ -208,7 +208,7 @@ class GrpcOffersService extends OffersImplBase {
|
|||
put(getGetOffersMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 3, SECONDS));
|
||||
put(getGetMyOffersMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getPostOfferMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getCancelOfferMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 10 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getCancelOfferMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -252,14 +252,14 @@ class GrpcTradesService extends TradesImplBase {
|
|||
.or(() -> Optional.of(CallRateMeteringInterceptor.valueOf(
|
||||
new HashMap<>() {{
|
||||
put(getGetTradeMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 1, SECONDS));
|
||||
put(getGetTradesMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 10 : 1, SECONDS));
|
||||
put(getTakeOfferMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 20 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getConfirmPaymentSentMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 10 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getConfirmPaymentReceivedMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 10 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getCompleteTradeMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 10 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getGetTradesMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 1, SECONDS));
|
||||
put(getTakeOfferMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getConfirmPaymentSentMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getConfirmPaymentReceivedMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getCompleteTradeMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 3, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getWithdrawFundsMethod().getFullMethodName(), new GrpcCallRateMeter(3, MINUTES));
|
||||
put(getGetChatMessagesMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 10 : 4, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getSendChatMessageMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 10 : 4, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getGetChatMessagesMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 4, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
put(getSendChatMessageMethod().getFullMethodName(), new GrpcCallRateMeter(Config.baseCurrencyNetwork().isTestnet() ? 30 : 4, Config.baseCurrencyNetwork().isTestnet() ? SECONDS : MINUTES));
|
||||
}}
|
||||
)));
|
||||
}
|
||||
|
|
|
@ -60,6 +60,6 @@
|
|||
</content_rating>
|
||||
|
||||
<releases>
|
||||
<release version="1.0.18" date="2025-01-19"/>
|
||||
<release version="1.0.19" date="2025-03-10"/>
|
||||
</releases>
|
||||
</component>
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
<!-- See: https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -->
|
||||
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1.0.18</string>
|
||||
<string>1.0.19</string>
|
||||
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0.18</string>
|
||||
<string>1.0.19</string>
|
||||
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>Haveno</string>
|
||||
|
|
|
@ -420,7 +420,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
|
|||
|
||||
havenoSetup.setRejectedTxErrorMessageHandler(msg -> new Popup().width(850).warning(msg).show());
|
||||
|
||||
havenoSetup.setShowPopupIfInvalidBtcConfigHandler(this::showPopupIfInvalidBtcConfig);
|
||||
havenoSetup.setShowPopupIfInvalidXmrConfigHandler(this::showPopupIfInvalidXmrConfig);
|
||||
|
||||
havenoSetup.setRevolutAccountsUpdateHandler(revolutAccountList -> {
|
||||
// We copy the array as we will mutate it later
|
||||
|
@ -536,7 +536,7 @@ public class MainViewModel implements ViewModel, HavenoSetup.HavenoSetupListener
|
|||
});
|
||||
}
|
||||
|
||||
private void showPopupIfInvalidBtcConfig() {
|
||||
private void showPopupIfInvalidXmrConfig() {
|
||||
preferences.setMoneroNodesOptionOrdinal(0);
|
||||
new Popup().warning(Res.get("settings.net.warn.invalidXmrConfig"))
|
||||
.hideCloseButton()
|
||||
|
|
|
@ -225,8 +225,8 @@ public class LockedView extends ActivatableView<VBox, Void> {
|
|||
Optional<Trade> tradeOptional = tradeManager.getOpenTrade(offerId);
|
||||
if (tradeOptional.isPresent()) {
|
||||
return Optional.of(tradeOptional.get());
|
||||
} else if (openOfferManager.getOpenOfferById(offerId).isPresent()) {
|
||||
return Optional.of(openOfferManager.getOpenOfferById(offerId).get());
|
||||
} else if (openOfferManager.getOpenOffer(offerId).isPresent()) {
|
||||
return Optional.of(openOfferManager.getOpenOffer(offerId).get());
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
|
|
@ -224,8 +224,8 @@ public class ReservedView extends ActivatableView<VBox, Void> {
|
|||
Optional<Trade> tradeOptional = tradeManager.getOpenTrade(offerId);
|
||||
if (tradeOptional.isPresent()) {
|
||||
return Optional.of(tradeOptional.get());
|
||||
} else if (openOfferManager.getOpenOfferById(offerId).isPresent()) {
|
||||
return Optional.of(openOfferManager.getOpenOfferById(offerId).get());
|
||||
} else if (openOfferManager.getOpenOffer(offerId).isPresent()) {
|
||||
return Optional.of(openOfferManager.getOpenOffer(offerId).get());
|
||||
} else {
|
||||
return Optional.<Tradable>empty();
|
||||
}
|
||||
|
|
|
@ -665,7 +665,7 @@ public abstract class MutableOfferViewModel<M extends MutableOfferDataModel> ext
|
|||
createOfferRequested = false;
|
||||
createOfferCanceled = true;
|
||||
OpenOfferManager openOfferManager = HavenoUtils.openOfferManager;
|
||||
Optional<OpenOffer> openOffer = openOfferManager.getOpenOfferById(offer.getId());
|
||||
Optional<OpenOffer> openOffer = openOfferManager.getOpenOffer(offer.getId());
|
||||
if (openOffer.isPresent()) {
|
||||
openOfferManager.cancelOpenOffer(openOffer.get(), () -> {
|
||||
UserThread.execute(() -> {
|
||||
|
|
|
@ -711,6 +711,6 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
|
|||
abstract String getCurrencyCodeFromPreferences(OfferDirection direction);
|
||||
|
||||
public OpenOffer getOpenOffer(Offer offer) {
|
||||
return openOfferManager.getOpenOfferById(offer.getId()).orElse(null);
|
||||
return openOfferManager.getOpenOffer(offer.getId()).orElse(null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -284,6 +284,7 @@ class TakeOfferDataModel extends OfferDataModel {
|
|||
// handle error
|
||||
if (errorMsg != null) {
|
||||
new Popup().warning(errorMsg).show();
|
||||
log.warn("Error taking offer " + offer.getId() + ": " + errorMsg);
|
||||
errorMessageHandler.handleErrorMessage(errorMsg);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -765,9 +765,7 @@ public abstract class Overlay<T extends Overlay<T>> {
|
|||
FormBuilder.getIconForLabel(AwesomeIcon.COPY, copyIcon, "1.1em");
|
||||
copyIcon.addEventHandler(MOUSE_CLICKED, mouseEvent -> {
|
||||
if (message != null) {
|
||||
String forClipboard = headLineLabel.getText() + System.lineSeparator() + message
|
||||
+ System.lineSeparator() + (messageHyperlinks == null ? "" : messageHyperlinks.toString());
|
||||
Utilities.copyToClipboard(forClipboard);
|
||||
Utilities.copyToClipboard(getClipboardText());
|
||||
Tooltip tp = new Tooltip(Res.get("shared.copiedToClipboard"));
|
||||
Node node = (Node) mouseEvent.getSource();
|
||||
UserThread.runAfter(() -> tp.hide(), 1);
|
||||
|
@ -1083,6 +1081,11 @@ public abstract class Overlay<T extends Overlay<T>> {
|
|||
return isDisplayed;
|
||||
}
|
||||
|
||||
public String getClipboardText() {
|
||||
return headLineLabel.getText() + System.lineSeparator() + message
|
||||
+ System.lineSeparator() + (messageHyperlinks == null ? "" : messageHyperlinks.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Popup{" +
|
||||
|
|
|
@ -342,7 +342,7 @@ public class OfferDetailsWindow extends Overlay<OfferDetailsWindow> {
|
|||
BigInteger reservedAmount = isMyOffer ? offer.getReservedAmount() : null;
|
||||
|
||||
// get offer challenge
|
||||
OpenOffer myOpenOffer = HavenoUtils.openOfferManager.getOpenOfferById(offer.getId()).orElse(null);
|
||||
OpenOffer myOpenOffer = HavenoUtils.openOfferManager.getOpenOffer(offer.getId()).orElse(null);
|
||||
String offerChallenge = myOpenOffer == null ? null : myOpenOffer.getChallenge();
|
||||
|
||||
rows = 3;
|
||||
|
|
|
@ -37,10 +37,10 @@ import java.io.ByteArrayInputStream;
|
|||
public class QRCodeWindow extends Overlay<QRCodeWindow> {
|
||||
private static final Logger log = LoggerFactory.getLogger(QRCodeWindow.class);
|
||||
private final ImageView qrCodeImageView;
|
||||
private final String bitcoinURI;
|
||||
private final String moneroUri;
|
||||
|
||||
public QRCodeWindow(String bitcoinURI) {
|
||||
this.bitcoinURI = bitcoinURI;
|
||||
this.moneroUri = bitcoinURI;
|
||||
final byte[] imageBytes = QRCode
|
||||
.from(bitcoinURI)
|
||||
.withSize(300, 300)
|
||||
|
@ -70,7 +70,7 @@ public class QRCodeWindow extends Overlay<QRCodeWindow> {
|
|||
GridPane.setHalignment(qrCodeImageView, HPos.CENTER);
|
||||
gridPane.getChildren().add(qrCodeImageView);
|
||||
|
||||
String request = bitcoinURI.replace("%20", " ").replace("?", "\n?").replace("&", "\n&");
|
||||
String request = moneroUri.replace("%20", " ").replace("?", "\n?").replace("&", "\n&");
|
||||
Label infoLabel = new AutoTooltipLabel(Res.get("qRCodeWindow.request", request));
|
||||
infoLabel.setMouseTransparent(true);
|
||||
infoLabel.setWrapText(true);
|
||||
|
@ -87,4 +87,8 @@ public class QRCodeWindow extends Overlay<QRCodeWindow> {
|
|||
applyStyles();
|
||||
display();
|
||||
}
|
||||
|
||||
public String getClipboardText() {
|
||||
return moneroUri;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -100,7 +100,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
private final ObjectProperty<BuyerState> buyerState = new SimpleObjectProperty<>();
|
||||
private final ObjectProperty<SellerState> sellerState = new SimpleObjectProperty<>();
|
||||
@Getter
|
||||
private final ObjectProperty<MessageState> messageStateProperty = new SimpleObjectProperty<>(MessageState.UNDEFINED);
|
||||
private final ObjectProperty<MessageState> paymentSentMessageStateProperty = new SimpleObjectProperty<>(MessageState.UNDEFINED);
|
||||
private Subscription tradeStateSubscription;
|
||||
private Subscription paymentAccountDecryptedSubscription;
|
||||
private Subscription payoutStateSubscription;
|
||||
|
@ -186,7 +186,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
|
||||
if (messageStateSubscription != null) {
|
||||
messageStateSubscription.unsubscribe();
|
||||
messageStateProperty.set(MessageState.UNDEFINED);
|
||||
paymentSentMessageStateProperty.set(MessageState.UNDEFINED);
|
||||
}
|
||||
|
||||
if (selectedItem != null) {
|
||||
|
@ -200,7 +200,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
payoutStateSubscription = EasyBind.subscribe(trade.payoutStateProperty(), state -> {
|
||||
onPayoutStateChanged(state);
|
||||
});
|
||||
messageStateSubscription = EasyBind.subscribe(trade.getProcessModel().getPaymentSentMessageStateProperty(), this::onMessageStateChanged);
|
||||
messageStateSubscription = EasyBind.subscribe(trade.getProcessModel().getPaymentSentMessageStatePropertySeller(), this::onPaymentSentMessageStateChanged);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -215,8 +215,8 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
});
|
||||
}
|
||||
|
||||
private void onMessageStateChanged(MessageState messageState) {
|
||||
messageStateProperty.set(messageState);
|
||||
private void onPaymentSentMessageStateChanged(MessageState messageState) {
|
||||
paymentSentMessageStateProperty.set(messageState);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
|
@ -52,7 +52,7 @@ public class BuyerStep3View extends TradeStepView {
|
|||
public void activate() {
|
||||
super.activate();
|
||||
|
||||
model.getMessageStateProperty().addListener(messageStateChangeListener);
|
||||
model.getPaymentSentMessageStateProperty().addListener(messageStateChangeListener);
|
||||
|
||||
updateMessageStateInfo();
|
||||
}
|
||||
|
@ -60,7 +60,7 @@ public class BuyerStep3View extends TradeStepView {
|
|||
public void deactivate() {
|
||||
super.deactivate();
|
||||
|
||||
model.getMessageStateProperty().removeListener(messageStateChangeListener);
|
||||
model.getPaymentSentMessageStateProperty().removeListener(messageStateChangeListener);
|
||||
}
|
||||
|
||||
|
||||
|
@ -87,7 +87,7 @@ public class BuyerStep3View extends TradeStepView {
|
|||
}
|
||||
|
||||
private void updateMessageStateInfo() {
|
||||
MessageState messageState = model.getMessageStateProperty().get();
|
||||
MessageState messageState = model.getPaymentSentMessageStateProperty().get();
|
||||
textFieldWithIcon.setText(Res.get("message.state." + messageState.name()));
|
||||
Label iconLabel = textFieldWithIcon.getIconLabel();
|
||||
switch (messageState) {
|
||||
|
|
|
@ -270,7 +270,7 @@ Then follow these instructions: https://github.com/haveno-dex/haveno/blob/master
|
|||
|
||||
<b>Set the mandatory minimum version for trading (optional)</b>
|
||||
|
||||
If applicable, update the mandatory minimum version for trading, by entering `ctrl + f` to open the Filter window, enter a private key with developer privileges, and enter the minimum version (e.g. 1.0.18) in the field labeled "Min. version required for trading".
|
||||
If applicable, update the mandatory minimum version for trading, by entering `ctrl + f` to open the Filter window, enter a private key with developer privileges, and enter the minimum version (e.g. 1.0.19) in the field labeled "Min. version required for trading".
|
||||
|
||||
<b>Send update alert</b>
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ As per the project's authors, `netlayer` is _"essentially a wrapper around the o
|
|||
easy use and convenient integration into Kotlin/Java projects"_.
|
||||
|
||||
Similarly, `tor-binary` is _"[the] Tor binary packaged in a way that can be used for java projects"_. The project
|
||||
unpacks the tor browser binaries to extract and repackage the tor binaries themselves.
|
||||
unpacks the Tor Browser binaries to extract and repackage the tor binaries themselves.
|
||||
|
||||
Therefore, upgrading tor in Haveno comes down to upgrading these two artefacts.
|
||||
|
||||
|
@ -22,8 +22,8 @@ Therefore, upgrading tor in Haveno comes down to upgrading these two artefacts.
|
|||
|
||||
- Find out which tor version Haveno currently uses
|
||||
- Find out the current `netlayer` version (see `netlayerVersion` in `haveno/build.gradle`)
|
||||
- Find that release on the project's [releases page][3]
|
||||
- The release description says which tor version it includes
|
||||
- Find that tag on the project's [Tags page][3]
|
||||
- The tag description says which tor version it includes
|
||||
- Find out the latest available tor release
|
||||
- See the [official tor changelog][4]
|
||||
|
||||
|
@ -32,23 +32,24 @@ Therefore, upgrading tor in Haveno comes down to upgrading these two artefacts.
|
|||
|
||||
During this update, you will need to keep track of:
|
||||
|
||||
- the new tor browser version
|
||||
- the new Tor Browser version
|
||||
- the new tor binary version
|
||||
|
||||
Create a PR for the `master` branch of [tor-binary][2] with the following changes:
|
||||
|
||||
- Decide which tor browser version contains the desired tor binary version
|
||||
- The official tor browser releases are here: https://dist.torproject.org/torbrowser/
|
||||
- For the chosen tor browser version, get the list of SHA256 checksums and its signature
|
||||
- For example, for tor browser 10.0.12:
|
||||
- https://dist.torproject.org/torbrowser/10.0.12/sha256sums-signed-build.txt
|
||||
- https://dist.torproject.org/torbrowser/10.0.12/sha256sums-signed-build.txt.asc
|
||||
- Decide which Tor Browser version contains the desired tor binary version
|
||||
- The latest official Tor Browser releases are here: https://dist.torproject.org/torbrowser/
|
||||
- All official Tor Browser releases are here: https://archive.torproject.org/tor-package-archive/torbrowser/
|
||||
- For the chosen Tor Browser version, get the list of SHA256 checksums and its signature
|
||||
- For example, for Tor Browser 14.0.7:
|
||||
- https://dist.torproject.org/torbrowser/14.0.7/sha256sums-signed-build.txt
|
||||
- https://dist.torproject.org/torbrowser/14.0.7/sha256sums-signed-build.txt.asc
|
||||
- Verify the signature of the checksums list (see [instructions][5])
|
||||
- Update the `tor-binary` checksums
|
||||
- For each file present in `tor-binary/tor-binary-resources/checksums`:
|
||||
- Rename the file such that it reflects the new tor browser version, but preserves the naming scheme
|
||||
- Rename the file such that it reflects the new Tor Browser version, but preserves the naming scheme
|
||||
- Update the contents of the file with the corresponding SHA256 checksum from the list
|
||||
- Update `torbrowser.version` to the new tor browser version in:
|
||||
- Update `torbrowser.version` to the new Tor Browser version in:
|
||||
- `tor-binary/build.xml`
|
||||
- `tor-binary/pom.xml`
|
||||
- Update `version` to the new tor binary version in:
|
||||
|
@ -72,7 +73,7 @@ next.
|
|||
|
||||
### 3. Update `netlayer`
|
||||
|
||||
Create a PR for the `externaltor` branch of [netlayer][1] with the following changes:
|
||||
Create a PR for the `master` branch of [netlayer][1] with the following changes:
|
||||
|
||||
- In `netlayer/pom.xml`:
|
||||
- Update `tor-binary.version` to the `tor-binary` commit ID from above (e.g. `a4b868a`)
|
||||
|
@ -82,13 +83,13 @@ Create a PR for the `externaltor` branch of [netlayer][1] with the following cha
|
|||
- `netlayer/tor.external/pom.xml`
|
||||
- `netlayer/tor.native/pom.xml`
|
||||
|
||||
Once the PR is merged, make a note of the commit ID in the `externaltor` branch (for example `32779ac`), as it will be
|
||||
Once the PR is merged, make a note of the commit ID in the `master` branch (for example `32779ac`), as it will be
|
||||
needed next.
|
||||
|
||||
Create a tag for the new artefact version, having the new tor binary version as description, for example:
|
||||
|
||||
```
|
||||
# Create tag locally for new netlayer release, on the externaltor branch
|
||||
# Create tag locally for new netlayer release, on the master branch
|
||||
git tag -s 0.7.0 -m"tor 0.4.5.6"
|
||||
|
||||
# Push it to netlayer repo
|
||||
|
@ -105,8 +106,6 @@ Create a Haveno PR with the following changes:
|
|||
- See instructions in `haveno/gradle/witness/gradle-witness.gradle`
|
||||
|
||||
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
Thanks to freimair, JesusMcCloud, mrosseel, sschuberth and cedricwalter for their work on the original
|
||||
|
@ -115,8 +114,8 @@ Thanks to freimair, JesusMcCloud, mrosseel, sschuberth and cedricwalter for thei
|
|||
|
||||
|
||||
|
||||
[1]: https://github.com/bisq-network/netlayer "netlayer"
|
||||
[2]: https://github.com/bisq-network/tor-binary "tor-binary"
|
||||
[3]: https://github.com/bisq-network/netlayer/releases "netlayer releases"
|
||||
[1]: https://github.com/haveno-dex/netlayer "netlayer"
|
||||
[2]: https://github.com/haveno-dex/tor-binary "tor-binary"
|
||||
[3]: https://github.com/haveno-dex/netlayer/tags "netlayer Tags"
|
||||
[4]: https://gitweb.torproject.org/tor.git/plain/ChangeLog "tor changelog"
|
||||
[5]: https://support.torproject.org/tbb/how-to-verify-signature/ "verify tor signature"
|
||||
|
|
|
@ -205,44 +205,44 @@
|
|||
<sha256 value="d4ea711258c783e0accb8feaaa204f0414781551b0159fa17e5f1869200f96f7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.haveno-dex.netlayer" name="tor" version="700ec94f0f">
|
||||
<artifact name="tor-700ec94f0f.jar">
|
||||
<sha256 value="63eafc2bf43ae2556d5a24f23b5ddfe371c1ac01b7bc595d6fdb7eadcba37d52" origin="Generated by Gradle"/>
|
||||
<component group="com.github.haveno-dex.netlayer" name="tor" version="d4f9d0ce24">
|
||||
<artifact name="tor-d4f9d0ce24.jar">
|
||||
<sha256 value="58c7df7cab675ec3db4f80b456282d49e8d983676af7f053c7514fc5dfb83cee" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.haveno-dex.netlayer" name="tor.external" version="700ec94f0f">
|
||||
<artifact name="tor.external-700ec94f0f.jar">
|
||||
<sha256 value="01b506ec84697a08abfad2ab1928a8ba8bda36f588f05fbb14e5b9cacdbd0e3d" origin="Generated by Gradle"/>
|
||||
<component group="com.github.haveno-dex.netlayer" name="tor.external" version="d4f9d0ce24">
|
||||
<artifact name="tor.external-d4f9d0ce24.jar">
|
||||
<sha256 value="1231429367e83f7c7536cf6febd4df2e95fec81a776f199791d5b9790aa1d25e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.haveno-dex.netlayer" name="tor.native" version="700ec94f0f">
|
||||
<artifact name="tor.native-700ec94f0f.jar">
|
||||
<sha256 value="51886b73f9c41d1d16ab7995af9846f67d6f7942def0c182b56a2a801ae301d3" origin="Generated by Gradle"/>
|
||||
<component group="com.github.haveno-dex.netlayer" name="tor.native" version="d4f9d0ce24">
|
||||
<artifact name="tor.native-d4f9d0ce24.jar">
|
||||
<sha256 value="5d83e8fb429e0fe79df55b220731567b9290f84bd2f1ee03b2a194284290e052" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.haveno-dex.tor-binary" name="tor-binary-geoip" version="6bdcb0303ae92f4b7bd7102706467f10fcac4d9e">
|
||||
<artifact name="tor-binary-geoip-6bdcb0303ae92f4b7bd7102706467f10fcac4d9e.jar">
|
||||
<sha256 value="7bd0fa5e818825d8f0c87a52cc0c468a06fd7850c825b9b36ba82d7a3d9f2fa5" origin="Generated by Gradle"/>
|
||||
<component group="com.github.haveno-dex.tor-binary" name="tor-binary-geoip" version="462c44c157cbf0b7574b7ab14d0bf231df770a63">
|
||||
<artifact name="tor-binary-geoip-462c44c157cbf0b7574b7ab14d0bf231df770a63.jar">
|
||||
<sha256 value="af2c7a517d45c7640f11c28fa5987201ddcb4ea139ef4d56fbcff17336a83289" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.haveno-dex.tor-binary" name="tor-binary-linux32" version="6bdcb0303ae92f4b7bd7102706467f10fcac4d9e">
|
||||
<artifact name="tor-binary-linux32-6bdcb0303ae92f4b7bd7102706467f10fcac4d9e.jar">
|
||||
<sha256 value="4b4b3d822d8ad88f874450385751d0b26b41e2724d0d9b703acd9e4b73b3ba5d" origin="Generated by Gradle"/>
|
||||
<component group="com.github.haveno-dex.tor-binary" name="tor-binary-linux32" version="462c44c157cbf0b7574b7ab14d0bf231df770a63">
|
||||
<artifact name="tor-binary-linux32-462c44c157cbf0b7574b7ab14d0bf231df770a63.jar">
|
||||
<sha256 value="6ee4cb8f9cd33bb255fa0e9991e9be49338f574999270434a4499b4554e5e714" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.haveno-dex.tor-binary" name="tor-binary-linux64" version="6bdcb0303ae92f4b7bd7102706467f10fcac4d9e">
|
||||
<artifact name="tor-binary-linux64-6bdcb0303ae92f4b7bd7102706467f10fcac4d9e.jar">
|
||||
<sha256 value="512b6d52217feed0efe84c1f43888fc8a8ba32a8998486c32e233a031dddbd94" origin="Generated by Gradle"/>
|
||||
<component group="com.github.haveno-dex.tor-binary" name="tor-binary-linux64" version="462c44c157cbf0b7574b7ab14d0bf231df770a63">
|
||||
<artifact name="tor-binary-linux64-462c44c157cbf0b7574b7ab14d0bf231df770a63.jar">
|
||||
<sha256 value="7da86fc024976ab46b6e92f4f5c0046b7df1f800cd09f297232698b1a6b7f961" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.haveno-dex.tor-binary" name="tor-binary-macos" version="6bdcb0303ae92f4b7bd7102706467f10fcac4d9e">
|
||||
<artifact name="tor-binary-macos-6bdcb0303ae92f4b7bd7102706467f10fcac4d9e.jar">
|
||||
<sha256 value="f68f9c6a3f56d084bd9426ff3834bcc90b07a4489357b04ef8151465a73c8783" origin="Generated by Gradle"/>
|
||||
<component group="com.github.haveno-dex.tor-binary" name="tor-binary-macos" version="462c44c157cbf0b7574b7ab14d0bf231df770a63">
|
||||
<artifact name="tor-binary-macos-462c44c157cbf0b7574b7ab14d0bf231df770a63.jar">
|
||||
<sha256 value="f1cd51f9acf7562190fc1ea327325116fcd006f98e16eaef279554f20cca86c3" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.haveno-dex.tor-binary" name="tor-binary-windows" version="6bdcb0303ae92f4b7bd7102706467f10fcac4d9e">
|
||||
<artifact name="tor-binary-windows-6bdcb0303ae92f4b7bd7102706467f10fcac4d9e.jar">
|
||||
<sha256 value="94f7090f34dc6f12cdd5e5a247f7f0c4aeb40693258fd5365db41a2b8a71b197" origin="Generated by Gradle"/>
|
||||
<component group="com.github.haveno-dex.tor-binary" name="tor-binary-windows" version="462c44c157cbf0b7574b7ab14d0bf231df770a63">
|
||||
<artifact name="tor-binary-windows-462c44c157cbf0b7574b7ab14d0bf231df770a63.jar">
|
||||
<sha256 value="39ae9a91803dae23fc5573f46f136b4ee973babfd72c3495963f1c430d20f162" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="com.github.johnrengelman" name="shadow" version="8.1.1">
|
||||
|
|
|
@ -1568,7 +1568,7 @@ message ProcessModel {
|
|||
bytes payout_tx_signature = 4;
|
||||
bool use_savings_wallet = 5;
|
||||
int64 funds_needed_for_trade = 6;
|
||||
string payment_sent_message_state = 7;
|
||||
string payment_sent_message_state_seller = 7;
|
||||
string payment_sent_message_state_arbitrator = 8;
|
||||
bytes maker_signature = 9;
|
||||
TradePeer maker = 10;
|
||||
|
@ -1581,6 +1581,7 @@ message ProcessModel {
|
|||
int64 seller_payout_amount_from_mediation = 17;
|
||||
int64 trade_protocol_error_height = 18;
|
||||
string trade_fee_address = 19;
|
||||
bool import_multisig_hex_scheduled = 20;
|
||||
}
|
||||
|
||||
message TradePeer {
|
||||
|
|
|
@ -41,7 +41,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
@Slf4j
|
||||
public class SeedNodeMain extends ExecutableForAppWithP2p {
|
||||
private static final long CHECK_CONNECTION_LOSS_SEC = 30;
|
||||
private static final String VERSION = "1.0.18";
|
||||
private static final String VERSION = "1.0.19";
|
||||
private SeedNode seedNode;
|
||||
private Timer checkConnectionLossTime;
|
||||
|
||||
|
|
Loading…
Reference in a new issue