mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-10-01 01:35:48 -04:00
refactor base wallet so trades can sync with progress timeout, etc
This commit is contained in:
parent
1b5c03bce8
commit
ca7d596175
@ -118,7 +118,7 @@ public class CoreDisputesService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Dispute createDisputeForTrade(Trade trade, Offer offer, PubKeyRing pubKey, boolean isMaker, boolean isSupportTicket) {
|
public Dispute createDisputeForTrade(Trade trade, Offer offer, PubKeyRing pubKey, boolean isMaker, boolean isSupportTicket) {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
byte[] payoutTxSerialized = null;
|
byte[] payoutTxSerialized = null;
|
||||||
String payoutTxHashAsString = null;
|
String payoutTxHashAsString = null;
|
||||||
|
|
||||||
@ -163,7 +163,7 @@ public class CoreDisputesService {
|
|||||||
if (winningDisputeOptional.isPresent()) winningDispute = winningDisputeOptional.get();
|
if (winningDisputeOptional.isPresent()) winningDispute = winningDisputeOptional.get();
|
||||||
else throw new IllegalStateException(format("dispute for tradeId '%s' not found", tradeId));
|
else throw new IllegalStateException(format("dispute for tradeId '%s' not found", tradeId));
|
||||||
|
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// create dispute result
|
// create dispute result
|
||||||
|
@ -1085,7 +1085,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||||||
BigInteger reserveAmount = openOffer.getOffer().getAmountNeeded();
|
BigInteger reserveAmount = openOffer.getOffer().getAmountNeeded();
|
||||||
xmrWalletService.swapAddressEntryToAvailable(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING); // change funding subaddress in case funded with unsuitable output(s)
|
xmrWalletService.swapAddressEntryToAvailable(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING); // change funding subaddress in case funded with unsuitable output(s)
|
||||||
MoneroTxWallet splitOutputTx = null;
|
MoneroTxWallet splitOutputTx = null;
|
||||||
synchronized (XmrWalletService.WALLET_LOCK) {
|
synchronized (HavenoUtils.xmrWalletService.getWalletLock()) {
|
||||||
XmrAddressEntry entry = xmrWalletService.getOrCreateAddressEntry(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING);
|
XmrAddressEntry entry = xmrWalletService.getOrCreateAddressEntry(openOffer.getId(), XmrAddressEntry.Context.OFFER_FUNDING);
|
||||||
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
|
@ -30,7 +30,6 @@ import haveno.core.offer.placeoffer.PlaceOfferModel;
|
|||||||
import haveno.core.trade.HavenoUtils;
|
import haveno.core.trade.HavenoUtils;
|
||||||
import haveno.core.trade.protocol.TradeProtocol;
|
import haveno.core.trade.protocol.TradeProtocol;
|
||||||
import haveno.core.xmr.model.XmrAddressEntry;
|
import haveno.core.xmr.model.XmrAddressEntry;
|
||||||
import haveno.core.xmr.wallet.XmrWalletService;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import monero.common.MoneroRpcConnection;
|
import monero.common.MoneroRpcConnection;
|
||||||
import monero.daemon.model.MoneroOutput;
|
import monero.daemon.model.MoneroOutput;
|
||||||
@ -60,11 +59,11 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// verify monero connection
|
// verify monero connection
|
||||||
model.getXmrWalletService().getConnectionService().verifyConnection();
|
model.getXmrWalletService().getXmrConnectionService().verifyConnection();
|
||||||
|
|
||||||
// create reserve tx
|
// create reserve tx
|
||||||
MoneroTxWallet reserveTx = null;
|
MoneroTxWallet reserveTx = null;
|
||||||
synchronized (XmrWalletService.WALLET_LOCK) {
|
synchronized (HavenoUtils.xmrWalletService.getWalletLock()) {
|
||||||
|
|
||||||
// reset protocol timeout
|
// reset protocol timeout
|
||||||
verifyPending();
|
verifyPending();
|
||||||
@ -83,7 +82,7 @@ public class MakerReserveOfferFunds extends Task<PlaceOfferModel> {
|
|||||||
try {
|
try {
|
||||||
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||||
MoneroRpcConnection sourceConnection = model.getXmrWalletService().getConnectionService().getConnection();
|
MoneroRpcConnection sourceConnection = model.getXmrWalletService().getXmrConnectionService().getConnection();
|
||||||
try {
|
try {
|
||||||
//if (true) throw new RuntimeException("Pretend error");
|
//if (true) throw new RuntimeException("Pretend error");
|
||||||
reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, makerFee, sendAmount, securityDeposit, returnAddress, openOffer.isReserveExactAmount(), preferredSubaddressIndex);
|
reserveTx = model.getXmrWalletService().createReserveTx(penaltyFee, makerFee, sendAmount, securityDeposit, returnAddress, openOffer.isReserveExactAmount(), preferredSubaddressIndex);
|
||||||
|
@ -499,7 +499,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
|||||||
|
|
||||||
// process on trade thread
|
// process on trade thread
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
String errorMessage = null;
|
String errorMessage = null;
|
||||||
PubKeyRing senderPubKeyRing = null;
|
PubKeyRing senderPubKeyRing = null;
|
||||||
try {
|
try {
|
||||||
|
@ -240,7 +240,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
|||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
ChatMessage chatMessage = null;
|
ChatMessage chatMessage = null;
|
||||||
Dispute dispute = null;
|
Dispute dispute = null;
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
try {
|
try {
|
||||||
DisputeResult disputeResult = disputeClosedMessage.getDisputeResult();
|
DisputeResult disputeResult = disputeClosedMessage.getDisputeResult();
|
||||||
chatMessage = disputeResult.getChatMessage();
|
chatMessage = disputeResult.getChatMessage();
|
||||||
@ -384,7 +384,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
|||||||
public void maybeReprocessDisputeClosedMessage(Trade trade, boolean reprocessOnError) {
|
public void maybeReprocessDisputeClosedMessage(Trade trade, boolean reprocessOnError) {
|
||||||
if (trade.isShutDownStarted()) return;
|
if (trade.isShutDownStarted()) return;
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
|
|
||||||
// skip if no need to reprocess
|
// 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()) {
|
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);
|
trade.setPayoutTxHex(signedMultisigTxHex);
|
||||||
requestPersistence(trade);
|
requestPersistence(trade);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(e.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
// verify mining fee is within tolerance by recreating payout tx
|
// verify mining fee is within tolerance by recreating payout tx
|
||||||
|
@ -28,6 +28,7 @@ import haveno.common.crypto.KeyRing;
|
|||||||
import haveno.common.crypto.PubKeyRing;
|
import haveno.common.crypto.PubKeyRing;
|
||||||
import haveno.common.crypto.Sig;
|
import haveno.common.crypto.Sig;
|
||||||
import haveno.common.util.Utilities;
|
import haveno.common.util.Utilities;
|
||||||
|
import haveno.core.api.XmrConnectionService;
|
||||||
import haveno.core.app.HavenoSetup;
|
import haveno.core.app.HavenoSetup;
|
||||||
import haveno.core.offer.OfferPayload;
|
import haveno.core.offer.OfferPayload;
|
||||||
import haveno.core.offer.OpenOfferManager;
|
import haveno.core.offer.OpenOfferManager;
|
||||||
@ -106,6 +107,7 @@ public class HavenoUtils {
|
|||||||
public static HavenoSetup havenoSetup;
|
public static HavenoSetup havenoSetup;
|
||||||
public static ArbitrationManager arbitrationManager;
|
public static ArbitrationManager arbitrationManager;
|
||||||
public static XmrWalletService xmrWalletService;
|
public static XmrWalletService xmrWalletService;
|
||||||
|
public static XmrConnectionService xmrConnectionService;
|
||||||
public static OpenOfferManager openOfferManager;
|
public static OpenOfferManager openOfferManager;
|
||||||
|
|
||||||
public static boolean isSeedNode() {
|
public static boolean isSeedNode() {
|
||||||
|
@ -44,7 +44,6 @@ import haveno.common.crypto.PubKeyRing;
|
|||||||
import haveno.common.proto.ProtoUtil;
|
import haveno.common.proto.ProtoUtil;
|
||||||
import haveno.common.taskrunner.Model;
|
import haveno.common.taskrunner.Model;
|
||||||
import haveno.common.util.Utilities;
|
import haveno.common.util.Utilities;
|
||||||
import haveno.core.api.XmrConnectionService;
|
|
||||||
import haveno.core.monetary.Price;
|
import haveno.core.monetary.Price;
|
||||||
import haveno.core.monetary.Volume;
|
import haveno.core.monetary.Volume;
|
||||||
import haveno.core.network.MessageState;
|
import haveno.core.network.MessageState;
|
||||||
@ -69,6 +68,7 @@ import haveno.core.trade.protocol.TradeProtocol;
|
|||||||
import haveno.core.trade.statistics.TradeStatistics3;
|
import haveno.core.trade.statistics.TradeStatistics3;
|
||||||
import haveno.core.util.VolumeUtil;
|
import haveno.core.util.VolumeUtil;
|
||||||
import haveno.core.xmr.model.XmrAddressEntry;
|
import haveno.core.xmr.model.XmrAddressEntry;
|
||||||
|
import haveno.core.xmr.wallet.XmrWalletBase;
|
||||||
import haveno.core.xmr.wallet.XmrWalletService;
|
import haveno.core.xmr.wallet.XmrWalletService;
|
||||||
import haveno.network.p2p.AckMessage;
|
import haveno.network.p2p.AckMessage;
|
||||||
import haveno.network.p2p.NodeAddress;
|
import haveno.network.p2p.NodeAddress;
|
||||||
@ -76,14 +76,12 @@ import haveno.network.p2p.P2PService;
|
|||||||
import haveno.network.p2p.network.TorNetworkNode;
|
import haveno.network.p2p.network.TorNetworkNode;
|
||||||
import javafx.beans.property.DoubleProperty;
|
import javafx.beans.property.DoubleProperty;
|
||||||
import javafx.beans.property.IntegerProperty;
|
import javafx.beans.property.IntegerProperty;
|
||||||
import javafx.beans.property.LongProperty;
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
import javafx.beans.property.ObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
import javafx.beans.property.ReadOnlyObjectProperty;
|
||||||
import javafx.beans.property.ReadOnlyStringProperty;
|
import javafx.beans.property.ReadOnlyStringProperty;
|
||||||
import javafx.beans.property.SimpleDoubleProperty;
|
import javafx.beans.property.SimpleDoubleProperty;
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
import javafx.beans.property.SimpleIntegerProperty;
|
||||||
import javafx.beans.property.SimpleLongProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
import javafx.beans.property.SimpleObjectProperty;
|
||||||
import javafx.beans.property.SimpleStringProperty;
|
import javafx.beans.property.SimpleStringProperty;
|
||||||
import javafx.beans.property.StringProperty;
|
import javafx.beans.property.StringProperty;
|
||||||
@ -135,19 +133,17 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||||||
* stored in the task model.
|
* stored in the task model.
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@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 String MONERO_TRADE_WALLET_PREFIX = "xmr_trade_";
|
||||||
private static final long SHUTDOWN_TIMEOUT_MS = 60000;
|
private static final long SHUTDOWN_TIMEOUT_MS = 60000;
|
||||||
private static final long SYNC_EVERY_NUM_BLOCKS = 360; // ~1/2 day
|
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 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 EXTENDED_RPC_TIMEOUT = 600000; // 10 minutes
|
||||||
private static final long DELETE_AFTER_MS = TradeProtocol.TRADE_STEP_TIMEOUT_SECONDS;
|
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 Object pollLock = new Object();
|
||||||
private final LongProperty walletHeight = new SimpleLongProperty(0);
|
|
||||||
private MoneroWallet wallet;
|
|
||||||
private boolean wasWalletSynced;
|
|
||||||
private boolean pollInProgress;
|
private boolean pollInProgress;
|
||||||
private boolean restartInProgress;
|
private boolean restartInProgress;
|
||||||
private Subscription protocolErrorStateSubscription;
|
private Subscription protocolErrorStateSubscription;
|
||||||
@ -413,9 +409,6 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
// Immutable
|
// Immutable
|
||||||
@Getter
|
@Getter
|
||||||
transient final private XmrWalletService xmrWalletService;
|
transient final private XmrWalletService xmrWalletService;
|
||||||
@Getter
|
|
||||||
transient final private XmrConnectionService xmrConnectionService;
|
|
||||||
|
|
||||||
transient final private DoubleProperty initProgressProperty = new SimpleDoubleProperty(0.0);
|
transient final private DoubleProperty initProgressProperty = new SimpleDoubleProperty(0.0);
|
||||||
transient final private ObjectProperty<State> stateProperty = new SimpleObjectProperty<>(state);
|
transient final private ObjectProperty<State> stateProperty = new SimpleObjectProperty<>(state);
|
||||||
transient final private ObjectProperty<Phase> phaseProperty = new SimpleObjectProperty<>(state.phase);
|
transient final private ObjectProperty<Phase> phaseProperty = new SimpleObjectProperty<>(state.phase);
|
||||||
@ -441,10 +434,6 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
@Getter
|
@Getter
|
||||||
transient private boolean isInitialized;
|
transient private boolean isInitialized;
|
||||||
transient private boolean isFullyInitialized;
|
transient private boolean isFullyInitialized;
|
||||||
@Getter
|
|
||||||
transient private boolean isShutDownStarted;
|
|
||||||
@Getter
|
|
||||||
transient private boolean isShutDown;
|
|
||||||
|
|
||||||
// Added in v1.2.0
|
// Added in v1.2.0
|
||||||
transient private ObjectProperty<BigInteger> tradeAmountProperty;
|
transient private ObjectProperty<BigInteger> tradeAmountProperty;
|
||||||
@ -511,11 +500,12 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
@Nullable NodeAddress makerNodeAddress,
|
@Nullable NodeAddress makerNodeAddress,
|
||||||
@Nullable NodeAddress takerNodeAddress,
|
@Nullable NodeAddress takerNodeAddress,
|
||||||
@Nullable NodeAddress arbitratorNodeAddress) {
|
@Nullable NodeAddress arbitratorNodeAddress) {
|
||||||
|
super();
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
this.amount = tradeAmount.longValueExact();
|
this.amount = tradeAmount.longValueExact();
|
||||||
this.price = tradePrice;
|
this.price = tradePrice;
|
||||||
this.xmrWalletService = xmrWalletService;
|
this.xmrWalletService = xmrWalletService;
|
||||||
this.xmrConnectionService = xmrWalletService.getConnectionService();
|
this.xmrConnectionService = xmrWalletService.getXmrConnectionService();
|
||||||
this.processModel = processModel;
|
this.processModel = processModel;
|
||||||
this.uid = uid;
|
this.uid = uid;
|
||||||
this.takeOfferDate = new Date().getTime();
|
this.takeOfferDate = new Date().getTime();
|
||||||
@ -1512,13 +1502,13 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
|
|
||||||
// repeatedly acquire lock to clear tasks
|
// repeatedly acquire lock to clear tasks
|
||||||
for (int i = 0; i < 20; i++) {
|
for (int i = 0; i < 20; i++) {
|
||||||
synchronized (this) {
|
synchronized (getLock()) {
|
||||||
HavenoUtils.waitFor(10);
|
HavenoUtils.waitFor(10);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// shut down trade threads
|
// shut down trade threads
|
||||||
synchronized (this) {
|
synchronized (getLock()) {
|
||||||
isInitialized = false;
|
isInitialized = false;
|
||||||
isShutDown = true;
|
isShutDown = true;
|
||||||
List<Runnable> shutDownThreads = new ArrayList<>();
|
List<Runnable> shutDownThreads = new ArrayList<>();
|
||||||
@ -2636,8 +2626,7 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
private void syncWalletIfBehind() {
|
private void syncWalletIfBehind() {
|
||||||
if (isWalletBehind()) {
|
if (isWalletBehind()) {
|
||||||
synchronized (walletLock) {
|
synchronized (walletLock) {
|
||||||
xmrWalletService.syncWallet(wallet);
|
syncWithProgress();
|
||||||
walletHeight.set(wallet.getHeight());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2812,7 +2801,7 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
if (!isInitialized || isShutDownStarted) return;
|
if (!isInitialized || isShutDownStarted) return;
|
||||||
if (isWalletConnectedToDaemon()) {
|
if (isWalletConnectedToDaemon()) {
|
||||||
e.printStackTrace();
|
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());
|
}, getId());
|
||||||
|
@ -45,7 +45,7 @@ public class ArbitratorProtocol extends DisputeProtocol {
|
|||||||
public void handleInitTradeRequest(InitTradeRequest message, NodeAddress peer, ErrorMessageHandler errorMessageHandler) {
|
public void handleInitTradeRequest(InitTradeRequest message, NodeAddress peer, ErrorMessageHandler errorMessageHandler) {
|
||||||
System.out.println("ArbitratorProtocol.handleInitTradeRequest()");
|
System.out.println("ArbitratorProtocol.handleInitTradeRequest()");
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
this.errorMessageHandler = errorMessageHandler;
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
processModel.setTradeMessage(message); // TODO (woodser): confirm these are null without being set
|
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) {
|
public void handleDepositRequest(DepositRequest request, NodeAddress sender) {
|
||||||
System.out.println("ArbitratorProtocol.handleDepositRequest() " + trade.getId());
|
System.out.println("ArbitratorProtocol.handleDepositRequest() " + trade.getId());
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
processModel.setTradeMessage(request);
|
processModel.setTradeMessage(request);
|
||||||
|
@ -62,7 +62,7 @@ public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol
|
|||||||
ErrorMessageHandler errorMessageHandler) {
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
this.errorMessageHandler = errorMessageHandler;
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
expect(phase(Trade.Phase.INIT)
|
expect(phase(Trade.Phase.INIT)
|
||||||
|
@ -70,7 +70,7 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
|
|||||||
ErrorMessageHandler errorMessageHandler) {
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
System.out.println(getClass().getSimpleName() + ".onTakeOffer()");
|
System.out.println(getClass().getSimpleName() + ".onTakeOffer()");
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
this.tradeResultHandler = tradeResultHandler;
|
this.tradeResultHandler = tradeResultHandler;
|
||||||
this.errorMessageHandler = errorMessageHandler;
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
@ -101,7 +101,7 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
|
|||||||
NodeAddress peer) {
|
NodeAddress peer) {
|
||||||
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
expect(phase(Trade.Phase.INIT)
|
expect(phase(Trade.Phase.INIT)
|
||||||
.with(message)
|
.with(message)
|
||||||
|
@ -75,7 +75,7 @@ public class BuyerProtocol extends DisputeProtocol {
|
|||||||
// re-send payment sent message if not acked
|
// re-send payment sent message if not acked
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
if (trade.isShutDownStarted() || trade.isPayoutPublished()) return;
|
if (trade.isShutDownStarted() || trade.isPayoutPublished()) return;
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
if (trade.isShutDownStarted() || trade.isPayoutPublished()) return;
|
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()) {
|
if (trade.getState().ordinal() >= Trade.State.BUYER_SENT_PAYMENT_SENT_MSG.ordinal() && trade.getState().ordinal() < Trade.State.SELLER_RECEIVED_PAYMENT_SENT_MSG.ordinal()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
@ -121,7 +121,7 @@ public class BuyerProtocol extends DisputeProtocol {
|
|||||||
public void onPaymentSent(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
public void onPaymentSent(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
System.out.println("BuyerProtocol.onPaymentSent()");
|
System.out.println("BuyerProtocol.onPaymentSent()");
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
this.errorMessageHandler = errorMessageHandler;
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
BuyerEvent event = BuyerEvent.PAYMENT_SENT;
|
BuyerEvent event = BuyerEvent.PAYMENT_SENT;
|
||||||
|
@ -67,7 +67,7 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
|
|||||||
ErrorMessageHandler errorMessageHandler) {
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
this.errorMessageHandler = errorMessageHandler;
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
expect(phase(Trade.Phase.INIT)
|
expect(phase(Trade.Phase.INIT)
|
||||||
|
@ -70,7 +70,7 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
|
|||||||
ErrorMessageHandler errorMessageHandler) {
|
ErrorMessageHandler errorMessageHandler) {
|
||||||
System.out.println(getClass().getSimpleName() + ".onTakeOffer()");
|
System.out.println(getClass().getSimpleName() + ".onTakeOffer()");
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
this.tradeResultHandler = tradeResultHandler;
|
this.tradeResultHandler = tradeResultHandler;
|
||||||
this.errorMessageHandler = errorMessageHandler;
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
@ -101,7 +101,7 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
|
|||||||
NodeAddress peer) {
|
NodeAddress peer) {
|
||||||
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
System.out.println(getClass().getCanonicalName() + ".handleInitTradeRequest()");
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
expect(phase(Trade.Phase.INIT)
|
expect(phase(Trade.Phase.INIT)
|
||||||
.with(message)
|
.with(message)
|
||||||
|
@ -70,7 +70,7 @@ public class SellerProtocol extends DisputeProtocol {
|
|||||||
// re-send payment received message if payout not published
|
// re-send payment received message if payout not published
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
if (trade.isShutDownStarted() || trade.isPayoutPublished()) return;
|
if (trade.isShutDownStarted() || trade.isPayoutPublished()) return;
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
if (trade.isShutDownStarted() || trade.isPayoutPublished()) return;
|
if (trade.isShutDownStarted() || trade.isPayoutPublished()) return;
|
||||||
if (trade.getState().ordinal() >= Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal() && !trade.isPayoutPublished()) {
|
if (trade.getState().ordinal() >= Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal() && !trade.isPayoutPublished()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
@ -117,7 +117,7 @@ public class SellerProtocol extends DisputeProtocol {
|
|||||||
public void onPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
public void onPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
log.info("SellerProtocol.onPaymentReceived()");
|
log.info("SellerProtocol.onPaymentReceived()");
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
this.errorMessageHandler = errorMessageHandler;
|
this.errorMessageHandler = errorMessageHandler;
|
||||||
SellerEvent event = SellerEvent.PAYMENT_RECEIVED;
|
SellerEvent event = SellerEvent.PAYMENT_RECEIVED;
|
||||||
|
@ -243,7 +243,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||||||
if (!trade.isCompleted()) processModel.getP2PService().addDecryptedDirectMessageListener(this);
|
if (!trade.isCompleted()) processModel.getP2PService().addDecryptedDirectMessageListener(this);
|
||||||
|
|
||||||
// initialize trade
|
// initialize trade
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
trade.initialize(processModel.getProvider());
|
trade.initialize(processModel.getProvider());
|
||||||
|
|
||||||
// process mailbox messages
|
// process mailbox messages
|
||||||
@ -261,7 +261,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
if (!trade.isDepositsConfirmed() || trade.isDepositsConfirmedAcked() || trade.isPayoutPublished() || depositsConfirmedTasksCalled) return;
|
if (!trade.isDepositsConfirmed() || trade.isDepositsConfirmedAcked() || trade.isPayoutPublished() || depositsConfirmedTasksCalled) return;
|
||||||
depositsConfirmedTasksCalled = true;
|
depositsConfirmedTasksCalled = true;
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
if (!trade.isInitialized() || trade.isShutDownStarted()) return; // skip if shutting down
|
if (!trade.isInitialized() || trade.isShutDownStarted()) return; // skip if shutting down
|
||||||
latchTrade();
|
latchTrade();
|
||||||
expect(new Condition(trade))
|
expect(new Condition(trade))
|
||||||
@ -282,7 +282,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||||||
public void maybeReprocessPaymentReceivedMessage(boolean reprocessOnError) {
|
public void maybeReprocessPaymentReceivedMessage(boolean reprocessOnError) {
|
||||||
if (trade.isShutDownStarted()) return;
|
if (trade.isShutDownStarted()) return;
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
|
|
||||||
// skip if no need to reprocess
|
// 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())) {
|
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());
|
System.out.println(getClass().getSimpleName() + ".handleInitMultisigRequest() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||||
trade.addInitProgressStep();
|
trade.addInitProgressStep();
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
|
|
||||||
// check trade
|
// check trade
|
||||||
if (trade.hasFailed()) {
|
if (trade.hasFailed()) {
|
||||||
@ -335,7 +335,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||||||
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
|
public void handleSignContractRequest(SignContractRequest message, NodeAddress sender) {
|
||||||
System.out.println(getClass().getSimpleName() + ".handleSignContractRequest() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
System.out.println(getClass().getSimpleName() + ".handleSignContractRequest() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
|
|
||||||
// check trade
|
// check trade
|
||||||
if (trade.hasFailed()) {
|
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());
|
System.out.println(getClass().getSimpleName() + ".handleSignContractResponse() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||||
trade.addInitProgressStep();
|
trade.addInitProgressStep();
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
|
|
||||||
// check trade
|
// check trade
|
||||||
if (trade.hasFailed()) {
|
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());
|
System.out.println(getClass().getSimpleName() + ".handleDepositResponse() for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||||
trade.addInitProgressStep();
|
trade.addInitProgressStep();
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
Validator.checkTradeId(processModel.getOfferId(), response);
|
Validator.checkTradeId(processModel.getOfferId(), response);
|
||||||
latchTrade();
|
latchTrade();
|
||||||
processModel.setTradeMessage(response);
|
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());
|
System.out.println(getClass().getSimpleName() + ".handle(DepositsConfirmedMessage) from " + sender + " for " + trade.getClass().getSimpleName() + " " + trade.getShortId());
|
||||||
if (!trade.isInitialized() || trade.isShutDown()) return;
|
if (!trade.isInitialized() || trade.isShutDown()) return;
|
||||||
ThreadUtils.execute(() -> {
|
ThreadUtils.execute(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
if (!trade.isInitialized() || trade.isShutDown()) return;
|
if (!trade.isInitialized() || trade.isShutDown()) return;
|
||||||
latchTrade();
|
latchTrade();
|
||||||
this.errorMessageHandler = null;
|
this.errorMessageHandler = null;
|
||||||
@ -493,7 +493,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||||||
// a mailbox message with PaymentSentMessage.
|
// a mailbox message with PaymentSentMessage.
|
||||||
// TODO A better fix would be to add a listener for the wallet sync state and process
|
// 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.
|
// the mailbox msg once wallet is ready and trade state set.
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
if (!trade.isInitialized() || trade.isShutDown()) return;
|
if (!trade.isInitialized() || trade.isShutDown()) return;
|
||||||
if (trade.getPhase().ordinal() >= Trade.Phase.PAYMENT_SENT.ordinal()) {
|
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());
|
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");
|
log.warn("Ignoring PaymentReceivedMessage since not buyer or arbitrator");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
synchronized (trade) {
|
synchronized (trade.getLock()) {
|
||||||
if (!trade.isInitialized() || trade.isShutDown()) return;
|
if (!trade.isInitialized() || trade.isShutDown()) return;
|
||||||
latchTrade();
|
latchTrade();
|
||||||
Validator.checkTradeId(processModel.getOfferId(), message);
|
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) {
|
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) {
|
if (message != null) {
|
||||||
sendAckMessage(ackReceiver, message, false, errorMessage);
|
sendAckMessage(ackReceiver, message, false, errorMessage);
|
||||||
|
@ -28,7 +28,6 @@ import haveno.core.trade.Trade.State;
|
|||||||
import haveno.core.trade.messages.SignContractRequest;
|
import haveno.core.trade.messages.SignContractRequest;
|
||||||
import haveno.core.trade.protocol.TradeProtocol;
|
import haveno.core.trade.protocol.TradeProtocol;
|
||||||
import haveno.core.xmr.model.XmrAddressEntry;
|
import haveno.core.xmr.model.XmrAddressEntry;
|
||||||
import haveno.core.xmr.wallet.XmrWalletService;
|
|
||||||
import haveno.network.p2p.SendDirectMessageListener;
|
import haveno.network.p2p.SendDirectMessageListener;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import monero.common.MoneroRpcConnection;
|
import monero.common.MoneroRpcConnection;
|
||||||
@ -78,7 +77,7 @@ public class MaybeSendSignContractRequest extends TradeTask {
|
|||||||
|
|
||||||
// create deposit tx and freeze inputs
|
// create deposit tx and freeze inputs
|
||||||
MoneroTxWallet depositTx = null;
|
MoneroTxWallet depositTx = null;
|
||||||
synchronized (XmrWalletService.WALLET_LOCK) {
|
synchronized (HavenoUtils.xmrWalletService.getWalletLock()) {
|
||||||
|
|
||||||
// check for timeout
|
// check for timeout
|
||||||
if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while getting lock to create deposit tx, tradeId=" + trade.getShortId());
|
if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while getting lock to create deposit tx, tradeId=" + trade.getShortId());
|
||||||
|
@ -24,7 +24,6 @@ import haveno.core.trade.TakerTrade;
|
|||||||
import haveno.core.trade.Trade;
|
import haveno.core.trade.Trade;
|
||||||
import haveno.core.trade.protocol.TradeProtocol;
|
import haveno.core.trade.protocol.TradeProtocol;
|
||||||
import haveno.core.xmr.model.XmrAddressEntry;
|
import haveno.core.xmr.model.XmrAddressEntry;
|
||||||
import haveno.core.xmr.wallet.XmrWalletService;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import monero.common.MoneroRpcConnection;
|
import monero.common.MoneroRpcConnection;
|
||||||
import monero.wallet.model.MoneroTxWallet;
|
import monero.wallet.model.MoneroTxWallet;
|
||||||
@ -50,7 +49,7 @@ public class TakerReserveTradeFunds extends TradeTask {
|
|||||||
|
|
||||||
// create reserve tx
|
// create reserve tx
|
||||||
MoneroTxWallet reserveTx = null;
|
MoneroTxWallet reserveTx = null;
|
||||||
synchronized (XmrWalletService.WALLET_LOCK) {
|
synchronized (HavenoUtils.xmrWalletService.getWalletLock()) {
|
||||||
|
|
||||||
// check for timeout
|
// check for timeout
|
||||||
if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while getting lock to create reserve tx, tradeId=" + trade.getShortId());
|
if (isTimedOut()) throw new RuntimeException("Trade protocol has timed out while getting lock to create reserve tx, tradeId=" + trade.getShortId());
|
||||||
|
@ -44,6 +44,7 @@ import haveno.core.offer.OpenOfferManager;
|
|||||||
import haveno.core.support.dispute.Dispute;
|
import haveno.core.support.dispute.Dispute;
|
||||||
import haveno.core.support.dispute.refund.RefundManager;
|
import haveno.core.support.dispute.refund.RefundManager;
|
||||||
import haveno.core.trade.ClosedTradableManager;
|
import haveno.core.trade.ClosedTradableManager;
|
||||||
|
import haveno.core.trade.HavenoUtils;
|
||||||
import haveno.core.trade.MakerTrade;
|
import haveno.core.trade.MakerTrade;
|
||||||
import haveno.core.trade.Trade;
|
import haveno.core.trade.Trade;
|
||||||
import haveno.core.trade.TradeManager;
|
import haveno.core.trade.TradeManager;
|
||||||
@ -124,7 +125,7 @@ public class Balances {
|
|||||||
|
|
||||||
private void doUpdateBalances() {
|
private void doUpdateBalances() {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
synchronized (XmrWalletService.WALLET_LOCK) {
|
synchronized (HavenoUtils.xmrWalletService.getWalletLock()) {
|
||||||
|
|
||||||
// get wallet balances
|
// get wallet balances
|
||||||
BigInteger balance = xmrWalletService.getWallet() == null ? BigInteger.ZERO : xmrWalletService.getBalance();
|
BigInteger balance = xmrWalletService.getWallet() == null ? BigInteger.ZERO : xmrWalletService.getBalance();
|
||||||
|
165
core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java
Normal file
165
core/src/main/java/haveno/core/xmr/wallet/XmrWalletBase.java
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
@ -24,7 +24,6 @@ import com.google.inject.name.Named;
|
|||||||
|
|
||||||
import common.utils.JsonUtils;
|
import common.utils.JsonUtils;
|
||||||
import haveno.common.ThreadUtils;
|
import haveno.common.ThreadUtils;
|
||||||
import haveno.common.Timer;
|
|
||||||
import haveno.common.UserThread;
|
import haveno.common.UserThread;
|
||||||
import haveno.common.config.Config;
|
import haveno.common.config.Config;
|
||||||
import haveno.common.file.FileUtil;
|
import haveno.common.file.FileUtil;
|
||||||
@ -43,7 +42,6 @@ import haveno.core.user.User;
|
|||||||
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 java.io.File;
|
import java.io.File;
|
||||||
@ -55,26 +53,22 @@ import java.util.ArrayList;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
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.concurrent.TimeUnit;
|
|
||||||
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.LongProperty;
|
||||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
import javafx.beans.property.ReadOnlyDoubleProperty;
|
||||||
import javafx.beans.property.SimpleLongProperty;
|
|
||||||
import javafx.beans.value.ChangeListener;
|
import javafx.beans.value.ChangeListener;
|
||||||
|
import lombok.Getter;
|
||||||
import monero.common.MoneroError;
|
import monero.common.MoneroError;
|
||||||
import monero.common.MoneroRpcConnection;
|
import monero.common.MoneroRpcConnection;
|
||||||
import monero.common.MoneroRpcError;
|
import monero.common.MoneroRpcError;
|
||||||
@ -103,14 +97,13 @@ import monero.wallet.model.MoneroTxPriority;
|
|||||||
import monero.wallet.model.MoneroTxQuery;
|
import monero.wallet.model.MoneroTxQuery;
|
||||||
import monero.wallet.model.MoneroTxWallet;
|
import monero.wallet.model.MoneroTxWallet;
|
||||||
import monero.wallet.model.MoneroWalletConfig;
|
import monero.wallet.model.MoneroWalletConfig;
|
||||||
import monero.wallet.model.MoneroWalletListener;
|
|
||||||
import monero.wallet.model.MoneroWalletListenerI;
|
import monero.wallet.model.MoneroWalletListenerI;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
|
||||||
public class XmrWalletService {
|
public class XmrWalletService extends XmrWalletBase {
|
||||||
private static final Logger log = LoggerFactory.getLogger(XmrWalletService.class);
|
private static final Logger log = LoggerFactory.getLogger(XmrWalletService.class);
|
||||||
|
|
||||||
// monero configuration
|
// monero configuration
|
||||||
@ -138,11 +131,8 @@ public class XmrWalletService {
|
|||||||
private final User user;
|
private final User user;
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
private final CoreAccountService accountService;
|
private final CoreAccountService accountService;
|
||||||
private final XmrConnectionService xmrConnectionService;
|
|
||||||
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;
|
||||||
@ -153,22 +143,10 @@ public class XmrWalletService {
|
|||||||
|
|
||||||
private ChangeListener<? super Number> walletInitListener;
|
private ChangeListener<? super Number> walletInitListener;
|
||||||
private TradeManager tradeManager;
|
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 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 TaskLooper pollLooper;
|
||||||
private boolean pollInProgress;
|
private boolean pollInProgress;
|
||||||
private Long pollPeriodMs;
|
private Long pollPeriodMs;
|
||||||
@ -180,9 +158,6 @@ public class XmrWalletService {
|
|||||||
private List<MoneroSubaddress> cachedSubaddresses;
|
private List<MoneroSubaddress> cachedSubaddresses;
|
||||||
private List<MoneroOutputWallet> cachedOutputs;
|
private List<MoneroOutputWallet> cachedOutputs;
|
||||||
private List<MoneroTxWallet> cachedTxs;
|
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")
|
@SuppressWarnings("unused")
|
||||||
@Inject
|
@Inject
|
||||||
@ -198,7 +173,6 @@ public class XmrWalletService {
|
|||||||
this.user = user;
|
this.user = user;
|
||||||
this.preferences = preferences;
|
this.preferences = preferences;
|
||||||
this.accountService = accountService;
|
this.accountService = accountService;
|
||||||
this.xmrConnectionService = xmrConnectionService;
|
|
||||||
this.walletsSetup = walletsSetup;
|
this.walletsSetup = walletsSetup;
|
||||||
this.xmrAddressEntryList = xmrAddressEntryList;
|
this.xmrAddressEntryList = xmrAddressEntryList;
|
||||||
this.walletDir = walletDir;
|
this.walletDir = walletDir;
|
||||||
@ -206,6 +180,8 @@ public class XmrWalletService {
|
|||||||
this.useNativeXmrWallet = useNativeXmrWallet;
|
this.useNativeXmrWallet = useNativeXmrWallet;
|
||||||
this.xmrWalletFile = new File(walletDir, MONERO_WALLET_NAME);
|
this.xmrWalletFile = new File(walletDir, MONERO_WALLET_NAME);
|
||||||
HavenoUtils.xmrWalletService = this;
|
HavenoUtils.xmrWalletService = this;
|
||||||
|
HavenoUtils.xmrConnectionService = xmrConnectionService;
|
||||||
|
this.xmrConnectionService = xmrConnectionService; // TODO: super's is null unless set here from injection
|
||||||
|
|
||||||
// set monero logging
|
// set monero logging
|
||||||
if (MONERO_LOG_LEVEL >= 0) MoneroUtils.setLogLevel(MONERO_LOG_LEVEL);
|
if (MONERO_LOG_LEVEL >= 0) MoneroUtils.setLogLevel(MONERO_LOG_LEVEL);
|
||||||
@ -320,10 +296,6 @@ public class XmrWalletService {
|
|||||||
return xmrConnectionService.getDaemon();
|
return xmrConnectionService.getDaemon();
|
||||||
}
|
}
|
||||||
|
|
||||||
public XmrConnectionService getConnectionService() {
|
|
||||||
return xmrConnectionService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isProxyApplied() {
|
public boolean isProxyApplied() {
|
||||||
return isProxyApplied(wasWalletSynced);
|
return isProxyApplied(wasWalletSynced);
|
||||||
}
|
}
|
||||||
@ -464,7 +436,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public MoneroTxWallet createTx(MoneroTxConfig txConfig) {
|
public MoneroTxWallet createTx(MoneroTxConfig txConfig) {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||||
MoneroTxWallet tx = wallet.createTx(txConfig);
|
MoneroTxWallet tx = wallet.createTx(txConfig);
|
||||||
if (Boolean.TRUE.equals(txConfig.getRelay())) {
|
if (Boolean.TRUE.equals(txConfig.getRelay())) {
|
||||||
@ -478,7 +450,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public String relayTx(String metadata) {
|
public String relayTx(String metadata) {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
String txId = wallet.relayTx(metadata);
|
String txId = wallet.relayTx(metadata);
|
||||||
requestSaveMainWallet();
|
requestSaveMainWallet();
|
||||||
return txId;
|
return txId;
|
||||||
@ -495,7 +467,7 @@ public class XmrWalletService {
|
|||||||
* Freeze reserved outputs and thaw unreserved outputs.
|
* Freeze reserved outputs and thaw unreserved outputs.
|
||||||
*/
|
*/
|
||||||
public void fixReservedOutputs() {
|
public void fixReservedOutputs() {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
|
|
||||||
// collect reserved outputs
|
// collect reserved outputs
|
||||||
Set<String> reservedKeyImages = new HashSet<String>();
|
Set<String> reservedKeyImages = new HashSet<String>();
|
||||||
@ -514,7 +486,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void freezeReservedOutputs(Set<String> reservedKeyImages) {
|
private void freezeReservedOutputs(Set<String> reservedKeyImages) {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
|
|
||||||
// ensure wallet is open
|
// ensure wallet is open
|
||||||
if (wallet == null) {
|
if (wallet == null) {
|
||||||
@ -538,7 +510,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void thawUnreservedOutputs(Set<String> reservedKeyImages) {
|
private void thawUnreservedOutputs(Set<String> reservedKeyImages) {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
|
|
||||||
// ensure wallet is open
|
// ensure wallet is open
|
||||||
if (wallet == null) {
|
if (wallet == null) {
|
||||||
@ -568,7 +540,7 @@ public class XmrWalletService {
|
|||||||
*/
|
*/
|
||||||
public void freezeOutputs(Collection<String> keyImages) {
|
public void freezeOutputs(Collection<String> keyImages) {
|
||||||
if (keyImages == null || keyImages.isEmpty()) return;
|
if (keyImages == null || keyImages.isEmpty()) return;
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
|
|
||||||
// collect outputs to freeze
|
// collect outputs to freeze
|
||||||
List<String> unfrozenKeyImages = getOutputs(new MoneroOutputQuery().setIsFrozen(false).setIsSpent(false)).stream()
|
List<String> unfrozenKeyImages = getOutputs(new MoneroOutputQuery().setIsFrozen(false).setIsSpent(false)).stream()
|
||||||
@ -590,7 +562,7 @@ public class XmrWalletService {
|
|||||||
*/
|
*/
|
||||||
public void thawOutputs(Collection<String> keyImages) {
|
public void thawOutputs(Collection<String> keyImages) {
|
||||||
if (keyImages == null || keyImages.isEmpty()) return;
|
if (keyImages == null || keyImages.isEmpty()) return;
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
|
|
||||||
// collect outputs to thaw
|
// collect outputs to thaw
|
||||||
List<String> frozenKeyImages = getOutputs(new MoneroOutputQuery().setIsFrozen(true).setIsSpent(false)).stream()
|
List<String> frozenKeyImages = getOutputs(new MoneroOutputQuery().setIsFrozen(true).setIsSpent(false)).stream()
|
||||||
@ -643,7 +615,7 @@ public class XmrWalletService {
|
|||||||
* @return the reserve tx
|
* @return the reserve tx
|
||||||
*/
|
*/
|
||||||
public MoneroTxWallet createReserveTx(BigInteger penaltyFee, BigInteger tradeFee, BigInteger sendTradeAmount, BigInteger securityDeposit, String returnAddress, boolean reserveExactAmount, Integer preferredSubaddressIndex) {
|
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()) {
|
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||||
log.info("Creating reserve tx with preferred subaddress index={}, return address={}", preferredSubaddressIndex, returnAddress);
|
log.info("Creating reserve tx with preferred subaddress index={}, return address={}", preferredSubaddressIndex, returnAddress);
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
@ -664,7 +636,7 @@ public class XmrWalletService {
|
|||||||
* @return MoneroTxWallet the multisig deposit tx
|
* @return MoneroTxWallet the multisig deposit tx
|
||||||
*/
|
*/
|
||||||
public MoneroTxWallet createDepositTx(Trade trade, boolean reserveExactAmount, Integer preferredSubaddressIndex) {
|
public MoneroTxWallet createDepositTx(Trade trade, boolean reserveExactAmount, Integer preferredSubaddressIndex) {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
synchronized (HavenoUtils.getWalletFunctionLock()) {
|
||||||
BigInteger feeAmount = trade instanceof MakerTrade ? trade.getMakerFee() : trade.getTakerFee();
|
BigInteger feeAmount = trade instanceof MakerTrade ? trade.getMakerFee() : trade.getTakerFee();
|
||||||
String feeAddress = trade.getProcessModel().getTradeFeeAddress();
|
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) {
|
private MoneroTxWallet createTradeTx(BigInteger feeAmount, String feeAddress, BigInteger sendAmount, String sendAddress, boolean reserveExactAmount, Integer preferredSubaddressIndex) {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
MoneroWallet wallet = getWallet();
|
MoneroWallet wallet = getWallet();
|
||||||
|
|
||||||
// create a list of subaddresses to attempt spending from in preferred order
|
// create a list of subaddresses to attempt spending from in preferred order
|
||||||
@ -919,7 +891,7 @@ public class XmrWalletService {
|
|||||||
Runnable shutDownTask = () -> {
|
Runnable shutDownTask = () -> {
|
||||||
|
|
||||||
// remove listeners
|
// remove listeners
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
if (wallet != null) {
|
if (wallet != null) {
|
||||||
for (MoneroWalletListenerI listener : new HashSet<>(wallet.getListeners())) {
|
for (MoneroWalletListenerI listener : new HashSet<>(wallet.getListeners())) {
|
||||||
wallet.removeListener(listener);
|
wallet.removeListener(listener);
|
||||||
@ -929,7 +901,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// shut down threads
|
// shut down threads
|
||||||
synchronized (this) {
|
synchronized (getLock()) {
|
||||||
List<Runnable> shutDownThreads = new ArrayList<>();
|
List<Runnable> shutDownThreads = new ArrayList<>();
|
||||||
shutDownThreads.add(() -> ThreadUtils.shutDown(THREAD_ID));
|
shutDownThreads.add(() -> ThreadUtils.shutDown(THREAD_ID));
|
||||||
ThreadUtils.awaitTasks(shutDownThreads);
|
ThreadUtils.awaitTasks(shutDownThreads);
|
||||||
@ -1345,7 +1317,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void doMaybeInitMainWallet(boolean sync, int numAttempts) {
|
private void doMaybeInitMainWallet(boolean sync, int numAttempts) {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
if (isShutDownStarted) return;
|
if (isShutDownStarted) return;
|
||||||
|
|
||||||
// open or create wallet main wallet
|
// 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) {
|
private MoneroWalletFull createWalletFull(MoneroWalletConfig config) {
|
||||||
|
|
||||||
// must be connected to daemon
|
// must be connected to daemon
|
||||||
@ -1724,7 +1591,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void onConnectionChanged(MoneroRpcConnection connection) {
|
private void onConnectionChanged(MoneroRpcConnection connection) {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
|
|
||||||
// use current connection
|
// use current connection
|
||||||
connection = xmrConnectionService.getConnection();
|
connection = xmrConnectionService.getConnection();
|
||||||
@ -1798,7 +1665,7 @@ public class XmrWalletService {
|
|||||||
|
|
||||||
private void closeMainWallet(boolean save) {
|
private void closeMainWallet(boolean save) {
|
||||||
stopPolling();
|
stopPolling();
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
try {
|
try {
|
||||||
if (wallet != null) {
|
if (wallet != null) {
|
||||||
isClosingWallet = true;
|
isClosingWallet = true;
|
||||||
@ -1834,7 +1701,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void startPolling() {
|
private void startPolling() {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
if (isShutDownStarted || isPolling()) return;
|
if (isShutDownStarted || isPolling()) return;
|
||||||
updatePollPeriod();
|
updatePollPeriod();
|
||||||
pollLooper = new TaskLooper(() -> pollWallet());
|
pollLooper = new TaskLooper(() -> pollWallet());
|
||||||
@ -1863,7 +1730,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setPollPeriod(long pollPeriodMs) {
|
private void setPollPeriod(long pollPeriodMs) {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
if (this.isShutDownStarted) return;
|
if (this.isShutDownStarted) return;
|
||||||
if (this.pollPeriodMs != null && this.pollPeriodMs == pollPeriodMs) return;
|
if (this.pollPeriodMs != null && this.pollPeriodMs == pollPeriodMs) return;
|
||||||
this.pollPeriodMs = pollPeriodMs;
|
this.pollPeriodMs = pollPeriodMs;
|
||||||
@ -1900,7 +1767,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
// sync wallet if behind daemon
|
// sync wallet if behind daemon
|
||||||
if (walletHeight.get() < xmrConnectionService.getTargetHeight()) {
|
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();
|
syncWithProgress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1908,7 +1775,7 @@ public class XmrWalletService {
|
|||||||
// fetch transactions from pool and store to cache
|
// fetch transactions from pool and store to cache
|
||||||
// TODO: ideally wallet should sync every poll and then avoid updating from pool on fetching txs?
|
// TODO: ideally wallet should sync every poll and then avoid updating from pool on fetching txs?
|
||||||
if (updateTxs) {
|
if (updateTxs) {
|
||||||
synchronized (WALLET_LOCK) { // avoid long fetch from blocking other operations
|
synchronized (walletLock) { // avoid long fetch from blocking other operations
|
||||||
synchronized (HavenoUtils.getDaemonLock()) {
|
synchronized (HavenoUtils.getDaemonLock()) {
|
||||||
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
MoneroRpcConnection sourceConnection = xmrConnectionService.getConnection();
|
||||||
try {
|
try {
|
||||||
@ -1931,13 +1798,13 @@ public class XmrWalletService {
|
|||||||
if (wallet == null || isShutDownStarted) return;
|
if (wallet == null || isShutDownStarted) return;
|
||||||
if (HavenoUtils.isUnresponsive(e)) forceRestartMainWallet();
|
if (HavenoUtils.isUnresponsive(e)) forceRestartMainWallet();
|
||||||
else if (isWalletConnectedToDaemon()) {
|
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();
|
//e.printStackTrace();
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
|
||||||
// cache wallet info last
|
// cache wallet info last
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
if (wallet != null && !isShutDownStarted) {
|
if (wallet != null && !isShutDownStarted) {
|
||||||
try {
|
try {
|
||||||
cacheWalletInfo();
|
cacheWalletInfo();
|
||||||
@ -1954,7 +1821,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private MoneroSyncResult syncMainWallet() {
|
private MoneroSyncResult syncMainWallet() {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
MoneroSyncResult result = syncWallet(wallet);
|
MoneroSyncResult result = syncWallet(wallet);
|
||||||
walletHeight.set(wallet.getHeight());
|
walletHeight.set(wallet.getHeight());
|
||||||
return result;
|
return result;
|
||||||
@ -1962,7 +1829,7 @@ public class XmrWalletService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isWalletConnectedToDaemon() {
|
public boolean isWalletConnectedToDaemon() {
|
||||||
synchronized (WALLET_LOCK) {
|
synchronized (walletLock) {
|
||||||
try {
|
try {
|
||||||
if (wallet == null) return false;
|
if (wallet == null) return false;
|
||||||
return wallet.isConnectedToDaemon();
|
return wallet.isConnectedToDaemon();
|
||||||
|
@ -197,7 +197,7 @@ public class TxIdTextField extends AnchorPane {
|
|||||||
try {
|
try {
|
||||||
if (trade == null) {
|
if (trade == null) {
|
||||||
tx = useCache ? xmrWalletService.getDaemonTxWithCache(txId) : xmrWalletService.getDaemonTx(txId);
|
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 {
|
} else {
|
||||||
if (txId.equals(trade.getMaker().getDepositTxHash())) tx = trade.getMakerDepositTx();
|
if (txId.equals(trade.getMaker().getDepositTxHash())) tx = trade.getMakerDepositTx();
|
||||||
else if (txId.equals(trade.getTaker().getDepositTxHash())) tx = trade.getTakerDepositTx();
|
else if (txId.equals(trade.getTaker().getDepositTxHash())) tx = trade.getTakerDepositTx();
|
||||||
|
@ -257,7 +257,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||||||
// create tx
|
// create tx
|
||||||
MoneroTxWallet tx = null;
|
MoneroTxWallet tx = null;
|
||||||
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
for (int i = 0; i < TradeProtocol.MAX_ATTEMPTS; i++) {
|
||||||
MoneroRpcConnection sourceConnection = xmrWalletService.getConnectionService().getConnection();
|
MoneroRpcConnection sourceConnection = xmrWalletService.getXmrConnectionService().getConnection();
|
||||||
try {
|
try {
|
||||||
log.info("Creating withdraw tx");
|
log.info("Creating withdraw tx");
|
||||||
long startTime = System.currentTimeMillis();
|
long startTime = System.currentTimeMillis();
|
||||||
@ -272,7 +272,7 @@ public class WithdrawalView extends ActivatableView<VBox, Void> {
|
|||||||
if (isNotEnoughMoney(e.getMessage())) throw e;
|
if (isNotEnoughMoney(e.getMessage())) throw e;
|
||||||
log.warn("Error creating creating withdraw tx, attempt={}/{}, error={}", i + 1, TradeProtocol.MAX_ATTEMPTS, e.getMessage());
|
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 (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
|
HavenoUtils.waitFor(TradeProtocol.REPROCESS_DELAY_MS); // wait before retrying
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -701,7 +701,7 @@ public class GUIUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isReadyForTxBroadcastOrShowPopup(XmrWalletService xmrWalletService) {
|
public static boolean isReadyForTxBroadcastOrShowPopup(XmrWalletService xmrWalletService) {
|
||||||
XmrConnectionService xmrConnectionService = xmrWalletService.getConnectionService();
|
XmrConnectionService xmrConnectionService = xmrWalletService.getXmrConnectionService();
|
||||||
if (!xmrConnectionService.hasSufficientPeersForBroadcast()) {
|
if (!xmrConnectionService.hasSufficientPeersForBroadcast()) {
|
||||||
new Popup().information(Res.get("popup.warning.notSufficientConnectionsToXmrNetwork", xmrConnectionService.getMinBroadcastConnections())).show();
|
new Popup().information(Res.get("popup.warning.notSufficientConnectionsToXmrNetwork", xmrConnectionService.getMinBroadcastConnections())).show();
|
||||||
return false;
|
return false;
|
||||||
|
Loading…
Reference in New Issue
Block a user