From 14d17023a87cd276a6f0c315924734656a395119 Mon Sep 17 00:00:00 2001 From: woodser Date: Fri, 24 May 2024 14:03:18 -0400 Subject: [PATCH] seller publishes trade statistics if trade amount transferred --- .../main/java/haveno/core/trade/Trade.java | 77 +++++++++++++------ .../tasks/ProcessPaymentReceivedMessage.java | 4 +- .../statistics/TradeStatisticsManager.java | 6 +- 3 files changed, 57 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/haveno/core/trade/Trade.java b/core/src/main/java/haveno/core/trade/Trade.java index c9520ac036..9665bad45a 100644 --- a/core/src/main/java/haveno/core/trade/Trade.java +++ b/core/src/main/java/haveno/core/trade/Trade.java @@ -424,6 +424,7 @@ public abstract class Trade implements Tradable, Model { transient private Subscription tradeStateSubscription; transient private Subscription tradePhaseSubscription; transient private Subscription payoutStateSubscription; + transient private Subscription disputeStateSubscription; transient private TaskLooper pollLooper; transient private Long pollPeriodMs; transient private Long pollNormalStartTimeMs; @@ -696,6 +697,9 @@ public abstract class Trade implements Tradable, Model { // auto complete arbitrator trade if (isArbitrator() && !isCompleted()) processModel.getTradeManager().onTradeCompleted(this); + // maybe publish trade statistic + maybePublishTradeStatistics(); + // reset address entries processModel.getXmrWalletService().resetAddressEntriesForTrade(getId()); } @@ -709,6 +713,16 @@ public abstract class Trade implements Tradable, Model { }); }); + // handle dispute events + disputeStateSubscription = EasyBind.subscribe(disputeStateProperty, newValue -> { + if (!isInitialized || isShutDownStarted) return; + ThreadUtils.submitToPool(() -> { + if (isDisputeClosed()) { + maybePublishTradeStatistics(); + } + }); + }); + // arbitrator syncs idle wallet when payout unlock expected if (this instanceof ArbitratorTrade) { idlePayoutSyncer = new IdlePayoutSyncer(); @@ -1422,6 +1436,7 @@ public abstract class Trade implements Tradable, Model { if (tradeStateSubscription != null) tradeStateSubscription.unsubscribe(); if (tradePhaseSubscription != null) tradePhaseSubscription.unsubscribe(); if (payoutStateSubscription != null) payoutStateSubscription.unsubscribe(); + if (disputeStateSubscription != null) disputeStateSubscription.unsubscribe(); }); } @@ -1998,6 +2013,10 @@ public abstract class Trade implements Tradable, Model { return isArbitrator() ? getBuyer().getDisputeClosedMessage() != null || getSeller().getDisputeClosedMessage() != null : getArbitrator().getDisputeClosedMessage() != null; } + public boolean isDisputeClosed() { + return getDisputeState().isClosed(); + } + public boolean isPaymentReceived() { return getState().getPhase().ordinal() >= Phase.PAYMENT_RECEIVED.ordinal(); } @@ -2149,6 +2168,39 @@ public abstract class Trade implements Tradable, Model { return Math.min(MAX_REPROCESS_DELAY_SECONDS, delay); } + public void maybePublishTradeStatistics() { + if (shouldPublishTradeStatistics()) doPublishTradeStatistics(); + } + + public boolean shouldPublishTradeStatistics() { + if (!isSeller()) return false; + return tradeAmountTransferred(); + } + + private boolean tradeAmountTransferred() { + return isPaymentReceived() || (getDisputeResult() != null && getDisputeResult().getWinner() == DisputeResult.Winner.SELLER); + } + + private void doPublishTradeStatistics() { + processModel.getP2PService().findPeersCapabilities(getTradePeer().getNodeAddress()) + .filter(capabilities -> capabilities.containsAll(Capability.TRADE_STATISTICS_3)) + .ifPresentOrElse(capabilities -> { + String referralId = processModel.getReferralIdService().getOptionalReferralId().orElse(null); + boolean isTorNetworkNode = getProcessModel().getP2PService().getNetworkNode() instanceof TorNetworkNode; + TradeStatistics3 tradeStatistics = TradeStatistics3.from(this, referralId, isTorNetworkNode); + if (tradeStatistics.isValid()) { + log.info("Publishing trade statistics"); + processModel.getP2PService().addPersistableNetworkPayload(tradeStatistics, true); + } else { + log.warn("Trade statistics are invalid. We do not publish. {}", tradeStatistics); + } + }, + () -> { + log.info("Our peer does not has updated yet, so they will publish the trade statistics. " + + "To avoid duplicates we do not publish from our side."); + }); + } + /////////////////////////////////////////////////////////////////////////////////////////// // Private @@ -2566,31 +2618,6 @@ public abstract class Trade implements Tradable, Model { } else { getXmrWalletService().resetAddressEntriesForOpenOffer(getId()); } - - // seller publishes trade statistics - if (this instanceof SellerTrade) { - checkNotNull(getSeller().getDepositTx()); - processModel.getP2PService().findPeersCapabilities(getTradePeer().getNodeAddress()) - .filter(capabilities -> capabilities.containsAll(Capability.TRADE_STATISTICS_3)) - .ifPresentOrElse(capabilities -> { - - // Our peer has updated, so as we are the seller we will publish the trade statistics. - // The peer as buyer does not publish anymore with v.1.4.0 (where Capability.TRADE_STATISTICS_3 was added) - String referralId = processModel.getReferralIdService().getOptionalReferralId().orElse(null); - boolean isTorNetworkNode = getProcessModel().getP2PService().getNetworkNode() instanceof TorNetworkNode; - TradeStatistics3 tradeStatistics = TradeStatistics3.from(this, referralId, isTorNetworkNode); - if (tradeStatistics.isValid()) { - log.info("Publishing trade statistics"); - processModel.getP2PService().addPersistableNetworkPayload(tradeStatistics, true); - } else { - log.warn("Trade statistics are invalid. We do not publish. {}", tradeStatistics); - } - }, - () -> { - log.info("Our peer does not has updated yet, so they will publish the trade statistics. " + - "To avoid duplicates we do not publish from our side."); - }); - } } /////////////////////////////////////////////////////////////////////////////////////////// diff --git a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java index 1a35ec8cb7..7e430c1059 100644 --- a/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java +++ b/core/src/main/java/haveno/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java @@ -103,6 +103,9 @@ public class ProcessPaymentReceivedMessage extends TradeTask { for (Dispute dispute : trade.getDisputes()) dispute.setIsClosed(); } + // advance state, arbitrator auto completes when payout published + trade.advanceState(Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG); + // publish signed witness SignedWitness signedWitness = message.getBuyerSignedWitness(); if (signedWitness != null && trade instanceof BuyerTrade) { @@ -113,7 +116,6 @@ public class ProcessPaymentReceivedMessage extends TradeTask { } // complete - trade.advanceState(Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG); // arbitrator auto completes when payout published trade.requestPersistence(); complete(); } catch (Throwable t) { diff --git a/core/src/main/java/haveno/core/trade/statistics/TradeStatisticsManager.java b/core/src/main/java/haveno/core/trade/statistics/TradeStatisticsManager.java index 086470c318..34504f38f1 100644 --- a/core/src/main/java/haveno/core/trade/statistics/TradeStatisticsManager.java +++ b/core/src/main/java/haveno/core/trade/statistics/TradeStatisticsManager.java @@ -26,7 +26,6 @@ import haveno.core.locale.CurrencyTuple; import haveno.core.locale.CurrencyUtil; import haveno.core.locale.Res; import haveno.core.provider.price.PriceFeedService; -import haveno.core.trade.SellerTrade; import haveno.core.trade.Trade; import haveno.core.util.JsonUtil; import haveno.network.p2p.P2PService; @@ -200,9 +199,8 @@ public class TradeStatisticsManager { long ts = System.currentTimeMillis(); Set hashes = tradeStatistics3StorageService.getMapOfAllData().keySet(); trades.forEach(trade -> { - if (!(trade instanceof SellerTrade)) { - log.debug("Trade: {} is not a seller trade, we only republish if we were seller", - trade.getShortId()); + if (!trade.shouldPublishTradeStatistics()) { + log.debug("Trade: {} should not publish trade statistics", trade.getShortId()); return; }