trade initialization defers opening and syncing wallet if idling

This commit is contained in:
woodser 2022-11-28 10:08:55 +00:00
parent 7ca3b8cee1
commit a408b0e7ae
2 changed files with 62 additions and 59 deletions

View file

@ -517,6 +517,7 @@ public class XmrWalletService {
if (wallet != null) {
try {
wallet.sync(); // blocking
wallet.startSyncing(connectionsService.getDefaultRefreshPeriodMs()); // start syncing wallet in background
connectionsService.doneDownload(); // TODO: using this to signify both daemon and wallet synced, refactor sync handling of both
saveWallet(wallet);
} catch (Exception e) {
@ -548,11 +549,11 @@ public class XmrWalletService {
// must be connected to daemon
MoneroRpcConnection connection = connectionsService.getConnection();
if (connection == null || !Boolean.TRUE.equals(connection.isConnected())) throw new RuntimeException("Must be connected to daemon before creating wallet");
config.setServer(connection);
// create wallet
try {
log.info("Creating wallet " + config.getPath());
MoneroRpcConnection daemonConnection = config.getServer();
if (!sync) config.setServer(null);
walletRpc.createWallet(config);
if (sync) {
@ -560,7 +561,7 @@ public class XmrWalletService {
walletRpc.startSyncing(connectionsService.getDefaultRefreshPeriodMs());
log.info("Done starting background sync for wallet " + config.getPath());
} else {
walletRpc.setDaemonConnection(daemonConnection);
walletRpc.setDaemonConnection(connection);
}
log.info("Done creating wallet " + config.getPath());
return walletRpc;
@ -578,21 +579,9 @@ public class XmrWalletService {
// open wallet
try {
// open wallet
log.info("Opening wallet " + config.getPath());
walletRpc.openWallet(config);
// sync wallet
log.info("Syncing wallet " + config.getPath());
walletRpc.sync();
log.info("Done syncing wallet " + config.getPath());
// start syncing wallet in background
new Thread(() -> {
log.info("Starting background syncing for wallet " + config.getPath());
walletRpc.startSyncing(connectionsService.getDefaultRefreshPeriodMs());
log.info("Done starting background sync for wallet " + config.getPath());
}).start();
walletRpc.setDaemonConnection(connectionsService.getConnection());
return walletRpc;
} catch (Exception e) {
e.printStackTrace();

View file

@ -75,6 +75,7 @@ import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Collectors;
import lombok.Getter;
@ -373,7 +374,7 @@ public abstract class Trade implements Tradable, Model {
transient final private StringProperty errorMessageProperty = new SimpleStringProperty();
transient private Subscription tradePhaseSubscription;
transient private Subscription payoutStateSubscription;
transient private TaskLooper tradeTxsLooper;
transient private TaskLooper txPollLooper;
transient private Long walletRefreshPeriod;
transient private Long syncNormalStartTime;
private static final long IDLE_SYNC_PERIOD_MS = 3600000; // 1 hour
@ -585,10 +586,7 @@ public abstract class Trade implements Tradable, Model {
// handle trade state events
tradePhaseSubscription = EasyBind.subscribe(phaseProperty, newValue -> {
if (!isInitialized) return;
if (isDepositPublished() && !isPayoutUnlocked()) {
updateWalletRefreshPeriod();
listenToTradeTxs();
}
if (isDepositPublished() && !isPayoutUnlocked()) updateWalletRefreshPeriod();
if (isCompleted()) {
UserThread.execute(() -> {
if (tradePhaseSubscription != null) {
@ -620,9 +618,9 @@ public abstract class Trade implements Tradable, Model {
if (newValue == Trade.PayoutState.PAYOUT_UNLOCKED) {
log.info("Payout unlocked for {} {}, deleting multisig wallet", getClass().getSimpleName(), getId()); // TODO: retain backup for some time?
deleteWallet();
if (tradeTxsLooper != null) {
tradeTxsLooper.stop();
tradeTxsLooper = null;
if (txPollLooper != null) {
txPollLooper.stop();
txPollLooper = null;
}
UserThread.execute(() -> {
if (payoutStateSubscription != null) {
@ -637,8 +635,7 @@ public abstract class Trade implements Tradable, Model {
// start listening to trade wallet
if (isDepositPublished()) {
updateWalletRefreshPeriod();
listenToTradeTxs();
updateSyncing();
// allow state notifications to process before returning
CountDownLatch latch = new CountDownLatch(1);
@ -914,10 +911,15 @@ public abstract class Trade implements Tradable, Model {
log.warn("Cannot sync multisig wallet because it doesn't exist for {}, {}", getClass().getSimpleName(), getId());
return;
}
if (getWallet().getDaemonConnection() == null) {
log.warn("Cannot sync multisig wallet because it's not connected to a Monero daemon for {}, {}", getClass().getSimpleName(), getId());
return;
}
log.info("Syncing wallet for {} {}", getClass().getSimpleName(), getId());
getWallet().sync();
pollWallet();
log.info("Done syncing wallet for {} {}", getClass().getSimpleName(), getId());
updateWalletRefreshPeriod();
}
public void syncWalletNormallyForMs(long syncNormalDuration) {
@ -939,9 +941,9 @@ public abstract class Trade implements Tradable, Model {
public void shutDown() {
isInitialized = false;
if (tradeTxsLooper != null) {
tradeTxsLooper.stop();
tradeTxsLooper = null;
if (txPollLooper != null) {
txPollLooper.stop();
txPollLooper = null;
}
if (tradePhaseSubscription != null) tradePhaseSubscription.unsubscribe();
if (payoutStateSubscription != null) payoutStateSubscription.unsubscribe();
@ -1397,14 +1399,44 @@ public abstract class Trade implements Tradable, Model {
return tradeVolumeProperty;
}
private void listenToTradeTxs() {
if (tradeTxsLooper != null) return;
log.info("Listening for payout tx for {} {}", getClass().getSimpleName(), getId());
private void setDaemonConnection(MoneroRpcConnection connection) {
if (getWallet() == null) return;
log.info("Setting daemon connection for trade wallet {}: {}: ", getId() , connection == null ? null : connection.getUri());
if (getWallet() != null) getWallet().setDaemonConnection(connection);
updateSyncing();
}
// poll wallet for tx state
pollWallet();
tradeTxsLooper = new TaskLooper(() -> { pollWallet(); });
tradeTxsLooper.start(walletRefreshPeriod);
private void updateSyncing() {
if (!isIdling()) syncWallet();
else {
long startSyncingInMs = ThreadLocalRandom.current().nextLong(0, getWalletRefreshPeriod()); // random time to start syncing
UserThread.runAfter(() -> {
if (isInitialized) syncWallet();
}, startSyncingInMs / 1000l);
}
}
private void updateWalletRefreshPeriod() {
setWalletRefreshPeriod(getWalletRefreshPeriod());
}
private void setWalletRefreshPeriod(long walletRefreshPeriod) {
if (this.walletRefreshPeriod != null && this.walletRefreshPeriod == walletRefreshPeriod) return;
log.info("Setting wallet refresh rate for {} {} to {}", getClass().getSimpleName(), getId(), walletRefreshPeriod);
this.walletRefreshPeriod = walletRefreshPeriod;
getWallet().startSyncing(getWalletRefreshPeriod()); // TODO (monero-project): wallet rpc waits until last sync period finishes before starting new sync period
if (txPollLooper != null) {
txPollLooper.stop();
txPollLooper = null;
}
startPolling();
}
private void startPolling() {
if (txPollLooper != null) return;
log.info("Listening for payout tx for {} {}", getClass().getSimpleName(), getId());
txPollLooper = new TaskLooper(() -> { pollWallet(); });
txPollLooper.start(walletRefreshPeriod);
}
private void pollWallet() {
@ -1462,32 +1494,14 @@ public abstract class Trade implements Tradable, Model {
}
}
private void setDaemonConnection(MoneroRpcConnection connection) {
if (getWallet() == null) return;
log.info("Setting daemon connection for trade wallet {}: {}: ", getId() , connection == null ? null : connection.getUri());
getWallet().setDaemonConnection(connection);
updateWalletRefreshPeriod();
}
private void updateWalletRefreshPeriod() {
setWalletRefreshPeriod(getWalletRefreshPeriod());
}
private void setWalletRefreshPeriod(long walletRefreshPeriod) {
if (this.walletRefreshPeriod != null && this.walletRefreshPeriod == walletRefreshPeriod) return;
log.info("Setting wallet refresh rate for {} {} to {}", getClass().getSimpleName(), getId(), walletRefreshPeriod);
this.walletRefreshPeriod = walletRefreshPeriod;
getWallet().startSyncing(getWalletRefreshPeriod()); // TODO (monero-project): wallet rpc waits until last sync period finishes before starting new sync period
if (tradeTxsLooper != null) {
tradeTxsLooper.stop();
tradeTxsLooper = null;
listenToTradeTxs();
}
}
private long getWalletRefreshPeriod() {
if (this instanceof ArbitratorTrade && isDepositConfirmed()) return IDLE_SYNC_PERIOD_MS; // slow arbitrator trade wallet after deposits confirm
return xmrWalletService.getConnectionsService().getDefaultRefreshPeriodMs(); // else sync at default rate
if (isIdling()) return IDLE_SYNC_PERIOD_MS;
return xmrWalletService.getConnectionsService().getDefaultRefreshPeriodMs();
}
private boolean isIdling() {
return this instanceof ArbitratorTrade && isDepositConfirmed(); // arbitrator idles after deposits confirm
}
private void setStateDepositsPublished() {