start main wallet when daemon synced with improved UserThread

This commit is contained in:
woodser 2023-12-31 10:38:02 -05:00
parent 5466689857
commit 5d88936600
3 changed files with 69 additions and 49 deletions

View File

@ -45,7 +45,7 @@ public class UserThread {
@Getter
@Setter
private static Executor executor;
private static final String USER_THREAD_NAME = "UserThread";
private static Thread USER_THREAD;
public static void setTimerClass(Class<? extends Timer> timerClass) {
UserThread.timerClass = timerClass;
@ -59,8 +59,10 @@ public class UserThread {
public static void execute(Runnable command) {
executor.execute(() -> {
Thread.currentThread().setName(USER_THREAD_NAME);
synchronized (executor) {
USER_THREAD = Thread.currentThread();
command.run();
}
});
}
@ -79,9 +81,9 @@ public class UserThread {
}
}
// TODO: better way to determine if on UserThread, since this is not reliable
private static boolean isUserThread(Thread thread) {
return USER_THREAD_NAME.equals(thread.getName());
public static boolean isUserThread(Thread thread) {
return thread == USER_THREAD;
}
// Prefer FxTimer if a delay is needed in a JavaFx class (gui module)
@ -99,7 +101,7 @@ public class UserThread {
}
public static Timer runAfter(Runnable runnable, long delay, TimeUnit timeUnit) {
return getTimer().runLater(Duration.ofMillis(timeUnit.toMillis(delay)), runnable);
return getTimer().runLater(Duration.ofMillis(timeUnit.toMillis(delay)), () -> execute(runnable));
}
public static Timer runPeriodically(Runnable runnable, long intervalInSec) {
@ -107,7 +109,7 @@ public class UserThread {
}
public static Timer runPeriodically(Runnable runnable, long interval, TimeUnit timeUnit) {
return getTimer().runPeriodically(Duration.ofMillis(timeUnit.toMillis(interval)), runnable);
return getTimer().runPeriodically(Duration.ofMillis(timeUnit.toMillis(interval)), () -> execute(runnable));
}
private static Timer getTimer() {

View File

@ -87,7 +87,6 @@ import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import lombok.Getter;
import monero.daemon.model.MoneroTx;
import monero.wallet.model.MoneroOutputQuery;
import org.bitcoinj.core.Coin;
import org.bouncycastle.crypto.params.KeyParameter;
import org.fxmisc.easybind.EasyBind;
@ -353,35 +352,6 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
}
private void thawUnreservedOutputs() {
if (xmrWalletService.getWallet() == null) return;
// collect reserved outputs
Set<String> reservedKeyImages = new HashSet<String>();
for (Trade trade : getObservableList()) {
if (trade.getSelf().getReserveTxKeyImages() == null) continue;
reservedKeyImages.addAll(trade.getSelf().getReserveTxKeyImages());
}
for (OpenOffer openOffer : openOfferManager.getObservableList()) {
if (openOffer.getOffer().getOfferPayload().getReserveTxKeyImages() == null) continue;
reservedKeyImages.addAll(openOffer.getOffer().getOfferPayload().getReserveTxKeyImages());
}
// thaw unreserved outputs
Set<String> unreservedFrozenKeyImages = xmrWalletService.getWallet().getOutputs(new MoneroOutputQuery()
.setIsFrozen(true)
.setIsSpent(false))
.stream()
.map(output -> output.getKeyImage().getHex())
.collect(Collectors.toSet());
unreservedFrozenKeyImages.removeAll(reservedKeyImages);
if (!unreservedFrozenKeyImages.isEmpty()) {
log.warn("Thawing outputs which are not reserved for offer or trade: " + unreservedFrozenKeyImages);
xmrWalletService.thawOutputs(unreservedFrozenKeyImages);
xmrWalletService.saveMainWallet();
}
}
public TradeProtocol getTradeProtocol(Trade trade) {
synchronized (tradeProtocolByTradeId) {
return tradeProtocolByTradeId.get(trade.getUid());
@ -464,9 +434,10 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
// thaw unreserved outputs
thawUnreservedOutputs();
xmrWalletService.thawUnreservedOutputs();
// reset any available funded address entries
if (isShutDownStarted) return;
xmrWalletService.getAddressEntriesForAvailableBalanceStream()
.filter(addressEntry -> addressEntry.getOfferId() != null)
.forEach(addressEntry -> {
@ -476,6 +447,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
}
// notify that persisted trades initialized
if (isShutDownStarted) return;
persistedTradesInitialized.set(true);
getObservableList().addListener((ListChangeListener<Trade>) change -> onTradesChanged());
onTradesChanged();

View File

@ -12,6 +12,7 @@ import haveno.common.util.Utilities;
import haveno.core.api.AccountServiceListener;
import haveno.core.api.CoreAccountService;
import haveno.core.api.XmrConnectionService;
import haveno.core.offer.OpenOffer;
import haveno.core.trade.BuyerTrade;
import haveno.core.trade.HavenoUtils;
import haveno.core.trade.MakerTrade;
@ -80,6 +81,7 @@ import java.util.stream.Stream;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.value.ChangeListener;
import static com.google.common.base.Preconditions.checkState;
@ -120,6 +122,7 @@ public class XmrWalletService {
protected final CopyOnWriteArraySet<XmrBalanceListener> balanceListeners = new CopyOnWriteArraySet<>();
protected final CopyOnWriteArraySet<MoneroWalletListenerI> walletListeners = new CopyOnWriteArraySet<>();
private ChangeListener<? super Number> walletInitListener;
private TradeManager tradeManager;
private MoneroWalletRpc wallet;
private Object walletLock = new Object();
@ -364,6 +367,45 @@ public class XmrWalletService {
}
}
/**
* Thaw all outputs not reserved for a trade.
*/
public void thawUnreservedOutputs() {
synchronized (walletLock) {
// collect reserved outputs
Set<String> reservedKeyImages = new HashSet<String>();
for (Trade trade : tradeManager.getObservableList()) {
if (trade.getSelf().getReserveTxKeyImages() == null) continue;
reservedKeyImages.addAll(trade.getSelf().getReserveTxKeyImages());
}
for (OpenOffer openOffer : tradeManager.getOpenOfferManager().getObservableList()) {
if (openOffer.getOffer().getOfferPayload().getReserveTxKeyImages() == null) continue;
reservedKeyImages.addAll(openOffer.getOffer().getOfferPayload().getReserveTxKeyImages());
}
// ensure wallet is open
if (wallet == null) {
log.warn("Cannot thaw unreserved outputs because wallet not open");
return;
}
// thaw unreserved outputs
Set<String> unreservedFrozenKeyImages = wallet.getOutputs(new MoneroOutputQuery()
.setIsFrozen(true)
.setIsSpent(false))
.stream()
.map(output -> output.getKeyImage().getHex())
.collect(Collectors.toSet());
unreservedFrozenKeyImages.removeAll(reservedKeyImages);
if (!unreservedFrozenKeyImages.isEmpty()) {
log.warn("Thawing outputs which are not reserved for offer or trade: " + unreservedFrozenKeyImages);
thawOutputs(unreservedFrozenKeyImages);
saveMainWallet();
}
}
}
/**
* Thaw the given outputs with a lock on the wallet.
*
@ -695,17 +737,21 @@ public class XmrWalletService {
HavenoUtils.submitToThread(() -> onConnectionChanged(connection), THREAD_ID);
});
// wait for monerod to sync
if (xmrConnectionService.downloadPercentageProperty().get() != 1) {
CountDownLatch latch = new CountDownLatch(1);
xmrConnectionService.downloadPercentageProperty().addListener((obs, oldVal, newVal) -> {
if (xmrConnectionService.downloadPercentageProperty().get() == 1) latch.countDown();
});
HavenoUtils.awaitLatch(latch);
// initialize main wallet when daemon synced
walletInitListener = (obs, oldVal, newVal) -> initMainWalletIfConnected();
xmrConnectionService.downloadPercentageProperty().addListener(walletInitListener);
initMainWalletIfConnected();
}
// initialize main wallet
private void initMainWalletIfConnected() {
HavenoUtils.submitToThread(() -> {
synchronized (walletLock) {
if (xmrConnectionService.downloadPercentageProperty().get() == 1 && wallet == null && !isShutDownStarted) {
maybeInitMainWallet(true);
if (walletInitListener != null) xmrConnectionService.downloadPercentageProperty().removeListener(walletInitListener);
}
}
}, THREAD_ID);
}
private void maybeInitMainWallet(boolean sync) {