show daemon sync progress on startup then sync wallet

This commit is contained in:
woodser 2023-11-24 13:33:55 -05:00
parent 846b278b5d
commit d094997666
17 changed files with 119 additions and 86 deletions

View file

@ -2,6 +2,7 @@ package haveno.core.api;
import haveno.common.UserThread; import haveno.common.UserThread;
import haveno.common.app.DevEnv; import haveno.common.app.DevEnv;
import haveno.common.config.BaseCurrencyNetwork;
import haveno.common.config.Config; import haveno.common.config.Config;
import haveno.core.trade.HavenoUtils; import haveno.core.trade.HavenoUtils;
import haveno.core.user.Preferences; import haveno.core.user.Preferences;
@ -69,6 +70,7 @@ public final class CoreMoneroConnectionsService {
private MoneroDaemonRpc daemon; private MoneroDaemonRpc daemon;
@Getter @Getter
private MoneroDaemonInfo lastInfo; private MoneroDaemonInfo lastInfo;
private Long syncStartHeight = null;
private TaskLooper daemonPollLooper; private TaskLooper daemonPollLooper;
private boolean isShutDownStarted; private boolean isShutDownStarted;
private List<MoneroConnectionManagerListener> listeners = new ArrayList<>(); private List<MoneroConnectionManagerListener> listeners = new ArrayList<>();
@ -299,17 +301,12 @@ public final class CoreMoneroConnectionsService {
return downloadPercentageProperty().get() == 1d; return downloadPercentageProperty().get() == 1d;
} }
/** // ------------------------------- HELPERS --------------------------------
* Signals that both the daemon and wallet have synced.
* private void doneDownload() {
* TODO: separate daemon and wallet download/done listeners
*/
public void doneDownload() {
downloadListener.doneDownload(); downloadListener.doneDownload();
} }
// ------------------------------- HELPERS --------------------------------
private boolean isConnectionLocal(MoneroRpcConnection connection) { private boolean isConnectionLocal(MoneroRpcConnection connection) {
return connection != null && HavenoUtils.isLocalHost(connection.getUri()); return connection != null && HavenoUtils.isLocalHost(connection.getUri());
} }
@ -555,11 +552,26 @@ public final class CoreMoneroConnectionsService {
synchronized (lock) { synchronized (lock) {
if (isShutDownStarted) return; if (isShutDownStarted) return;
try { try {
// poll daemon
log.debug("Polling daemon info"); log.debug("Polling daemon info");
if (daemon == null) throw new RuntimeException("No daemon connection"); if (daemon == null) throw new RuntimeException("No daemon connection");
lastInfo = daemon.getInfo(); lastInfo = daemon.getInfo();
// set chain height
chainHeight.set(lastInfo.getHeight()); chainHeight.set(lastInfo.getHeight());
// update sync progress
boolean isTestnet = Config.baseCurrencyNetwork() == BaseCurrencyNetwork.XMR_LOCAL;
if (lastInfo.isSynchronized() || isTestnet) doneDownload(); // TODO: skipping synchronized check for testnet because tests cannot sync 3rd local node, see "Can manage Monero daemon connections"
else if (lastInfo.isBusySyncing()) {
long targetHeight = lastInfo.getTargetHeight();
long blocksLeft = targetHeight - lastInfo.getHeight();
if (syncStartHeight == null) syncStartHeight = lastInfo.getHeight();
double percent = ((double) Math.max(1, lastInfo.getHeight() - syncStartHeight) / (double) (targetHeight - syncStartHeight)) * 100d; // grant at least 1 block to show progress
downloadListener.progress(percent, blocksLeft, null);
}
// set peer connections // set peer connections
// TODO: peers often uknown due to restricted RPC call, skipping call to get peer connections // TODO: peers often uknown due to restricted RPC call, skipping call to get peer connections
// try { // try {

View file

@ -19,6 +19,7 @@ package haveno.core.app;
import haveno.core.api.CoreMoneroConnectionsService; import haveno.core.api.CoreMoneroConnectionsService;
import haveno.core.api.CoreNotificationService; import haveno.core.api.CoreNotificationService;
import haveno.core.xmr.wallet.XmrWalletService;
import haveno.network.p2p.BootstrapListener; import haveno.network.p2p.BootstrapListener;
import haveno.network.p2p.P2PService; import haveno.network.p2p.P2PService;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
@ -47,11 +48,13 @@ public class AppStartupState {
private final BooleanProperty applicationFullyInitialized = new SimpleBooleanProperty(); private final BooleanProperty applicationFullyInitialized = new SimpleBooleanProperty();
private final BooleanProperty updatedDataReceived = new SimpleBooleanProperty(); private final BooleanProperty updatedDataReceived = new SimpleBooleanProperty();
private final BooleanProperty isBlockDownloadComplete = new SimpleBooleanProperty(); private final BooleanProperty isBlockDownloadComplete = new SimpleBooleanProperty();
private final BooleanProperty isWalletSynced = new SimpleBooleanProperty();
private final BooleanProperty hasSufficientPeersForBroadcast = new SimpleBooleanProperty(); private final BooleanProperty hasSufficientPeersForBroadcast = new SimpleBooleanProperty();
@Inject @Inject
public AppStartupState(CoreNotificationService notificationService, public AppStartupState(CoreNotificationService notificationService,
CoreMoneroConnectionsService connectionsService, CoreMoneroConnectionsService connectionsService,
XmrWalletService xmrWalletService,
P2PService p2PService) { P2PService p2PService) {
p2PService.addP2PServiceListener(new BootstrapListener() { p2PService.addP2PServiceListener(new BootstrapListener() {
@ -66,6 +69,11 @@ public class AppStartupState {
isBlockDownloadComplete.set(true); isBlockDownloadComplete.set(true);
}); });
xmrWalletService.downloadPercentageProperty().addListener((observable, oldValue, newValue) -> {
if (xmrWalletService.isWalletSynced())
isWalletSynced.set(true);
});
connectionsService.numPeersProperty().addListener((observable, oldValue, newValue) -> { connectionsService.numPeersProperty().addListener((observable, oldValue, newValue) -> {
if (connectionsService.hasSufficientPeersForBroadcast()) if (connectionsService.hasSufficientPeersForBroadcast())
hasSufficientPeersForBroadcast.set(true); hasSufficientPeersForBroadcast.set(true);
@ -73,14 +81,15 @@ public class AppStartupState {
p2pNetworkAndWalletInitialized = EasyBind.combine(updatedDataReceived, p2pNetworkAndWalletInitialized = EasyBind.combine(updatedDataReceived,
isBlockDownloadComplete, isBlockDownloadComplete,
isWalletSynced,
hasSufficientPeersForBroadcast, // TODO: consider sufficient number of peers? hasSufficientPeersForBroadcast, // TODO: consider sufficient number of peers?
allDomainServicesInitialized, allDomainServicesInitialized,
(a, b, c, d) -> { (a, b, c, d, e) -> {
log.info("Combined initialized state = {} = updatedDataReceived={} && isBlockDownloadComplete={} && hasSufficientPeersForBroadcast={} && allDomainServicesInitialized={}", (a && b && c && d), updatedDataReceived.get(), isBlockDownloadComplete.get(), hasSufficientPeersForBroadcast.get(), allDomainServicesInitialized.get()); log.info("Combined initialized state = {} = updatedDataReceived={} && isBlockDownloadComplete={} && isWalletSynced={} && hasSufficientPeersForBroadcast={} && allDomainServicesInitialized={}", (a && b && c && d && e), updatedDataReceived.get(), isBlockDownloadComplete.get(), isWalletSynced.get(), hasSufficientPeersForBroadcast.get(), allDomainServicesInitialized.get());
if (a && b) { if (a && b && c) {
walletAndNetworkReady.set(true); walletAndNetworkReady.set(true);
} }
return a && d; // app fully initialized before daemon connection and wallet by default // TODO: rename variable return a && e; // app fully initialized before daemon connection and wallet by default
}); });
p2pNetworkAndWalletInitialized.subscribe((observable, oldValue, newValue) -> { p2pNetworkAndWalletInitialized.subscribe((observable, oldValue, newValue) -> {
if (newValue) { if (newValue) {
@ -136,6 +145,10 @@ public class AppStartupState {
return isBlockDownloadComplete.get(); return isBlockDownloadComplete.get();
} }
public boolean isWalletSynced() {
return isWalletSynced.get();
}
public ReadOnlyBooleanProperty isBlockDownloadCompleteProperty() { public ReadOnlyBooleanProperty isBlockDownloadCompleteProperty() {
return isBlockDownloadComplete; return isBlockDownloadComplete;
} }

View file

@ -438,7 +438,7 @@ public class HavenoSetup {
revolutAccountsUpdateHandler, revolutAccountsUpdateHandler,
amazonGiftCardAccountsUpdateHandler); amazonGiftCardAccountsUpdateHandler);
if (walletsSetup.downloadPercentageProperty().get() == 1) { // TODO: update for XMR if (xmrWalletService.downloadPercentageProperty().get() == 1) {
checkForLockedUpFunds(); checkForLockedUpFunds();
} }

View file

@ -31,6 +31,7 @@ import haveno.core.xmr.exceptions.InvalidHostException;
import haveno.core.xmr.exceptions.RejectedTxException; import haveno.core.xmr.exceptions.RejectedTxException;
import haveno.core.xmr.setup.WalletsSetup; import haveno.core.xmr.setup.WalletsSetup;
import haveno.core.xmr.wallet.WalletsManager; import haveno.core.xmr.wallet.WalletsManager;
import haveno.core.xmr.wallet.XmrWalletService;
import javafx.beans.property.DoubleProperty; import javafx.beans.property.DoubleProperty;
import javafx.beans.property.ObjectProperty; import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleDoubleProperty; import javafx.beans.property.SimpleDoubleProperty;
@ -39,9 +40,8 @@ import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty; import javafx.beans.property.StringProperty;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import monero.common.MoneroUtils;
import org.bitcoinj.core.RejectMessage; import org.bitcoinj.core.RejectMessage;
import org.bitcoinj.core.VersionMessage;
import org.bitcoinj.store.BlockStoreException; import org.bitcoinj.store.BlockStoreException;
import org.bitcoinj.store.ChainFileLockedException; import org.bitcoinj.store.ChainFileLockedException;
import org.fxmisc.easybind.EasyBind; import org.fxmisc.easybind.EasyBind;
@ -61,6 +61,7 @@ public class WalletAppSetup {
private final WalletsManager walletsManager; private final WalletsManager walletsManager;
private final WalletsSetup walletsSetup; private final WalletsSetup walletsSetup;
private final CoreMoneroConnectionsService connectionService; private final CoreMoneroConnectionsService connectionService;
private final XmrWalletService xmrWalletService;
private final Config config; private final Config config;
private final Preferences preferences; private final Preferences preferences;
@ -85,12 +86,14 @@ public class WalletAppSetup {
WalletsManager walletsManager, WalletsManager walletsManager,
WalletsSetup walletsSetup, WalletsSetup walletsSetup,
CoreMoneroConnectionsService connectionService, CoreMoneroConnectionsService connectionService,
XmrWalletService xmrWalletService,
Config config, Config config,
Preferences preferences) { Preferences preferences) {
this.coreContext = coreContext; this.coreContext = coreContext;
this.walletsManager = walletsManager; this.walletsManager = walletsManager;
this.walletsSetup = walletsSetup; this.walletsSetup = walletsSetup;
this.connectionService = connectionService; this.connectionService = connectionService;
this.xmrWalletService = xmrWalletService;
this.config = config; this.config = config;
this.preferences = preferences; this.preferences = preferences;
this.useTorForXmr.set(preferences.getUseTorForXmr()); this.useTorForXmr.set(preferences.getUseTorForXmr());
@ -101,18 +104,21 @@ public class WalletAppSetup {
@Nullable Runnable showPopupIfInvalidBtcConfigHandler, @Nullable Runnable showPopupIfInvalidBtcConfigHandler,
Runnable downloadCompleteHandler, Runnable downloadCompleteHandler,
Runnable walletInitializedHandler) { Runnable walletInitializedHandler) {
log.info("Initialize WalletAppSetup with BitcoinJ version {} and hash of BitcoinJ commit {}", log.info("Initialize WalletAppSetup with monero-java version {}", MoneroUtils.getVersion());
VersionMessage.BITCOINJ_VERSION, "2a80db4");
ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>(); ObjectProperty<Throwable> walletServiceException = new SimpleObjectProperty<>();
xmrInfoBinding = EasyBind.combine(connectionService.downloadPercentageProperty(), // TODO (woodser): update to XMR xmrInfoBinding = EasyBind.combine(connectionService.downloadPercentageProperty(),
connectionService.chainHeightProperty(), connectionService.chainHeightProperty(),
xmrWalletService.downloadPercentageProperty(),
xmrWalletService.walletHeightProperty(),
walletServiceException, walletServiceException,
getWalletServiceErrorMsg(), getWalletServiceErrorMsg(),
(downloadPercentage, chainHeight, exception, errorMsg) -> { (chainDownloadPercentage, chainHeight, walletDownloadPercentage, walletHeight, exception, errorMsg) -> {
String result; String result;
if (exception == null && errorMsg == null) { if (exception == null && errorMsg == null) {
double percentage = (double) downloadPercentage;
// TODO: update for daemon and wallet sync progress
double percentage = (double) chainDownloadPercentage;
xmrSyncProgress.set(percentage); xmrSyncProgress.set(percentage);
Long bestChainHeight = chainHeight == null ? null : (Long) chainHeight; Long bestChainHeight = chainHeight == null ? null : (Long) chainHeight;
String chainHeightAsString = bestChainHeight != null && bestChainHeight > 0 ? String chainHeightAsString = bestChainHeight != null && bestChainHeight > 0 ?

View file

@ -120,8 +120,10 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
private final KeyRing keyRing; private final KeyRing keyRing;
private final User user; private final User user;
private final P2PService p2PService; private final P2PService p2PService;
@Getter
private final CoreMoneroConnectionsService connectionsService; private final CoreMoneroConnectionsService connectionsService;
private final BtcWalletService btcWalletService; private final BtcWalletService btcWalletService;
@Getter
private final XmrWalletService xmrWalletService; private final XmrWalletService xmrWalletService;
private final TradeWalletService tradeWalletService; private final TradeWalletService tradeWalletService;
private final OfferBookService offerBookService; private final OfferBookService offerBookService;

View file

@ -31,6 +31,7 @@ import haveno.core.trade.Trade;
import haveno.core.trade.TradeManager; import haveno.core.trade.TradeManager;
import haveno.core.trade.protocol.TradeProtocol; import haveno.core.trade.protocol.TradeProtocol;
import haveno.core.trade.protocol.TradeProtocol.MailboxMessageComparator; import haveno.core.trade.protocol.TradeProtocol.MailboxMessageComparator;
import haveno.core.xmr.wallet.XmrWalletService;
import haveno.network.p2p.AckMessage; import haveno.network.p2p.AckMessage;
import haveno.network.p2p.AckMessageSourceType; import haveno.network.p2p.AckMessageSourceType;
import haveno.network.p2p.DecryptedMessageWithPubKey; import haveno.network.p2p.DecryptedMessageWithPubKey;
@ -53,6 +54,7 @@ public abstract class SupportManager {
protected final P2PService p2PService; protected final P2PService p2PService;
protected final TradeManager tradeManager; protected final TradeManager tradeManager;
protected final CoreMoneroConnectionsService connectionService; protected final CoreMoneroConnectionsService connectionService;
protected final XmrWalletService xmrWalletService;
protected final CoreNotificationService notificationService; protected final CoreNotificationService notificationService;
protected final Map<String, Timer> delayMsgMap = new HashMap<>(); protected final Map<String, Timer> delayMsgMap = new HashMap<>();
private final Object lock = new Object(); private final Object lock = new Object();
@ -68,10 +70,12 @@ public abstract class SupportManager {
public SupportManager(P2PService p2PService, public SupportManager(P2PService p2PService,
CoreMoneroConnectionsService connectionService, CoreMoneroConnectionsService connectionService,
XmrWalletService xmrWalletService,
CoreNotificationService notificationService, CoreNotificationService notificationService,
TradeManager tradeManager) { TradeManager tradeManager) {
this.p2PService = p2PService; this.p2PService = p2PService;
this.connectionService = connectionService; this.connectionService = connectionService;
this.xmrWalletService = xmrWalletService;
this.mailboxMessageService = p2PService.getMailboxMessageService(); this.mailboxMessageService = p2PService.getMailboxMessageService();
this.notificationService = notificationService; this.notificationService = notificationService;
this.tradeManager = tradeManager; this.tradeManager = tradeManager;
@ -333,7 +337,8 @@ public abstract class SupportManager {
return allServicesInitialized && return allServicesInitialized &&
p2PService.isBootstrapped() && p2PService.isBootstrapped() &&
connectionService.isDownloadComplete() && connectionService.isDownloadComplete() &&
connectionService.hasSufficientPeersForBroadcast(); connectionService.hasSufficientPeersForBroadcast() &&
xmrWalletService.isWalletSynced();
} }

View file

@ -114,7 +114,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
DisputeListService<T> disputeListService, DisputeListService<T> disputeListService,
Config config, Config config,
PriceFeedService priceFeedService) { PriceFeedService priceFeedService) {
super(p2PService, connectionService, notificationService, tradeManager); super(p2PService, connectionService, xmrWalletService, notificationService, tradeManager);
this.tradeWalletService = tradeWalletService; this.tradeWalletService = tradeWalletService;
this.xmrWalletService = xmrWalletService; this.xmrWalletService = xmrWalletService;
@ -257,6 +257,11 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
tryApplyMessages(); tryApplyMessages();
}); });
xmrWalletService.downloadPercentageProperty().addListener((observable, oldValue, newValue) -> {
if (xmrWalletService.isWalletSynced())
tryApplyMessages();
});
connectionService.numPeersProperty().addListener((observable, oldValue, newValue) -> { connectionService.numPeersProperty().addListener((observable, oldValue, newValue) -> {
if (connectionService.hasSufficientPeersForBroadcast()) if (connectionService.hasSufficientPeersForBroadcast())
tryApplyMessages(); tryApplyMessages();

View file

@ -28,6 +28,7 @@ import haveno.core.support.messages.ChatMessage;
import haveno.core.support.messages.SupportMessage; import haveno.core.support.messages.SupportMessage;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.TradeManager; import haveno.core.trade.TradeManager;
import haveno.core.xmr.wallet.XmrWalletService;
import haveno.network.p2p.AckMessageSourceType; import haveno.network.p2p.AckMessageSourceType;
import haveno.network.p2p.NodeAddress; import haveno.network.p2p.NodeAddress;
import haveno.network.p2p.P2PService; import haveno.network.p2p.P2PService;
@ -53,10 +54,11 @@ public class TraderChatManager extends SupportManager {
@Inject @Inject
public TraderChatManager(P2PService p2PService, public TraderChatManager(P2PService p2PService,
CoreMoneroConnectionsService connectionService, CoreMoneroConnectionsService connectionService,
XmrWalletService xmrWalletService,
CoreNotificationService notificationService, CoreNotificationService notificationService,
TradeManager tradeManager, TradeManager tradeManager,
PubKeyRingProvider pubKeyRingProvider) { PubKeyRingProvider pubKeyRingProvider) {
super(p2PService, connectionService, notificationService, tradeManager); super(p2PService, connectionService, xmrWalletService, notificationService, tradeManager);
this.pubKeyRingProvider = pubKeyRingProvider; this.pubKeyRingProvider = pubKeyRingProvider;
} }

View file

@ -86,7 +86,7 @@ public class Balances {
} }
private void updateBalances() { private void updateBalances() {
if (!xmrWalletService.isWalletReady()) return; if (!xmrWalletService.isWalletAvailable()) return;
try { try {
updateAvailableBalance(); updateAvailableBalance();
updatePendingBalance(); updatePendingBalance();
@ -94,7 +94,7 @@ public class Balances {
updateReservedTradeBalance(); updateReservedTradeBalance();
updateReservedBalance(); updateReservedBalance();
} catch (Exception e) { } catch (Exception e) {
if (xmrWalletService.isWalletReady()) throw e; // ignore exception if wallet isn't ready if (xmrWalletService.isWalletAvailable()) throw e; // ignore exception if wallet isn't ready
} }
} }

View file

@ -10,7 +10,7 @@ import java.util.Date;
public class DownloadListener { public class DownloadListener {
private final DoubleProperty percentage = new SimpleDoubleProperty(-1); private final DoubleProperty percentage = new SimpleDoubleProperty(-1);
public void progress(double percentage, int blocksLeft, Date date) { public void progress(double percentage, long blocksLeft, Date date) {
UserThread.execute(() -> this.percentage.set(percentage / 100d)); UserThread.execute(() -> this.percentage.set(percentage / 100d));
} }

View file

@ -32,7 +32,6 @@ import org.bitcoinj.core.Context;
import org.bitcoinj.core.NetworkParameters; import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.core.PeerAddress; import org.bitcoinj.core.PeerAddress;
import org.bitcoinj.core.PeerGroup; import org.bitcoinj.core.PeerGroup;
import org.bitcoinj.core.listeners.DownloadProgressTracker;
import org.bitcoinj.crypto.KeyCrypter; import org.bitcoinj.crypto.KeyCrypter;
import org.bitcoinj.net.discovery.PeerDiscovery; import org.bitcoinj.net.discovery.PeerDiscovery;
import org.bitcoinj.script.Script; import org.bitcoinj.script.Script;
@ -92,7 +91,6 @@ public class WalletConfig extends AbstractIdleService {
protected volatile File vBtcWalletFile; protected volatile File vBtcWalletFile;
protected PeerAddress[] peerAddresses; protected PeerAddress[] peerAddresses;
protected DownloadListener downloadListener;
protected InputStream checkpoints; protected InputStream checkpoints;
protected String userAgent, version; protected String userAgent, version;
@Nullable @Nullable
@ -169,15 +167,6 @@ public class WalletConfig extends AbstractIdleService {
return setPeerNodes(new PeerAddress(params, localHost, params.getPort())); return setPeerNodes(new PeerAddress(params, localHost, params.getPort()));
} }
/**
* If you want to learn about the sync process, you can provide a listener here. For instance, a
* {@link DownloadProgressTracker} is a good choice.
*/
public WalletConfig setDownloadListener(DownloadListener listener) {
this.downloadListener = listener;
return this;
}
/** /**
* If set, the file is expected to contain a checkpoints file calculated with BuildCheckpoints. It makes initial * If set, the file is expected to contain a checkpoints file calculated with BuildCheckpoints. It makes initial
* block sync faster for new users - please refer to the documentation on the bitcoinj website * block sync faster for new users - please refer to the documentation on the bitcoinj website

View file

@ -42,13 +42,7 @@ import haveno.core.xmr.nodes.XmrNodesSetupPreferences;
import haveno.network.Socks5MultiDiscovery; import haveno.network.Socks5MultiDiscovery;
import haveno.network.Socks5ProxyProvider; import haveno.network.Socks5ProxyProvider;
import javafx.beans.property.BooleanProperty; import javafx.beans.property.BooleanProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyIntegerProperty;
import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleLongProperty;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -107,9 +101,6 @@ public class WalletsSetup {
private final NetworkParameters params; private final NetworkParameters params;
private final File walletDir; private final File walletDir;
private final int socks5DiscoverMode; private final int socks5DiscoverMode;
private final IntegerProperty numPeers = new SimpleIntegerProperty(0);
private final LongProperty chainHeight = new SimpleLongProperty(0);
private final DownloadListener downloadListener = new DownloadListener();
private final List<Runnable> setupTaskHandlers = new ArrayList<>(); private final List<Runnable> setupTaskHandlers = new ArrayList<>();
private final List<Runnable> setupCompletedHandlers = new ArrayList<>(); private final List<Runnable> setupCompletedHandlers = new ArrayList<>();
public final BooleanProperty shutDownComplete = new SimpleBooleanProperty(); public final BooleanProperty shutDownComplete = new SimpleBooleanProperty();
@ -172,12 +163,12 @@ public class WalletsSetup {
backupWallets(); backupWallets();
final Socks5Proxy socks5Proxy = socks5ProxyProvider.getSocks5Proxy(); final Socks5Proxy socks5Proxy = socks5ProxyProvider.getSocks5Proxy();
log.info("Socks5Proxy for bitcoinj: socks5Proxy=" + socks5Proxy); log.info("Using Socks5Proxy: " + socks5Proxy);
walletConfig = new WalletConfig(params, walletDir, "haveno") { walletConfig = new WalletConfig(params, walletDir, "haveno") {
@Override @Override
protected void onSetupCompleted() { protected void onSetupCompleted() {
//We are here in the btcj thread Thread[ STARTING,5,main]
super.onSetupCompleted(); super.onSetupCompleted();
// run external startup handlers // run external startup handlers
@ -244,8 +235,6 @@ public class WalletsSetup {
} }
} }
walletConfig.setDownloadListener(downloadListener);
// If seed is non-null it means we are restoring from backup. // If seed is non-null it means we are restoring from backup.
if (seed != null) { if (seed != null) {
walletConfig.restoreWalletFromSeed(seed); walletConfig.restoreWalletFromSeed(seed);
@ -430,26 +419,6 @@ public class WalletsSetup {
return walletConfig; return walletConfig;
} }
public ReadOnlyIntegerProperty numPeersProperty() {
return numPeers;
}
public LongProperty chainHeightProperty() {
return chainHeight;
}
public ReadOnlyDoubleProperty downloadPercentageProperty() {
return downloadListener.percentageProperty();
}
public boolean isDownloadComplete() {
return downloadPercentageProperty().get() == 1d;
}
public boolean isChainHeightSyncedWithinTolerance() {
throw new RuntimeException("WalletsSetup.isChainHeightSyncedWithinTolerance() not implemented for BTC");
}
public Set<Address> getAddressesByContext(@SuppressWarnings("SameParameterValue") AddressEntry.Context context) { public Set<Address> getAddressesByContext(@SuppressWarnings("SameParameterValue") AddressEntry.Context context) {
return addressEntryList.getAddressEntriesAsListImmutable().stream() return addressEntryList.getAddressEntriesAsListImmutable().stream()
.filter(addressEntry -> addressEntry.getContext() == context) .filter(addressEntry -> addressEntry.getContext() == context)
@ -463,10 +432,6 @@ public class WalletsSetup {
.collect(Collectors.toSet()); .collect(Collectors.toSet());
} }
public boolean hasSufficientPeersForBroadcast() {
return numPeers.get() >= getMinBroadcastConnections();
}
public int getMinBroadcastConnections() { public int getMinBroadcastConnections() {
return walletConfig.getMinBroadcastConnections(); return walletConfig.getMinBroadcastConnections();
} }

View file

@ -92,6 +92,10 @@ public class BtcWalletService extends WalletService {
// Overridden Methods // Overridden Methods
/////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////
public boolean isWalletSyncedWithinTolerance() {
throw new RuntimeException("Not implemented");
}
@Override @Override
void decryptWallet(@NotNull KeyParameter key) { void decryptWallet(@NotNull KeyParameter key) {
super.decryptWallet(key); super.decryptWallet(key);

View file

@ -538,9 +538,7 @@ public abstract class WalletService {
return isWalletReady() && chain != null ? chain.getBestChainHeight() : 0; return isWalletReady() && chain != null ? chain.getBestChainHeight() : 0;
} }
public boolean isChainHeightSyncedWithinTolerance() { public abstract boolean isWalletSyncedWithinTolerance();
return walletsSetup.isChainHeightSyncedWithinTolerance();
}
public Transaction getClonedTransaction(Transaction tx) { public Transaction getClonedTransaction(Transaction tx) {
return new Transaction(params, tx.bitcoinSerialize()); return new Transaction(params, tx.bitcoinSerialize());

View file

@ -97,7 +97,7 @@ public class WalletsManager {
} }
public boolean areWalletsAvailable() { public boolean areWalletsAvailable() {
return xmrWalletService.isWalletReady(); return xmrWalletService.isWalletAvailable();
} }
public KeyCrypterScrypt getKeyCrypterScrypt() { public KeyCrypterScrypt getKeyCrypterScrypt() {

View file

@ -21,6 +21,7 @@ import haveno.core.user.Preferences;
import haveno.core.xmr.listeners.XmrBalanceListener; import haveno.core.xmr.listeners.XmrBalanceListener;
import haveno.core.xmr.model.XmrAddressEntry; import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.model.XmrAddressEntryList; import haveno.core.xmr.model.XmrAddressEntryList;
import haveno.core.xmr.setup.DownloadListener;
import haveno.core.xmr.setup.MoneroWalletRpcManager; import haveno.core.xmr.setup.MoneroWalletRpcManager;
import haveno.core.xmr.setup.WalletsSetup; import haveno.core.xmr.setup.WalletsSetup;
import monero.common.MoneroError; import monero.common.MoneroError;
@ -69,11 +70,15 @@ import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.Future; import java.util.concurrent.Future;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream; import java.util.stream.Stream;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
import static com.google.common.base.Preconditions.checkState; import static com.google.common.base.Preconditions.checkState;
@ -104,6 +109,8 @@ public class XmrWalletService {
private final CoreMoneroConnectionsService connectionsService; private final CoreMoneroConnectionsService connectionsService;
private final XmrAddressEntryList xmrAddressEntryList; private final XmrAddressEntryList xmrAddressEntryList;
private final WalletsSetup walletsSetup; private final WalletsSetup walletsSetup;
private final DownloadListener downloadListener = new DownloadListener();
private final LongProperty walletHeight = new SimpleLongProperty(0);
private final File walletDir; private final File walletDir;
private final File xmrWalletFile; private final File xmrWalletFile;
@ -196,7 +203,7 @@ public class XmrWalletService {
saveWallet(getWallet(), backup); saveWallet(getWallet(), backup);
} }
public boolean isWalletReady() { public boolean isWalletAvailable() {
try { try {
return getWallet() != null; return getWallet() != null;
} catch (Exception e) { } catch (Exception e) {
@ -208,6 +215,22 @@ public class XmrWalletService {
return accountService.getPassword() != null; return accountService.getPassword() != null;
} }
public boolean isWalletSynced() {
return downloadPercentageProperty().get() == 1d;
}
public ReadOnlyDoubleProperty downloadPercentageProperty() {
return downloadListener.percentageProperty();
}
private void doneDownload() {
downloadListener.doneDownload();
}
public LongProperty walletHeightProperty() {
return walletHeight;
}
public MoneroDaemonRpc getDaemon() { public MoneroDaemonRpc getDaemon() {
return connectionsService.getDaemon(); return connectionsService.getDaemon();
} }
@ -651,12 +674,19 @@ public class XmrWalletService {
private void initialize() { private void initialize() {
// set and listen to daemon connection // listen for connection changes
connectionsService.addConnectionListener(newConnection -> { connectionsService.addConnectionListener(newConnection -> onConnectionChanged(newConnection));
onConnectionChanged(newConnection);
});
// initialize main wallet if connected or previously created // wait for monerod to sync
if (connectionsService.downloadPercentageProperty().get() != 1) {
CountDownLatch latch = new CountDownLatch(1);
connectionsService.downloadPercentageProperty().addListener((obs, oldVal, newVal) -> {
if (connectionsService.downloadPercentageProperty().get() == 1) latch.countDown();
});
HavenoUtils.awaitLatch(latch);
}
// initialize main wallet
maybeInitMainWallet(true); maybeInitMainWallet(true);
} }
@ -700,8 +730,8 @@ public class XmrWalletService {
// reapply connection after wallet synced // reapply connection after wallet synced
onConnectionChanged(connectionsService.getConnection()); onConnectionChanged(connectionsService.getConnection());
// TODO: using this to signify both daemon and wallet synced, use separate sync handlers? // signal that main wallet is synced
connectionsService.doneDownload(); doneDownload();
// notify setup that main wallet is initialized // notify setup that main wallet is initialized
// TODO: app fully initializes after this is set to true, even though wallet might not be initialized if unconnected. wallet will be created when connection detected // TODO: app fully initializes after this is set to true, even though wallet might not be initialized if unconnected. wallet will be created when connection detected
@ -1242,6 +1272,7 @@ public class XmrWalletService {
UserThread.execute(new Runnable() { UserThread.execute(new Runnable() {
@Override @Override
public void run() { public void run() {
walletHeight.set(height);
for (MoneroWalletListenerI listener : walletListeners) listener.onNewBlock(height); for (MoneroWalletListenerI listener : walletListeners) listener.onNewBlock(height);
} }
}); });

View file

@ -553,6 +553,7 @@ abstract class OfferBookViewModel extends ActivatableViewModel {
boolean canCreateOrTakeOffer() { boolean canCreateOrTakeOffer() {
return GUIUtil.canCreateOrTakeOfferOrShowPopup(user, navigation) && return GUIUtil.canCreateOrTakeOfferOrShowPopup(user, navigation) &&
GUIUtil.isChainHeightSyncedWithinToleranceOrShowPopup(openOfferManager.getConnectionsService()) &&
GUIUtil.isBootstrappedOrShowPopup(p2PService); GUIUtil.isBootstrappedOrShowPopup(p2PService);
} }