mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 09:52:23 +01:00
Merge pull request #4474 from chimp1984/stop-xmr-proof-service-at-dispute
Stop xmr proof service at dispute
This commit is contained in:
commit
d33b40128e
@ -26,6 +26,7 @@ import bisq.core.setup.CorePersistedDataHost;
|
||||
import bisq.core.setup.CoreSetup;
|
||||
import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||
import bisq.core.trade.TradeManager;
|
||||
import bisq.core.trade.txproof.xmr.XmrTxProofService;
|
||||
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
@ -164,7 +165,6 @@ public abstract class BisqExecutable implements GracefulShutDownHandler, BisqSet
|
||||
|
||||
protected void setupDevEnv() {
|
||||
DevEnv.setDevMode(config.useDevMode);
|
||||
DevEnv.setDevMode(config.useDevModeHeader);
|
||||
DevEnv.setDaoActivated(config.daoActivated);
|
||||
}
|
||||
|
||||
@ -221,6 +221,7 @@ public abstract class BisqExecutable implements GracefulShutDownHandler, BisqSet
|
||||
try {
|
||||
injector.getInstance(ArbitratorManager.class).shutDown();
|
||||
injector.getInstance(TradeManager.class).shutDown();
|
||||
injector.getInstance(XmrTxProofService.class).shutDown();
|
||||
injector.getInstance(DaoSetup.class).shutDown();
|
||||
injector.getInstance(AvoidStandbyModeService.class).shutDown();
|
||||
injector.getInstance(OpenOfferManager.class).shutDown(() -> {
|
||||
|
@ -59,6 +59,7 @@ import bisq.core.support.traderchat.TraderChatManager;
|
||||
import bisq.core.trade.TradeManager;
|
||||
import bisq.core.trade.TradeTxException;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
import bisq.core.trade.txproof.xmr.XmrTxProofService;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.user.User;
|
||||
import bisq.core.util.FormattingUtils;
|
||||
@ -167,6 +168,7 @@ public class BisqSetup {
|
||||
private final PrivateNotificationManager privateNotificationManager;
|
||||
private final FilterManager filterManager;
|
||||
private final TradeStatisticsManager tradeStatisticsManager;
|
||||
private final XmrTxProofService xmrTxProofService;
|
||||
private final ClockWatcher clockWatcher;
|
||||
private final FeeService feeService;
|
||||
private final DaoSetup daoSetup;
|
||||
@ -263,6 +265,7 @@ public class BisqSetup {
|
||||
PrivateNotificationManager privateNotificationManager,
|
||||
FilterManager filterManager,
|
||||
TradeStatisticsManager tradeStatisticsManager,
|
||||
XmrTxProofService xmrTxProofService,
|
||||
ClockWatcher clockWatcher,
|
||||
FeeService feeService,
|
||||
DaoSetup daoSetup,
|
||||
@ -308,6 +311,7 @@ public class BisqSetup {
|
||||
this.privateNotificationManager = privateNotificationManager;
|
||||
this.filterManager = filterManager;
|
||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||
this.xmrTxProofService = xmrTxProofService;
|
||||
this.clockWatcher = clockWatcher;
|
||||
this.feeService = feeService;
|
||||
this.daoSetup = daoSetup;
|
||||
@ -686,6 +690,7 @@ public class BisqSetup {
|
||||
traderChatManager.onAllServicesInitialized();
|
||||
|
||||
tradeManager.onAllServicesInitialized();
|
||||
xmrTxProofService.onAllServicesInitialized();
|
||||
|
||||
if (walletsSetup.downloadPercentageProperty().get() == 1) {
|
||||
checkForLockedUpFunds();
|
||||
|
@ -46,8 +46,6 @@ import bisq.core.trade.messages.PeerPublishedDelayedPayoutTxMessage;
|
||||
import bisq.core.trade.messages.TradeMessage;
|
||||
import bisq.core.trade.statistics.ReferralIdService;
|
||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||
import bisq.core.trade.txproof.AssetTxProofResult;
|
||||
import bisq.core.trade.txproof.xmr.XmrTxProofService;
|
||||
import bisq.core.user.User;
|
||||
import bisq.core.util.Validator;
|
||||
|
||||
@ -59,7 +57,6 @@ import bisq.network.p2p.P2PService;
|
||||
import bisq.network.p2p.SendMailboxMessageListener;
|
||||
|
||||
import bisq.common.ClockWatcher;
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.config.Config;
|
||||
import bisq.common.crypto.KeyRing;
|
||||
import bisq.common.handlers.ErrorMessageHandler;
|
||||
@ -111,8 +108,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
|
||||
public class TradeManager implements PersistedDataHost {
|
||||
private static final Logger log = LoggerFactory.getLogger(TradeManager.class);
|
||||
|
||||
@ -132,7 +127,6 @@ public class TradeManager implements PersistedDataHost {
|
||||
private final ReferralIdService referralIdService;
|
||||
private final AccountAgeWitnessService accountAgeWitnessService;
|
||||
@Getter
|
||||
private final XmrTxProofService xmrTxProofService;
|
||||
private final ArbitratorManager arbitratorManager;
|
||||
private final MediatorManager mediatorManager;
|
||||
private final RefundAgentManager refundAgentManager;
|
||||
@ -174,7 +168,6 @@ public class TradeManager implements PersistedDataHost {
|
||||
TradeStatisticsManager tradeStatisticsManager,
|
||||
ReferralIdService referralIdService,
|
||||
AccountAgeWitnessService accountAgeWitnessService,
|
||||
XmrTxProofService xmrTxProofService,
|
||||
ArbitratorManager arbitratorManager,
|
||||
MediatorManager mediatorManager,
|
||||
RefundAgentManager refundAgentManager,
|
||||
@ -197,7 +190,6 @@ public class TradeManager implements PersistedDataHost {
|
||||
this.tradeStatisticsManager = tradeStatisticsManager;
|
||||
this.referralIdService = referralIdService;
|
||||
this.accountAgeWitnessService = accountAgeWitnessService;
|
||||
this.xmrTxProofService = xmrTxProofService;
|
||||
this.arbitratorManager = arbitratorManager;
|
||||
this.mediatorManager = mediatorManager;
|
||||
this.refundAgentManager = refundAgentManager;
|
||||
@ -286,7 +278,7 @@ public class TradeManager implements PersistedDataHost {
|
||||
}
|
||||
|
||||
public void shutDown() {
|
||||
xmrTxProofService.shutDown();
|
||||
// Do nothing here
|
||||
}
|
||||
|
||||
private void initPendingTrades() {
|
||||
@ -329,13 +321,6 @@ public class TradeManager implements PersistedDataHost {
|
||||
addTradeToFailedTradesList.add(trade);
|
||||
}
|
||||
}
|
||||
|
||||
if (trade.getState() == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG) {
|
||||
// This state can be only appear at a SellerTrade
|
||||
checkArgument(trade instanceof SellerTrade, "Trade must be instance of SellerTrade");
|
||||
// We delay a bit as at startup lots of stuff is happening
|
||||
UserThread.runAfter(() -> maybeStartXmrTxProofServices((SellerTrade) trade), 1);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
@ -360,31 +345,10 @@ public class TradeManager implements PersistedDataHost {
|
||||
pendingTradesInitialized.set(true);
|
||||
}
|
||||
|
||||
public void maybeStartXmrTxProofServices(SellerTrade sellerTrade) {
|
||||
xmrTxProofService.maybeStartRequests(sellerTrade, tradableList.getList(),
|
||||
assetTxProofResult -> {
|
||||
if (assetTxProofResult == AssetTxProofResult.COMPLETED) {
|
||||
log.info("###########################################################################################");
|
||||
log.info("We auto-confirm trade {} as our all our services for the tx proof completed successfully", sellerTrade.getShortId());
|
||||
log.info("###########################################################################################");
|
||||
autoConfirmFiatPaymentReceived(sellerTrade);
|
||||
}
|
||||
},
|
||||
(errorMessage, throwable) -> {
|
||||
log.error(errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
private void autoConfirmFiatPaymentReceived(SellerTrade sellerTrade) {
|
||||
onFiatPaymentReceived(sellerTrade,
|
||||
() -> {
|
||||
}, errorMessage -> {
|
||||
});
|
||||
}
|
||||
|
||||
public void onFiatPaymentReceived(SellerTrade sellerTrade,
|
||||
ResultHandler resultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
public void onUserConfirmedFiatPaymentReceived(SellerTrade sellerTrade,
|
||||
ResultHandler resultHandler,
|
||||
ErrorMessageHandler errorMessageHandler) {
|
||||
sellerTrade.onFiatPaymentReceived(resultHandler, errorMessageHandler);
|
||||
|
||||
//TODO move to trade protocol task
|
||||
|
@ -17,7 +17,6 @@
|
||||
|
||||
package bisq.core.trade.protocol.tasks.seller;
|
||||
|
||||
import bisq.core.trade.SellerTrade;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.messages.CounterCurrencyTransferStartedMessage;
|
||||
import bisq.core.trade.protocol.tasks.TradeTask;
|
||||
@ -27,7 +26,6 @@ import bisq.common.taskrunner.TaskRunner;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkArgument;
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
@Slf4j
|
||||
@ -62,10 +60,6 @@ public class SellerProcessCounterCurrencyTransferStartedMessage extends TradeTas
|
||||
trade.setCounterCurrencyExtraData(counterCurrencyExtraData);
|
||||
}
|
||||
|
||||
checkArgument(trade instanceof SellerTrade, "Trade must be instance of SellerTrade");
|
||||
// We return early in the service if its not XMR. We prefer to not have additional checks outside...
|
||||
processModel.getTradeManager().maybeStartXmrTxProofServices((SellerTrade) trade);
|
||||
|
||||
processModel.removeMailboxMessageAfterProcessing(trade);
|
||||
|
||||
trade.setState(Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG);
|
||||
|
@ -26,6 +26,7 @@ public enum AssetTxProofResult {
|
||||
TRADE_LIMIT_EXCEEDED,
|
||||
INVALID_DATA, // Peer provided invalid data. Might be a scam attempt (e.g. txKey reused)
|
||||
PAYOUT_TX_ALREADY_PUBLISHED,
|
||||
DISPUTE_OPENED,
|
||||
|
||||
REQUESTS_STARTED(false),
|
||||
PENDING(false),
|
||||
@ -44,6 +45,10 @@ public enum AssetTxProofResult {
|
||||
@Getter
|
||||
private int numRequiredSuccessResults;
|
||||
@Getter
|
||||
private int numConfirmations;
|
||||
@Getter
|
||||
private int numRequiredConfirmations;
|
||||
@Getter
|
||||
private String details = "";
|
||||
// If isTerminal is set it means that we stop the service
|
||||
@Getter
|
||||
@ -68,6 +73,16 @@ public enum AssetTxProofResult {
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssetTxProofResult numConfirmations(int numConfirmations) {
|
||||
this.numConfirmations = numConfirmations;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssetTxProofResult numRequiredConfirmations(int numRequiredConfirmations) {
|
||||
this.numRequiredConfirmations = numRequiredConfirmations;
|
||||
return this;
|
||||
}
|
||||
|
||||
public AssetTxProofResult details(String details) {
|
||||
this.details = details;
|
||||
return this;
|
||||
@ -78,6 +93,8 @@ public enum AssetTxProofResult {
|
||||
return "AssetTxProofResult{" +
|
||||
"\n numSuccessResults=" + numSuccessResults +
|
||||
",\n requiredSuccessResults=" + numRequiredSuccessResults +
|
||||
",\n numConfirmations=" + numConfirmations +
|
||||
",\n numRequiredConfirmations=" + numRequiredConfirmations +
|
||||
",\n details='" + details + '\'' +
|
||||
"\n} " + super.toString();
|
||||
}
|
||||
|
@ -17,18 +17,8 @@
|
||||
|
||||
package bisq.core.trade.txproof;
|
||||
|
||||
import bisq.core.trade.Trade;
|
||||
|
||||
import bisq.common.handlers.FaultHandler;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public interface AssetTxProofService {
|
||||
void maybeStartRequests(Trade trade,
|
||||
List<Trade> activeTrades,
|
||||
Consumer<AssetTxProofResult> resultHandler,
|
||||
FaultHandler faultHandler);
|
||||
void onAllServicesInitialized();
|
||||
|
||||
void shutDown();
|
||||
}
|
||||
|
@ -162,7 +162,7 @@ public class XmrTxProofParser implements AssetTxProofParser<XmrTxProofRequest.Re
|
||||
if (confirmations < confirmsRequired) {
|
||||
return XmrTxProofRequest.Result.PENDING.with(XmrTxProofRequest.Detail.PENDING_CONFIRMATIONS.numConfirmations(confirmations));
|
||||
} else {
|
||||
return XmrTxProofRequest.Result.SUCCESS;
|
||||
return XmrTxProofRequest.Result.SUCCESS.with(XmrTxProofRequest.Detail.SUCCESS.numConfirmations(confirmations));
|
||||
}
|
||||
|
||||
} catch (JsonParseException | NullPointerException e) {
|
||||
|
@ -85,6 +85,8 @@ class XmrTxProofRequest implements AssetTxProofRequest<XmrTxProofRequest.Result>
|
||||
TX_NOT_FOUND, // Tx not visible in network yet. Could be also other error
|
||||
PENDING_CONFIRMATIONS,
|
||||
|
||||
SUCCESS,
|
||||
|
||||
// Error states
|
||||
CONNECTION_FAILURE,
|
||||
API_INVALID,
|
||||
@ -268,12 +270,10 @@ class XmrTxProofRequest implements AssetTxProofRequest<XmrTxProofRequest.Result>
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private String getShortId() {
|
||||
return Utilities.getShortId(model.getTradeId()) + " @ " +
|
||||
model.getServiceAddress().substring(0, 6);
|
||||
return Utilities.getShortId(model.getTradeId()) + " @ " + model.getServiceAddress().substring(0, 6);
|
||||
}
|
||||
|
||||
private boolean isTimeOutReached() {
|
||||
return System.currentTimeMillis() - firstRequest > MAX_REQUEST_PERIOD;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,6 +18,9 @@
|
||||
package bisq.core.trade.txproof.xmr;
|
||||
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.support.dispute.Dispute;
|
||||
import bisq.core.support.dispute.mediation.MediationManager;
|
||||
import bisq.core.support.dispute.refund.RefundManager;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.txproof.AssetTxProofHttpClient;
|
||||
import bisq.core.trade.txproof.AssetTxProofRequestsPerTrade;
|
||||
@ -30,6 +33,9 @@ import org.bitcoinj.core.Coin;
|
||||
|
||||
import javafx.beans.value.ChangeListener;
|
||||
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -46,6 +52,8 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade {
|
||||
@Getter
|
||||
private final Trade trade;
|
||||
private final AutoConfirmSettings autoConfirmSettings;
|
||||
private final MediationManager mediationManager;
|
||||
private final RefundManager refundManager;
|
||||
private final AssetTxProofHttpClient httpClient;
|
||||
|
||||
private int numRequiredSuccessResults;
|
||||
@ -54,6 +62,7 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade {
|
||||
private int numSuccessResults;
|
||||
private ChangeListener<Trade.State> tradeStateListener;
|
||||
private AutoConfirmSettings.Listener autoConfirmSettingsListener;
|
||||
private ListChangeListener<Dispute> mediationListener, refundListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -62,10 +71,14 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade {
|
||||
|
||||
XmrTxProofRequestsPerTrade(AssetTxProofHttpClient httpClient,
|
||||
Trade trade,
|
||||
AutoConfirmSettings autoConfirmSettings) {
|
||||
AutoConfirmSettings autoConfirmSettings,
|
||||
MediationManager mediationManager,
|
||||
RefundManager refundManager) {
|
||||
this.httpClient = httpClient;
|
||||
this.trade = trade;
|
||||
this.autoConfirmSettings = autoConfirmSettings;
|
||||
this.mediationManager = mediationManager;
|
||||
this.refundManager = refundManager;
|
||||
}
|
||||
|
||||
|
||||
@ -75,6 +88,49 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade {
|
||||
|
||||
@Override
|
||||
public void requestFromAllServices(Consumer<AssetTxProofResult> resultHandler, FaultHandler faultHandler) {
|
||||
// isTradeAmountAboveLimit
|
||||
if (isTradeAmountAboveLimit(trade)) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.TRADE_LIMIT_EXCEEDED);
|
||||
return;
|
||||
}
|
||||
|
||||
// isPayoutPublished
|
||||
if (trade.isPayoutPublished()) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.PAYOUT_TX_ALREADY_PUBLISHED);
|
||||
return;
|
||||
}
|
||||
|
||||
// IsEnabled()
|
||||
// We will stop all our services if the user changes the enable state in the AutoConfirmSettings
|
||||
if (!autoConfirmSettings.isEnabled()) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.FEATURE_DISABLED);
|
||||
return;
|
||||
}
|
||||
addSettingsListener(resultHandler);
|
||||
|
||||
// TradeState
|
||||
setupTradeStateListener(resultHandler);
|
||||
// We checked initially for current trade state so no need to check again here
|
||||
|
||||
// Check if mediation dispute and add listener
|
||||
ObservableList<Dispute> mediationDisputes = mediationManager.getDisputesAsObservableList();
|
||||
if (isDisputed(mediationDisputes)) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.DISPUTE_OPENED);
|
||||
return;
|
||||
}
|
||||
setupMediationListener(resultHandler, mediationDisputes);
|
||||
|
||||
// Check if arbitration dispute and add listener
|
||||
ObservableList<Dispute> refundDisputes = refundManager.getDisputesAsObservableList();
|
||||
if (isDisputed(refundDisputes)) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.DISPUTE_OPENED);
|
||||
return;
|
||||
}
|
||||
setupArbitrationListener(resultHandler, refundDisputes);
|
||||
|
||||
// All good so we start
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.REQUESTS_STARTED);
|
||||
|
||||
// We set serviceAddresses at request time. If user changes AutoConfirmSettings after request has started
|
||||
// it will have no impact on serviceAddresses and numRequiredSuccessResults.
|
||||
// Thought numRequiredConfirmations can be changed during request process and will be read from
|
||||
@ -82,37 +138,6 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade {
|
||||
List<String> serviceAddresses = autoConfirmSettings.getServiceAddresses();
|
||||
numRequiredSuccessResults = serviceAddresses.size();
|
||||
|
||||
if (isTradeAmountAboveLimit(trade)) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.TRADE_LIMIT_EXCEEDED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (trade.isPayoutPublished()) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.PAYOUT_TX_ALREADY_PUBLISHED);
|
||||
return;
|
||||
}
|
||||
|
||||
// We will stop all our services if the user changes the enable state in the AutoConfirmSettings
|
||||
autoConfirmSettingsListener = () -> {
|
||||
if (!autoConfirmSettings.isEnabled()) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.FEATURE_DISABLED);
|
||||
}
|
||||
};
|
||||
autoConfirmSettings.addListener(autoConfirmSettingsListener);
|
||||
if (!autoConfirmSettings.isEnabled()) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.FEATURE_DISABLED);
|
||||
return;
|
||||
}
|
||||
|
||||
tradeStateListener = (observable, oldValue, newValue) -> {
|
||||
if (trade.isPayoutPublished()) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.PAYOUT_TX_ALREADY_PUBLISHED);
|
||||
}
|
||||
};
|
||||
trade.stateProperty().addListener(tradeStateListener);
|
||||
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.REQUESTS_STARTED);
|
||||
|
||||
for (String serviceAddress : serviceAddresses) {
|
||||
XmrTxProofModel model = new XmrTxProofModel(trade, serviceAddress, autoConfirmSettings);
|
||||
XmrTxProofRequest request = new XmrTxProofRequest(httpClient, model);
|
||||
@ -146,7 +171,12 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade {
|
||||
// have completed on the service level.
|
||||
log.info("All {} tx proof requests for trade {} have been successful.",
|
||||
numRequiredSuccessResults, trade.getShortId());
|
||||
assetTxProofResult = AssetTxProofResult.COMPLETED;
|
||||
XmrTxProofRequest.Detail detail = result.getDetail();
|
||||
assetTxProofResult = AssetTxProofResult.COMPLETED
|
||||
.numSuccessResults(numSuccessResults)
|
||||
.numRequiredSuccessResults(numRequiredSuccessResults)
|
||||
.numConfirmations(detail != null ? detail.getNumConfirmations() : 0)
|
||||
.numRequiredConfirmations(autoConfirmSettings.getRequiredConfirmations());
|
||||
}
|
||||
break;
|
||||
case FAILED:
|
||||
@ -174,16 +204,66 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade {
|
||||
}
|
||||
}
|
||||
|
||||
protected void addSettingsListener(Consumer<AssetTxProofResult> resultHandler) {
|
||||
autoConfirmSettingsListener = () -> {
|
||||
if (!autoConfirmSettings.isEnabled()) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.FEATURE_DISABLED);
|
||||
}
|
||||
};
|
||||
autoConfirmSettings.addListener(autoConfirmSettingsListener);
|
||||
}
|
||||
|
||||
protected void setupTradeStateListener(Consumer<AssetTxProofResult> resultHandler) {
|
||||
tradeStateListener = (observable, oldValue, newValue) -> {
|
||||
if (trade.isPayoutPublished()) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.PAYOUT_TX_ALREADY_PUBLISHED);
|
||||
}
|
||||
};
|
||||
trade.stateProperty().addListener(tradeStateListener);
|
||||
}
|
||||
|
||||
protected void setupArbitrationListener(Consumer<AssetTxProofResult> resultHandler,
|
||||
ObservableList<Dispute> refundDisputes) {
|
||||
refundListener = c -> {
|
||||
c.next();
|
||||
if (c.wasAdded() && isDisputed(c.getAddedSubList())) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.DISPUTE_OPENED);
|
||||
}
|
||||
};
|
||||
refundDisputes.addListener(refundListener);
|
||||
}
|
||||
|
||||
protected void setupMediationListener(Consumer<AssetTxProofResult> resultHandler,
|
||||
ObservableList<Dispute> mediationDisputes) {
|
||||
mediationListener = c -> {
|
||||
c.next();
|
||||
if (c.wasAdded() && isDisputed(c.getAddedSubList())) {
|
||||
callResultHandlerAndMaybeTerminate(resultHandler, AssetTxProofResult.DISPUTE_OPENED);
|
||||
}
|
||||
};
|
||||
mediationDisputes.addListener(mediationListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void terminate() {
|
||||
requests.forEach(XmrTxProofRequest::terminate);
|
||||
requests.clear();
|
||||
|
||||
if (tradeStateListener != null) {
|
||||
trade.stateProperty().removeListener(tradeStateListener);
|
||||
}
|
||||
|
||||
if (autoConfirmSettingsListener != null) {
|
||||
autoConfirmSettings.removeListener(autoConfirmSettingsListener);
|
||||
}
|
||||
|
||||
if (mediationListener != null) {
|
||||
mediationManager.getDisputesAsObservableList().removeListener(mediationListener);
|
||||
}
|
||||
|
||||
if (refundListener != null) {
|
||||
refundManager.getDisputesAsObservableList().removeListener(refundListener);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -217,6 +297,8 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade {
|
||||
return AssetTxProofResult.PENDING
|
||||
.numSuccessResults(numSuccessResults)
|
||||
.numRequiredSuccessResults(numRequiredSuccessResults)
|
||||
.numConfirmations(detail != null ? detail.getNumConfirmations() : 0)
|
||||
.numRequiredConfirmations(autoConfirmSettings.getRequiredConfirmations())
|
||||
.details(detailString);
|
||||
}
|
||||
|
||||
@ -235,4 +317,8 @@ class XmrTxProofRequestsPerTrade implements AssetTxProofRequestsPerTrade {
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isDisputed(List<? extends Dispute> disputes) {
|
||||
return disputes.stream().anyMatch(e -> e.getTradeId().equals(trade.getId()));
|
||||
}
|
||||
}
|
||||
|
@ -20,9 +20,11 @@ package bisq.core.trade.txproof.xmr;
|
||||
import bisq.core.btc.setup.WalletsSetup;
|
||||
import bisq.core.filter.FilterManager;
|
||||
import bisq.core.locale.Res;
|
||||
import bisq.core.payment.payload.AssetsAccountPayload;
|
||||
import bisq.core.support.dispute.mediation.MediationManager;
|
||||
import bisq.core.support.dispute.refund.RefundManager;
|
||||
import bisq.core.trade.SellerTrade;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.TradeManager;
|
||||
import bisq.core.trade.closed.ClosedTradableManager;
|
||||
import bisq.core.trade.failed.FailedTradesManager;
|
||||
import bisq.core.trade.txproof.AssetTxProofHttpClient;
|
||||
@ -31,19 +33,27 @@ import bisq.core.trade.txproof.AssetTxProofService;
|
||||
import bisq.core.user.AutoConfirmSettings;
|
||||
import bisq.core.user.Preferences;
|
||||
|
||||
import bisq.network.p2p.BootstrapListener;
|
||||
import bisq.network.p2p.P2PService;
|
||||
|
||||
import bisq.common.app.DevEnv;
|
||||
import bisq.common.handlers.FaultHandler;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.monadic.MonadicBinding;
|
||||
|
||||
import javafx.beans.property.BooleanProperty;
|
||||
import javafx.beans.property.SimpleBooleanProperty;
|
||||
import javafx.beans.value.ChangeListener;
|
||||
|
||||
import javafx.collections.ListChangeListener;
|
||||
import javafx.collections.ObservableList;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -59,12 +69,19 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||
public class XmrTxProofService implements AssetTxProofService {
|
||||
private final FilterManager filterManager;
|
||||
private final Preferences preferences;
|
||||
private final TradeManager tradeManager;
|
||||
private final ClosedTradableManager closedTradableManager;
|
||||
private final FailedTradesManager failedTradesManager;
|
||||
private final MediationManager mediationManager;
|
||||
private final RefundManager refundManager;
|
||||
private final P2PService p2PService;
|
||||
private final WalletsSetup walletsSetup;
|
||||
private final AssetTxProofHttpClient httpClient;
|
||||
private final Map<String, XmrTxProofRequestsPerTrade> servicesByTradeId = new HashMap<>();
|
||||
private AutoConfirmSettings autoConfirmSettings;
|
||||
private Map<String, ChangeListener<Trade.State>> tradeStateListenerMap = new HashMap<>();
|
||||
private ChangeListener<Number> btcPeersListener, btcBlockListener;
|
||||
private BootstrapListener bootstrapListener;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -75,27 +92,24 @@ public class XmrTxProofService implements AssetTxProofService {
|
||||
@Inject
|
||||
public XmrTxProofService(FilterManager filterManager,
|
||||
Preferences preferences,
|
||||
TradeManager tradeManager,
|
||||
ClosedTradableManager closedTradableManager,
|
||||
FailedTradesManager failedTradesManager,
|
||||
MediationManager mediationManager,
|
||||
RefundManager refundManager,
|
||||
P2PService p2PService,
|
||||
WalletsSetup walletsSetup,
|
||||
AssetTxProofHttpClient httpClient) {
|
||||
this.filterManager = filterManager;
|
||||
this.preferences = preferences;
|
||||
this.tradeManager = tradeManager;
|
||||
this.closedTradableManager = closedTradableManager;
|
||||
this.failedTradesManager = failedTradesManager;
|
||||
this.mediationManager = mediationManager;
|
||||
this.refundManager = refundManager;
|
||||
this.p2PService = p2PService;
|
||||
this.walletsSetup = walletsSetup;
|
||||
this.httpClient = httpClient;
|
||||
|
||||
filterManager.filterProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (isAutoConfDisabledByFilter()) {
|
||||
servicesByTradeId.values().stream().map(XmrTxProofRequestsPerTrade::getTrade).forEach(trade ->
|
||||
trade.setAssetTxProofResult(AssetTxProofResult.FEATURE_DISABLED
|
||||
.details(Res.get("portfolio.pending.autoConf.state.filterDisabledFeature"))));
|
||||
shutDown();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -104,60 +118,22 @@ public class XmrTxProofService implements AssetTxProofService {
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
@Override
|
||||
public void maybeStartRequests(Trade trade,
|
||||
List<Trade> activeTrades,
|
||||
Consumer<AssetTxProofResult> resultHandler,
|
||||
FaultHandler faultHandler) {
|
||||
if (!isXmrBuyer(trade)) {
|
||||
return;
|
||||
}
|
||||
public void onAllServicesInitialized() {
|
||||
// As we might trigger the payout tx we want to be sure that we are well connected to the Bitcoin network.
|
||||
// onAllServicesInitialized is called once we have received the initial data but we want to have our
|
||||
// hidden service published and upDatedDataResponse received before we start.
|
||||
MonadicBinding<Boolean> p2pNetworkAndWalletReady = EasyBind.combine(isP2pBootstrapped(), hasSufficientBtcPeers(), isBtcBlockDownloadComplete(),
|
||||
(isP2pBootstrapped, hasSufficientBtcPeers, isBtcBlockDownloadComplete) -> {
|
||||
log.info("isP2pBootstrapped={}, hasSufficientBtcPeers={} isBtcBlockDownloadComplete={}",
|
||||
isP2pBootstrapped, hasSufficientBtcPeers, isBtcBlockDownloadComplete);
|
||||
return isP2pBootstrapped && hasSufficientBtcPeers && isBtcBlockDownloadComplete;
|
||||
});
|
||||
|
||||
String txId = trade.getCounterCurrencyTxId();
|
||||
String txHash = trade.getCounterCurrencyExtraData();
|
||||
if (is32BitHexStringInValid(txId) || is32BitHexStringInValid(txHash)) {
|
||||
trade.setAssetTxProofResult(AssetTxProofResult.INVALID_DATA.details(Res.get("portfolio.pending.autoConf.state.txKeyOrTxIdInvalid")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (!networkAndWalletReady()) {
|
||||
return;
|
||||
}
|
||||
|
||||
Optional<AutoConfirmSettings> optionalAutoConfirmSettings = preferences.findAutoConfirmSettings("XMR");
|
||||
if (!optionalAutoConfirmSettings.isPresent()) {
|
||||
// Not expected
|
||||
log.error("autoConfirmSettings is not present");
|
||||
return;
|
||||
}
|
||||
AutoConfirmSettings autoConfirmSettings = optionalAutoConfirmSettings.get();
|
||||
|
||||
if (isAutoConfDisabledByFilter()) {
|
||||
trade.setAssetTxProofResult(AssetTxProofResult.FEATURE_DISABLED
|
||||
.details(Res.get("portfolio.pending.autoConf.state.filterDisabledFeature")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (wasTxKeyReUsed(trade, activeTrades)) {
|
||||
trade.setAssetTxProofResult(AssetTxProofResult.INVALID_DATA
|
||||
.details(Res.get("portfolio.pending.autoConf.state.xmr.txKeyReused")));
|
||||
return;
|
||||
}
|
||||
|
||||
XmrTxProofRequestsPerTrade service = new XmrTxProofRequestsPerTrade(httpClient,
|
||||
trade,
|
||||
autoConfirmSettings);
|
||||
servicesByTradeId.put(trade.getId(), service);
|
||||
service.requestFromAllServices(
|
||||
assetTxProofResult -> {
|
||||
trade.setAssetTxProofResult(assetTxProofResult);
|
||||
|
||||
if (assetTxProofResult.isTerminal()) {
|
||||
servicesByTradeId.remove(trade.getId());
|
||||
}
|
||||
|
||||
resultHandler.accept(assetTxProofResult);
|
||||
},
|
||||
faultHandler);
|
||||
p2pNetworkAndWalletReady.subscribe((observable, oldValue, newValue) -> {
|
||||
if (newValue) {
|
||||
onP2pNetworkAndWalletReady();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -167,20 +143,188 @@ public class XmrTxProofService implements AssetTxProofService {
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private void onP2pNetworkAndWalletReady() {
|
||||
if (!preferences.findAutoConfirmSettings("XMR").isPresent()) {
|
||||
log.error("AutoConfirmSettings is not present");
|
||||
}
|
||||
autoConfirmSettings = preferences.findAutoConfirmSettings("XMR").get();
|
||||
|
||||
// We register a listener to stop running services. For new trades we check anyway in the trade validation
|
||||
filterManager.filterProperty().addListener((observable, oldValue, newValue) -> {
|
||||
if (isAutoConfDisabledByFilter()) {
|
||||
servicesByTradeId.values().stream().map(XmrTxProofRequestsPerTrade::getTrade).forEach(trade ->
|
||||
trade.setAssetTxProofResult(AssetTxProofResult.FEATURE_DISABLED
|
||||
.details(Res.get("portfolio.pending.autoConf.state.filterDisabledFeature"))));
|
||||
shutDown();
|
||||
}
|
||||
});
|
||||
|
||||
// We listen on new trades
|
||||
ObservableList<Trade> tradableList = tradeManager.getTradableList();
|
||||
tradableList.addListener((ListChangeListener<Trade>) c -> {
|
||||
c.next();
|
||||
if (c.wasAdded()) {
|
||||
processTrades(c.getAddedSubList());
|
||||
}
|
||||
});
|
||||
|
||||
// Process existing trades
|
||||
processTrades(tradableList);
|
||||
}
|
||||
|
||||
private void processTrades(List<? extends Trade> trades) {
|
||||
trades.stream()
|
||||
.filter(trade -> trade instanceof SellerTrade)
|
||||
.map(trade -> (SellerTrade) trade)
|
||||
.filter(this::isXmrTrade)
|
||||
.filter(trade -> !trade.isFiatReceived()) // Phase name is from the time when it was fiat only. Means counter currency (XMR) received.
|
||||
.forEach(this::processTradeOrAddListener);
|
||||
}
|
||||
|
||||
// Basic requirements are fulfilled.
|
||||
// We process further if we are in the expected state or register a listener
|
||||
private void processTradeOrAddListener(SellerTrade trade) {
|
||||
if (isExpectedTradeState(trade.getState())) {
|
||||
startRequestsIfValid(trade);
|
||||
} else {
|
||||
// We are expecting SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG in the future, so listen on changes
|
||||
ChangeListener<Trade.State> tradeStateListener = (observable, oldValue, newValue) -> {
|
||||
if (isExpectedTradeState(newValue)) {
|
||||
ChangeListener<Trade.State> listener = tradeStateListenerMap.remove(trade.getId());
|
||||
if (listener != null) {
|
||||
trade.stateProperty().removeListener(listener);
|
||||
}
|
||||
|
||||
startRequestsIfValid(trade);
|
||||
}
|
||||
};
|
||||
tradeStateListenerMap.put(trade.getId(), tradeStateListener);
|
||||
trade.stateProperty().addListener(tradeStateListener);
|
||||
}
|
||||
}
|
||||
|
||||
private void startRequestsIfValid(SellerTrade trade) {
|
||||
String txId = trade.getCounterCurrencyTxId();
|
||||
String txHash = trade.getCounterCurrencyExtraData();
|
||||
if (is32BitHexStringInValid(txId) || is32BitHexStringInValid(txHash)) {
|
||||
trade.setAssetTxProofResult(AssetTxProofResult.INVALID_DATA.details(Res.get("portfolio.pending.autoConf.state.txKeyOrTxIdInvalid")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (isAutoConfDisabledByFilter()) {
|
||||
trade.setAssetTxProofResult(AssetTxProofResult.FEATURE_DISABLED
|
||||
.details(Res.get("portfolio.pending.autoConf.state.filterDisabledFeature")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (wasTxKeyReUsed(trade, tradeManager.getTradableList())) {
|
||||
trade.setAssetTxProofResult(AssetTxProofResult.INVALID_DATA
|
||||
.details(Res.get("portfolio.pending.autoConf.state.xmr.txKeyReused")));
|
||||
return;
|
||||
}
|
||||
|
||||
startRequests(trade);
|
||||
}
|
||||
|
||||
private void startRequests(SellerTrade trade) {
|
||||
XmrTxProofRequestsPerTrade service = new XmrTxProofRequestsPerTrade(httpClient,
|
||||
trade,
|
||||
autoConfirmSettings,
|
||||
mediationManager,
|
||||
refundManager);
|
||||
servicesByTradeId.put(trade.getId(), service);
|
||||
service.requestFromAllServices(
|
||||
assetTxProofResult -> {
|
||||
trade.setAssetTxProofResult(assetTxProofResult);
|
||||
|
||||
if (assetTxProofResult == AssetTxProofResult.COMPLETED) {
|
||||
log.info("###########################################################################################");
|
||||
log.info("We auto-confirm trade {} as our all our services for the tx proof completed successfully", trade.getShortId());
|
||||
log.info("###########################################################################################");
|
||||
|
||||
trade.onFiatPaymentReceived(() -> {
|
||||
}, errorMessage -> {
|
||||
});
|
||||
}
|
||||
|
||||
if (assetTxProofResult.isTerminal()) {
|
||||
servicesByTradeId.remove(trade.getId());
|
||||
}
|
||||
},
|
||||
(errorMessage, throwable) -> {
|
||||
log.error(errorMessage);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Startup checks
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private BooleanProperty isBtcBlockDownloadComplete() {
|
||||
BooleanProperty result = new SimpleBooleanProperty();
|
||||
if (walletsSetup.isDownloadComplete()) {
|
||||
result.set(true);
|
||||
} else {
|
||||
btcBlockListener = (observable, oldValue, newValue) -> {
|
||||
if (walletsSetup.isDownloadComplete()) {
|
||||
walletsSetup.downloadPercentageProperty().removeListener(btcBlockListener);
|
||||
result.set(true);
|
||||
}
|
||||
};
|
||||
walletsSetup.downloadPercentageProperty().addListener(btcBlockListener);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private BooleanProperty hasSufficientBtcPeers() {
|
||||
BooleanProperty result = new SimpleBooleanProperty();
|
||||
if (walletsSetup.hasSufficientPeersForBroadcast()) {
|
||||
result.set(true);
|
||||
} else {
|
||||
btcPeersListener = (observable, oldValue, newValue) -> {
|
||||
if (walletsSetup.hasSufficientPeersForBroadcast()) {
|
||||
walletsSetup.numPeersProperty().removeListener(btcPeersListener);
|
||||
result.set(true);
|
||||
}
|
||||
};
|
||||
walletsSetup.numPeersProperty().addListener(btcPeersListener);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private BooleanProperty isP2pBootstrapped() {
|
||||
BooleanProperty result = new SimpleBooleanProperty();
|
||||
if (p2PService.isBootstrapped()) {
|
||||
result.set(true);
|
||||
} else {
|
||||
bootstrapListener = new BootstrapListener() {
|
||||
@Override
|
||||
public void onUpdatedDataReceived() {
|
||||
p2PService.removeP2PServiceListener(bootstrapListener);
|
||||
result.set(true);
|
||||
}
|
||||
};
|
||||
p2PService.addP2PServiceListener(bootstrapListener);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Validation
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private boolean isXmrBuyer(Trade trade) {
|
||||
if (!checkNotNull(trade.getOffer()).getCurrencyCode().equals("XMR")) {
|
||||
return false;
|
||||
}
|
||||
private boolean isXmrTrade(Trade trade) {
|
||||
return (checkNotNull(trade.getOffer()).getCurrencyCode().equals("XMR"));
|
||||
}
|
||||
|
||||
if (!(trade instanceof SellerTrade)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return checkNotNull(trade.getContract()).getSellerPaymentAccountPayload() instanceof AssetsAccountPayload;
|
||||
private boolean isExpectedTradeState(Trade.State newValue) {
|
||||
return newValue == Trade.State.SELLER_RECEIVED_FIAT_PAYMENT_INITIATED_MSG;
|
||||
}
|
||||
|
||||
private boolean is32BitHexStringInValid(String hexString) {
|
||||
@ -192,12 +336,6 @@ public class XmrTxProofService implements AssetTxProofService {
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean networkAndWalletReady() {
|
||||
return p2PService.isBootstrapped() &&
|
||||
walletsSetup.isDownloadComplete() &&
|
||||
walletsSetup.hasSufficientPeersForBroadcast();
|
||||
}
|
||||
|
||||
private boolean isAutoConfDisabledByFilter() {
|
||||
return filterManager.getFilter() != null &&
|
||||
filterManager.getFilter().isDisableAutoConf();
|
||||
|
@ -25,10 +25,13 @@ import org.bitcoinj.core.Coin;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
@Getter
|
||||
public final class AutoConfirmSettings implements PersistablePayload {
|
||||
public interface Listener {
|
||||
@ -42,13 +45,21 @@ public final class AutoConfirmSettings implements PersistablePayload {
|
||||
private String currencyCode;
|
||||
private List<Listener> listeners = new CopyOnWriteArrayList<>();
|
||||
|
||||
static AutoConfirmSettings getDefaultForXmr(List<String> serviceAddresses) {
|
||||
return new AutoConfirmSettings(
|
||||
false,
|
||||
5,
|
||||
Coin.COIN.value,
|
||||
serviceAddresses,
|
||||
"XMR");
|
||||
@SuppressWarnings("SameParameterValue")
|
||||
static Optional<AutoConfirmSettings> getDefault(List<String> serviceAddresses, String currencyCode) {
|
||||
//noinspection SwitchStatementWithTooFewBranches
|
||||
switch (currencyCode) {
|
||||
case "XMR":
|
||||
return Optional.of(new AutoConfirmSettings(
|
||||
false,
|
||||
5,
|
||||
Coin.COIN.value,
|
||||
serviceAddresses,
|
||||
"XMR"));
|
||||
default:
|
||||
log.error("No AutoConfirmSettings supported yet for currency {}", currencyCode);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
public AutoConfirmSettings(boolean enabled,
|
||||
|
@ -33,7 +33,6 @@ import bisq.core.setup.CoreNetworkCapabilities;
|
||||
|
||||
import bisq.network.p2p.network.BridgeAddressProvider;
|
||||
|
||||
import bisq.common.app.DevEnv;
|
||||
import bisq.common.config.BaseCurrencyNetwork;
|
||||
import bisq.common.config.Config;
|
||||
import bisq.common.proto.persistable.PersistedDataHost;
|
||||
@ -124,16 +123,12 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
new BlockChainExplorer("bsq.bisq.cc (@m52go)", "https://bsq.bisq.cc/tx.html?tx=", "https://bsq.bisq.cc/Address.html?addr=")
|
||||
));
|
||||
|
||||
// list of XMR proof providers : this list will be used if no preference has been set
|
||||
public static final List<String> getDefaultXmrProofProviders() {
|
||||
if (DevEnv.isDevMode()) {
|
||||
return new ArrayList<>(Arrays.asList("78.47.61.90:8081"));
|
||||
} else {
|
||||
// TODO we need at least 2 for release
|
||||
return new ArrayList<>(Arrays.asList(
|
||||
"monero3bec7m26vx6si6qo7q7imlaoz45ot5m2b5z2ppgoooo6jx2rqd.onion"));
|
||||
}
|
||||
}
|
||||
//TODO add a second before release
|
||||
private static final ArrayList<String> XMR_TX_PROOF_SERVICES_CLEAR_NET = new ArrayList<>(Arrays.asList(
|
||||
"78.47.61.90:8081"));
|
||||
//TODO add a second before release
|
||||
private static final ArrayList<String> XMR_TX_PROOF_SERVICES = new ArrayList<>(Arrays.asList(
|
||||
"monero3bec7m26vx6si6qo7q7imlaoz45ot5m2b5z2ppgoooo6jx2rqd.onion"));
|
||||
|
||||
public static final boolean USE_SYMMETRIC_SECURITY_DEPOSIT = true;
|
||||
|
||||
@ -321,7 +316,11 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
}
|
||||
|
||||
if (prefPayload.getAutoConfirmSettingsList().isEmpty()) {
|
||||
getAutoConfirmSettingsList().add(AutoConfirmSettings.getDefaultForXmr(getDefaultXmrProofProviders()));
|
||||
List<String> defaultXmrTxProofServices = getDefaultXmrTxProofServices();
|
||||
AutoConfirmSettings.getDefault(defaultXmrTxProofServices, "XMR")
|
||||
.ifPresent(xmrAutoConfirmSettings -> {
|
||||
getAutoConfirmSettingsList().add(xmrAutoConfirmSettings);
|
||||
});
|
||||
}
|
||||
|
||||
// We set the capability in CoreNetworkCapabilities if the program argument is set.
|
||||
@ -332,7 +331,6 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
persist();
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -875,6 +873,14 @@ public final class Preferences implements PersistedDataHost, BridgeAddressProvid
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> getDefaultXmrTxProofServices() {
|
||||
if (config.useLocalhostForP2P) {
|
||||
return XMR_TX_PROOF_SERVICES_CLEAR_NET;
|
||||
} else {
|
||||
return XMR_TX_PROOF_SERVICES;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// Private
|
||||
|
@ -52,9 +52,7 @@ import static bisq.core.btc.wallet.Restrictions.getDefaultBuyerSecurityDepositAs
|
||||
public final class PreferencesPayload implements UserThreadMappedPersistableEnvelope {
|
||||
private String userLanguage;
|
||||
private Country userCountry;
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
|
||||
private List<FiatCurrency> fiatCurrencies = new ArrayList<>();
|
||||
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
|
||||
private List<CryptoCurrency> cryptoCurrencies = new ArrayList<>();
|
||||
private BlockChainExplorer blockChainExplorerMainNet;
|
||||
private BlockChainExplorer blockChainExplorerTestNet;
|
||||
|
@ -572,9 +572,9 @@ portfolio.pending.step5.completed=Completed
|
||||
|
||||
portfolio.pending.step3_seller.autoConf.status.label=Auto-confirm status
|
||||
portfolio.pending.autoConf=Auto-confirmed
|
||||
|
||||
portfolio.pending.autoConf.blocks=XMR confirmations: {0} / Required: {1}
|
||||
portfolio.pending.autoConf.state.xmr.txKeyReused=Transaction key re-used. Please open a dispute.
|
||||
portfolio.pending.autoConf.state.confirmations=Confirmations: {0}/{1}
|
||||
portfolio.pending.autoConf.state.confirmations=XMR confirmations: {0}/{1}
|
||||
portfolio.pending.autoConf.state.txNotFound=Transaction not seen in mem-pool yet
|
||||
portfolio.pending.autoConf.state.txKeyOrTxIdInvalid=No valid transaction ID / transaction key
|
||||
portfolio.pending.autoConf.state.filterDisabledFeature=Disabled by developers.
|
||||
@ -588,9 +588,11 @@ portfolio.pending.autoConf.state.INVALID_DATA=Peer provided invalid data. {0}
|
||||
# suppress inspection "UnusedProperty"
|
||||
portfolio.pending.autoConf.state.PAYOUT_TX_ALREADY_PUBLISHED=Payout transaction was already published.
|
||||
# suppress inspection "UnusedProperty"
|
||||
portfolio.pending.autoConf.state.REQUESTS_STARTED=Proof requests started
|
||||
portfolio.pending.autoConf.state.DISPUTE_OPENED=Dispute was opened. Auto-confirm is deactivated for that trade.
|
||||
# suppress inspection "UnusedProperty"
|
||||
portfolio.pending.autoConf.state.PENDING=Service results: {0}/{1}; {2}
|
||||
portfolio.pending.autoConf.state.REQUESTS_STARTED=Transaction proof requests started
|
||||
# suppress inspection "UnusedProperty"
|
||||
portfolio.pending.autoConf.state.PENDING=Success results: {0}/{1}; {2}
|
||||
# suppress inspection "UnusedProperty"
|
||||
portfolio.pending.autoConf.state.COMPLETED=Proof at all services succeeded
|
||||
# suppress inspection "UnusedProperty"
|
||||
|
@ -436,8 +436,10 @@ tree-table-view:focused {
|
||||
-fx-pref-width: 30;
|
||||
}
|
||||
|
||||
.jfx-badge.autoconf .badge-pane {
|
||||
-fx-pref-width: 100;
|
||||
.jfx-badge.auto-conf .badge-pane {
|
||||
-fx-background-color: -xmr-orange;
|
||||
-fx-pref-width: -1;
|
||||
-fx-padding: -1 10 0 10;
|
||||
}
|
||||
|
||||
.jfx-badge .badge-pane .label {
|
||||
@ -856,6 +858,12 @@ textfield */
|
||||
-fx-max-height: 20;
|
||||
}
|
||||
|
||||
#xmr-confidence {
|
||||
-fx-progress-color: -xmr-orange;
|
||||
-fx-max-width: 20;
|
||||
-fx-max-height: 20;
|
||||
}
|
||||
|
||||
.hyperlink,
|
||||
.hyperlink.force-underline .text,
|
||||
.hyperlink:hover,
|
||||
|
@ -194,7 +194,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
checkNotNull(getTrade(), "trade must not be null");
|
||||
checkArgument(getTrade() instanceof SellerTrade, "Trade must be instance of SellerTrade");
|
||||
tradeManager.onFiatPaymentReceived((SellerTrade) getTrade(), resultHandler, errorMessageHandler);
|
||||
tradeManager.onUserConfirmedFiatPaymentReceived((SellerTrade) getTrade(), resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
public void onWithdrawRequest(String toAddress,
|
||||
|
@ -112,7 +112,7 @@ public class BuyerStep4View extends TradeStepView {
|
||||
|
||||
JFXBadge autoConfBadge = new JFXBadge(new Label(""), Pos.BASELINE_RIGHT);
|
||||
autoConfBadge.setText(Res.get("portfolio.pending.autoConf"));
|
||||
autoConfBadge.getStyleClass().add("autoconf");
|
||||
autoConfBadge.getStyleClass().add("auto-conf");
|
||||
|
||||
HBox hBox2 = new HBox(1, completedTradeLabel, autoConfBadge);
|
||||
GridPane.setMargin(hBox2, new Insets(18, -10, -12, -10));
|
||||
|
@ -18,8 +18,10 @@
|
||||
package bisq.desktop.main.portfolio.pendingtrades.steps.seller;
|
||||
|
||||
import bisq.desktop.components.BusyAnimation;
|
||||
import bisq.desktop.components.InfoTextField;
|
||||
import bisq.desktop.components.TextFieldWithCopyIcon;
|
||||
import bisq.desktop.components.TitledGroupBg;
|
||||
import bisq.desktop.components.indicator.TxConfidenceIndicator;
|
||||
import bisq.desktop.main.overlays.popups.Popup;
|
||||
import bisq.desktop.main.portfolio.pendingtrades.PendingTradesViewModel;
|
||||
import bisq.desktop.main.portfolio.pendingtrades.steps.TradeStepView;
|
||||
@ -49,6 +51,7 @@ import bisq.core.user.DontShowAgainLookup;
|
||||
import bisq.common.Timer;
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.app.DevEnv;
|
||||
import bisq.common.util.Tuple2;
|
||||
import bisq.common.util.Tuple4;
|
||||
|
||||
import javafx.scene.control.Button;
|
||||
@ -57,6 +60,9 @@ import javafx.scene.control.Tooltip;
|
||||
import javafx.scene.layout.GridPane;
|
||||
import javafx.scene.layout.HBox;
|
||||
import javafx.scene.layout.Priority;
|
||||
import javafx.scene.layout.VBox;
|
||||
|
||||
import javafx.geometry.Insets;
|
||||
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
@ -65,20 +71,20 @@ import javafx.beans.value.ChangeListener;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import static bisq.desktop.util.FormBuilder.addButtonBusyAnimationLabelAfterGroup;
|
||||
import static bisq.desktop.util.FormBuilder.addCompactTopLabelTextFieldWithCopyIcon;
|
||||
import static bisq.desktop.util.FormBuilder.addTitledGroupBg;
|
||||
import static bisq.desktop.util.FormBuilder.addTopLabelTextFieldWithCopyIcon;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static bisq.desktop.util.FormBuilder.*;
|
||||
|
||||
public class SellerStep3View extends TradeStepView {
|
||||
|
||||
private final ChangeListener<Number> proofResultListener;
|
||||
private Button confirmButton;
|
||||
private Label statusLabel;
|
||||
private BusyAnimation busyAnimation;
|
||||
private Subscription tradeStatePropertySubscription;
|
||||
private Timer timeoutTimer;
|
||||
private TextFieldWithCopyIcon assetTxProofResultField;
|
||||
private final ChangeListener<Number> proofResultListener;
|
||||
private InfoTextField assetTxProofResultField;
|
||||
private TxConfidenceIndicator assetTxConfidenceIndicator;
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
@ -230,9 +236,28 @@ public class SellerStep3View extends TradeStepView {
|
||||
}
|
||||
|
||||
if (isBlockChain && trade.getOffer().getCurrencyCode().equals("XMR")) {
|
||||
assetTxProofResultField = addTopLabelTextFieldWithCopyIcon(gridPane, gridRow, 1,
|
||||
Res.get("portfolio.pending.step3_seller.autoConf.status.label"),
|
||||
"", Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE).second;
|
||||
assetTxProofResultField = new InfoTextField();
|
||||
|
||||
Tuple2<Label, VBox> topLabelWithVBox = getTopLabelWithVBox(Res.get("portfolio.pending.step3_seller.autoConf.status.label"), assetTxProofResultField);
|
||||
VBox vBox = topLabelWithVBox.second;
|
||||
|
||||
assetTxConfidenceIndicator = new TxConfidenceIndicator();
|
||||
assetTxConfidenceIndicator.setId("xmr-confidence");
|
||||
assetTxConfidenceIndicator.setProgress(0);
|
||||
assetTxConfidenceIndicator.setTooltip(new Tooltip());
|
||||
assetTxProofResultField.setContentForInfoPopOver(createPopoverLabel(Res.get("setting.info.msg")));
|
||||
|
||||
HBox.setMargin(assetTxConfidenceIndicator, new Insets(Layout.FLOATING_LABEL_DISTANCE, 0, 0, 0));
|
||||
|
||||
HBox hBox = new HBox();
|
||||
HBox.setHgrow(vBox, Priority.ALWAYS);
|
||||
hBox.setSpacing(10);
|
||||
hBox.getChildren().addAll(vBox, assetTxConfidenceIndicator);
|
||||
|
||||
GridPane.setRowIndex(hBox, gridRow);
|
||||
GridPane.setColumnIndex(hBox, 1);
|
||||
GridPane.setMargin(hBox, new Insets(Layout.COMPACT_FIRST_ROW_AND_GROUP_DISTANCE + Layout.FLOATING_LABEL_DISTANCE, 0, 0, 0));
|
||||
gridPane.getChildren().add(hBox);
|
||||
}
|
||||
|
||||
TextFieldWithCopyIcon myPaymentDetailsTextField = addCompactTopLabelTextFieldWithCopyIcon(gridPane, ++gridRow,
|
||||
@ -274,7 +299,6 @@ public class SellerStep3View extends TradeStepView {
|
||||
// Info
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
@Override
|
||||
protected String getInfoText() {
|
||||
String currencyCode = model.dataModel.getCurrencyCode();
|
||||
@ -443,10 +467,44 @@ public class SellerStep3View extends TradeStepView {
|
||||
}
|
||||
}
|
||||
|
||||
private void applyAssetTxProofResult(AssetTxProofResult result) {
|
||||
private void applyAssetTxProofResult(@Nullable AssetTxProofResult result) {
|
||||
String txt = GUIUtil.getProofResultAsString(result);
|
||||
assetTxProofResultField.setText(txt);
|
||||
assetTxProofResultField.setTooltip(new Tooltip(txt));
|
||||
if (result == null) {
|
||||
assetTxConfidenceIndicator.setProgress(0);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (result) {
|
||||
case PENDING:
|
||||
case COMPLETED:
|
||||
if (result.getNumRequiredConfirmations() > 0) {
|
||||
int numRequiredConfirmations = result.getNumRequiredConfirmations();
|
||||
int numConfirmations = result.getNumConfirmations();
|
||||
if (numConfirmations == 0) {
|
||||
assetTxConfidenceIndicator.setProgress(-1);
|
||||
} else {
|
||||
double progress = Math.min(1, (double) numConfirmations / (double) numRequiredConfirmations);
|
||||
assetTxConfidenceIndicator.setProgress(progress);
|
||||
assetTxConfidenceIndicator.getTooltip().setText(
|
||||
Res.get("portfolio.pending.autoConf.blocks",
|
||||
numConfirmations, numRequiredConfirmations));
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// Set invisible by default
|
||||
assetTxConfidenceIndicator.setProgress(0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private Label createPopoverLabel(String text) {
|
||||
Label label = new Label(text);
|
||||
label.setPrefWidth(600);
|
||||
label.setWrapText(true);
|
||||
label.setPadding(new Insets(10));
|
||||
return label;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -661,7 +661,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
||||
autoConfirmXmrToggle = addSlideToggleButton(autoConfirmGridPane, localRowIndex, Res.get("setting.preferences.autoConfirmEnabled"), Layout.FIRST_ROW_DISTANCE);
|
||||
|
||||
autoConfRequiredConfirmationsTf = addInputTextField(autoConfirmGridPane, ++localRowIndex, Res.get("setting.preferences.autoConfirmRequiredConfirmations"));
|
||||
autoConfRequiredConfirmationsTf.setValidator(new IntegerValidator(0, DevEnv.isDevMode() ? 100000000 : 1000));
|
||||
autoConfRequiredConfirmationsTf.setValidator(new IntegerValidator(1, DevEnv.isDevMode() ? 100000000 : 1000));
|
||||
|
||||
autoConfTradeLimitTf = addInputTextField(autoConfirmGridPane, ++localRowIndex, Res.get("setting.preferences.autoConfirmMaxTradeSize"));
|
||||
autoConfTradeLimitTf.setValidator(new BtcValidator(formatter));
|
||||
@ -677,7 +677,7 @@ public class PreferencesView extends ActivatableViewAndModel<GridPane, Preferenc
|
||||
List<String> serviceAddresses = Arrays.asList(StringUtils.deleteWhitespace(newValue).split(","));
|
||||
// revert to default service providers when user empties the list
|
||||
if (serviceAddresses.size() == 1 && serviceAddresses.get(0).isEmpty()) {
|
||||
serviceAddresses = Preferences.getDefaultXmrProofProviders();
|
||||
serviceAddresses = preferences.getDefaultXmrTxProofServices();
|
||||
}
|
||||
preferences.setAutoConfServiceAddresses("XMR", serviceAddresses);
|
||||
}
|
||||
|
@ -106,6 +106,9 @@
|
||||
/* dao chart colors */
|
||||
-bs-chart-dao-line1: -bs-color-green-3;
|
||||
-bs-chart-dao-line2: -bs-color-blue-5;
|
||||
|
||||
/* Monero orange color code */
|
||||
-xmr-orange: #f26822;
|
||||
}
|
||||
|
||||
.warning-box {
|
||||
|
@ -1176,6 +1176,7 @@ public class GUIUtil {
|
||||
case INVALID_DATA:
|
||||
return Res.get(key, result.getDetails());
|
||||
case PAYOUT_TX_ALREADY_PUBLISHED:
|
||||
case DISPUTE_OPENED:
|
||||
case REQUESTS_STARTED:
|
||||
return Res.get(key);
|
||||
case PENDING:
|
||||
|
Loading…
Reference in New Issue
Block a user