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
@ -568,8 +568,8 @@ public class CoreApi {
|
|||||||
coreTradesService.confirmPaymentReceived(tradeId, resultHandler, errorMessageHandler);
|
coreTradesService.confirmPaymentReceived(tradeId, resultHandler, errorMessageHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void keepFunds(String tradeId) {
|
public void closeTrade(String tradeId) {
|
||||||
coreTradesService.keepFunds(tradeId);
|
coreTradesService.closeTrade(tradeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void withdrawFunds(String tradeId, String address, String memo) {
|
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.verifyWalletsAreAvailable();
|
||||||
coreWalletsService.verifyEncryptedWalletIsUnlocked();
|
coreWalletsService.verifyEncryptedWalletIsUnlocked();
|
||||||
|
|
||||||
|
@ -812,6 +812,13 @@ public class XmrWalletService {
|
|||||||
return getAddressEntryListAsImmutableList().stream().filter(addressEntry -> XmrAddressEntry.Context.AVAILABLE == addressEntry.getContext()).collect(Collectors.toList());
|
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() {
|
public List<XmrAddressEntry> getAddressEntriesForTrade() {
|
||||||
return getAddressEntryListAsImmutableList().stream()
|
return getAddressEntryListAsImmutableList().stream()
|
||||||
.filter(addressEntry -> XmrAddressEntry.Context.MULTI_SIG == addressEntry.getContext() || XmrAddressEntry.Context.TRADE_PAYOUT == addressEntry.getContext())
|
.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() {
|
private void cleanUpAddressEntries() {
|
||||||
Set<String> openOffersIdSet = openOffers.getList().stream().map(OpenOffer::getId).collect(Collectors.toSet());
|
Set<String> openOffersIdSet = openOffers.getList().stream().map(OpenOffer::getId).collect(Collectors.toSet());
|
||||||
btcWalletService.getAddressEntriesForOpenOffer().stream()
|
xmrWalletService.getAddressEntriesForOpenOffer().stream()
|
||||||
.filter(e -> !openOffersIdSet.contains(e.getOfferId()))
|
.filter(e -> !openOffersIdSet.contains(e.getOfferId()))
|
||||||
.forEach(e -> {
|
.forEach(e -> {
|
||||||
log.warn("We found an outdated addressEntry for openOffer {} (openOffers does not contain that " +
|
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);
|
openOffer.setState(OpenOffer.State.CANCELED);
|
||||||
openOffers.remove(openOffer);
|
openOffers.remove(openOffer);
|
||||||
closedTradableManager.add(openOffer);
|
closedTradableManager.add(openOffer);
|
||||||
log.info("onRemoved offerId={}", offer.getId());
|
|
||||||
xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
xmrWalletService.resetAddressEntriesForOpenOffer(offer.getId());
|
||||||
|
log.info("onRemoved offerId={}", offer.getId());
|
||||||
requestPersistence();
|
requestPersistence();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,11 +407,11 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
|
|||||||
Contract contract = dispute.getContract();
|
Contract contract = dispute.getContract();
|
||||||
|
|
||||||
// verify sender is co-signer and receiver is arbitrator
|
// 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("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);
|
||||||
System.out.println(disputeResult.getWinner());
|
// System.out.println(disputeResult.getWinner());
|
||||||
System.out.println(contract.getBuyerNodeAddress());
|
// System.out.println(contract.getBuyerNodeAddress());
|
||||||
System.out.println(contract.getSellerNodeAddress());
|
// 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 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 senderIsCosigner = senderIsWinner || disputeResult.isLoserPublisher();
|
||||||
boolean receiverIsArbitrator = pubKeyRing.equals(dispute.getAgentPubKeyRing());
|
boolean receiverIsArbitrator = pubKeyRing.equals(dispute.getAgentPubKeyRing());
|
||||||
|
@ -50,6 +50,7 @@ import bisq.common.util.Utilities;
|
|||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
import com.google.protobuf.Message;
|
import com.google.protobuf.Message;
|
||||||
|
import common.utils.GenUtils;
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
|
|
||||||
@ -90,9 +91,12 @@ import monero.common.MoneroError;
|
|||||||
import monero.daemon.MoneroDaemon;
|
import monero.daemon.MoneroDaemon;
|
||||||
import monero.daemon.model.MoneroTx;
|
import monero.daemon.model.MoneroTx;
|
||||||
import monero.wallet.MoneroWallet;
|
import monero.wallet.MoneroWallet;
|
||||||
|
import monero.wallet.model.MoneroCheckTx;
|
||||||
import monero.wallet.model.MoneroDestination;
|
import monero.wallet.model.MoneroDestination;
|
||||||
import monero.wallet.model.MoneroMultisigSignResult;
|
import monero.wallet.model.MoneroMultisigSignResult;
|
||||||
|
import monero.wallet.model.MoneroTransferQuery;
|
||||||
import monero.wallet.model.MoneroTxConfig;
|
import monero.wallet.model.MoneroTxConfig;
|
||||||
|
import monero.wallet.model.MoneroTxQuery;
|
||||||
import monero.wallet.model.MoneroTxSet;
|
import monero.wallet.model.MoneroTxSet;
|
||||||
import monero.wallet.model.MoneroTxWallet;
|
import monero.wallet.model.MoneroTxWallet;
|
||||||
import monero.wallet.model.MoneroWalletListener;
|
import monero.wallet.model.MoneroWalletListener;
|
||||||
@ -127,7 +131,7 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
|
|
||||||
// deposit published
|
// deposit published
|
||||||
ARBITRATOR_PUBLISHED_DEPOSIT_TXS(Phase.DEPOSITS_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 confirmed
|
||||||
DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN(Phase.DEPOSITS_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_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG(Phase.PAYOUT_PUBLISHED),
|
||||||
SELLER_SEND_FAILED_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_RECEIVED_PAYOUT_TX_PUBLISHED_MSG(Phase.PAYOUT_PUBLISHED),
|
||||||
BUYER_SAW_PAYOUT_TX_IN_NETWORK(Phase.PAYOUT_PUBLISHED),
|
|
||||||
BUYER_PUBLISHED_PAYOUT_TX(Phase.PAYOUT_PUBLISHED),
|
BUYER_PUBLISHED_PAYOUT_TX(Phase.PAYOUT_PUBLISHED),
|
||||||
|
PAYOUT_TX_SEEN_IN_NETWORK(Phase.PAYOUT_PUBLISHED),
|
||||||
|
|
||||||
// trade completed
|
// trade completed
|
||||||
WITHDRAW_COMPLETED(Phase.WITHDRAWN);
|
WITHDRAW_COMPLETED(Phase.WITHDRAWN);
|
||||||
@ -310,9 +314,6 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
@Nullable
|
@Nullable
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
private String payoutTxId;
|
|
||||||
@Getter
|
|
||||||
@Setter
|
|
||||||
private long amountAsLong;
|
private long amountAsLong;
|
||||||
@Setter
|
@Setter
|
||||||
private long price;
|
private long price;
|
||||||
@ -366,9 +367,6 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
// Added in v1.2.0
|
// Added in v1.2.0
|
||||||
@Nullable
|
@Nullable
|
||||||
transient private Transaction delayedPayoutTx;
|
transient private Transaction delayedPayoutTx;
|
||||||
|
|
||||||
@Nullable
|
|
||||||
transient private MoneroTxWallet payoutTx;
|
|
||||||
@Nullable
|
@Nullable
|
||||||
transient private Coin tradeAmount;
|
transient private Coin tradeAmount;
|
||||||
|
|
||||||
@ -412,12 +410,24 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
@Getter
|
@Getter
|
||||||
transient final private IntegerProperty assetTxProofResultUpdateProperty = new SimpleIntegerProperty();
|
transient final private IntegerProperty assetTxProofResultUpdateProperty = new SimpleIntegerProperty();
|
||||||
|
|
||||||
|
|
||||||
// Added in XMR integration
|
// Added in XMR integration
|
||||||
private transient List<TradeListener> tradeListeners; // notified on fully validated trade messages
|
private transient List<TradeListener> tradeListeners; // notified on fully validated trade messages
|
||||||
transient MoneroWalletListener depositTxListener;
|
transient MoneroWalletListener depositTxListener;
|
||||||
|
transient MoneroWalletListener payoutTxListener;
|
||||||
transient Boolean makerDepositLocked; // null when unknown, true while locked, false when unlocked
|
transient Boolean makerDepositLocked; // null when unknown, true while locked, false when unlocked
|
||||||
transient Boolean takerDepositLocked;
|
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
|
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(counterCurrencyTxId).ifPresent(e -> builder.setCounterCurrencyTxId(counterCurrencyTxId));
|
||||||
Optional.ofNullable(mediationResultState).ifPresent(e -> builder.setMediationResultState(MediationResultState.toProtoMessage(mediationResultState)));
|
Optional.ofNullable(mediationResultState).ifPresent(e -> builder.setMediationResultState(MediationResultState.toProtoMessage(mediationResultState)));
|
||||||
Optional.ofNullable(refundResultState).ifPresent(e -> builder.setRefundResultState(RefundResultState.toProtoMessage(refundResultState)));
|
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(delayedPayoutTxBytes).ifPresent(e -> builder.setDelayedPayoutTxBytes(ByteString.copyFrom(delayedPayoutTxBytes)));
|
||||||
Optional.ofNullable(counterCurrencyExtraData).ifPresent(e -> builder.setCounterCurrencyExtraData(counterCurrencyExtraData));
|
Optional.ofNullable(counterCurrencyExtraData).ifPresent(e -> builder.setCounterCurrencyExtraData(counterCurrencyExtraData));
|
||||||
Optional.ofNullable(assetTxProofResult).ifPresent(e -> builder.setAssetTxProofResult(assetTxProofResult.name()));
|
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.setPeriodState(TradePeriodState.fromProto(proto.getPeriodState()));
|
||||||
trade.setTakerFeeTxId(ProtoUtil.stringOrNullFromProto(proto.getTakerFeeTxId()));
|
trade.setTakerFeeTxId(ProtoUtil.stringOrNullFromProto(proto.getTakerFeeTxId()));
|
||||||
trade.setPayoutTxId(ProtoUtil.stringOrNullFromProto(proto.getPayoutTxId()));
|
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.setContract(proto.hasContract() ? Contract.fromProto(proto.getContract(), coreProtoResolver) : null);
|
||||||
trade.setContractAsJson(ProtoUtil.stringOrNullFromProto(proto.getContractAsJson()));
|
trade.setContractAsJson(ProtoUtil.stringOrNullFromProto(proto.getContractAsJson()));
|
||||||
trade.setContractHash(ProtoUtil.byteArrayOrNullFromProto(proto.getContractHash()));
|
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 buyerDepositAmount = multisigWallet.getTx(getBuyer().getDepositTxHash()).getIncomingAmount();
|
||||||
BigInteger tradeAmount = ParsingUtils.coinToAtomicUnits(getAmount());
|
BigInteger tradeAmount = ParsingUtils.coinToAtomicUnits(getAmount());
|
||||||
|
|
||||||
// parse payout tx
|
// describe payout tx
|
||||||
MoneroTxSet describedTxSet = multisigWallet.describeTxSet(new MoneroTxSet().setMultisigTxHex(payoutTxHex));
|
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
|
if (describedTxSet.getTxs() == null || describedTxSet.getTxs().size() != 1) throw new RuntimeException("Bad payout tx"); // TODO (woodser): test nack
|
||||||
MoneroTxWallet payoutTx = describedTxSet.getTxs().get(0);
|
MoneroTxWallet payoutTx = describedTxSet.getTxs().get(0);
|
||||||
@ -747,8 +761,8 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// update trade state
|
// update trade state
|
||||||
getSelf().setPayoutTxHex(payoutTxHex);
|
|
||||||
setPayoutTx(describedTxSet.getTxs().get(0));
|
setPayoutTx(describedTxSet.getTxs().get(0));
|
||||||
|
setPayoutTxHex(payoutTxHex);
|
||||||
|
|
||||||
// submit payout tx
|
// submit payout tx
|
||||||
if (publish) {
|
if (publish) {
|
||||||
@ -807,17 +821,17 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
|
|
||||||
// handle deposit txs seen
|
// handle deposit txs seen
|
||||||
if (txs.size() == 2) {
|
if (txs.size() == 2) {
|
||||||
setStatePublished();
|
setStateDepositsPublished();
|
||||||
boolean makerFirst = txs.get(0).getHash().equals(processModel.getMaker().getDepositTxHash());
|
boolean makerFirst = txs.get(0).getHash().equals(processModel.getMaker().getDepositTxHash());
|
||||||
getMaker().setDepositTx(makerFirst ? txs.get(0) : txs.get(1));
|
getMaker().setDepositTx(makerFirst ? txs.get(0) : txs.get(1));
|
||||||
getTaker().setDepositTx(makerFirst ? txs.get(1) : txs.get(0));
|
getTaker().setDepositTx(makerFirst ? txs.get(1) : txs.get(0));
|
||||||
|
|
||||||
// check if deposit txs unlocked
|
// check if deposit txs unlocked
|
||||||
if (txs.get(0).isConfirmed() && txs.get(1).isConfirmed()) {
|
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;
|
long unlockHeight = Math.max(txs.get(0).getHeight(), txs.get(1).getHeight()) + XmrWalletService.NUM_BLOCKS_UNLOCK;
|
||||||
if (havenoWallet.getHeight() >= unlockHeight) {
|
if (havenoWallet.getHeight() >= unlockHeight) {
|
||||||
setStateUnlocked();
|
setStateDepositsUnlocked();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -844,7 +858,7 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
|
|
||||||
// skip if deposit txs not seen
|
// skip if deposit txs not seen
|
||||||
if (txs.size() != 2) return;
|
if (txs.size() != 2) return;
|
||||||
setStatePublished();
|
setStateDepositsPublished();
|
||||||
|
|
||||||
// update deposit txs
|
// update deposit txs
|
||||||
boolean makerFirst = txs.get(0).getHash().equals(processModel.getMaker().getDepositTxHash());
|
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
|
// check if deposit txs confirmed and compute unlock height
|
||||||
if (txs.size() == 2 && txs.get(0).isConfirmed() && txs.get(1).isConfirmed() && unlockHeight == null) {
|
if (txs.size() == 2 && txs.get(0).isConfirmed() && txs.get(1).isConfirmed() && unlockHeight == null) {
|
||||||
log.info("Multisig deposits confirmed for trade {}", getId());
|
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;
|
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());
|
log.info("Multisig deposits unlocked for trade {}", getId());
|
||||||
xmrWalletService.removeWalletListener(depositTxListener); // remove listener when notified
|
xmrWalletService.removeWalletListener(depositTxListener); // remove listener when notified
|
||||||
depositTxListener = null; // prevent re-applying trade state in subsequent requests
|
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);
|
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
|
@Nullable
|
||||||
public MoneroTx getTakerDepositTx() {
|
public MoneroTx getTakerDepositTx() {
|
||||||
String depositTxHash = getProcessModel().getTaker().getDepositTxHash();
|
String depositTxHash = getProcessModel().getTaker().getDepositTxHash();
|
||||||
@ -1045,6 +1104,7 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
public void setPayoutTx(MoneroTxWallet payoutTx) {
|
public void setPayoutTx(MoneroTxWallet payoutTx) {
|
||||||
this.payoutTx = payoutTx;
|
this.payoutTx = payoutTx;
|
||||||
payoutTxId = payoutTx.getHash();
|
payoutTxId = payoutTx.getHash();
|
||||||
|
payoutTxKey = payoutTx.getKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setErrorMessage(String errorMessage) {
|
public void setErrorMessage(String errorMessage) {
|
||||||
@ -1275,7 +1335,6 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPayoutPublished() {
|
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();
|
return getState().getPhase().ordinal() >= Phase.PAYOUT_PUBLISHED.ordinal() || isWithdrawn();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1397,15 +1456,15 @@ public abstract class Trade implements Tradable, Model {
|
|||||||
return tradeVolumeProperty;
|
return tradeVolumeProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setStatePublished() {
|
private void setStateDepositsPublished() {
|
||||||
if (!isDepositPublished()) setState(State.DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN);
|
if (!isDepositPublished()) setState(State.DEPOSIT_TXS_SEEN_IN_NETWORK);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setStateConfirmed() {
|
private void setStateDepositsConfirmed() {
|
||||||
if (!isDepositConfirmed()) setState(State.DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN);
|
if (!isDepositConfirmed()) setState(State.DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setStateUnlocked() {
|
private void setStateDepositsUnlocked() {
|
||||||
if (!isDepositUnlocked()) setState(State.DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN);
|
if (!isDepositUnlocked()) setState(State.DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -284,7 +284,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||||||
xmrWalletService.getAddressEntriesForAvailableBalanceStream()
|
xmrWalletService.getAddressEntriesForAvailableBalanceStream()
|
||||||
.filter(addressEntry -> addressEntry.getOfferId() != null)
|
.filter(addressEntry -> addressEntry.getOfferId() != null)
|
||||||
.forEach(addressEntry -> {
|
.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);
|
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
|
// 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) {
|
public void onTradeCompleted(Trade trade) {
|
||||||
|
if (trade.getState() == Trade.State.WITHDRAW_COMPLETED) return;
|
||||||
closedTradableManager.add(trade);
|
closedTradableManager.add(trade);
|
||||||
trade.setState(Trade.State.WITHDRAW_COMPLETED);
|
trade.setState(Trade.State.WITHDRAW_COMPLETED);
|
||||||
maybeRemoveTrade(trade);
|
maybeRemoveTrade(trade);
|
||||||
|
@ -17,6 +17,9 @@
|
|||||||
|
|
||||||
package bisq.core.trade.protocol;
|
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.BuyerTrade;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.messages.PaymentAccountKeyResponse;
|
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.messages.TradeMessage;
|
||||||
import bisq.core.trade.protocol.FluentProtocol.Condition;
|
import bisq.core.trade.protocol.FluentProtocol.Condition;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
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.BuyerPreparePaymentSentMessage;
|
||||||
|
import bisq.core.trade.protocol.tasks.BuyerProcessPaymentAccountKeyResponse;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerProcessPaymentReceivedMessage;
|
import bisq.core.trade.protocol.tasks.BuyerProcessPaymentReceivedMessage;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerSendPaymentAccountKeyRequestToArbitrator;
|
import bisq.core.trade.protocol.tasks.BuyerSendPaymentAccountKeyRequestToArbitrator;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerSendPaymentSentMessage;
|
import bisq.core.trade.protocol.tasks.BuyerSendPaymentSentMessage;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerSetupPayoutTxListener;
|
import bisq.core.trade.protocol.tasks.BuyerSendPayoutTxPublishedMessage;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerProcessPaymentAccountKeyResponse;
|
|
||||||
import bisq.core.trade.protocol.tasks.SetupDepositTxsListener;
|
import bisq.core.trade.protocol.tasks.SetupDepositTxsListener;
|
||||||
|
import bisq.core.trade.protocol.tasks.SetupPayoutTxListener;
|
||||||
import bisq.core.util.Validator;
|
import bisq.core.util.Validator;
|
||||||
import bisq.network.p2p.NodeAddress;
|
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 lombok.extern.slf4j.Slf4j;
|
||||||
import org.fxmisc.easybind.EasyBind;
|
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
|
// request key to decrypt seller's payment account payload after first confirmation
|
||||||
sendPaymentAccountKeyRequestIfWhenNeeded(BuyerEvent.STARTUP, false);
|
sendPaymentAccountKeyRequestIfWhenNeeded(BuyerEvent.STARTUP, false);
|
||||||
|
|
||||||
|
// listen for deposit txs
|
||||||
given(anyPhase(Trade.Phase.DEPOSIT_REQUESTED, Trade.Phase.DEPOSITS_PUBLISHED, Trade.Phase.DEPOSITS_CONFIRMED)
|
given(anyPhase(Trade.Phase.DEPOSIT_REQUESTED, Trade.Phase.DEPOSITS_PUBLISHED, Trade.Phase.DEPOSITS_CONFIRMED)
|
||||||
.with(BuyerEvent.STARTUP))
|
.with(BuyerEvent.STARTUP))
|
||||||
.setup(tasks(SetupDepositTxsListener.class))
|
.setup(tasks(SetupDepositTxsListener.class))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
|
|
||||||
|
// listen for payout tx
|
||||||
given(anyPhase(Trade.Phase.PAYMENT_SENT, Trade.Phase.PAYMENT_RECEIVED)
|
given(anyPhase(Trade.Phase.PAYMENT_SENT, Trade.Phase.PAYMENT_RECEIVED)
|
||||||
.with(BuyerEvent.STARTUP))
|
.with(BuyerEvent.STARTUP))
|
||||||
.setup(tasks(BuyerSetupPayoutTxListener.class)) // TODO (woodser): mirror deposit listener setup?
|
.setup(tasks(SetupPayoutTxListener.class))
|
||||||
.executeTasks();
|
.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)
|
.anyState(Trade.State.BUYER_STORED_IN_MAILBOX_PAYMENT_SENT_MSG, Trade.State.BUYER_SEND_FAILED_PAYMENT_SENT_MSG)
|
||||||
.with(BuyerEvent.STARTUP))
|
.with(BuyerEvent.STARTUP))
|
||||||
.setup(tasks(BuyerSendPaymentSentMessage.class))
|
.setup(tasks(BuyerSendPaymentSentMessage.class))
|
||||||
|
@ -22,6 +22,7 @@ import bisq.core.trade.Trade;
|
|||||||
import bisq.core.trade.messages.PaymentSentMessage;
|
import bisq.core.trade.messages.PaymentSentMessage;
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.core.trade.messages.TradeMessage;
|
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.FluentProtocol.Condition;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
import bisq.core.trade.protocol.tasks.SellerMaybeSendPayoutTxPublishedMessage;
|
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.SellerSendPaymentReceivedMessage;
|
||||||
import bisq.core.trade.protocol.tasks.SellerSendPaymentAccountPayloadKey;
|
import bisq.core.trade.protocol.tasks.SellerSendPaymentAccountPayloadKey;
|
||||||
import bisq.core.trade.protocol.tasks.SetupDepositTxsListener;
|
import bisq.core.trade.protocol.tasks.SetupDepositTxsListener;
|
||||||
|
import bisq.core.trade.protocol.tasks.SetupPayoutTxListener;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.common.handlers.ErrorMessageHandler;
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
import bisq.common.handlers.ResultHandler;
|
import bisq.common.handlers.ResultHandler;
|
||||||
@ -60,11 +62,17 @@ public abstract class SellerProtocol extends DisputeProtocol {
|
|||||||
sendPaymentAccountPayloadKeyWhenConfirmed(SellerEvent.STARTUP);
|
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)
|
given(anyPhase(Trade.Phase.DEPOSIT_REQUESTED, Trade.Phase.DEPOSITS_PUBLISHED, Trade.Phase.DEPOSITS_CONFIRMED)
|
||||||
.with(SellerEvent.STARTUP))
|
.with(SellerEvent.STARTUP))
|
||||||
.setup(tasks(SetupDepositTxsListener.class))
|
.setup(tasks(SetupDepositTxsListener.class))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
|
|
||||||
|
// listen for payout tx
|
||||||
|
given(anyPhase(Trade.Phase.PAYMENT_SENT, Trade.Phase.PAYMENT_RECEIVED)
|
||||||
|
.with(BuyerEvent.STARTUP))
|
||||||
|
.setup(tasks(SetupPayoutTxListener.class))
|
||||||
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -335,7 +335,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
|||||||
latchTrade();
|
latchTrade();
|
||||||
Validator.checkTradeId(processModel.getOfferId(), response);
|
Validator.checkTradeId(processModel.getOfferId(), response);
|
||||||
processModel.setTradeMessage(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)
|
.with(response)
|
||||||
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
||||||
.setup(tasks(
|
.setup(tasks(
|
||||||
|
@ -124,10 +124,6 @@ public final class TradingPeer implements PersistablePayload {
|
|||||||
@Nullable
|
@Nullable
|
||||||
private String depositTxKey;
|
private String depositTxKey;
|
||||||
@Nullable
|
@Nullable
|
||||||
transient private MoneroTxWallet payoutTx;
|
|
||||||
@Nullable
|
|
||||||
private String payoutTxHex;
|
|
||||||
@Nullable
|
|
||||||
private String updatedMultisigHex;
|
private String updatedMultisigHex;
|
||||||
|
|
||||||
public TradingPeer() {
|
public TradingPeer() {
|
||||||
@ -164,7 +160,6 @@ public final class TradingPeer implements PersistablePayload {
|
|||||||
Optional.ofNullable(preparedMultisigHex).ifPresent(e -> builder.setPreparedMultisigHex(preparedMultisigHex));
|
Optional.ofNullable(preparedMultisigHex).ifPresent(e -> builder.setPreparedMultisigHex(preparedMultisigHex));
|
||||||
Optional.ofNullable(madeMultisigHex).ifPresent(e -> builder.setMadeMultisigHex(madeMultisigHex));
|
Optional.ofNullable(madeMultisigHex).ifPresent(e -> builder.setMadeMultisigHex(madeMultisigHex));
|
||||||
Optional.ofNullable(exchangedMultisigHex).ifPresent(e -> builder.setExchangedMultisigHex(exchangedMultisigHex));
|
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(depositTxHash).ifPresent(e -> builder.setDepositTxHash(depositTxHash));
|
||||||
Optional.ofNullable(depositTxHex).ifPresent(e -> builder.setDepositTxHex(depositTxHex));
|
Optional.ofNullable(depositTxHex).ifPresent(e -> builder.setDepositTxHex(depositTxHex));
|
||||||
Optional.ofNullable(depositTxKey).ifPresent(e -> builder.setDepositTxKey(depositTxKey));
|
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.setDepositTxHash(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHash()));
|
||||||
tradingPeer.setDepositTxHex(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHex()));
|
tradingPeer.setDepositTxHex(ProtoUtil.stringOrNullFromProto(proto.getDepositTxHex()));
|
||||||
tradingPeer.setDepositTxKey(ProtoUtil.stringOrNullFromProto(proto.getDepositTxKey()));
|
tradingPeer.setDepositTxKey(ProtoUtil.stringOrNullFromProto(proto.getDepositTxKey()));
|
||||||
tradingPeer.setPayoutTxHex(ProtoUtil.stringOrNullFromProto(proto.getPayoutTxHex()));
|
|
||||||
tradingPeer.setUpdatedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()));
|
tradingPeer.setUpdatedMultisigHex(ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()));
|
||||||
return tradingPeer;
|
return tradingPeer;
|
||||||
}
|
}
|
||||||
|
@ -65,11 +65,16 @@ public class BuyerPreparePaymentSentMessage extends TradeTask {
|
|||||||
|
|
||||||
// create payout tx if we have seller's updated multisig hex
|
// create payout tx if we have seller's updated multisig hex
|
||||||
if (trade.getTradingPeer().getUpdatedMultisigHex() != null) {
|
if (trade.getTradingPeer().getUpdatedMultisigHex() != null) {
|
||||||
|
|
||||||
|
// create payout tx
|
||||||
log.info("Buyer creating unsigned payout tx");
|
log.info("Buyer creating unsigned payout tx");
|
||||||
multisigWallet.importMultisigHex(trade.getTradingPeer().getUpdatedMultisigHex());
|
multisigWallet.importMultisigHex(trade.getTradingPeer().getUpdatedMultisigHex());
|
||||||
MoneroTxWallet payoutTx = trade.createPayoutTx();
|
MoneroTxWallet payoutTx = trade.createPayoutTx();
|
||||||
trade.getBuyer().setPayoutTx(payoutTx);
|
trade.setPayoutTx(payoutTx);
|
||||||
trade.getBuyer().setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
trade.setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||||
|
|
||||||
|
// start listening for published payout tx
|
||||||
|
trade.listenForPayoutTx();
|
||||||
} else {
|
} else {
|
||||||
if (trade.getSelf().getUpdatedMultisigHex() == null) trade.getSelf().setUpdatedMultisigHex(multisigWallet.exportMultisigHex()); // only export multisig hex once
|
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()) {
|
if (trade.getPhase().ordinal() < Trade.Phase.PAYOUT_PUBLISHED.ordinal()) {
|
||||||
|
|
||||||
// publish payout tx if signed. otherwise verify, sign, and publish payout tx
|
// 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) {
|
if (previouslySigned) {
|
||||||
log.info("Buyer publishing signed payout tx from seller");
|
log.info("Buyer publishing signed payout tx from seller");
|
||||||
XmrWalletService walletService = processModel.getProvider().getXmrWalletService();
|
XmrWalletService walletService = processModel.getProvider().getXmrWalletService();
|
||||||
@ -67,14 +67,17 @@ public class BuyerProcessPaymentReceivedMessage extends TradeTask {
|
|||||||
List<String> txHashes = multisigWallet.submitMultisigTxHex(message.getPayoutTxHex());
|
List<String> txHashes = multisigWallet.submitMultisigTxHex(message.getPayoutTxHex());
|
||||||
trade.setPayoutTx(multisigWallet.getTx(txHashes.get(0)));
|
trade.setPayoutTx(multisigWallet.getTx(txHashes.get(0)));
|
||||||
XmrWalletService.printTxs("payoutTx received from peer", trade.getPayoutTx());
|
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());
|
walletService.closeMultisigWallet(trade.getId());
|
||||||
} else {
|
} else {
|
||||||
log.info("Buyer verifying, signing, and publishing seller's payout tx");
|
log.info("Buyer verifying, signing, and publishing seller's payout tx");
|
||||||
trade.verifyPayoutTx(message.getPayoutTxHex(), true, true);
|
trade.verifyPayoutTx(message.getPayoutTxHex(), true, true);
|
||||||
trade.setState(Trade.State.BUYER_PUBLISHED_PAYOUT_TX);
|
trade.setStateIfValidTransitionTo(Trade.State.BUYER_PUBLISHED_PAYOUT_TX);
|
||||||
// TODO (woodser): send PayoutTxPublishedMessage to arbitrator and seller
|
// TODO (woodser): send PayoutTxPublishedMessage to seller
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// mark address entries as available
|
||||||
|
processModel.getXmrWalletService().resetAddressEntriesForPendingTrade(trade.getId());
|
||||||
} else {
|
} else {
|
||||||
log.info("We got the payout tx already set from BuyerSetupPayoutTxListener and do nothing here. trade ID={}", trade.getId());
|
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();
|
processModel.getTradeManager().requestPersistence();
|
||||||
|
|
||||||
complete();
|
complete();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(t);
|
failed(t);
|
||||||
|
@ -62,7 +62,7 @@ public class BuyerSendPaymentSentMessage extends SendMailboxMessageTask {
|
|||||||
trade.getCounterCurrencyTxId(),
|
trade.getCounterCurrencyTxId(),
|
||||||
trade.getCounterCurrencyExtraData(),
|
trade.getCounterCurrencyExtraData(),
|
||||||
deterministicId,
|
deterministicId,
|
||||||
trade.getBuyer().getPayoutTxHex(),
|
trade.getPayoutTxHex(),
|
||||||
trade.getBuyer().getUpdatedMultisigHex(),
|
trade.getBuyer().getUpdatedMultisigHex(),
|
||||||
trade.getSelf().getPaymentAccountKey()
|
trade.getSelf().getPaymentAccountKey()
|
||||||
);
|
);
|
||||||
|
@ -50,13 +50,13 @@ public class BuyerSendPayoutTxPublishedMessage extends SendMailboxMessageTask {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) {
|
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(
|
return new PayoutTxPublishedMessage(
|
||||||
tradeId,
|
tradeId,
|
||||||
processModel.getMyNodeAddress(),
|
processModel.getMyNodeAddress(),
|
||||||
trade.isMaker(),
|
trade.isMaker(),
|
||||||
null, // TODO: send witness data?
|
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
|
@Override
|
||||||
protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) {
|
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(
|
return new PayoutTxPublishedMessage(
|
||||||
tradeId,
|
tradeId,
|
||||||
processModel.getMyNodeAddress(),
|
processModel.getMyNodeAddress(),
|
||||||
trade.isMaker(),
|
trade.isMaker(),
|
||||||
null, // TODO: send witness data?
|
null, // TODO: send witness data?
|
||||||
trade.getSelf().getPayoutTxHex()
|
trade.getPayoutTxHex()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,17 +38,21 @@ public class SellerPreparePaymentReceivedMessage extends TradeTask {
|
|||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
// verify, sign, and publish payout tx if given. otherwise create payout tx
|
// 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");
|
log.info("Seller verifying, signing, and publishing payout tx");
|
||||||
trade.verifyPayoutTx(trade.getBuyer().getPayoutTxHex(), true, true);
|
trade.verifyPayoutTx(trade.getPayoutTxHex(), true, true);
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
// create unsigned payout tx
|
||||||
log.info("Seller creating unsigned payout tx");
|
log.info("Seller creating unsigned payout tx");
|
||||||
MoneroTxWallet payoutTx = trade.createPayoutTx();
|
MoneroTxWallet payoutTx = trade.createPayoutTx();
|
||||||
System.out.println("created payout tx: " + payoutTx);
|
System.out.println("created payout tx: " + payoutTx);
|
||||||
trade.getSeller().setPayoutTx(payoutTx);
|
trade.setPayoutTx(payoutTx);
|
||||||
trade.getSeller().setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
trade.setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||||
}
|
|
||||||
|
|
||||||
|
// start listening for published payout tx
|
||||||
|
trade.listenForPayoutTx();
|
||||||
|
}
|
||||||
complete();
|
complete();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(t);
|
failed(t);
|
||||||
|
@ -43,7 +43,7 @@ public class SellerProcessPaymentSentMessage extends TradeTask {
|
|||||||
checkNotNull(message);
|
checkNotNull(message);
|
||||||
|
|
||||||
// store buyer info
|
// store buyer info
|
||||||
trade.getBuyer().setPayoutTxHex(message.getPayoutTxHex());
|
trade.setPayoutTxHex(message.getPayoutTxHex());
|
||||||
trade.getBuyer().setUpdatedMultisigHex(message.getUpdatedMultisigHex());
|
trade.getBuyer().setUpdatedMultisigHex(message.getUpdatedMultisigHex());
|
||||||
|
|
||||||
// decrypt buyer's payment account payload
|
// decrypt buyer's payment account payload
|
||||||
|
@ -42,7 +42,7 @@ public class SellerSendPaymentReceivedMessage extends SendMailboxMessageTask {
|
|||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
if (trade.getSeller().getPayoutTxHex() == null) {
|
if (trade.getPayoutTxHex() == null) {
|
||||||
log.error("Payout tx is null");
|
log.error("Payout tx is null");
|
||||||
failed("Payout tx is null");
|
failed("Payout tx is null");
|
||||||
return;
|
return;
|
||||||
@ -56,12 +56,12 @@ public class SellerSendPaymentReceivedMessage extends SendMailboxMessageTask {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected TradeMailboxMessage getTradeMailboxMessage(String id) {
|
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(
|
return new PaymentReceivedMessage(
|
||||||
id,
|
id,
|
||||||
processModel.getMyNodeAddress(),
|
processModel.getMyNodeAddress(),
|
||||||
signedWitness,
|
signedWitness,
|
||||||
trade.getSeller().getPayoutTxHex()
|
trade.getPayoutTxHex()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,120 +17,50 @@
|
|||||||
|
|
||||||
package bisq.core.trade.protocol.tasks;
|
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.UserThread;
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
|
import bisq.core.trade.Trade;
|
||||||
import org.bitcoinj.core.TransactionConfidence;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.fxmisc.easybind.EasyBind;
|
||||||
import org.fxmisc.easybind.Subscription;
|
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
|
@Slf4j
|
||||||
public abstract class SetupPayoutTxListener extends TradeTask {
|
public class SetupPayoutTxListener extends TradeTask {
|
||||||
// Use instance fields to not get eaten up by the GC
|
|
||||||
private Subscription tradeStateSubscription;
|
|
||||||
private AddressConfidenceListener confidenceListener;
|
|
||||||
|
|
||||||
public SetupPayoutTxListener(TaskRunner<Trade> taskHandler, Trade trade) {
|
private Subscription tradeStateSubscription;
|
||||||
|
|
||||||
|
@SuppressWarnings({ "unused" })
|
||||||
|
public SetupPayoutTxListener(TaskRunner taskHandler, Trade trade) {
|
||||||
super(taskHandler, trade);
|
super(taskHandler, trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected abstract void setState();
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void run() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
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();
|
complete();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(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() {
|
private void unSubscribe() {
|
||||||
if (tradeStateSubscription != null)
|
if (tradeStateSubscription != null) tradeStateSubscription.unsubscribe();
|
||||||
tradeStateSubscription.unsubscribe();
|
|
||||||
|
|
||||||
if (confidenceListener != null)
|
|
||||||
processModel.getBtcWalletService().removeAddressConfidenceListener(confidenceListener);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,17 +17,16 @@
|
|||||||
|
|
||||||
package bisq.core.trade.protocol.tasks.mediation;
|
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.common.taskrunner.TaskRunner;
|
||||||
|
import bisq.core.trade.Trade;
|
||||||
|
import bisq.core.trade.protocol.tasks.TradeTask;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class SetupMediatedPayoutTxListener extends SetupPayoutTxListener {
|
public class SetupMediatedPayoutTxListener extends TradeTask {
|
||||||
public SetupMediatedPayoutTxListener(TaskRunner<Trade> taskHandler, Trade trade) {
|
|
||||||
|
@SuppressWarnings({ "unused" })
|
||||||
|
public SetupMediatedPayoutTxListener(TaskRunner taskHandler, Trade trade) {
|
||||||
super(taskHandler, trade);
|
super(taskHandler, trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,20 +34,10 @@ public class SetupMediatedPayoutTxListener extends SetupPayoutTxListener {
|
|||||||
protected void run() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
if (true) throw new RuntimeException("Not implemented");
|
||||||
super.run();
|
complete();
|
||||||
|
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(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
|
@Override
|
||||||
public void keepFunds(KeepFundsRequest req,
|
public void keepFunds(KeepFundsRequest req,
|
||||||
StreamObserver<KeepFundsReply> responseObserver) {
|
StreamObserver<KeepFundsReply> responseObserver) {
|
||||||
try {
|
try {
|
||||||
coreApi.keepFunds(req.getTradeId());
|
coreApi.closeTrade(req.getTradeId());
|
||||||
var reply = KeepFundsReply.newBuilder().build();
|
var reply = KeepFundsReply.newBuilder().build();
|
||||||
responseObserver.onNext(reply);
|
responseObserver.onNext(reply);
|
||||||
responseObserver.onCompleted();
|
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.BuyerPreparePaymentSentMessage;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerProcessPaymentReceivedMessage;
|
import bisq.core.trade.protocol.tasks.BuyerProcessPaymentReceivedMessage;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerSendPaymentSentMessage;
|
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.MakerSetLockTime;
|
||||||
import bisq.core.trade.protocol.tasks.MakerRemoveOpenOffer;
|
import bisq.core.trade.protocol.tasks.MakerRemoveOpenOffer;
|
||||||
import bisq.core.trade.protocol.tasks.SellerPreparePaymentReceivedMessage;
|
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.SellerPublishDepositTx;
|
||||||
import bisq.core.trade.protocol.tasks.SellerPublishTradeStatistics;
|
import bisq.core.trade.protocol.tasks.SellerPublishTradeStatistics;
|
||||||
import bisq.core.trade.protocol.tasks.SellerSendPaymentReceivedMessage;
|
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.TakerVerifyMakerFeePayment;
|
||||||
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
|
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
|
||||||
import bisq.common.taskrunner.Task;
|
import bisq.common.taskrunner.Task;
|
||||||
@ -123,7 +123,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||||||
|
|
||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
BuyerPreparePaymentSentMessage.class,
|
BuyerPreparePaymentSentMessage.class,
|
||||||
BuyerSetupPayoutTxListener.class,
|
SetupPayoutTxListener.class,
|
||||||
BuyerSendPaymentSentMessage.class,
|
BuyerSendPaymentSentMessage.class,
|
||||||
|
|
||||||
BuyerProcessPaymentReceivedMessage.class
|
BuyerProcessPaymentReceivedMessage.class
|
||||||
@ -142,7 +142,7 @@ public class DebugView extends InitializableView<GridPane, Void> {
|
|||||||
ApplyFilter.class,
|
ApplyFilter.class,
|
||||||
TakerVerifyMakerFeePayment.class,
|
TakerVerifyMakerFeePayment.class,
|
||||||
BuyerPreparePaymentSentMessage.class,
|
BuyerPreparePaymentSentMessage.class,
|
||||||
BuyerSetupPayoutTxListener.class,
|
SetupPayoutTxListener.class,
|
||||||
BuyerSendPaymentSentMessage.class,
|
BuyerSendPaymentSentMessage.class,
|
||||||
|
|
||||||
BuyerProcessPaymentReceivedMessage.class)
|
BuyerProcessPaymentReceivedMessage.class)
|
||||||
|
@ -200,7 +200,7 @@ public class TradeDetailsWindow extends Overlay<TradeDetailsWindow> {
|
|||||||
trade.getAssetTxProofResult() != null &&
|
trade.getAssetTxProofResult() != null &&
|
||||||
trade.getAssetTxProofResult() != AssetTxProofResult.UNDEFINED;
|
trade.getAssetTxProofResult() != AssetTxProofResult.UNDEFINED;
|
||||||
|
|
||||||
if (trade.getPayoutTx() != null)
|
if (trade.getPayoutTxId() != null)
|
||||||
rows++;
|
rows++;
|
||||||
boolean showDisputedTx = arbitrationManager.findOwnDispute(trade.getId()).isPresent() &&
|
boolean showDisputedTx = arbitrationManager.findOwnDispute(trade.getId()).isPresent() &&
|
||||||
arbitrationManager.findOwnDispute(trade.getId()).get().getDisputePayoutTxId() != null;
|
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
|
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.depositTransactionId"), // TODO (woodser): separate UI labels for deposit tx ids
|
||||||
trade.getTakerDepositTx().getHash());
|
trade.getTakerDepositTx().getHash());
|
||||||
|
|
||||||
if (trade.getPayoutTx() != null)
|
if (trade.getPayoutTxId() != null)
|
||||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.payoutTxId"),
|
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("shared.payoutTxId"),
|
||||||
trade.getPayoutTx().getHash());
|
trade.getPayoutTxId());
|
||||||
if (showDisputedTx)
|
if (showDisputedTx)
|
||||||
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.disputedPayoutTxId"),
|
addLabelTxIdTextField(gridPane, ++rowIndex, Res.get("tradeDetailsWindow.disputedPayoutTxId"),
|
||||||
arbitrationManager.findOwnDispute(trade.getId()).get().getDisputePayoutTxId());
|
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.daemon.model.MoneroTx;
|
||||||
import monero.wallet.MoneroWallet;
|
import monero.wallet.MoneroWallet;
|
||||||
import monero.wallet.model.MoneroTxWallet;
|
|
||||||
|
|
||||||
public class PendingTradesDataModel extends ActivatableDataModel {
|
public class PendingTradesDataModel extends ActivatableDataModel {
|
||||||
@Getter
|
@Getter
|
||||||
@ -465,11 +464,10 @@ public class PendingTradesDataModel extends ActivatableDataModel {
|
|||||||
|
|
||||||
byte[] payoutTxSerialized = null;
|
byte[] payoutTxSerialized = null;
|
||||||
String payoutTxHashAsString = null;
|
String payoutTxHashAsString = null;
|
||||||
MoneroTxWallet payoutTx = trade.getPayoutTx();
|
|
||||||
MoneroWallet multisigWallet = xmrWalletService.getMultisigWallet(trade.getId());
|
MoneroWallet multisigWallet = xmrWalletService.getMultisigWallet(trade.getId());
|
||||||
String updatedMultisigHex = multisigWallet.exportMultisigHex();
|
String updatedMultisigHex = multisigWallet.exportMultisigHex();
|
||||||
xmrWalletService.closeMultisigWallet(trade.getId()); // close multisig wallet
|
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
|
// payoutTxSerialized = payoutTx.bitcoinSerialize(); // TODO (woodser): no need to pass serialized txs for xmr
|
||||||
// payoutTxHashAsString = payoutTx.getHashAsString();
|
// payoutTxHashAsString = payoutTx.getHashAsString();
|
||||||
}
|
}
|
||||||
|
@ -420,7 +420,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||||||
|
|
||||||
// deposit published
|
// deposit published
|
||||||
case ARBITRATOR_PUBLISHED_DEPOSIT_TXS:
|
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
|
case DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN: // TODO: separate step to wait for first confirmation
|
||||||
buyerState.set(BuyerState.STEP1);
|
buyerState.set(BuyerState.STEP1);
|
||||||
sellerState.set(SellerState.STEP1);
|
sellerState.set(SellerState.STEP1);
|
||||||
@ -472,7 +472,7 @@ public class PendingTradesViewModel extends ActivatableWithDataModel<PendingTrad
|
|||||||
// buyer step 4
|
// buyer step 4
|
||||||
case BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG:
|
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:
|
// 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
|
// Alternatively the buyer could fully sign and publish the payout tx
|
||||||
case BUYER_PUBLISHED_PAYOUT_TX:
|
case BUYER_PUBLISHED_PAYOUT_TX:
|
||||||
buyerState.set(BuyerState.STEP4);
|
buyerState.set(BuyerState.STEP4);
|
||||||
|
@ -203,7 +203,7 @@ public class TradeStepInfo {
|
|||||||
footerLabel.setVisible(false);
|
footerLabel.setVisible(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trade != null && trade.getPayoutTx() != null) {
|
if (trade != null && trade.getPayoutTxId() != null) {
|
||||||
button.setDisable(true);
|
button.setDisable(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -653,7 +653,7 @@ public abstract class TradeStepView extends AnchorPane {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trade.getPayoutTx() != null) {
|
if (trade.getPayoutTxId() != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1648,7 +1648,7 @@ message Trade {
|
|||||||
STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST = 10;
|
STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST = 10;
|
||||||
SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST = 11;
|
SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST = 11;
|
||||||
ARBITRATOR_PUBLISHED_DEPOSIT_TXS = 12;
|
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_CONFIRMED_IN_BLOCKCHAIN = 14;
|
||||||
DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN = 15;
|
DEPOSIT_TXS_UNLOCKED_IN_BLOCKCHAIN = 15;
|
||||||
BUYER_CONFIRMED_IN_UI_PAYMENT_SENT = 16;
|
BUYER_CONFIRMED_IN_UI_PAYMENT_SENT = 16;
|
||||||
@ -1668,8 +1668,8 @@ message Trade {
|
|||||||
SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG = 30;
|
SELLER_STORED_IN_MAILBOX_PAYOUT_TX_PUBLISHED_MSG = 30;
|
||||||
SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG = 31;
|
SELLER_SEND_FAILED_PAYOUT_TX_PUBLISHED_MSG = 31;
|
||||||
BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG = 32;
|
BUYER_RECEIVED_PAYOUT_TX_PUBLISHED_MSG = 32;
|
||||||
BUYER_SAW_PAYOUT_TX_IN_NETWORK = 33;
|
BUYER_PUBLISHED_PAYOUT_TX = 33;
|
||||||
BUYER_PUBLISHED_PAYOUT_TX = 34;
|
PAYOUT_TX_SEEN_IN_NETWORK = 34;
|
||||||
WITHDRAW_COMPLETED = 35;
|
WITHDRAW_COMPLETED = 35;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1712,31 +1712,33 @@ message Trade {
|
|||||||
string taker_fee_tx_id = 3;
|
string taker_fee_tx_id = 3;
|
||||||
reserved 4;
|
reserved 4;
|
||||||
string payout_tx_id = 5;
|
string payout_tx_id = 5;
|
||||||
int64 amount_as_long = 6;
|
string payout_tx_hex = 6;
|
||||||
int64 tx_fee_as_long = 7;
|
string payout_tx_key = 7;
|
||||||
int64 taker_fee_as_long = 8;
|
int64 amount_as_long = 8;
|
||||||
int64 take_offer_date = 9;
|
int64 tx_fee_as_long = 9;
|
||||||
int64 price = 10;
|
int64 taker_fee_as_long = 10;
|
||||||
State state = 11;
|
int64 take_offer_date = 11;
|
||||||
DisputeState dispute_state = 12;
|
int64 price = 12;
|
||||||
TradePeriodState period_state = 13;
|
State state = 13;
|
||||||
Contract contract = 14;
|
DisputeState dispute_state = 14;
|
||||||
string contract_as_json = 15;
|
TradePeriodState period_state = 15;
|
||||||
bytes contract_hash = 16;
|
Contract contract = 16;
|
||||||
NodeAddress arbitrator_node_address = 17;
|
string contract_as_json = 17;
|
||||||
NodeAddress mediator_node_address = 18;
|
bytes contract_hash = 18;
|
||||||
|
NodeAddress arbitrator_node_address = 19;
|
||||||
|
NodeAddress mediator_node_address = 20;
|
||||||
string error_message = 21;
|
string error_message = 21;
|
||||||
string counter_currency_tx_id = 24;
|
string counter_currency_tx_id = 22;
|
||||||
repeated ChatMessage chat_message = 25;
|
repeated ChatMessage chat_message = 23;
|
||||||
MediationResultState mediation_result_state = 26;
|
MediationResultState mediation_result_state = 24;
|
||||||
int64 lock_time = 27;
|
int64 lock_time = 25;
|
||||||
bytes delayed_payout_tx_bytes = 28;
|
bytes delayed_payout_tx_bytes = 26;
|
||||||
NodeAddress refund_agent_node_address = 29;
|
NodeAddress refund_agent_node_address = 27;
|
||||||
RefundResultState refund_result_state = 30;
|
RefundResultState refund_result_state = 28;
|
||||||
int64 last_refresh_request_date = 31 [deprecated = true];
|
int64 last_refresh_request_date = 29 [deprecated = true];
|
||||||
string counter_currency_extra_data = 32;
|
string counter_currency_extra_data = 30;
|
||||||
string asset_tx_proof_result = 33; // name of AssetTxProofResult enum
|
string asset_tx_proof_result = 31; // name of AssetTxProofResult enum
|
||||||
string uid = 34;
|
string uid = 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
message BuyerAsMakerTrade {
|
message BuyerAsMakerTrade {
|
||||||
@ -1819,11 +1821,10 @@ message TradingPeer {
|
|||||||
string prepared_multisig_hex = 1005;
|
string prepared_multisig_hex = 1005;
|
||||||
string made_multisig_hex = 1006;
|
string made_multisig_hex = 1006;
|
||||||
string exchanged_multisig_hex = 1007;
|
string exchanged_multisig_hex = 1007;
|
||||||
string payout_tx_hex = 1008;
|
string deposit_tx_hash = 1008;
|
||||||
string deposit_tx_hash = 1009;
|
string deposit_tx_hex = 1009;
|
||||||
string deposit_tx_hex = 1010;
|
string deposit_tx_key = 1010;
|
||||||
string deposit_tx_key = 1011;
|
string updated_multisig_hex = 1011;
|
||||||
string updated_multisig_hex = 1012;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
Loading…
Reference in New Issue
Block a user