mirror of
https://github.com/haveno-dex/haveno.git
synced 2025-08-03 12:16:27 -04:00
refactor payout protocol
send payment key & multisig hex on deposit confirm for resilience support payout published, confirmed, unlocked states keep trade wallets open throughout trade close and delete trade wallets when payout unlocks arbitrator idles trade wallets after deposits confirm (1/hour)
This commit is contained in:
parent
45bac8c264
commit
f36dde2857
84 changed files with 1486 additions and 2272 deletions
|
@ -28,7 +28,7 @@ import bisq.core.offer.placeoffer.tasks.MakerReserveOfferFunds;
|
|||
import bisq.core.offer.placeoffer.tasks.ValidateOffer;
|
||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||
import bisq.core.trade.protocol.tasks.BuyerPreparePaymentSentMessage;
|
||||
import bisq.core.trade.protocol.tasks.BuyerProcessPaymentReceivedMessage;
|
||||
import bisq.core.trade.protocol.tasks.ProcessPaymentReceivedMessage;
|
||||
import bisq.core.trade.protocol.tasks.BuyerSendPaymentSentMessage;
|
||||
import bisq.core.trade.protocol.tasks.MakerSetLockTime;
|
||||
import bisq.core.trade.protocol.tasks.RemoveOffer;
|
||||
|
@ -36,8 +36,7 @@ import bisq.core.trade.protocol.tasks.SellerPreparePaymentReceivedMessage;
|
|||
import bisq.core.trade.protocol.tasks.SellerProcessPaymentSentMessage;
|
||||
import bisq.core.trade.protocol.tasks.SellerPublishDepositTx;
|
||||
import bisq.core.trade.protocol.tasks.SellerPublishTradeStatistics;
|
||||
import bisq.core.trade.protocol.tasks.SellerSendPaymentReceivedMessage;
|
||||
import bisq.core.trade.protocol.tasks.SetupPayoutTxListener;
|
||||
import bisq.core.trade.protocol.tasks.SellerSendPaymentReceivedMessageToBuyer;
|
||||
import bisq.core.trade.protocol.tasks.TakerVerifyMakerFeePayment;
|
||||
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
|
||||
import bisq.common.taskrunner.Task;
|
||||
|
@ -109,7 +108,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
TakerVerifyMakerFeePayment.class,
|
||||
SellerPreparePaymentReceivedMessage.class,
|
||||
//SellerBroadcastPayoutTx.class, // TODO (woodser): removed from main pipeline; debug view?
|
||||
SellerSendPaymentReceivedMessage.class
|
||||
SellerSendPaymentReceivedMessageToBuyer.class
|
||||
|
||||
)
|
||||
));
|
||||
|
@ -123,10 +122,9 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
|
||||
ApplyFilter.class,
|
||||
BuyerPreparePaymentSentMessage.class,
|
||||
SetupPayoutTxListener.class,
|
||||
BuyerSendPaymentSentMessage.class,
|
||||
|
||||
BuyerProcessPaymentReceivedMessage.class
|
||||
ProcessPaymentReceivedMessage.class
|
||||
)
|
||||
));
|
||||
|
||||
|
@ -142,10 +140,9 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
ApplyFilter.class,
|
||||
TakerVerifyMakerFeePayment.class,
|
||||
BuyerPreparePaymentSentMessage.class,
|
||||
SetupPayoutTxListener.class,
|
||||
BuyerSendPaymentSentMessage.class,
|
||||
|
||||
BuyerProcessPaymentReceivedMessage.class)
|
||||
ProcessPaymentReceivedMessage.class)
|
||||
));
|
||||
addGroup("SellerAsMakerProtocol",
|
||||
FXCollections.observableArrayList(Arrays.asList(
|
||||
|
@ -166,7 +163,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
ApplyFilter.class,
|
||||
SellerPreparePaymentReceivedMessage.class,
|
||||
//SellerBroadcastPayoutTx.class, // TODO (woodser): removed from main pipeline; debug view?
|
||||
SellerSendPaymentReceivedMessage.class
|
||||
SellerSendPaymentReceivedMessageToBuyer.class
|
||||
)
|
||||
));
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ import bisq.core.offer.Offer;
|
|||
import bisq.core.offer.OfferDirection;
|
||||
import bisq.core.offer.OpenOfferManager;
|
||||
import bisq.core.provider.price.PriceFeedService;
|
||||
import bisq.core.trade.TradeUtils;
|
||||
import bisq.core.trade.HavenoUtils;
|
||||
import bisq.core.user.Preferences;
|
||||
import bisq.core.util.VolumeUtil;
|
||||
|
||||
|
|
|
@ -424,8 +424,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
case PAYMENT_RECEIVED:
|
||||
appendMsg = Res.get("takeOffer.error.depositPublished");
|
||||
break;
|
||||
case PAYOUT_PUBLISHED:
|
||||
case WITHDRAWN:
|
||||
case COMPLETED:
|
||||
appendMsg = Res.get("takeOffer.error.payoutPublished");
|
||||
break;
|
||||
default:
|
||||
|
@ -444,7 +443,7 @@ class TakeOfferViewModel extends ActivatableWithDataModel<TakeOfferDataModel> im
|
|||
}
|
||||
|
||||
private void applyTradeState() {
|
||||
if (trade.isTakerFeePublished()) {
|
||||
if (trade.isDepositRequested()) {
|
||||
if (takeOfferResultHandler != null)
|
||||
takeOfferResultHandler.run();
|
||||
|
||||
|
|
|
@ -183,12 +183,11 @@ public class NotificationCenter {
|
|||
|
||||
private void onTradePhaseChanged(Trade trade, Trade.Phase phase) {
|
||||
String message = null;
|
||||
if (trade.isPayoutPublished() && !trade.isWithdrawn()) {
|
||||
if (trade.isPayoutPublished() && !trade.isCompleted()) {
|
||||
message = Res.get("notification.trade.completed");
|
||||
} else {
|
||||
if (trade instanceof MakerTrade &&
|
||||
phase.ordinal() == Trade.Phase.DEPOSITS_PUBLISHED.ordinal() ||
|
||||
phase.ordinal() == Trade.Phase.DEPOSITS_CONFIRMED.ordinal()) {
|
||||
phase.ordinal() == Trade.Phase.DEPOSITS_PUBLISHED.ordinal()) {
|
||||
final String role = trade instanceof BuyerTrade ? Res.get("shared.seller") : Res.get("shared.buyer");
|
||||
message = Res.get("notification.trade.accepted", role);
|
||||
}
|
||||
|
|
|
@ -590,7 +590,7 @@ public class DisputeSummaryWindow extends Overlay<DisputeSummaryWindow> {
|
|||
Button cancelButton = tuple.second;
|
||||
|
||||
closeTicketButton.setOnAction(e -> {
|
||||
disputesService.resolveDisputePayout(dispute, disputeResult, contract);
|
||||
disputesService.applyDisputePayout(dispute, disputeResult, contract);
|
||||
doClose(closeTicketButton);
|
||||
|
||||
// if (dispute.getDepositTxSerialized() == null) {
|
||||
|
|
|
@ -200,7 +200,7 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
((BuyerProtocol) tradeManager.getTradeProtocol(trade)).onPaymentStarted(resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
public void onFiatPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
public void onPaymentReceived(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||
Trade trade = getTrade();
|
||||
checkNotNull(trade, "trade must not be null");
|
||||
checkArgument(trade instanceof SellerTrade, "Trade must be instance of SellerTrade");
|
||||
|
@ -466,7 +466,6 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
String payoutTxHashAsString = null;
|
||||
MoneroWallet multisigWallet = xmrWalletService.getMultisigWallet(trade.getId());
|
||||
String updatedMultisigHex = multisigWallet.exportMultisigHex();
|
||||
xmrWalletService.closeMultisigWallet(trade.getId()); // close multisig wallet
|
||||
if (trade.getPayoutTxId() != null) {
|
||||
// payoutTxSerialized = payoutTx.bitcoinSerialize(); // TODO (woodser): no need to pass serialized txs for xmr
|
||||
// payoutTxHashAsString = payoutTx.getHashAsString();
|
||||
|
|
|
@ -30,8 +30,11 @@ import bisq.core.offer.Offer;
|
|||
import bisq.core.offer.OfferUtil;
|
||||
import bisq.core.provider.fee.FeeService;
|
||||
import bisq.core.provider.mempool.MempoolService;
|
||||
import bisq.core.trade.ArbitratorTrade;
|
||||
import bisq.core.trade.BuyerTrade;
|
||||
import bisq.core.trade.ClosedTradableManager;
|
||||
import bisq.core.trade.Contract;
|
||||
import bisq.core.trade.SellerTrade;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.TradeUtil;
|
||||
import bisq.core.user.User;
|
||||
|
@ -115,6 +118,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
@Getter
|
||||
private final ObjectProperty<MessageState> messageStateProperty = new SimpleObjectProperty<>(MessageState.UNDEFINED);
|
||||
private Subscription tradeStateSubscription;
|
||||
private Subscription payoutStateSubscription;
|
||||
private Subscription messageStateSubscription;
|
||||
@Getter
|
||||
protected final IntegerProperty mempoolStatus = new SimpleIntegerProperty();
|
||||
|
@ -160,6 +164,11 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
tradeStateSubscription = null;
|
||||
}
|
||||
|
||||
if (payoutStateSubscription != null) {
|
||||
payoutStateSubscription.unsubscribe();
|
||||
payoutStateSubscription = null;
|
||||
}
|
||||
|
||||
if (messageStateSubscription != null) {
|
||||
messageStateSubscription.unsubscribe();
|
||||
messageStateSubscription = null;
|
||||
|
@ -174,6 +183,12 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
buyerState.set(BuyerState.UNDEFINED);
|
||||
}
|
||||
|
||||
if (payoutStateSubscription != null) {
|
||||
payoutStateSubscription.unsubscribe();
|
||||
sellerState.set(SellerState.UNDEFINED);
|
||||
buyerState.set(BuyerState.UNDEFINED);
|
||||
}
|
||||
|
||||
if (messageStateSubscription != null) {
|
||||
messageStateSubscription.unsubscribe();
|
||||
messageStateProperty.set(MessageState.UNDEFINED);
|
||||
|
@ -184,6 +199,9 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), state -> {
|
||||
UserThread.execute(() -> onTradeStateChanged(state));
|
||||
});
|
||||
payoutStateSubscription = EasyBind.subscribe(trade.payoutStateProperty(), state -> {
|
||||
UserThread.execute(() -> onPayoutStateChanged(state));
|
||||
});
|
||||
messageStateSubscription = EasyBind.subscribe(trade.getProcessModel().getPaymentStartedMessageStateProperty(), this::onMessageStateChanged);
|
||||
}
|
||||
}
|
||||
|
@ -399,6 +417,13 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
tradeState,
|
||||
trade != null ? trade.getShortId() : "trade is null");
|
||||
|
||||
// arbitrator trade view only shows tx status
|
||||
if (trade instanceof ArbitratorTrade) {
|
||||
buyerState.set(BuyerState.STEP1);
|
||||
sellerState.set(SellerState.STEP1);
|
||||
return;
|
||||
}
|
||||
|
||||
switch (tradeState) {
|
||||
// preparation
|
||||
case PREPARATION:
|
||||
|
@ -414,9 +439,8 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
|
||||
// deposit requested
|
||||
case SENT_PUBLISH_DEPOSIT_TX_REQUEST:
|
||||
case SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST:
|
||||
case STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST:
|
||||
case SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST:
|
||||
case SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST:
|
||||
|
||||
// deposit published
|
||||
case ARBITRATOR_PUBLISHED_DEPOSIT_TXS:
|
||||
|
@ -456,29 +480,16 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
// seller step 4
|
||||
case SELLER_CONFIRMED_IN_UI_PAYMENT_RECEIPT: // UI action
|
||||
case SELLER_SENT_PAYMENT_RECEIVED_MSG:
|
||||
case SELLER_PUBLISHED_PAYOUT_TX: // payout tx broadcasted
|
||||
case SELLER_SENT_PAYOUT_TX_PUBLISHED_MSG: // PAYOUT_TX_PUBLISHED_MSG sent
|
||||
sellerState.set(SellerState.STEP3);
|
||||
if (trade instanceof BuyerTrade) buyerState.set(BuyerState.STEP4);
|
||||
else if (trade instanceof SellerTrade) sellerState.set(SellerState.STEP3);
|
||||
break;
|
||||
case SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG:
|
||||
case SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG:
|
||||
case SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG:
|
||||
case SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG: // PAYOUT_TX_PUBLISHED_MSG arrived
|
||||
case SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG: // PAYOUT_TX_PUBLISHED_MSG mailbox
|
||||
case SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG: // PAYOUT_TX_PUBLISHED_MSG failed - payout tx is published, peer will see it in network so we ignore failure and complete
|
||||
sellerState.set(SellerState.STEP4);
|
||||
break;
|
||||
|
||||
// buyer step 4
|
||||
case BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG:
|
||||
// Alternatively the maker could have seen the payout tx earlier before he received the PAYOUT_TX_PUBLISHED_MSG:
|
||||
case PAYOUT_TX_SEEN_IN_NETWORK:
|
||||
// Alternatively the buyer could fully sign and publish the payout tx
|
||||
case BUYER_PUBLISHED_PAYOUT_TX:
|
||||
buyerState.set(BuyerState.STEP4);
|
||||
break;
|
||||
|
||||
case WITHDRAW_COMPLETED:
|
||||
case TRADE_COMPLETED:
|
||||
sellerState.set(UNDEFINED);
|
||||
buyerState.set(BuyerState.UNDEFINED);
|
||||
break;
|
||||
|
@ -491,4 +502,21 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void onPayoutStateChanged(Trade.PayoutState payoutState) {
|
||||
log.info("UI payoutState={}, id={}",
|
||||
payoutState,
|
||||
trade != null ? trade.getShortId() : "trade is null");
|
||||
|
||||
if (trade instanceof ArbitratorTrade) return;
|
||||
|
||||
switch (payoutState) {
|
||||
case PUBLISHED:
|
||||
sellerState.set(SellerState.STEP4);
|
||||
buyerState.set(BuyerState.STEP4);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -123,8 +123,6 @@ public class SellerStep3View extends TradeStepView {
|
|||
busyAnimation.play();
|
||||
statusLabel.setText(Res.get("Confirming payment received. This can take up to a few minutes. Please wait..."));
|
||||
break;
|
||||
case SELLER_PUBLISHED_PAYOUT_TX:
|
||||
case SELLER_SENT_PAYOUT_TX_PUBLISHED_MSG:
|
||||
case SELLER_SENT_PAYMENT_RECEIVED_MSG:
|
||||
busyAnimation.play();
|
||||
statusLabel.setText(Res.get("shared.sendingConfirmation"));
|
||||
|
@ -135,16 +133,14 @@ public class SellerStep3View extends TradeStepView {
|
|||
}, 10);
|
||||
break;
|
||||
case SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG:
|
||||
case SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG:
|
||||
busyAnimation.stop();
|
||||
statusLabel.setText(Res.get("shared.messageArrived"));
|
||||
break;
|
||||
case SELLER_STORED_IN_MAILBOX_PAYMENT_RECEIVED_MSG:
|
||||
case SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG:
|
||||
busyAnimation.stop();
|
||||
statusLabel.setText(Res.get("shared.messageStoredInMailbox"));
|
||||
break;
|
||||
case SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG:
|
||||
case SELLER_SEND_FAILED_PAYMENT_RECEIVED_MSG:
|
||||
// We get a popup and the trade closed, so we dont need to show anything here
|
||||
busyAnimation.stop();
|
||||
statusLabel.setText("");
|
||||
|
@ -464,7 +460,7 @@ public class SellerStep3View extends TradeStepView {
|
|||
busyAnimation.play();
|
||||
statusLabel.setText(Res.get("shared.sendingConfirmation"));
|
||||
|
||||
model.dataModel.onFiatPaymentReceived(() -> {
|
||||
model.dataModel.onPaymentReceived(() -> {
|
||||
}, errorMessage -> {
|
||||
busyAnimation.stop();
|
||||
new Popup().warning(Res.get("popup.warning.sendMsgFailed")).show();
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue