mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-11-17 00:07:49 +00:00
listen for published payout tx
fix "Swapping pending OFFER_FUNDING" warning move payout tx from TradingPeer to Trade
This commit is contained in:
parent
5fbc41946e
commit
dc9c04759f
30 changed files with 231 additions and 279 deletions
|
@ -568,8 +568,8 @@ public class CoreApi {
|
|||
coreTradesService.confirmPaymentReceived(tradeId, resultHandler, errorMessageHandler);
|
||||
}
|
||||
|
||||
public void keepFunds(String tradeId) {
|
||||
coreTradesService.keepFunds(tradeId);
|
||||
public void closeTrade(String tradeId) {
|
||||
coreTradesService.closeTrade(tradeId);
|
||||
}
|
||||
|
||||
public void withdrawFunds(String tradeId, String address, String memo) {
|
||||
|
|
|
@ -155,7 +155,7 @@ class CoreTradesService {
|
|||
}
|
||||
}
|
||||
|
||||
void keepFunds(String tradeId) {
|
||||
void closeTrade(String tradeId) {
|
||||
coreWalletsService.verifyWalletsAreAvailable();
|
||||
coreWalletsService.verifyEncryptedWalletIsUnlocked();
|
||||
|
||||
|
|
|
@ -812,6 +812,13 @@ public class XmrWalletService {
|
|||
return getAddressEntryListAsImmutableList().stream().filter(addressEntry -> XmrAddressEntry.Context.AVAILABLE == addressEntry.getContext()).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<XmrAddressEntry> getAddressEntriesForOpenOffer() {
|
||||
return getAddressEntryListAsImmutableList().stream()
|
||||
.filter(addressEntry -> XmrAddressEntry.Context.OFFER_FUNDING == addressEntry.getContext() ||
|
||||
XmrAddressEntry.Context.RESERVED_FOR_TRADE == addressEntry.getContext())
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<XmrAddressEntry> getAddressEntriesForTrade() {
|
||||
return getAddressEntryListAsImmutableList().stream()
|
||||
.filter(addressEntry -> XmrAddressEntry.Context.MULTI_SIG == addressEntry.getContext() || XmrAddressEntry.Context.TRADE_PAYOUT == addressEntry.getContext())
|
||||
|
|
|
@ -248,7 +248,7 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
|
||||
private void cleanUpAddressEntries() {
|
||||
Set<String> openOffersIdSet = openOffers.getList().stream().map(OpenOffer::getId).collect(Collectors.toSet());
|
||||
btcWalletService.getAddressEntriesForOpenOffer().stream()
|
||||
xmrWalletService.getAddressEntriesForOpenOffer().stream()
|
||||
.filter(e -> !openOffersIdSet.contains(e.getOfferId()))
|
||||
.forEach(e -> {
|
||||
log.warn("We found an outdated addressEntry for openOffer {} (openOffers does not contain that " +
|
||||
|
@ -568,8 +568,8 @@ public class OpenOfferManager implements PeerManager.Listener, DecryptedDirectMe
|
|||
openOffer.setState(OpenOffer.State.CANCELED);
|
||||
openOffers.remove(openOffer);
|
||||
closedTradableManager.add(openOffer);
|
||||
log.info("onRemoved offerId={}", offer.getId());
|
||||
xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
||||
log.info("onRemoved offerId={}", offer.getId());
|
||||
requestPersistence();
|
||||
}
|
||||
|
||||
|
|
|
@ -407,11 +407,11 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
|||
Contract contract = dispute.getContract();
|
||||
|
||||
// verify sender is co-signer and receiver is arbitrator
|
||||
System.out.println("Any of these null???"); // TODO (woodser): NPE if dispute opener's peer-as-cosigner's ticket is closed first
|
||||
System.out.println(disputeResult);
|
||||
System.out.println(disputeResult.getWinner());
|
||||
System.out.println(contract.getBuyerNodeAddress());
|
||||
System.out.println(contract.getSellerNodeAddress());
|
||||
// System.out.println("Any of these null???"); // TODO (woodser): NPE if dispute opener's peer-as-cosigner's ticket is closed first
|
||||
// System.out.println(disputeResult);
|
||||
// System.out.println(disputeResult.getWinner());
|
||||
// System.out.println(contract.getBuyerNodeAddress());
|
||||
// System.out.println(contract.getSellerNodeAddress());
|
||||
boolean senderIsWinner = (disputeResult.getWinner() == Winner.BUYER && contract.getBuyerNodeAddress().equals(request.getSenderNodeAddress())) || (disputeResult.getWinner() == Winner.SELLER && contract.getSellerNodeAddress().equals(request.getSenderNodeAddress()));
|
||||
boolean senderIsCosigner = senderIsWinner || disputeResult.isLoserPublisher();
|
||||
boolean receiverIsArbitrator = pubKeyRing.equals(dispute.getAgentPubKeyRing());
|
||||
|
|
|
@ -50,6 +50,7 @@ import bisq.common.util.Utilities;
|
|||
import com.google.common.base.Preconditions;
|
||||
import com.google.protobuf.ByteString;
|
||||
import com.google.protobuf.Message;
|
||||
import common.utils.GenUtils;
|
||||
import org.bitcoinj.core.Coin;
|
||||
import org.bitcoinj.core.Transaction;
|
||||
|
||||
|
@ -90,9 +91,12 @@ import monero.common.MoneroError;
|
|||
import monero.daemon.MoneroDaemon;
|
||||
import monero.daemon.model.MoneroTx;
|
||||
import monero.wallet.MoneroWallet;
|
||||
import monero.wallet.model.MoneroCheckTx;
|
||||
import monero.wallet.model.MoneroDestination;
|
||||
import monero.wallet.model.MoneroMultisigSignResult;
|
||||
import monero.wallet.model.MoneroTransferQuery;
|
||||
import monero.wallet.model.MoneroTxConfig;
|
||||
import monero.wallet.model.MoneroTxQuery;
|
||||
import monero.wallet.model.MoneroTxSet;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
import monero.wallet.model.MoneroWalletListener;
|
||||
|
@ -127,7 +131,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
|
||||
// deposit published
|
||||
ARBITRATOR_PUBLISHED_DEPOSIT_TXS(Phase.DEPOSITS_PUBLISHED),
|
||||
DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN(Phase.DEPOSITS_PUBLISHED),
|
||||
DEPOSIT_TXS_SEEN_IN_NETWORK(Phase.DEPOSITS_PUBLISHED),
|
||||
|
||||
// deposit confirmed
|
||||
DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN(Phase.DEPOSITS_CONFIRMED),
|
||||
|
@ -157,8 +161,8 @@ public abstract class Trade implements Tradable, Model {
|
|||
SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG(Phase.PAYOUT_PUBLISHED),
|
||||
SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG(Phase.PAYOUT_PUBLISHED),
|
||||
BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG(Phase.PAYOUT_PUBLISHED),
|
||||
BUYER_SAW_PAYOUT_TX_IN_NETWORK(Phase.PAYOUT_PUBLISHED),
|
||||
BUYER_PUBLISHED_PAYOUT_TX(Phase.PAYOUT_PUBLISHED),
|
||||
PAYOUT_TX_SEEN_IN_NETWORK(Phase.PAYOUT_PUBLISHED),
|
||||
|
||||
// trade completed
|
||||
WITHDRAW_COMPLETED(Phase.WITHDRAWN);
|
||||
|
@ -310,9 +314,6 @@ public abstract class Trade implements Tradable, Model {
|
|||
@Nullable
|
||||
@Getter
|
||||
@Setter
|
||||
private String payoutTxId;
|
||||
@Getter
|
||||
@Setter
|
||||
private long amountAsLong;
|
||||
@Setter
|
||||
private long price;
|
||||
|
@ -366,9 +367,6 @@ public abstract class Trade implements Tradable, Model {
|
|||
// Added in v1.2.0
|
||||
@Nullable
|
||||
transient private Transaction delayedPayoutTx;
|
||||
|
||||
@Nullable
|
||||
transient private MoneroTxWallet payoutTx;
|
||||
@Nullable
|
||||
transient private Coin tradeAmount;
|
||||
|
||||
|
@ -412,12 +410,24 @@ public abstract class Trade implements Tradable, Model {
|
|||
@Getter
|
||||
transient final private IntegerProperty assetTxProofResultUpdateProperty = new SimpleIntegerProperty();
|
||||
|
||||
|
||||
// Added in XMR integration
|
||||
private transient List<TradeListener> tradeListeners; // notified on fully validated trade messages
|
||||
transient MoneroWalletListener depositTxListener;
|
||||
transient MoneroWalletListener payoutTxListener;
|
||||
transient Boolean makerDepositLocked; // null when unknown, true while locked, false when unlocked
|
||||
transient Boolean takerDepositLocked;
|
||||
@Nullable
|
||||
transient private MoneroTxWallet payoutTx;
|
||||
@Getter
|
||||
@Setter
|
||||
private String payoutTxId;
|
||||
@Nullable
|
||||
@Getter
|
||||
@Setter
|
||||
private String payoutTxHex;
|
||||
@Getter
|
||||
@Setter
|
||||
private String payoutTxKey;
|
||||
private Long startTime; // cache
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -547,6 +557,8 @@ public abstract class Trade implements Tradable, Model {
|
|||
Optional.ofNullable(counterCurrencyTxId).ifPresent(e -> builder.setCounterCurrencyTxId(counterCurrencyTxId));
|
||||
Optional.ofNullable(mediationResultState).ifPresent(e -> builder.setMediationResultState(MediationResultState.toProtoMessage(mediationResultState)));
|
||||
Optional.ofNullable(refundResultState).ifPresent(e -> builder.setRefundResultState(RefundResultState.toProtoMessage(refundResultState)));
|
||||
Optional.ofNullable(payoutTxHex).ifPresent(e -> builder.setPayoutTxHex(payoutTxHex));
|
||||
Optional.ofNullable(payoutTxKey).ifPresent(e -> builder.setPayoutTxHex(payoutTxKey));
|
||||
Optional.ofNullable(delayedPayoutTxBytes).ifPresent(e -> builder.setDelayedPayoutTxBytes(ByteString.copyFrom(delayedPayoutTxBytes)));
|
||||
Optional.ofNullable(counterCurrencyExtraData).ifPresent(e -> builder.setCounterCurrencyExtraData(counterCurrencyExtraData));
|
||||
Optional.ofNullable(assetTxProofResult).ifPresent(e -> builder.setAssetTxProofResult(assetTxProofResult.name()));
|
||||
|
@ -560,6 +572,8 @@ public abstract class Trade implements Tradable, Model {
|
|||
trade.setPeriodState(TradePeriodState.fromProto(proto.getPeriodState()));
|
||||
trade.setTakerFeeTxId(ProtoUtil.stringOrNullFromProto(proto.getTakerFeeTxId()));
|
||||
trade.setPayoutTxId(ProtoUtil.stringOrNullFromProto(proto.getPayoutTxId()));
|
||||
trade.setPayoutTxHex(ProtoUtil.stringOrNullFromProto(proto.getPayoutTxHex()));
|
||||
trade.setPayoutTxKey(ProtoUtil.stringOrNullFromProto(proto.getPayoutTxKey()));
|
||||
trade.setContract(proto.hasContract() ? Contract.fromProto(proto.getContract(), coreProtoResolver) : null);
|
||||
trade.setContractAsJson(ProtoUtil.stringOrNullFromProto(proto.getContractAsJson()));
|
||||
trade.setContractHash(ProtoUtil.byteArrayOrNullFromProto(proto.getContractHash()));
|
||||
|
@ -705,7 +719,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
BigInteger buyerDepositAmount = multisigWallet.getTx(getBuyer().getDepositTxHash()).getIncomingAmount();
|
||||
BigInteger tradeAmount = ParsingUtils.coinToAtomicUnits(getAmount());
|
||||
|
||||
// parse payout tx
|
||||
// describe payout tx
|
||||
MoneroTxSet describedTxSet = multisigWallet.describeTxSet(new MoneroTxSet().setMultisigTxHex(payoutTxHex));
|
||||
if (describedTxSet.getTxs() == null || describedTxSet.getTxs().size() != 1) throw new RuntimeException("Bad payout tx"); // TODO (woodser): test nack
|
||||
MoneroTxWallet payoutTx = describedTxSet.getTxs().get(0);
|
||||
|
@ -747,8 +761,8 @@ public abstract class Trade implements Tradable, Model {
|
|||
}
|
||||
|
||||
// update trade state
|
||||
getSelf().setPayoutTxHex(payoutTxHex);
|
||||
setPayoutTx(describedTxSet.getTxs().get(0));
|
||||
setPayoutTxHex(payoutTxHex);
|
||||
|
||||
// submit payout tx
|
||||
if (publish) {
|
||||
|
@ -807,17 +821,17 @@ public abstract class Trade implements Tradable, Model {
|
|||
|
||||
// handle deposit txs seen
|
||||
if (txs.size() == 2) {
|
||||
setStatePublished();
|
||||
setStateDepositsPublished();
|
||||
boolean makerFirst = txs.get(0).getHash().equals(processModel.getMaker().getDepositTxHash());
|
||||
getMaker().setDepositTx(makerFirst ? txs.get(0) : txs.get(1));
|
||||
getTaker().setDepositTx(makerFirst ? txs.get(1) : txs.get(0));
|
||||
|
||||
// check if deposit txs unlocked
|
||||
if (txs.get(0).isConfirmed() && txs.get(1).isConfirmed()) {
|
||||
setStateConfirmed();
|
||||
setStateDepositsConfirmed();
|
||||
long unlockHeight = Math.max(txs.get(0).getHeight(), txs.get(1).getHeight()) + XmrWalletService.NUM_BLOCKS_UNLOCK;
|
||||
if (havenoWallet.getHeight() >= unlockHeight) {
|
||||
setStateUnlocked();
|
||||
setStateDepositsUnlocked();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -844,7 +858,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
|
||||
// skip if deposit txs not seen
|
||||
if (txs.size() != 2) return;
|
||||
setStatePublished();
|
||||
setStateDepositsPublished();
|
||||
|
||||
// update deposit txs
|
||||
boolean makerFirst = txs.get(0).getHash().equals(processModel.getMaker().getDepositTxHash());
|
||||
|
@ -854,7 +868,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
// check if deposit txs confirmed and compute unlock height
|
||||
if (txs.size() == 2 && txs.get(0).isConfirmed() && txs.get(1).isConfirmed() && unlockHeight == null) {
|
||||
log.info("Multisig deposits confirmed for trade {}", getId());
|
||||
setStateConfirmed();
|
||||
setStateDepositsConfirmed();
|
||||
unlockHeight = Math.max(txs.get(0).getHeight(), txs.get(1).getHeight()) + XmrWalletService.NUM_BLOCKS_UNLOCK;
|
||||
}
|
||||
|
||||
|
@ -863,7 +877,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
log.info("Multisig deposits unlocked for trade {}", getId());
|
||||
xmrWalletService.removeWalletListener(depositTxListener); // remove listener when notified
|
||||
depositTxListener = null; // prevent re-applying trade state in subsequent requests
|
||||
setStateUnlocked();
|
||||
setStateDepositsUnlocked();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -872,6 +886,51 @@ public abstract class Trade implements Tradable, Model {
|
|||
xmrWalletService.addWalletListener(depositTxListener);
|
||||
}
|
||||
|
||||
public void listenForPayoutTx() {
|
||||
log.info("Listening for payout tx for trade {}", getId());
|
||||
|
||||
// check if payout tx already seen
|
||||
if (getState().ordinal() >= Trade.State.PAYOUT_TX_SEEN_IN_NETWORK.ordinal()) {
|
||||
log.warn("We had a payout tx already set. tradeId={}, state={}", getId(), getState());
|
||||
return;
|
||||
}
|
||||
|
||||
// get payout address entry
|
||||
Optional<XmrAddressEntry> optionalPayoutEntry = xmrWalletService.getAddressEntry(getId(), XmrAddressEntry.Context.TRADE_PAYOUT);
|
||||
if (!optionalPayoutEntry.isPresent()) throw new RuntimeException("Trade does not have address entry for payout");
|
||||
XmrAddressEntry payoutEntry = optionalPayoutEntry.get();
|
||||
|
||||
// watch for payout tx on loop
|
||||
new Thread(() -> { // TODO: use thread manager
|
||||
boolean found = false;
|
||||
while (!found) {
|
||||
if (getPayoutTxKey() != null) {
|
||||
|
||||
// get txs to payout address
|
||||
List<MoneroTxWallet> txs = xmrWalletService.getWallet().getTxs(new MoneroTxQuery()
|
||||
.setTransferQuery(new MoneroTransferQuery()
|
||||
.setAccountIndex(0)
|
||||
.setSubaddressIndex(payoutEntry.getSubaddressIndex())
|
||||
.setIsIncoming(true)));
|
||||
|
||||
// check for payout tx
|
||||
for (MoneroTxWallet tx : txs) {
|
||||
MoneroCheckTx txCheck = xmrWalletService.getWallet().checkTxKey(tx.getHash(), getPayoutTxKey(), payoutEntry.getAddressString());
|
||||
if (txCheck.isGood() && txCheck.receivedAmount.compareTo(new BigInteger("0")) > 0) {
|
||||
found = true;
|
||||
setPayoutTx(tx);
|
||||
setStateIfValidTransitionTo(Trade.State.PAYOUT_TX_SEEN_IN_NETWORK);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// wait to loop
|
||||
GenUtils.waitFor(xmrWalletService.getConnectionsService().getDefaultRefreshPeriodMs());
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public MoneroTx getTakerDepositTx() {
|
||||
String depositTxHash = getProcessModel().getTaker().getDepositTxHash();
|
||||
|
@ -1045,6 +1104,7 @@ public abstract class Trade implements Tradable, Model {
|
|||
public void setPayoutTx(MoneroTxWallet payoutTx) {
|
||||
this.payoutTx = payoutTx;
|
||||
payoutTxId = payoutTx.getHash();
|
||||
payoutTxKey = payoutTx.getKey();
|
||||
}
|
||||
|
||||
public void setErrorMessage(String errorMessage) {
|
||||
|
@ -1275,7 +1335,6 @@ public abstract class Trade implements Tradable, Model {
|
|||
}
|
||||
|
||||
public boolean isPayoutPublished() {
|
||||
if (getState() == Trade.State.SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG) return true; // TODO: this is a hack because seller has not seen signed payout tx. replace when payout process refactored
|
||||
return getState().getPhase().ordinal() >= Phase.PAYOUT_PUBLISHED.ordinal() || isWithdrawn();
|
||||
}
|
||||
|
||||
|
@ -1397,15 +1456,15 @@ public abstract class Trade implements Tradable, Model {
|
|||
return tradeVolumeProperty;
|
||||
}
|
||||
|
||||
private void setStatePublished() {
|
||||
if (!isDepositPublished()) setState(State.DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN);
|
||||
private void setStateDepositsPublished() {
|
||||
if (!isDepositPublished()) setState(State.DEPOSIT_TXS_SEEN_IN_NETWORK);
|
||||
}
|
||||
|
||||
private void setStateConfirmed() {
|
||||
private void setStateDepositsConfirmed() {
|
||||
if (!isDepositConfirmed()) setState(State.DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN);
|
||||
}
|
||||
|
||||
private void setStateUnlocked() {
|
||||
private void setStateDepositsUnlocked() {
|
||||
if (!isDepositUnlocked()) setState(State.DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN);
|
||||
}
|
||||
|
||||
|
|
|
@ -284,7 +284,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
xmrWalletService.getAddressEntriesForAvailableBalanceStream()
|
||||
.filter(addressEntry -> addressEntry.getOfferId() != null)
|
||||
.forEach(addressEntry -> {
|
||||
log.warn("Swapping pending OFFER_FUNDING entries at startup. offerId={}", addressEntry.getOfferId());
|
||||
log.warn("Swapping pending {} entries at startup. offerId={}", addressEntry.getContext(), addressEntry.getOfferId());
|
||||
xmrWalletService.swapTradeEntryToAvailableEntry(addressEntry.getOfferId(), XmrAddressEntry.Context.OFFER_FUNDING);
|
||||
});
|
||||
|
||||
|
@ -837,6 +837,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
|
||||
// If trade was completed (closed without fault but might be closed by a dispute) we move it to the closed trades
|
||||
public void onTradeCompleted(Trade trade) {
|
||||
if (trade.getState() == Trade.State.WITHDRAW_COMPLETED) return;
|
||||
closedTradableManager.add(trade);
|
||||
trade.setState(Trade.State.WITHDRAW_COMPLETED);
|
||||
maybeRemoveTrade(trade);
|
||||
|
|
|
@ -17,6 +17,9 @@
|
|||
|
||||
package bisq.core.trade.protocol;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.handlers.ErrorMessageHandler;
|
||||
import bisq.common.handlers.ResultHandler;
|
||||
import bisq.core.trade.BuyerTrade;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.messages.PaymentAccountKeyResponse;
|
||||
|
@ -25,20 +28,16 @@ import bisq.core.trade.messages.SignContractResponse;
|
|||
import bisq.core.trade.messages.TradeMessage;
|
||||
import bisq.core.trade.protocol.FluentProtocol.Condition;
|
||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||
import bisq.core.trade.protocol.tasks.BuyerSendPayoutTxPublishedMessage;
|
||||
import bisq.core.trade.protocol.tasks.BuyerPreparePaymentSentMessage;
|
||||
import bisq.core.trade.protocol.tasks.BuyerProcessPaymentAccountKeyResponse;
|
||||
import bisq.core.trade.protocol.tasks.BuyerProcessPaymentReceivedMessage;
|
||||
import bisq.core.trade.protocol.tasks.BuyerSendPaymentAccountKeyRequestToArbitrator;
|
||||
import bisq.core.trade.protocol.tasks.BuyerSendPaymentSentMessage;
|
||||
import bisq.core.trade.protocol.tasks.BuyerSetupPayoutTxListener;
|
||||
import bisq.core.trade.protocol.tasks.BuyerProcessPaymentAccountKeyResponse;
|
||||
import bisq.core.trade.protocol.tasks.BuyerSendPayoutTxPublishedMessage;
|
||||
import bisq.core.trade.protocol.tasks.SetupDepositTxsListener;
|
||||
import bisq.core.trade.protocol.tasks.SetupPayoutTxListener;
|
||||
import bisq.core.util.Validator;
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.handlers.ErrorMessageHandler;
|
||||
import bisq.common.handlers.ResultHandler;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
|
||||
|
@ -70,17 +69,20 @@ public abstract class BuyerProtocol extends DisputeProtocol {
|
|||
// request key to decrypt seller's payment account payload after first confirmation
|
||||
sendPaymentAccountKeyRequestIfWhenNeeded(BuyerEvent.STARTUP, false);
|
||||
|
||||
// listen for deposit txs
|
||||
given(anyPhase(Trade.Phase.DEPOSIT_REQUESTED, Trade.Phase.DEPOSITS_PUBLISHED, Trade.Phase.DEPOSITS_CONFIRMED)
|
||||
.with(BuyerEvent.STARTUP))
|
||||
.setup(tasks(SetupDepositTxsListener.class))
|
||||
.executeTasks();
|
||||
|
||||
// listen for payout tx
|
||||
given(anyPhase(Trade.Phase.PAYMENT_SENT, Trade.Phase.PAYMENT_RECEIVED)
|
||||
.with(BuyerEvent.STARTUP))
|
||||
.setup(tasks(BuyerSetupPayoutTxListener.class)) // TODO (woodser): mirror deposit listener setup?
|
||||
.setup(tasks(SetupPayoutTxListener.class))
|
||||
.executeTasks();
|
||||
|
||||
given(anyPhase(Trade.Phase.PAYMENT_SENT, Trade.Phase.PAYMENT_RECEIVED)
|
||||
// send payment sent message
|
||||
given(anyPhase(Trade.Phase.PAYMENT_SENT, Trade.Phase.PAYMENT_RECEIVED) // TODO: remove payment received phase?
|
||||
.anyState(Trade.State.BUYER_STORED_IN_MAILBOX_PAYMENT_SENT_MSG, Trade.State.BUYER_SEND_FAILED_PAYMENT_SENT_MSG)
|
||||
.with(BuyerEvent.STARTUP))
|
||||
.setup(tasks(BuyerSendPaymentSentMessage.class))
|
||||
|
|
|
@ -22,6 +22,7 @@ import bisq.core.trade.Trade;
|
|||
import bisq.core.trade.messages.PaymentSentMessage;
|
||||
import bisq.core.trade.messages.SignContractResponse;
|
||||
import bisq.core.trade.messages.TradeMessage;
|
||||
import bisq.core.trade.protocol.BuyerProtocol.BuyerEvent;
|
||||
import bisq.core.trade.protocol.FluentProtocol.Condition;
|
||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||
import bisq.core.trade.protocol.tasks.SellerMaybeSendPayoutTxPublishedMessage;
|
||||
|
@ -30,6 +31,7 @@ import bisq.core.trade.protocol.tasks.SellerProcessPaymentSentMessage;
|
|||
import bisq.core.trade.protocol.tasks.SellerSendPaymentReceivedMessage;
|
||||
import bisq.core.trade.protocol.tasks.SellerSendPaymentAccountPayloadKey;
|
||||
import bisq.core.trade.protocol.tasks.SetupDepositTxsListener;
|
||||
import bisq.core.trade.protocol.tasks.SetupPayoutTxListener;
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
import bisq.common.handlers.ErrorMessageHandler;
|
||||
import bisq.common.handlers.ResultHandler;
|
||||
|
@ -60,11 +62,17 @@ public abstract class SellerProtocol extends DisputeProtocol {
|
|||
sendPaymentAccountPayloadKeyWhenConfirmed(SellerEvent.STARTUP);
|
||||
}
|
||||
|
||||
// listen for changes to deposit txs
|
||||
// listen for deposit txs
|
||||
given(anyPhase(Trade.Phase.DEPOSIT_REQUESTED, Trade.Phase.DEPOSITS_PUBLISHED, Trade.Phase.DEPOSITS_CONFIRMED)
|
||||
.with(SellerEvent.STARTUP))
|
||||
.setup(tasks(SetupDepositTxsListener.class))
|
||||
.executeTasks();
|
||||
|
||||
// listen for payout tx
|
||||
given(anyPhase(Trade.Phase.PAYMENT_SENT, Trade.Phase.PAYMENT_RECEIVED)
|
||||
.with(BuyerEvent.STARTUP))
|
||||
.setup(tasks(SetupPayoutTxListener.class))
|
||||
.executeTasks();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -335,7 +335,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||
latchTrade();
|
||||
Validator.checkTradeId(processModel.getOfferId(), response);
|
||||
processModel.setTradeMessage(response);
|
||||
expect(anyState(Trade.State.SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST, Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS, Trade.State.DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN)
|
||||
expect(anyState(Trade.State.SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST, Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS, Trade.State.DEPOSIT_TXS_SEEN_IN_NETWORK)
|
||||
.with(response)
|
||||
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
||||
.setup(tasks(
|
||||
|
|
|
@ -124,10 +124,6 @@ public final class TradingPeer implements PersistablePayload {
|
|||
@Nullable
|
||||
private String depositTxKey;
|
||||
@Nullable
|
||||
transient private MoneroTxWallet payoutTx;
|
||||
@Nullable
|
||||
private String payoutTxHex;
|
||||
@Nullable
|
||||
private String updatedMultisigHex;
|
||||
|
||||
public TradingPeer() {
|
||||
|
@ -164,7 +160,6 @@ public final class TradingPeer implements PersistablePayload {
|
|||
Optional.ofNullable(preparedMultisigHex).ifPresent(e -> builder.setPreparedMultisigHex(preparedMultisigHex));
|
||||
Optional.ofNullable(madeMultisigHex).ifPresent(e -> builder.setMadeMultisigHex(madeMultisigHex));
|
||||
Optional.ofNullable(exchangedMultisigHex).ifPresent(e -> builder.setExchangedMultisigHex(exchangedMultisigHex));
|
||||
Optional.ofNullable(payoutTxHex).ifPresent(e -> builder.setPayoutTxHex(payoutTxHex));
|
||||
Optional.ofNullable(depositTxHash).ifPresent(e -> builder.setDepositTxHash(depositTxHash));
|
||||
Optional.ofNullable(depositTxHex).ifPresent(e -> builder.setDepositTxHex(depositTxHex));
|
||||
Optional.ofNullable(depositTxKey).ifPresent(e -> builder.setDepositTxKey(depositTxKey));
|
||||
|
@ -216,7 +211,6 @@ public final class TradingPeer implements PersistablePayload {
|
|||
tradingPeer.setDepositTxHash(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHash()));
|
||||
tradingPeer.setDepositTxHex(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHex()));
|
||||
tradingPeer.setDepositTxKey(ProtoUtil.stringOrNullFromProto(proto.getDepositTxKey()));
|
||||
tradingPeer.setPayoutTxHex(ProtoUtil.stringOrNullFromProto(proto.getPayoutTxHex()));
|
||||
tradingPeer.setUpdatedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()));
|
||||
return tradingPeer;
|
||||
}
|
||||
|
|
|
@ -65,11 +65,16 @@ public class BuyerPreparePaymentSentMessage extends TradeTask {
|
|||
|
||||
// create payout tx if we have seller's updated multisig hex
|
||||
if (trade.getTradingPeer().getUpdatedMultisigHex() != null) {
|
||||
|
||||
// create payout tx
|
||||
log.info("Buyer creating unsigned payout tx");
|
||||
multisigWallet.importMultisigHex(trade.getTradingPeer().getUpdatedMultisigHex());
|
||||
MoneroTxWallet payoutTx = trade.createPayoutTx();
|
||||
trade.getBuyer().setPayoutTx(payoutTx);
|
||||
trade.getBuyer().setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||
trade.setPayoutTx(payoutTx);
|
||||
trade.setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||
|
||||
// start listening for published payout tx
|
||||
trade.listenForPayoutTx();
|
||||
} else {
|
||||
if (trade.getSelf().getUpdatedMultisigHex() == null) trade.getSelf().setUpdatedMultisigHex(multisigWallet.exportMultisigHex()); // only export multisig hex once
|
||||
}
|
||||
|
|
|
@ -59,7 +59,7 @@ public class BuyerProcessPaymentReceivedMessage extends TradeTask {
|
|||
if (trade.getPhase().ordinal() < Trade.Phase.PAYOUT_PUBLISHED.ordinal()) {
|
||||
|
||||
// publish payout tx if signed. otherwise verify, sign, and publish payout tx
|
||||
boolean previouslySigned = trade.getBuyer().getPayoutTxHex() != null;
|
||||
boolean previouslySigned = trade.getPayoutTxHex() != null;
|
||||
if (previouslySigned) {
|
||||
log.info("Buyer publishing signed payout tx from seller");
|
||||
XmrWalletService walletService = processModel.getProvider().getXmrWalletService();
|
||||
|
@ -67,14 +67,17 @@ public class BuyerProcessPaymentReceivedMessage extends TradeTask {
|
|||
List<String> txHashes = multisigWallet.submitMultisigTxHex(message.getPayoutTxHex());
|
||||
trade.setPayoutTx(multisigWallet.getTx(txHashes.get(0)));
|
||||
XmrWalletService.printTxs("payoutTx received from peer", trade.getPayoutTx());
|
||||
trade.setState(Trade.State.BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG);
|
||||
trade.setStateIfValidTransitionTo(Trade.State.BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG);
|
||||
walletService.closeMultisigWallet(trade.getId());
|
||||
} else {
|
||||
log.info("Buyer verifying, signing, and publishing seller's payout tx");
|
||||
trade.verifyPayoutTx(message.getPayoutTxHex(), true, true);
|
||||
trade.setState(Trade.State.BUYER_PUBLISHED_PAYOUT_TX);
|
||||
// TODO (woodser): send PayoutTxPublishedMessage to arbitrator and seller
|
||||
trade.setStateIfValidTransitionTo(Trade.State.BUYER_PUBLISHED_PAYOUT_TX);
|
||||
// TODO (woodser): send PayoutTxPublishedMessage to seller
|
||||
}
|
||||
|
||||
// mark address entries as available
|
||||
processModel.getXmrWalletService().resetAddressEntriesForPendingTrade(trade.getId());
|
||||
} else {
|
||||
log.info("We got the payout tx already set from BuyerSetupPayoutTxListener and do nothing here. trade ID={}", trade.getId());
|
||||
}
|
||||
|
@ -89,7 +92,6 @@ public class BuyerProcessPaymentReceivedMessage extends TradeTask {
|
|||
}
|
||||
|
||||
processModel.getTradeManager().requestPersistence();
|
||||
|
||||
complete();
|
||||
} catch (Throwable t) {
|
||||
failed(t);
|
||||
|
|
|
@ -62,7 +62,7 @@ public class BuyerSendPaymentSentMessage extends SendMailboxMessageTask {
|
|||
trade.getCounterCurrencyTxId(),
|
||||
trade.getCounterCurrencyExtraData(),
|
||||
deterministicId,
|
||||
trade.getBuyer().getPayoutTxHex(),
|
||||
trade.getPayoutTxHex(),
|
||||
trade.getBuyer().getUpdatedMultisigHex(),
|
||||
trade.getSelf().getPaymentAccountKey()
|
||||
);
|
||||
|
|
|
@ -50,13 +50,13 @@ public class BuyerSendPayoutTxPublishedMessage extends SendMailboxMessageTask {
|
|||
|
||||
@Override
|
||||
protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) {
|
||||
checkNotNull(trade.getSelf().getPayoutTxHex(), "Payout tx must not be null");
|
||||
checkNotNull(trade.getPayoutTxHex(), "Payout tx must not be null");
|
||||
return new PayoutTxPublishedMessage(
|
||||
tradeId,
|
||||
processModel.getMyNodeAddress(),
|
||||
trade.isMaker(),
|
||||
null, // TODO: send witness data?
|
||||
trade.getSelf().getPayoutTxHex()
|
||||
trade.getPayoutTxHex()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* This file is part of Haveno.
|
||||
*
|
||||
* Haveno is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Haveno is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package bisq.core.trade.protocol.tasks;
|
||||
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.common.taskrunner.TaskRunner;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class BuyerSetupPayoutTxListener extends SetupPayoutTxListener {
|
||||
public BuyerSetupPayoutTxListener(TaskRunner<Trade> taskHandler, Trade trade) {
|
||||
super(taskHandler, trade);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void run() {
|
||||
try {
|
||||
runInterceptHook();
|
||||
|
||||
super.run();
|
||||
|
||||
} catch (Throwable t) {
|
||||
failed(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setState() {
|
||||
trade.setStateIfValidTransitionTo(Trade.State.BUYER_SAW_PAYOUT_TX_IN_NETWORK);
|
||||
|
||||
processModel.getTradeManager().requestPersistence();
|
||||
}
|
||||
}
|
|
@ -65,13 +65,13 @@ public class SellerMaybeSendPayoutTxPublishedMessage extends SendMailboxMessageT
|
|||
|
||||
@Override
|
||||
protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) {
|
||||
checkNotNull(trade.getSelf().getPayoutTxHex(), "Payout tx must not be null");
|
||||
checkNotNull(trade.getPayoutTxHex(), "Payout tx must not be null");
|
||||
return new PayoutTxPublishedMessage(
|
||||
tradeId,
|
||||
processModel.getMyNodeAddress(),
|
||||
trade.isMaker(),
|
||||
null, // TODO: send witness data?
|
||||
trade.getSelf().getPayoutTxHex()
|
||||
trade.getPayoutTxHex()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,17 +38,21 @@ public class SellerPreparePaymentReceivedMessage extends TradeTask {
|
|||
runInterceptHook();
|
||||
|
||||
// verify, sign, and publish payout tx if given. otherwise create payout tx
|
||||
if (trade.getBuyer().getPayoutTxHex() != null) {
|
||||
if (trade.getPayoutTxHex() != null) {
|
||||
log.info("Seller verifying, signing, and publishing payout tx");
|
||||
trade.verifyPayoutTx(trade.getBuyer().getPayoutTxHex(), true, true);
|
||||
trade.verifyPayoutTx(trade.getPayoutTxHex(), true, true);
|
||||
} else {
|
||||
|
||||
// create unsigned payout tx
|
||||
log.info("Seller creating unsigned payout tx");
|
||||
MoneroTxWallet payoutTx = trade.createPayoutTx();
|
||||
System.out.println("created payout tx: " + payoutTx);
|
||||
trade.getSeller().setPayoutTx(payoutTx);
|
||||
trade.getSeller().setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||
}
|
||||
trade.setPayoutTx(payoutTx);
|
||||
trade.setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||
|
||||
// start listening for published payout tx
|
||||
trade.listenForPayoutTx();
|
||||
}
|
||||
complete();
|
||||
} catch (Throwable t) {
|
||||
failed(t);
|
||||
|
|
|
@ -43,7 +43,7 @@ public class SellerProcessPaymentSentMessage extends TradeTask {
|
|||
checkNotNull(message);
|
||||
|
||||
// store buyer info
|
||||
trade.getBuyer().setPayoutTxHex(message.getPayoutTxHex());
|
||||
trade.setPayoutTxHex(message.getPayoutTxHex());
|
||||
trade.getBuyer().setUpdatedMultisigHex(message.getUpdatedMultisigHex());
|
||||
|
||||
// decrypt buyer's payment account payload
|
||||
|
|
|
@ -42,7 +42,7 @@ public class SellerSendPaymentReceivedMessage extends SendMailboxMessageTask {
|
|||
try {
|
||||
runInterceptHook();
|
||||
|
||||
if (trade.getSeller().getPayoutTxHex() == null) {
|
||||
if (trade.getPayoutTxHex() == null) {
|
||||
log.error("Payout tx is null");
|
||||
failed("Payout tx is null");
|
||||
return;
|
||||
|
@ -56,12 +56,12 @@ public class SellerSendPaymentReceivedMessage extends SendMailboxMessageTask {
|
|||
|
||||
@Override
|
||||
protected TradeMailboxMessage getTradeMailboxMessage(String id) {
|
||||
checkNotNull(trade.getSeller().getPayoutTxHex(), "Payout tx must not be null");
|
||||
checkNotNull(trade.getPayoutTxHex(), "Payout tx must not be null");
|
||||
return new PaymentReceivedMessage(
|
||||
id,
|
||||
processModel.getMyNodeAddress(),
|
||||
signedWitness,
|
||||
trade.getSeller().getPayoutTxHex()
|
||||
trade.getPayoutTxHex()
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,120 +17,50 @@
|
|||
|
||||
package bisq.core.trade.protocol.tasks;
|
||||
|
||||
import bisq.core.btc.listeners.AddressConfidenceListener;
|
||||
import bisq.core.btc.wallet.XmrWalletService;
|
||||
import bisq.core.trade.Trade;
|
||||
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.taskrunner.TaskRunner;
|
||||
|
||||
import org.bitcoinj.core.TransactionConfidence;
|
||||
|
||||
import bisq.core.trade.Trade;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.fxmisc.easybind.EasyBind;
|
||||
import org.fxmisc.easybind.Subscription;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
|
||||
|
||||
import monero.wallet.model.MoneroTransferQuery;
|
||||
import monero.wallet.model.MoneroTxQuery;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
@Slf4j
|
||||
public abstract class SetupPayoutTxListener extends TradeTask {
|
||||
// Use instance fields to not get eaten up by the GC
|
||||
private Subscription tradeStateSubscription;
|
||||
private AddressConfidenceListener confidenceListener;
|
||||
public class SetupPayoutTxListener extends TradeTask {
|
||||
|
||||
public SetupPayoutTxListener(TaskRunner<Trade> taskHandler, Trade trade) {
|
||||
private Subscription tradeStateSubscription;
|
||||
|
||||
@SuppressWarnings({ "unused" })
|
||||
public SetupPayoutTxListener(TaskRunner taskHandler, Trade trade) {
|
||||
super(taskHandler, trade);
|
||||
}
|
||||
|
||||
|
||||
protected abstract void setState();
|
||||
|
||||
@Override
|
||||
protected void run() {
|
||||
try {
|
||||
runInterceptHook();
|
||||
System.out.println("NEED TO IMPLEMENT PAYOUT TX LISTENER!"); // TODO (woodser): implement SetupPayoutTxListener
|
||||
// if (!trade.isPayoutPublished()) {
|
||||
// BtcWalletService walletService = processModel.getBtcWalletService();
|
||||
// String id = processModel.getOffer().getId();
|
||||
// Address address = walletService.getOrCreateAddressEntry(id, AddressEntry.Context.TRADE_PAYOUT).getAddress();
|
||||
//
|
||||
// TransactionConfidence confidence = walletService.getConfidenceForAddress(address);
|
||||
// if (isInNetwork(confidence)) {
|
||||
// applyConfidence(confidence);
|
||||
// } else {
|
||||
// confidenceListener = new AddressConfidenceListener(address) {
|
||||
// @Override
|
||||
// public void onTransactionConfidenceChanged(TransactionConfidence confidence) {
|
||||
// if (isInNetwork(confidence))
|
||||
// applyConfidence(confidence);
|
||||
// }
|
||||
// };
|
||||
// walletService.addAddressConfidenceListener(confidenceListener);
|
||||
//
|
||||
// tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> {
|
||||
// if (trade.isPayoutPublished()) {
|
||||
// processModel.getBtcWalletService().resetCoinLockedInMultiSigAddressEntry(trade.getId());
|
||||
//
|
||||
// // hack to remove tradeStateSubscription at callback
|
||||
// UserThread.execute(this::unSubscribe);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
// }
|
||||
|
||||
// we complete immediately, our object stays alive because the balanceListener is stored in the WalletService
|
||||
// skip if payout already published
|
||||
if (!trade.isPayoutPublished()) {
|
||||
|
||||
// listen for payout tx
|
||||
trade.listenForPayoutTx();
|
||||
tradeStateSubscription = EasyBind.subscribe(trade.stateProperty(), newValue -> {
|
||||
if (trade.isPayoutPublished()) {
|
||||
|
||||
// cleanup on trade completion
|
||||
processModel.getXmrWalletService().resetAddressEntriesForPendingTrade(trade.getId());
|
||||
UserThread.execute(this::unSubscribe); // unsubscribe
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
complete();
|
||||
} catch (Throwable t) {
|
||||
failed(t);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyPayoutTx(int accountIdx) {
|
||||
if (trade.getPayoutTx() == null) {
|
||||
|
||||
// get txs with transfers to payout subaddress
|
||||
List<MoneroTxWallet> txs = processModel.getProvider().getXmrWalletService().getWallet().getTxs(new MoneroTxQuery()
|
||||
.setTransferQuery(new MoneroTransferQuery().setAccountIndex(accountIdx).setSubaddressIndex(0).setIsIncoming(true))); // TODO (woodser): hardcode account 0 as savings wallet, subaddress 0 trade accounts in config
|
||||
|
||||
// resolve payout tx if multiple txs sent to payout address
|
||||
MoneroTxWallet payoutTx;
|
||||
if (txs.size() > 1) {
|
||||
throw new RuntimeException("Need to resolve multiple payout txs"); // TODO (woodser)
|
||||
} else {
|
||||
payoutTx = txs.get(0);
|
||||
}
|
||||
|
||||
trade.setPayoutTx(payoutTx);
|
||||
XmrWalletService.printTxs("payoutTx received from network", payoutTx);
|
||||
setState();
|
||||
} else {
|
||||
log.info("We had the payout tx already set. tradeId={}, state={}", trade.getId(), trade.getState());
|
||||
}
|
||||
|
||||
processModel.getBtcWalletService().resetCoinLockedInMultiSigAddressEntry(trade.getId());
|
||||
|
||||
// need delay as it can be called inside the handler before the listener and tradeStateSubscription are actually set.
|
||||
UserThread.execute(this::unSubscribe);
|
||||
}
|
||||
|
||||
private boolean isInNetwork(TransactionConfidence confidence) {
|
||||
return confidence != null &&
|
||||
(confidence.getConfidenceType().equals(TransactionConfidence.ConfidenceType.BUILDING) ||
|
||||
confidence.getConfidenceType().equals(TransactionConfidence.ConfidenceType.PENDING));
|
||||
}
|
||||
|
||||
private void unSubscribe() {
|
||||
if (tradeStateSubscription != null)
|
||||
tradeStateSubscription.unsubscribe();
|
||||
|
||||
if (confidenceListener != null)
|
||||
processModel.getBtcWalletService().removeAddressConfidenceListener(confidenceListener);
|
||||
if (tradeStateSubscription != null) tradeStateSubscription.unsubscribe();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,17 +17,16 @@
|
|||
|
||||
package bisq.core.trade.protocol.tasks.mediation;
|
||||
|
||||
import bisq.core.support.dispute.mediation.MediationResultState;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.protocol.tasks.SetupPayoutTxListener;
|
||||
|
||||
import bisq.common.taskrunner.TaskRunner;
|
||||
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.protocol.tasks.TradeTask;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
public class SetupMediatedPayoutTxListener extends SetupPayoutTxListener {
|
||||
public SetupMediatedPayoutTxListener(TaskRunner<Trade> taskHandler, Trade trade) {
|
||||
public class SetupMediatedPayoutTxListener extends TradeTask {
|
||||
|
||||
@SuppressWarnings({ "unused" })
|
||||
public SetupMediatedPayoutTxListener(TaskRunner taskHandler, Trade trade) {
|
||||
super(taskHandler, trade);
|
||||
}
|
||||
|
||||
|
@ -35,20 +34,10 @@ public class SetupMediatedPayoutTxListener extends SetupPayoutTxListener {
|
|||
protected void run() {
|
||||
try {
|
||||
runInterceptHook();
|
||||
|
||||
super.run();
|
||||
|
||||
if (true) throw new RuntimeException("Not implemented");
|
||||
complete();
|
||||
} catch (Throwable t) {
|
||||
failed(t);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setState() {
|
||||
trade.setMediationResultState(MediationResultState.PAYOUT_TX_SEEN_IN_NETWORK);
|
||||
if (trade.getPayoutTx() != null) {
|
||||
processModel.getTradeManager().closeDisputedTrade(trade.getId(), Trade.DisputeState.MEDIATION_CLOSED);
|
||||
}
|
||||
processModel.getTradeManager().requestPersistence();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -176,11 +176,12 @@ class GrpcTradesService extends TradesImplBase {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: rename KeepFundsRequest to CloseTradeRequest
|
||||
@Override
|
||||
public void keepFunds(KeepFundsRequest req,
|
||||
StreamObserver<KeepFundsReply> responseObserver) {
|
||||
try {
|
||||
coreApi.keepFunds(req.getTradeId());
|
||||
coreApi.closeTrade(req.getTradeId());
|
||||
var reply = KeepFundsReply.newBuilder().build();
|
||||
responseObserver.onNext(reply);
|
||||
responseObserver.onCompleted();
|
||||
|
|
|
@ -30,7 +30,6 @@ 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.BuyerSendPaymentSentMessage;
|
||||
import bisq.core.trade.protocol.tasks.BuyerSetupPayoutTxListener;
|
||||
import bisq.core.trade.protocol.tasks.MakerSetLockTime;
|
||||
import bisq.core.trade.protocol.tasks.MakerRemoveOpenOffer;
|
||||
import bisq.core.trade.protocol.tasks.SellerPreparePaymentReceivedMessage;
|
||||
|
@ -38,6 +37,7 @@ 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.TakerVerifyMakerFeePayment;
|
||||
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
|
||||
import bisq.common.taskrunner.Task;
|
||||
|
@ -123,7 +123,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
|
||||
ApplyFilter.class,
|
||||
BuyerPreparePaymentSentMessage.class,
|
||||
BuyerSetupPayoutTxListener.class,
|
||||
SetupPayoutTxListener.class,
|
||||
BuyerSendPaymentSentMessage.class,
|
||||
|
||||
BuyerProcessPaymentReceivedMessage.class
|
||||
|
@ -142,7 +142,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||
ApplyFilter.class,
|
||||
TakerVerifyMakerFeePayment.class,
|
||||
BuyerPreparePaymentSentMessage.class,
|
||||
BuyerSetupPayoutTxListener.class,
|
||||
SetupPayoutTxListener.class,
|
||||
BuyerSendPaymentSentMessage.class,
|
||||
|
||||
BuyerProcessPaymentReceivedMessage.class)
|
||||
|
|
|
@ -200,7 +200,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
|||
trade.getAssetTxProofResult() != null &&
|
||||
trade.getAssetTxProofResult() != AssetTxProofResult.UNDEFINED;
|
||||
|
||||
if (trade.getPayoutTx() != null)
|
||||
if (trade.getPayoutTxId() != null)
|
||||
rows++;
|
||||
boolean showDisputedTx = arbitrationManager.findOwnDispute(trade.getId()).isPresent() &&
|
||||
arbitrationManager.findOwnDispute(trade.getId()).get().getDisputePayoutTxId() != null;
|
||||
|
@ -283,9 +283,9 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
|||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.depositTransactionId"), // TODO (woodser): separate UI labels for deposit tx ids
|
||||
trade.getTakerDepositTx().getHash());
|
||||
|
||||
if (trade.getPayoutTx() != null)
|
||||
if (trade.getPayoutTxId() != null)
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.payoutTxId"),
|
||||
trade.getPayoutTx().getHash());
|
||||
trade.getPayoutTxId());
|
||||
if (showDisputedTx)
|
||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.disputedPayoutTxId"),
|
||||
arbitrationManager.findOwnDispute(trade.getId()).get().getDisputePayoutTxId());
|
||||
|
|
|
@ -91,7 +91,6 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
|||
|
||||
import monero.daemon.model.MoneroTx;
|
||||
import monero.wallet.MoneroWallet;
|
||||
import monero.wallet.model.MoneroTxWallet;
|
||||
|
||||
public class PendingTradesDataModel extends ActivatableDataModel {
|
||||
@Getter
|
||||
|
@ -465,11 +464,10 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||
|
||||
byte[] payoutTxSerialized = null;
|
||||
String payoutTxHashAsString = null;
|
||||
MoneroTxWallet payoutTx = trade.getPayoutTx();
|
||||
MoneroWallet multisigWallet = xmrWalletService.getMultisigWallet(trade.getId());
|
||||
String updatedMultisigHex = multisigWallet.exportMultisigHex();
|
||||
xmrWalletService.closeMultisigWallet(trade.getId()); // close multisig wallet
|
||||
if (payoutTx != null) {
|
||||
if (trade.getPayoutTxId() != null) {
|
||||
// payoutTxSerialized = payoutTx.bitcoinSerialize(); // TODO (woodser): no need to pass serialized txs for xmr
|
||||
// payoutTxHashAsString = payoutTx.getHashAsString();
|
||||
}
|
||||
|
|
|
@ -420,7 +420,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
|
||||
// deposit published
|
||||
case ARBITRATOR_PUBLISHED_DEPOSIT_TXS:
|
||||
case DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN:
|
||||
case DEPOSIT_TXS_SEEN_IN_NETWORK:
|
||||
case DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN: // TODO: separate step to wait for first confirmation
|
||||
buyerState.set(BuyerState.STEP1);
|
||||
sellerState.set(SellerState.STEP1);
|
||||
|
@ -472,7 +472,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||
// 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 BUYER_SAW_PAYOUT_TX_IN_NETWORK:
|
||||
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);
|
||||
|
|
|
@ -203,7 +203,7 @@ public class TradeStepInfo {
|
|||
footerLabel.setVisible(false);
|
||||
}
|
||||
|
||||
if (trade != null && trade.getPayoutTx() != null) {
|
||||
if (trade != null && trade.getPayoutTxId() != null) {
|
||||
button.setDisable(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -653,7 +653,7 @@ public abstract class TradeStepView extends AnchorPane {
|
|||
return;
|
||||
}
|
||||
|
||||
if (trade.getPayoutTx() != null) {
|
||||
if (trade.getPayoutTxId() != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1648,7 +1648,7 @@ message Trade {
|
|||
STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST = 10;
|
||||
SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST = 11;
|
||||
ARBITRATOR_PUBLISHED_DEPOSIT_TXS = 12;
|
||||
DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN = 13;
|
||||
DEPOSIT_TXS_SEEN_IN_NETWORK = 13;
|
||||
DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN = 14;
|
||||
DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN = 15;
|
||||
BUYER_CONFIRMED_IN_UI_PAYMENT_SENT = 16;
|
||||
|
@ -1668,8 +1668,8 @@ message Trade {
|
|||
SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG = 30;
|
||||
SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG = 31;
|
||||
BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG = 32;
|
||||
BUYER_SAW_PAYOUT_TX_IN_NETWORK = 33;
|
||||
BUYER_PUBLISHED_PAYOUT_TX = 34;
|
||||
BUYER_PUBLISHED_PAYOUT_TX = 33;
|
||||
PAYOUT_TX_SEEN_IN_NETWORK = 34;
|
||||
WITHDRAW_COMPLETED = 35;
|
||||
}
|
||||
|
||||
|
@ -1712,31 +1712,33 @@ message Trade {
|
|||
string taker_fee_tx_id = 3;
|
||||
reserved 4;
|
||||
string payout_tx_id = 5;
|
||||
int64 amount_as_long = 6;
|
||||
int64 tx_fee_as_long = 7;
|
||||
int64 taker_fee_as_long = 8;
|
||||
int64 take_offer_date = 9;
|
||||
int64 price = 10;
|
||||
State state = 11;
|
||||
DisputeState dispute_state = 12;
|
||||
TradePeriodState period_state = 13;
|
||||
Contract contract = 14;
|
||||
string contract_as_json = 15;
|
||||
bytes contract_hash = 16;
|
||||
NodeAddress arbitrator_node_address = 17;
|
||||
NodeAddress mediator_node_address = 18;
|
||||
string payout_tx_hex = 6;
|
||||
string payout_tx_key = 7;
|
||||
int64 amount_as_long = 8;
|
||||
int64 tx_fee_as_long = 9;
|
||||
int64 taker_fee_as_long = 10;
|
||||
int64 take_offer_date = 11;
|
||||
int64 price = 12;
|
||||
State state = 13;
|
||||
DisputeState dispute_state = 14;
|
||||
TradePeriodState period_state = 15;
|
||||
Contract contract = 16;
|
||||
string contract_as_json = 17;
|
||||
bytes contract_hash = 18;
|
||||
NodeAddress arbitrator_node_address = 19;
|
||||
NodeAddress mediator_node_address = 20;
|
||||
string error_message = 21;
|
||||
string counter_currency_tx_id = 24;
|
||||
repeated ChatMessage chat_message = 25;
|
||||
MediationResultState mediation_result_state = 26;
|
||||
int64 lock_time = 27;
|
||||
bytes delayed_payout_tx_bytes = 28;
|
||||
NodeAddress refund_agent_node_address = 29;
|
||||
RefundResultState refund_result_state = 30;
|
||||
int64 last_refresh_request_date = 31 [deprecated = true];
|
||||
string counter_currency_extra_data = 32;
|
||||
string asset_tx_proof_result = 33; // name of AssetTxProofResult enum
|
||||
string uid = 34;
|
||||
string counter_currency_tx_id = 22;
|
||||
repeated ChatMessage chat_message = 23;
|
||||
MediationResultState mediation_result_state = 24;
|
||||
int64 lock_time = 25;
|
||||
bytes delayed_payout_tx_bytes = 26;
|
||||
NodeAddress refund_agent_node_address = 27;
|
||||
RefundResultState refund_result_state = 28;
|
||||
int64 last_refresh_request_date = 29 [deprecated = true];
|
||||
string counter_currency_extra_data = 30;
|
||||
string asset_tx_proof_result = 31; // name of AssetTxProofResult enum
|
||||
string uid = 32;
|
||||
}
|
||||
|
||||
message BuyerAsMakerTrade {
|
||||
|
@ -1819,11 +1821,10 @@ message TradingPeer {
|
|||
string prepared_multisig_hex = 1005;
|
||||
string made_multisig_hex = 1006;
|
||||
string exchanged_multisig_hex = 1007;
|
||||
string payout_tx_hex = 1008;
|
||||
string deposit_tx_hash = 1009;
|
||||
string deposit_tx_hex = 1010;
|
||||
string deposit_tx_key = 1011;
|
||||
string updated_multisig_hex = 1012;
|
||||
string deposit_tx_hash = 1008;
|
||||
string deposit_tx_hex = 1009;
|
||||
string deposit_tx_key = 1010;
|
||||
string updated_multisig_hex = 1011;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
|
Loading…
Reference in a new issue