refactor base wallet so trades can sync with progress timeout, etc

This commit is contained in:
woodser 2024-08-04 13:26:59 -04:00
parent 1b5c03bce8
commit ca7d596175
23 changed files with 247 additions and 226 deletions

View file

@ -118,7 +118,7 @@ public class CoreDisputesService {
}
public Dispute createDisputeForTrade(Trade trade, Offer offer, PubKeyRing pubKey, boolean isMaker, boolean isSupportTicket) {
synchronized (trade) {
synchronized (trade.getLock()) {
byte[] payoutTxSerialized = null;
String payoutTxHashAsString = null;
@ -163,7 +163,7 @@ public class CoreDisputesService {
if (winningDisputeOptional.isPresent()) winningDispute = winningDisputeOptional.get();
else throw new IllegalStateException(format("dispute for tradeId '%s' not found", tradeId));
synchronized (trade) {
synchronized (trade.getLock()) {
try {
// create dispute result

View file

@ -1085,7 +1085,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
BigInteger reserveAmount = openOffer.getOffer().getAmountNeeded();
xmrWalletService.swapAddressEntryToAvailable(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING); // change funding subaddress in case funded with unsuitable output(s)
MoneroTxWallet splitOutputTx = null;
synchronized (XmrWalletService.WALLET_LOCK) {
synchronized (HavenoUtils.xmrWalletService.getWalletLock()) {
XmrAddressEntry entry = xmrWalletService.getOrCreateAddressEntry(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING);
synchronized (HavenoUtils.getWalletFunctionLock()) {
long startTime = System.currentTimeMillis();

View file

@ -30,7 +30,6 @@ import haveno.core.offer.placeoffer.PlaceOfferModel;
import haveno.core.trade.HavenoUtils;
import haveno.core.trade.protocol.TradeProtocol;
import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.wallet.XmrWalletService;
import lombok.extern.slf4j.Slf4j;
import monero.common.MoneroRpcConnection;
import monero.daemon.model.MoneroOutput;
@ -60,11 +59,11 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
}
// verify monero connection
model.getXmrWalletService().getConnectionService().verifyConnection();
model.getXmrWalletService().getXmrConnectionService().verifyConnection();
// create reserve tx
MoneroTxWallet reserveTx = null;
synchronized (XmrWalletService.WALLET_LOCK) {
synchronized (HavenoUtils.xmrWalletService.getWalletLock()) {
// reset protocol timeout
verifyPending();
@ -83,7 +82,7 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
try {
synchronized (HavenoUtils.getWalletFunctionLock()) {
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
MoneroRpcConnection sourceConnection = model.getXmrWalletService().getConnectionService().getConnection();
MoneroRpcConnection sourceConnection = model.getXmrWalletService().getXmrConnectionService().getConnection();
try {
//if (true) throw new RuntimeException("Pretend error");
reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, makerFee, sendAmount, securityDeposit, returnAddress, openOffer.isReserveExactAmount(), preferredSubaddressIndex);

View file

@ -499,7 +499,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
// process on trade thread
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
String errorMessage = null;
PubKeyRing senderPubKeyRing = null;
try {

View file

@ -240,7 +240,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
ThreadUtils.execute(() -> {
ChatMessage chatMessage = null;
Dispute dispute = null;
synchronized (trade) {
synchronized (trade.getLock()) {
try {
DisputeResult disputeResult = disputeClosedMessage.getDisputeResult();
chatMessage = disputeResult.getChatMessage();
@ -384,7 +384,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
public void maybeReprocessDisputeClosedMessage(Trade trade, boolean reprocessOnError) {
if (trade.isShutDownStarted()) return;
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
// skip if no need to reprocess
if (trade.isArbitrator() || trade.getArbitrator().getDisputeClosedMessage() == null || trade.getArbitrator().getDisputeClosedMessage().getUnsignedPayoutTxHex() == null || trade.getDisputeState().ordinal() >= Trade.DisputeState.DISPUTE_CLOSED.ordinal()) {
@ -478,7 +478,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
trade.setPayoutTxHex(signedMultisigTxHex);
requestPersistence(trade);
} catch (Exception e) {
throw new IllegalStateException(e);
throw new IllegalStateException(e.getMessage());
}
// verify mining fee is within tolerance by recreating payout tx

View file

@ -28,6 +28,7 @@ import haveno.common.crypto.KeyRing;
import haveno.common.crypto.PubKeyRing;
import haveno.common.crypto.Sig;
import haveno.common.util.Utilities;
import haveno.core.api.XmrConnectionService;
import haveno.core.app.HavenoSetup;
import haveno.core.offer.OfferPayload;
import haveno.core.offer.OpenOfferManager;
@ -106,6 +107,7 @@ public class HavenoUtils {
public static HavenoSetup havenoSetup;
public static ArbitrationManager arbitrationManager;
public static XmrWalletService xmrWalletService;
public static XmrConnectionService xmrConnectionService;
public static OpenOfferManager openOfferManager;
public static boolean isSeedNode() {

View file

@ -44,7 +44,6 @@ import haveno.common.crypto.PubKeyRing;
import haveno.common.proto.ProtoUtil;
import haveno.common.taskrunner.Model;
import haveno.common.util.Utilities;
import haveno.core.api.XmrConnectionService;
import haveno.core.monetary.Price;
import haveno.core.monetary.Volume;
import haveno.core.network.MessageState;
@ -69,6 +68,7 @@ import haveno.core.trade.protocol.TradeProtocol;
import haveno.core.trade.statistics.TradeStatistics3;
import haveno.core.util.VolumeUtil;
import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.wallet.XmrWalletBase;
import haveno.core.xmr.wallet.XmrWalletService;
import haveno.network.p2p.AckMessage;
import haveno.network.p2p.NodeAddress;
@ -76,14 +76,12 @@ import haveno.network.p2p.P2PService;
import haveno.network.p2p.network.TorNetworkNode;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.LongProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.ReadOnlyStringProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleLongProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
@ -135,19 +133,17 @@ import static com.google.common.base.Preconditions.checkNotNull;
* stored in the task model.
*/
@Slf4j
public abstract class Trade implements Tradable, Model {
public abstract class Trade extends XmrWalletBase implements Tradable, Model {
@Getter
public final Object lock = new Object();
private static final String MONERO_TRADE_WALLET_PREFIX = "xmr_trade_";
private static final long SHUTDOWN_TIMEOUT_MS = 60000;
private static final long SYNC_EVERY_NUM_BLOCKS = 360; // ~1/2 day
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 final Object walletLock = new Object();
private final Object pollLock = new Object();
private final LongProperty walletHeight = new SimpleLongProperty(0);
private MoneroWallet wallet;
private boolean wasWalletSynced;
private boolean pollInProgress;
private boolean restartInProgress;
private Subscription protocolErrorStateSubscription;
@ -413,9 +409,6 @@ public abstract class Trade implements Tradable, Model {
// Immutable
@Getter
transient final private XmrWalletService xmrWalletService;
@Getter
transient final private XmrConnectionService xmrConnectionService;
transient final private DoubleProperty initProgressProperty = new SimpleDoubleProperty(0.0);
transient final private ObjectProperty<State> stateProperty = new SimpleObjectProperty<>(state);
transient final private ObjectProperty<Phase> phaseProperty = new SimpleObjectProperty<>(state.phase);
@ -441,10 +434,6 @@ public abstract class Trade implements Tradable, Model {
@Getter
transient private boolean isInitialized;
transient private boolean isFullyInitialized;
@Getter
transient private boolean isShutDownStarted;
@Getter
transient private boolean isShutDown;
// Added in v1.2.0
transient private ObjectProperty<BigInteger> tradeAmountProperty;
@ -511,11 +500,12 @@ public abstract class Trade implements Tradable, Model {
@Nullable NodeAddress makerNodeAddress,
@Nullable NodeAddress takerNodeAddress,
@Nullable NodeAddress arbitratorNodeAddress) {
super();
this.offer = offer;
this.amount = tradeAmount.longValueExact();
this.price = tradePrice;
this.xmrWalletService = xmrWalletService;
this.xmrConnectionService = xmrWalletService.getConnectionService();
this.xmrConnectionService = xmrWalletService.getXmrConnectionService();
this.processModel = processModel;
this.uid = uid;
this.takeOfferDate = new Date().getTime();
@ -1512,13 +1502,13 @@ public abstract class Trade implements Tradable, Model {
// repeatedly acquire lock to clear tasks
for (int i = 0; i < 20; i++) {
synchronized (this) {
synchronized (getLock()) {
HavenoUtils.waitFor(10);
}
}
// shut down trade threads
synchronized (this) {
synchronized (getLock()) {
isInitialized = false;
isShutDown = true;
List<Runnable> shutDownThreads = new ArrayList<>();
@ -2636,8 +2626,7 @@ public abstract class Trade implements Tradable, Model {
private void syncWalletIfBehind() {
if (isWalletBehind()) {
synchronized (walletLock) {
xmrWalletService.syncWallet(wallet);
walletHeight.set(wallet.getHeight());
syncWithProgress();
}
}
}
@ -2812,7 +2801,7 @@ public abstract class Trade implements Tradable, Model {
if (!isInitialized || isShutDownStarted) return;
if (isWalletConnectedToDaemon()) {
e.printStackTrace();
log.warn("Error polling idle trade for {} {}: {}. Monerod={}", getClass().getSimpleName(), getId(), e.getMessage(), getXmrWalletService().getConnectionService().getConnection());
log.warn("Error polling idle trade for {} {}: {}. Monerod={}", getClass().getSimpleName(), getId(), e.getMessage(), getXmrWalletService().getXmrConnectionService().getConnection());
};
}
}, getId());

View file

@ -45,7 +45,7 @@ public class ArbitratorProtocol extends DisputeProtocol {
public void handleInitTradeRequest(InitTradeRequest message, NodeAddress peer, ErrorMessageHandler errorMessageHandler) {
System.out.println("ArbitratorProtocol.handleInitTradeRequest()");
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
latchTrade();
this.errorMessageHandler = errorMessageHandler;
processModel.setTradeMessage(message); // TODO (woodser): confirm these are null without being set
@ -80,7 +80,7 @@ public class ArbitratorProtocol extends DisputeProtocol {
public void handleDepositRequest(DepositRequest request, NodeAddress sender) {
System.out.println("ArbitratorProtocol.handleDepositRequest() " + trade.getId());
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), request);
processModel.setTradeMessage(request);

View file

@ -62,7 +62,7 @@ public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol
ErrorMessageHandler errorMessageHandler) {
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
latchTrade();
this.errorMessageHandler = errorMessageHandler;
expect(phase(Trade.Phase.INIT)

View file

@ -70,7 +70,7 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
ErrorMessageHandler errorMessageHandler) {
System.out.println(getClass().getSimpleName() + ".onTakeOffer()");
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
latchTrade();
this.tradeResultHandler = tradeResultHandler;
this.errorMessageHandler = errorMessageHandler;
@ -101,7 +101,7 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
NodeAddress peer) {
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
latchTrade();
expect(phase(Trade.Phase.INIT)
.with(message)

View file

@ -75,7 +75,7 @@ public class BuyerProtocol extends DisputeProtocol {
// re-send payment sent message if not acked
ThreadUtils.execute(() -> {
if (trade.isShutDownStarted() || trade.isPayoutPublished()) return;
synchronized (trade) {
synchronized (trade.getLock()) {
if (trade.isShutDownStarted() || trade.isPayoutPublished()) return;
if (trade.getState().ordinal() >= Trade.State.BUYER_SENT_PAYMENT_SENT_MSG.ordinal() && trade.getState().ordinal() < Trade.State.SELLER_RECEIVED_PAYMENT_SENT_MSG.ordinal()) {
latchTrade();
@ -121,7 +121,7 @@ public class BuyerProtocol extends DisputeProtocol {
public void onPaymentSent(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
System.out.println("BuyerProtocol.onPaymentSent()");
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
latchTrade();
this.errorMessageHandler = errorMessageHandler;
BuyerEvent event = BuyerEvent.PAYMENT_SENT;

View file

@ -67,7 +67,7 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
ErrorMessageHandler errorMessageHandler) {
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
latchTrade();
this.errorMessageHandler = errorMessageHandler;
expect(phase(Trade.Phase.INIT)

View file

@ -70,7 +70,7 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
ErrorMessageHandler errorMessageHandler) {
System.out.println(getClass().getSimpleName() + ".onTakeOffer()");
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
latchTrade();
this.tradeResultHandler = tradeResultHandler;
this.errorMessageHandler = errorMessageHandler;
@ -101,7 +101,7 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
NodeAddress peer) {
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
latchTrade();
expect(phase(Trade.Phase.INIT)
.with(message)

View file

@ -70,7 +70,7 @@ public class SellerProtocol extends DisputeProtocol {
// re-send payment received message if payout not published
ThreadUtils.execute(() -> {
if (trade.isShutDownStarted() || trade.isPayoutPublished()) return;
synchronized (trade) {
synchronized (trade.getLock()) {
if (trade.isShutDownStarted() || trade.isPayoutPublished()) return;
if (trade.getState().ordinal() >= Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal() && !trade.isPayoutPublished()) {
latchTrade();
@ -117,7 +117,7 @@ public class SellerProtocol extends DisputeProtocol {
public void onPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
log.info("SellerProtocol.onPaymentReceived()");
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
latchTrade();
this.errorMessageHandler = errorMessageHandler;
SellerEvent event = SellerEvent.PAYMENT_RECEIVED;

View file

@ -243,7 +243,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
if (!trade.isCompleted()) processModel.getP2PService().addDecryptedDirectMessageListener(this);
// initialize trade
synchronized (trade) {
synchronized (trade.getLock()) {
trade.initialize(processModel.getProvider());
// process mailbox messages
@ -261,7 +261,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
ThreadUtils.execute(() -> {
if (!trade.isDepositsConfirmed() || trade.isDepositsConfirmedAcked() || trade.isPayoutPublished() || depositsConfirmedTasksCalled) return;
depositsConfirmedTasksCalled = true;
synchronized (trade) {
synchronized (trade.getLock()) {
if (!trade.isInitialized() || trade.isShutDownStarted()) return; // skip if shutting down
latchTrade();
expect(new Condition(trade))
@ -282,7 +282,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
public void maybeReprocessPaymentReceivedMessage(boolean reprocessOnError) {
if (trade.isShutDownStarted()) return;
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
// skip if no need to reprocess
if (trade.isSeller() || trade.getSeller().getPaymentReceivedMessage() == null || (trade.getState().ordinal() >= Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal() && trade.isPayoutPublished())) {
@ -299,7 +299,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
System.out.println(getClass().getSimpleName() + ".handleInitMultisigRequest() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
trade.addInitProgressStep();
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
// check trade
if (trade.hasFailed()) {
@ -335,7 +335,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());
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
// check trade
if (trade.hasFailed()) {
@ -379,7 +379,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
System.out.println(getClass().getSimpleName() + ".handleSignContractResponse() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
trade.addInitProgressStep();
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
// check trade
if (trade.hasFailed()) {
@ -425,7 +425,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
System.out.println(getClass().getSimpleName() + ".handleDepositResponse() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
trade.addInitProgressStep();
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
Validator.checkTradeId(processModel.getOfferId(), response);
latchTrade();
processModel.setTradeMessage(response);
@ -455,7 +455,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
System.out.println(getClass().getSimpleName() + ".handle(DepositsConfirmedMessage) from " + sender + " for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
if (!trade.isInitialized() || trade.isShutDown()) return;
ThreadUtils.execute(() -> {
synchronized (trade) {
synchronized (trade.getLock()) {
if (!trade.isInitialized() || trade.isShutDown()) return;
latchTrade();
this.errorMessageHandler = null;
@ -493,7 +493,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
// a mailbox message with PaymentSentMessage.
// TODO A better fix would be to add a listener for the wallet sync state and process
// the mailbox msg once wallet is ready and trade state set.
synchronized (trade) {
synchronized (trade.getLock()) {
if (!trade.isInitialized() || trade.isShutDown()) return;
if (trade.getPhase().ordinal() >= Trade.Phase.PAYMENT_SENT.ordinal()) {
log.warn("Received another PaymentSentMessage which was already processed for {} {}, ACKing", trade.getClass().getSimpleName(), trade.getId());
@ -542,7 +542,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
log.warn("Ignoring PaymentReceivedMessage since not buyer or arbitrator");
return;
}
synchronized (trade) {
synchronized (trade.getLock()) {
if (!trade.isInitialized() || trade.isShutDown()) return;
latchTrade();
Validator.checkTradeId(processModel.getOfferId(), message);
@ -817,7 +817,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
}
void handleTaskRunnerFault(NodeAddress ackReceiver, @Nullable TradeMessage message, String source, String errorMessage) {
log.error("Task runner failed with error {}. Triggered from {}. Monerod={}" , errorMessage, source, trade.getXmrWalletService().getConnectionService().getConnection());
log.error("Task runner failed with error {}. Triggered from {}. Monerod={}" , errorMessage, source, trade.getXmrWalletService().getXmrConnectionService().getConnection());
if (message != null) {
sendAckMessage(ackReceiver, message, false, errorMessage);

View file

@ -28,7 +28,6 @@ import haveno.core.trade.Trade.State;
import haveno.core.trade.messages.SignContractRequest;
import haveno.core.trade.protocol.TradeProtocol;
import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.wallet.XmrWalletService;
import haveno.network.p2p.SendDirectMessageListener;
import lombok.extern.slf4j.Slf4j;
import monero.common.MoneroRpcConnection;
@ -78,7 +77,7 @@ public class MaybeSendSignContractRequest extends TradeTask {
// create deposit tx and freeze inputs
MoneroTxWallet depositTx = null;
synchronized (XmrWalletService.WALLET_LOCK) {
synchronized (HavenoUtils.xmrWalletService.getWalletLock()) {
// check for timeout
if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while getting lock to create deposit tx, tradeId=" + trade.getShortId());

View file

@ -24,7 +24,6 @@ import haveno.core.trade.TakerTrade;
import haveno.core.trade.Trade;
import haveno.core.trade.protocol.TradeProtocol;
import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.wallet.XmrWalletService;
import lombok.extern.slf4j.Slf4j;
import monero.common.MoneroRpcConnection;
import monero.wallet.model.MoneroTxWallet;
@ -50,7 +49,7 @@ public class TakerReserveTradeFunds extends TradeTask {
// create reserve tx
MoneroTxWallet reserveTx = null;
synchronized (XmrWalletService.WALLET_LOCK) {
synchronized (HavenoUtils.xmrWalletService.getWalletLock()) {
// check for timeout
if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while getting lock to create reserve tx, tradeId=" + trade.getShortId());

View file

@ -44,6 +44,7 @@ import haveno.core.offer.OpenOfferManager;
import haveno.core.support.dispute.Dispute;
import haveno.core.support.dispute.refund.RefundManager;
import haveno.core.trade.ClosedTradableManager;
import haveno.core.trade.HavenoUtils;
import haveno.core.trade.MakerTrade;
import haveno.core.trade.Trade;
import haveno.core.trade.TradeManager;
@ -124,7 +125,7 @@ public class Balances {
private void doUpdateBalances() {
synchronized (this) {
synchronized (XmrWalletService.WALLET_LOCK) {
synchronized (HavenoUtils.xmrWalletService.getWalletLock()) {
// get wallet balances
BigInteger balance = xmrWalletService.getWallet() == null ? BigInteger.ZERO : xmrWalletService.getBalance();

View file

@ -0,0 +1,165 @@
package haveno.core.xmr.wallet;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import haveno.common.Timer;
import haveno.common.UserThread;
import haveno.core.api.XmrConnectionService;
import haveno.core.trade.HavenoUtils;
import haveno.core.xmr.setup.DownloadListener;
import javafx.beans.property.LongProperty;
import javafx.beans.property.SimpleLongProperty;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import monero.common.TaskLooper;
import monero.daemon.model.MoneroTx;
import monero.wallet.MoneroWallet;
import monero.wallet.MoneroWalletFull;
import monero.wallet.model.MoneroWalletListener;
@Slf4j
public class XmrWalletBase {
// constants
public static final int SYNC_PROGRESS_TIMEOUT_SECONDS = 60;
// inherited
protected MoneroWallet wallet;
@Getter
protected final Object walletLock = new Object();
@Getter
protected XmrConnectionService xmrConnectionService;
protected boolean wasWalletSynced;
protected final Map<String, Optional<MoneroTx>> txCache = new HashMap<String, Optional<MoneroTx>>();
protected boolean isClosingWallet;
protected boolean isSyncingWithProgress;
protected Long syncStartHeight;
protected TaskLooper syncProgressLooper;
protected CountDownLatch syncProgressLatch;
protected Exception syncProgressError;
protected Timer syncProgressTimeout;
protected final DownloadListener downloadListener = new DownloadListener();
protected final LongProperty walletHeight = new SimpleLongProperty(0);
@Getter
protected boolean isShutDownStarted;
@Getter
protected boolean isShutDown;
// private
private boolean testReconnectOnStartup = false; // test reconnecting on startup while syncing so the wallet is blocked
private String testReconnectMonerod1 = "http://node.community.rino.io:18081";
private String testReconnectMonerod2 = "http://nodex.monerujo.io:18081";
public XmrWalletBase() {
this.xmrConnectionService = HavenoUtils.xmrConnectionService;
}
public void syncWithProgress() {
synchronized (walletLock) {
// set initial state
isSyncingWithProgress = true;
syncProgressError = null;
updateSyncProgress(walletHeight.get());
// test connection changing on startup before wallet synced
if (testReconnectOnStartup) {
UserThread.runAfter(() -> {
log.warn("Testing connection change on startup before wallet synced");
if (xmrConnectionService.getConnection().getUri().equals(testReconnectMonerod1)) xmrConnectionService.setConnection(testReconnectMonerod2);
else xmrConnectionService.setConnection(testReconnectMonerod1);
}, 1);
testReconnectOnStartup = false; // only run once
}
// native wallet provides sync notifications
if (wallet instanceof MoneroWalletFull) {
if (testReconnectOnStartup) HavenoUtils.waitFor(1000); // delay sync to test
wallet.sync(new MoneroWalletListener() {
@Override
public void onSyncProgress(long height, long startHeight, long endHeight, double percentDone, String message) {
updateSyncProgress(height);
}
});
setWalletSyncedWithProgress();
return;
}
// start polling wallet for progress
syncProgressLatch = new CountDownLatch(1);
syncProgressLooper = new TaskLooper(() -> {
if (wallet == null) return;
long height;
try {
height = wallet.getHeight(); // can get read timeout while syncing
} catch (Exception e) {
log.warn("Error getting wallet height while syncing with progress: " + e.getMessage());
if (wallet != null && !isShutDownStarted) e.printStackTrace();
// stop polling and release latch
syncProgressError = e;
syncProgressLatch.countDown();
return;
}
updateSyncProgress(height);
if (height >= xmrConnectionService.getTargetHeight()) {
setWalletSyncedWithProgress();
syncProgressLatch.countDown();
}
});
wallet.startSyncing(xmrConnectionService.getRefreshPeriodMs());
syncProgressLooper.start(1000);
// wait for sync to complete
HavenoUtils.awaitLatch(syncProgressLatch);
// stop polling
syncProgressLooper.stop();
syncProgressTimeout.stop();
if (wallet != null) wallet.stopSyncing(); // can become null if interrupted by force close
isSyncingWithProgress = false;
if (syncProgressError != null) throw new RuntimeException(syncProgressError);
}
}
private void updateSyncProgress(long height) {
resetSyncProgressTimeout();
UserThread.execute(() -> {
// set wallet height
walletHeight.set(height);
// new wallet reports height 1 before synced
if (height == 1) {
downloadListener.progress(0, xmrConnectionService.getTargetHeight() - height, null);
return;
}
// set progress
long targetHeight = xmrConnectionService.getTargetHeight();
long blocksLeft = targetHeight - walletHeight.get();
if (syncStartHeight == null) syncStartHeight = walletHeight.get();
double percent = Math.min(1.0, targetHeight == syncStartHeight ? 1.0 : ((double) walletHeight.get() - syncStartHeight) / (double) (targetHeight - syncStartHeight));
downloadListener.progress(percent, blocksLeft, null);
});
}
private synchronized void resetSyncProgressTimeout() {
if (syncProgressTimeout != null) syncProgressTimeout.stop();
syncProgressTimeout = UserThread.runAfter(() -> {
if (isShutDownStarted) return;
syncProgressError = new RuntimeException("Sync progress timeout called");
syncProgressLatch.countDown();
}, SYNC_PROGRESS_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
private void setWalletSyncedWithProgress() {
wasWalletSynced = true;
isSyncingWithProgress = false;
syncProgressTimeout.stop();
}
}

View file

@ -24,7 +24,6 @@ import com.google.inject.name.Named;
import common.utils.JsonUtils;
import haveno.common.ThreadUtils;
import haveno.common.Timer;
import haveno.common.UserThread;
import haveno.common.config.Config;
import haveno.common.file.FileUtil;
@ -43,7 +42,6 @@ import haveno.core.user.User;
import haveno.core.xmr.listeners.XmrBalanceListener;
import haveno.core.xmr.model.XmrAddressEntry;
import haveno.core.xmr.model.XmrAddressEntryList;
import haveno.core.xmr.setup.DownloadListener;
import haveno.core.xmr.setup.MoneroWalletRpcManager;
import haveno.core.xmr.setup.WalletsSetup;
import java.io.File;
@ -55,26 +53,22 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
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 lombok.Getter;
import monero.common.MoneroError;
import monero.common.MoneroRpcConnection;
import monero.common.MoneroRpcError;
@ -103,14 +97,13 @@ import monero.wallet.model.MoneroTxPriority;
import monero.wallet.model.MoneroTxQuery;
import monero.wallet.model.MoneroTxWallet;
import monero.wallet.model.MoneroWalletConfig;
import monero.wallet.model.MoneroWalletListener;
import monero.wallet.model.MoneroWalletListenerI;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class XmrWalletService {
public class XmrWalletService extends XmrWalletBase {
private static final Logger log = LoggerFactory.getLogger(XmrWalletService.class);
// monero configuration
@ -138,11 +131,8 @@ public class XmrWalletService {
private final User user;
private final Preferences preferences;
private final CoreAccountService accountService;
private final XmrConnectionService xmrConnectionService;
private final XmrAddressEntryList xmrAddressEntryList;
private final WalletsSetup walletsSetup;
private final DownloadListener downloadListener = new DownloadListener();
private final LongProperty walletHeight = new SimpleLongProperty(0);
private final File walletDir;
private final File xmrWalletFile;
@ -153,22 +143,10 @@ public class XmrWalletService {
private ChangeListener<? super Number> walletInitListener;
private TradeManager tradeManager;
private MoneroWallet wallet;
public static final Object WALLET_LOCK = new Object();
private boolean wasWalletSynced;
private final Map<String, Optional<MoneroTx>> txCache = new HashMap<String, Optional<MoneroTx>>();
private boolean isClosingWallet;
private boolean isShutDownStarted;
private ExecutorService syncWalletThreadPool = Executors.newFixedThreadPool(10); // TODO: adjust based on connection type
private boolean isSyncingWithProgress;
private Long syncStartHeight;
private TaskLooper syncProgressLooper;
private CountDownLatch syncProgressLatch;
private Exception syncProgressError;
private Timer syncProgressTimeout;
private static final int SYNC_PROGRESS_TIMEOUT_SECONDS = 60;
// wallet polling and cache
@Getter
public final Object lock = new Object();
private TaskLooper pollLooper;
private boolean pollInProgress;
private Long pollPeriodMs;
@ -180,9 +158,6 @@ public class XmrWalletService {
private List<MoneroSubaddress> cachedSubaddresses;
private List<MoneroOutputWallet> cachedOutputs;
private List<MoneroTxWallet> cachedTxs;
private boolean testReconnectOnStartup = false; // test reconnecting on startup while syncing so the wallet is blocked
private String testReconnectMonerod1 = "http://node.community.rino.io:18081";
private String testReconnectMonerod2 = "http://nodex.monerujo.io:18081";
@SuppressWarnings("unused")
@Inject
@ -198,7 +173,6 @@ public class XmrWalletService {
this.user = user;
this.preferences = preferences;
this.accountService = accountService;
this.xmrConnectionService = xmrConnectionService;
this.walletsSetup = walletsSetup;
this.xmrAddressEntryList = xmrAddressEntryList;
this.walletDir = walletDir;
@ -206,6 +180,8 @@ public class XmrWalletService {
this.useNativeXmrWallet = useNativeXmrWallet;
this.xmrWalletFile = new File(walletDir, MONERO_WALLET_NAME);
HavenoUtils.xmrWalletService = this;
HavenoUtils.xmrConnectionService = xmrConnectionService;
this.xmrConnectionService = xmrConnectionService; // TODO: super's is null unless set here from injection
// set monero logging
if (MONERO_LOG_LEVEL >= 0) MoneroUtils.setLogLevel(MONERO_LOG_LEVEL);
@ -320,10 +296,6 @@ public class XmrWalletService {
return xmrConnectionService.getDaemon();
}
public XmrConnectionService getConnectionService() {
return xmrConnectionService;
}
public boolean isProxyApplied() {
return isProxyApplied(wasWalletSynced);
}
@ -464,7 +436,7 @@ public class XmrWalletService {
}
public MoneroTxWallet createTx(MoneroTxConfig txConfig) {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
synchronized (HavenoUtils.getWalletFunctionLock()) {
MoneroTxWallet tx = wallet.createTx(txConfig);
if (Boolean.TRUE.equals(txConfig.getRelay())) {
@ -478,7 +450,7 @@ public class XmrWalletService {
}
public String relayTx(String metadata) {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
String txId = wallet.relayTx(metadata);
requestSaveMainWallet();
return txId;
@ -495,7 +467,7 @@ public class XmrWalletService {
* Freeze reserved outputs and thaw unreserved outputs.
*/
public void fixReservedOutputs() {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
// collect reserved outputs
Set<String> reservedKeyImages = new HashSet<String>();
@ -514,7 +486,7 @@ public class XmrWalletService {
}
private void freezeReservedOutputs(Set<String> reservedKeyImages) {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
// ensure wallet is open
if (wallet == null) {
@ -538,7 +510,7 @@ public class XmrWalletService {
}
private void thawUnreservedOutputs(Set<String> reservedKeyImages) {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
// ensure wallet is open
if (wallet == null) {
@ -568,7 +540,7 @@ public class XmrWalletService {
*/
public void freezeOutputs(Collection<String> keyImages) {
if (keyImages == null || keyImages.isEmpty()) return;
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
// collect outputs to freeze
List<String> unfrozenKeyImages = getOutputs(new MoneroOutputQuery().setIsFrozen(false).setIsSpent(false)).stream()
@ -590,7 +562,7 @@ public class XmrWalletService {
*/
public void thawOutputs(Collection<String> keyImages) {
if (keyImages == null || keyImages.isEmpty()) return;
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
// collect outputs to thaw
List<String> frozenKeyImages = getOutputs(new MoneroOutputQuery().setIsFrozen(true).setIsSpent(false)).stream()
@ -643,7 +615,7 @@ public class XmrWalletService {
* @return the reserve tx
*/
public MoneroTxWallet createReserveTx(BigInteger penaltyFee, BigInteger tradeFee, BigInteger sendTradeAmount, BigInteger securityDeposit, String returnAddress, boolean reserveExactAmount, Integer preferredSubaddressIndex) {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
synchronized (HavenoUtils.getWalletFunctionLock()) {
log.info("Creating reserve tx with preferred subaddress index={}, return address={}", preferredSubaddressIndex, returnAddress);
long time = System.currentTimeMillis();
@ -664,7 +636,7 @@ public class XmrWalletService {
* @return MoneroTxWallet the multisig deposit tx
*/
public MoneroTxWallet createDepositTx(Trade trade, boolean reserveExactAmount, Integer preferredSubaddressIndex) {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
synchronized (HavenoUtils.getWalletFunctionLock()) {
BigInteger feeAmount = trade instanceof MakerTrade ? trade.getMakerFee() : trade.getTakerFee();
String feeAddress = trade.getProcessModel().getTradeFeeAddress();
@ -682,7 +654,7 @@ public class XmrWalletService {
}
private MoneroTxWallet createTradeTx(BigInteger feeAmount, String feeAddress, BigInteger sendAmount, String sendAddress, boolean reserveExactAmount, Integer preferredSubaddressIndex) {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
MoneroWallet wallet = getWallet();
// create a list of subaddresses to attempt spending from in preferred order
@ -919,7 +891,7 @@ public class XmrWalletService {
Runnable shutDownTask = () -> {
// remove listeners
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
if (wallet != null) {
for (MoneroWalletListenerI listener : new HashSet<>(wallet.getListeners())) {
wallet.removeListener(listener);
@ -929,7 +901,7 @@ public class XmrWalletService {
}
// shut down threads
synchronized (this) {
synchronized (getLock()) {
List<Runnable> shutDownThreads = new ArrayList<>();
shutDownThreads.add(() -> ThreadUtils.shutDown(THREAD_ID));
ThreadUtils.awaitTasks(shutDownThreads);
@ -1345,7 +1317,7 @@ public class XmrWalletService {
}
private void doMaybeInitMainWallet(boolean sync, int numAttempts) {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
if (isShutDownStarted) return;
// open or create wallet main wallet
@ -1467,111 +1439,6 @@ public class XmrWalletService {
}
}
private void syncWithProgress() {
synchronized (WALLET_LOCK) {
// set initial state
isSyncingWithProgress = true;
syncProgressError = null;
updateSyncProgress(walletHeight.get());
// test connection changing on startup before wallet synced
if (testReconnectOnStartup) {
UserThread.runAfter(() -> {
log.warn("Testing connection change on startup before wallet synced");
if (xmrConnectionService.getConnection().getUri().equals(testReconnectMonerod1)) xmrConnectionService.setConnection(testReconnectMonerod2);
else xmrConnectionService.setConnection(testReconnectMonerod1);
}, 1);
testReconnectOnStartup = false; // only run once
}
// native wallet provides sync notifications
if (wallet instanceof MoneroWalletFull) {
if (testReconnectOnStartup) HavenoUtils.waitFor(1000); // delay sync to test
wallet.sync(new MoneroWalletListener() {
@Override
public void onSyncProgress(long height, long startHeight, long endHeight, double percentDone, String message) {
updateSyncProgress(height);
}
});
setWalletSyncedWithProgress();
return;
}
// start polling wallet for progress
syncProgressLatch = new CountDownLatch(1);
syncProgressLooper = new TaskLooper(() -> {
if (wallet == null) return;
long height;
try {
height = wallet.getHeight(); // can get read timeout while syncing
} catch (Exception e) {
log.warn("Error getting wallet height while syncing with progress: " + e.getMessage());
if (wallet != null && !isShutDownStarted) e.printStackTrace();
// stop polling and release latch
syncProgressError = e;
syncProgressLatch.countDown();
return;
}
updateSyncProgress(height);
if (height >= xmrConnectionService.getTargetHeight()) {
setWalletSyncedWithProgress();
syncProgressLatch.countDown();
}
});
wallet.startSyncing(xmrConnectionService.getRefreshPeriodMs());
syncProgressLooper.start(1000);
// wait for sync to complete
HavenoUtils.awaitLatch(syncProgressLatch);
// stop polling
syncProgressLooper.stop();
syncProgressTimeout.stop();
if (wallet != null) wallet.stopSyncing(); // can become null if interrupted by force close
isSyncingWithProgress = false;
if (syncProgressError != null) throw new RuntimeException(syncProgressError);
}
}
private void updateSyncProgress(long height) {
resetSyncProgressTimeout();
UserThread.execute(() -> {
// set wallet height
walletHeight.set(height);
// new wallet reports height 1 before synced
if (height == 1) {
downloadListener.progress(0, xmrConnectionService.getTargetHeight() - height, null);
return;
}
// set progress
long targetHeight = xmrConnectionService.getTargetHeight();
long blocksLeft = targetHeight - walletHeight.get();
if (syncStartHeight == null) syncStartHeight = walletHeight.get();
double percent = Math.min(1.0, targetHeight == syncStartHeight ? 1.0 : ((double) walletHeight.get() - syncStartHeight) / (double) (targetHeight - syncStartHeight));
downloadListener.progress(percent, blocksLeft, null);
});
}
private synchronized void resetSyncProgressTimeout() {
if (syncProgressTimeout != null) syncProgressTimeout.stop();
syncProgressTimeout = UserThread.runAfter(() -> {
if (isShutDownStarted) return;
syncProgressError = new RuntimeException("Sync progress timeout called");
syncProgressLatch.countDown();
}, SYNC_PROGRESS_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
private void setWalletSyncedWithProgress() {
wasWalletSynced = true;
isSyncingWithProgress = false;
syncProgressTimeout.stop();
}
private MoneroWalletFull createWalletFull(MoneroWalletConfig config) {
// must be connected to daemon
@ -1724,7 +1591,7 @@ public class XmrWalletService {
}
private void onConnectionChanged(MoneroRpcConnection connection) {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
// use current connection
connection = xmrConnectionService.getConnection();
@ -1798,7 +1665,7 @@ public class XmrWalletService {
private void closeMainWallet(boolean save) {
stopPolling();
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
try {
if (wallet != null) {
isClosingWallet = true;
@ -1834,7 +1701,7 @@ public class XmrWalletService {
}
private void startPolling() {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
if (isShutDownStarted || isPolling()) return;
updatePollPeriod();
pollLooper = new TaskLooper(() -> pollWallet());
@ -1863,7 +1730,7 @@ public class XmrWalletService {
}
private void setPollPeriod(long pollPeriodMs) {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
if (this.isShutDownStarted) return;
if (this.pollPeriodMs != null && this.pollPeriodMs == pollPeriodMs) return;
this.pollPeriodMs = pollPeriodMs;
@ -1900,7 +1767,7 @@ public class XmrWalletService {
}
// sync wallet if behind daemon
if (walletHeight.get() < xmrConnectionService.getTargetHeight()) {
synchronized (WALLET_LOCK) { // avoid long sync from blocking other operations
synchronized (walletLock) { // avoid long sync from blocking other operations
syncWithProgress();
}
}
@ -1908,7 +1775,7 @@ public class XmrWalletService {
// fetch transactions from pool and store to cache
// TODO: ideally wallet should sync every poll and then avoid updating from pool on fetching txs?
if (updateTxs) {
synchronized (WALLET_LOCK) { // avoid long fetch from blocking other operations
synchronized (walletLock) { // avoid long fetch from blocking other operations
synchronized (HavenoUtils.getDaemonLock()) {
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
try {
@ -1931,13 +1798,13 @@ public class XmrWalletService {
if (wallet == null || isShutDownStarted) return;
if (HavenoUtils.isUnresponsive(e)) forceRestartMainWallet();
else if (isWalletConnectedToDaemon()) {
log.warn("Error polling main wallet, errorMessage={}. Monerod={}", e.getMessage(), getConnectionService().getConnection());
log.warn("Error polling main wallet, errorMessage={}. Monerod={}", e.getMessage(), getXmrConnectionService().getConnection());
//e.printStackTrace();
}
} finally {
// cache wallet info last
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
if (wallet != null && !isShutDownStarted) {
try {
cacheWalletInfo();
@ -1954,7 +1821,7 @@ public class XmrWalletService {
}
private MoneroSyncResult syncMainWallet() {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
MoneroSyncResult result = syncWallet(wallet);
walletHeight.set(wallet.getHeight());
return result;
@ -1962,7 +1829,7 @@ public class XmrWalletService {
}
public boolean isWalletConnectedToDaemon() {
synchronized (WALLET_LOCK) {
synchronized (walletLock) {
try {
if (wallet == null) return false;
return wallet.isConnectedToDaemon();

View file

@ -197,7 +197,7 @@ public class TxIdTextField extends AnchorPane {
try {
if (trade == null) {
tx = useCache ? xmrWalletService.getDaemonTxWithCache(txId) : xmrWalletService.getDaemonTx(txId);
tx.setNumConfirmations(tx.isConfirmed() ? (height == null ? xmrWalletService.getConnectionService().getLastInfo().getHeight() : height) - tx.getHeight(): 0l); // TODO: don't set if tx.getNumConfirmations() works reliably on non-local testnet
tx.setNumConfirmations(tx.isConfirmed() ? (height == null ? xmrWalletService.getXmrConnectionService().getLastInfo().getHeight() : height) - tx.getHeight(): 0l); // TODO: don't set if tx.getNumConfirmations() works reliably on non-local testnet
} else {
if (txId.equals(trade.getMaker().getDepositTxHash())) tx = trade.getMakerDepositTx();
else if (txId.equals(trade.getTaker().getDepositTxHash())) tx = trade.getTakerDepositTx();

View file

@ -257,7 +257,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
// create tx
MoneroTxWallet tx = null;
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
MoneroRpcConnection sourceConnection = xmrWalletService.getConnectionService().getConnection();
MoneroRpcConnection sourceConnection = xmrWalletService.getXmrConnectionService().getConnection();
try {
log.info("Creating withdraw tx");
long startTime = System.currentTimeMillis();
@ -272,7 +272,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
if (isNotEnoughMoney(e.getMessage())) throw e;
log.warn("Error creating creating withdraw tx, attempt={}/{}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
if (i == TradeProtocol.MAX_ATTEMPTS - 1) throw e;
if (xmrWalletService.getConnectionService().isConnected()) xmrWalletService.requestSwitchToNextBestConnection(sourceConnection);
if (xmrWalletService.getXmrConnectionService().isConnected()) xmrWalletService.requestSwitchToNextBestConnection(sourceConnection);
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
}
}

View file

@ -701,7 +701,7 @@ public class GUIUtil {
}
public static boolean isReadyForTxBroadcastOrShowPopup(XmrWalletService xmrWalletService) {
XmrConnectionService xmrConnectionService = xmrWalletService.getConnectionService();
XmrConnectionService xmrConnectionService = xmrWalletService.getXmrConnectionService();
if (!xmrConnectionService.hasSufficientPeersForBroadcast()) {
new Popup().information(Res.get("popup.warning.notSufficientConnectionsToXmrNetwork", xmrConnectionService.getMinBroadcastConnections())).show();
return false;