stability improvements

synchronize timeout timer
fix concurrent modification exceptions
use wallet lock when saving multisig wallets
This commit is contained in:
woodser 2022-12-09 13:22:03 +00:00
parent 9fda20f88b
commit 3314eac881
8 changed files with 42 additions and 19 deletions

View File

@ -106,7 +106,7 @@ public class CoreOffersService {
}
Offer getOffer(String id) {
return offerBookService.getOffers().stream()
return new ArrayList<>(offerBookService.getOffers()).stream()
.filter(o -> o.getId().equals(id))
.filter(o -> !o.isMyOffer(keyRing))
.filter(o -> {
@ -120,7 +120,7 @@ public class CoreOffersService {
}
Offer getMyOffer(String id) {
return openOfferManager.getObservableList().stream()
return new ArrayList<>(openOfferManager.getObservableList()).stream()
.map(OpenOffer::getOffer)
.filter(o -> o.getId().equals(id))
.filter(o -> o.isMyOffer(keyRing))
@ -129,7 +129,7 @@ public class CoreOffersService {
}
List<Offer> getOffers(String direction, String currencyCode) {
List<Offer> offers = offerBookService.getOffers().stream()
List<Offer> offers = new ArrayList<>(offerBookService.getOffers()).stream()
.filter(o -> !o.isMyOffer(keyRing))
.filter(o -> offerMatchesDirectionAndCurrency(o, direction, currencyCode))
.filter(o -> {
@ -145,7 +145,7 @@ public class CoreOffersService {
List<Offer> getMyOffers(String direction, String currencyCode) {
// get my open offers
List<Offer> offers = openOfferManager.getObservableList().stream()
List<Offer> offers = new ArrayList<>(openOfferManager.getObservableList()).stream()
.map(OpenOffer::getOffer)
.filter(o -> o.isMyOffer(keyRing))
.filter(o -> offerMatchesDirectionAndCurrency(o, direction, currencyCode))

View File

@ -166,6 +166,10 @@ public class XmrWalletService {
return wallet;
}
public void saveWallet() {
saveWallet(getWallet());
}
public boolean isWalletReady() {
try {
return getWallet() != null;
@ -227,7 +231,21 @@ public class XmrWalletService {
}
}
public void saveWallet(MoneroWallet wallet) {
public void saveMultisigWallet(String tradeId) {
log.info("{}.saveMultisigWallet({})", getClass().getSimpleName(), tradeId);
initWalletLock(tradeId);
synchronized (walletLocks.get(tradeId)) {
String walletName = MONERO_MULTISIG_WALLET_PREFIX + tradeId;
if (!walletExists(walletName)) {
log.warn("Multisig wallet for trade {} does not exist");
return;
}
if (!multisigWallets.containsKey(tradeId)) throw new RuntimeException("Multisig wallet to save was not previously opened for trade " + tradeId);
saveWallet(multisigWallets.get(tradeId));
}
}
private void saveWallet(MoneroWallet wallet) {
wallet.save();
backupWallet(wallet.getPath());
}

View File

@ -610,11 +610,11 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
}
public Optional<OpenOffer> getOpenOfferById(String offerId) {
return openOffers.getObservableList().stream().filter(e -> e.getId().equals(offerId)).findFirst();
return new ArrayList<>(openOffers.getObservableList()).stream().filter(e -> e.getId().equals(offerId)).findFirst();
}
public Optional<SignedOffer> getSignedOfferById(String offerId) {
return signedOffers.getObservableList().stream().filter(e -> e.getOfferId().equals(offerId)).findFirst();
return new ArrayList<>(signedOffers.getObservableList()).stream().filter(e -> e.getOfferId().equals(offerId)).findFirst();
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@ -931,7 +931,7 @@ public abstract class Trade implements Tradable, Model {
}
public void saveWallet() {
xmrWalletService.saveWallet(getWallet());
xmrWalletService.saveMultisigWallet(getId());
}
public void deleteWallet() {

View File

@ -54,7 +54,7 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
@Override
public void onTakeOffer(TradeResultHandler tradeResultHandler,
ErrorMessageHandler errorMessageHandler) {
System.out.println(getClass().getCanonicalName() + ".onTakeOffer()");
System.out.println(getClass().getSimpleName() + ".onTakeOffer()");
new Thread(() -> {
synchronized (trade) {
latchTrade();

View File

@ -84,6 +84,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
protected final Trade trade;
protected CountDownLatch tradeLatch; // to synchronize on trade
private Timer timeoutTimer;
private Object timeoutTimerLock = new Object();
protected TradeResultHandler tradeResultHandler;
protected ErrorMessageHandler errorMessageHandler;
@ -357,7 +358,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), response);
processModel.setTradeMessage(response);
expect(anyState(Trade.State.SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST, Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS, Trade.State.DEPOSIT_TXS_SEEN_IN_NETWORK)
expect(anyState(Trade.State.SENT_PUBLISH_DEPOSIT_TX_REQUEST, Trade.State.SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST, Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS, Trade.State.DEPOSIT_TXS_SEEN_IN_NETWORK)
.with(response)
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
.setup(tasks(
@ -600,16 +601,20 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
///////////////////////////////////////////////////////////////////////////////////////////
protected synchronized void startTimeout(long timeoutSec) {
stopTimeout();
timeoutTimer = UserThread.runAfter(() -> {
handleError("Timeout reached. Protocol did not complete in " + timeoutSec + " sec. TradeID=" + trade.getId() + ", state=" + trade.stateProperty().get());
}, timeoutSec);
synchronized (timeoutTimerLock) {
stopTimeout();
timeoutTimer = UserThread.runAfter(() -> {
handleError("Timeout reached. Protocol did not complete in " + timeoutSec + " sec. TradeID=" + trade.getId() + ", state=" + trade.stateProperty().get());
}, timeoutSec);
}
}
protected synchronized void stopTimeout() {
if (timeoutTimer != null) {
timeoutTimer.stop();
timeoutTimer = null;
synchronized (timeoutTimerLock) {
if (timeoutTimer != null) {
timeoutTimer.stop();
timeoutTimer = null;
}
}
}

View File

@ -135,7 +135,7 @@ public class MaybeSendSignContractRequest extends TradeTask {
private void completeAux() {
trade.setState(State.CONTRACT_SIGNATURE_REQUESTED);
processModel.getTradeManager().requestPersistence();
processModel.getXmrWalletService().saveWallet(processModel.getXmrWalletService().getWallet());
processModel.getXmrWalletService().saveWallet();
complete();
}
}

View File

@ -122,7 +122,7 @@ public class ProcessInitMultisigRequest extends TradeTask {
log.info("Importing exchanged multisig hex for trade {}", trade.getId());
MoneroMultisigInitResult result = multisigWallet.exchangeMultisigKeys(Arrays.asList(peers[0].getExchangedMultisigHex(), peers[1].getExchangedMultisigHex()), xmrWalletService.getWalletPassword());
processModel.setMultisigAddress(result.getAddress());
processModel.getProvider().getXmrWalletService().saveWallet(multisigWallet); // save multisig wallet once it's created
processModel.getProvider().getXmrWalletService().saveMultisigWallet(trade.getId()); // save multisig wallet once it's created
trade.setStateIfValidTransitionTo(Trade.State.MULTISIG_COMPLETED);
}