diff --git a/core/src/main/java/bisq/core/trade/Trade.java b/core/src/main/java/bisq/core/trade/Trade.java
index 55d1e4bb05..e65d405c56 100644
--- a/core/src/main/java/bisq/core/trade/Trade.java
+++ b/core/src/main/java/bisq/core/trade/Trade.java
@@ -770,14 +770,15 @@ public abstract class Trade implements Tradable, Model {
}
/**
- * Verify and sign a payout tx.
+ * Verify a payout tx.
*
* @param payoutTxHex is the payout tx hex to verify
- * @return String the signed payout tx hex
+ * @param sign signs the payout tx if true
+ * @param publish publishes the signed payout tx if true
*/
- public void verifySignAndPublishPayoutTx(String payoutTxHex) {
+ public void verifyPayoutTx(String payoutTxHex, boolean sign, boolean publish) {
log.info("Verifying payout tx");
-
+
// gather relevant info
XmrWalletService walletService = processModel.getProvider().getXmrWalletService();
MoneroWallet multisigWallet = walletService.getMultisigWallet(getId());
@@ -787,9 +788,9 @@ public abstract class Trade implements Tradable, Model {
BigInteger tradeAmount = ParsingUtils.coinToAtomicUnits(getAmount());
// parse payout tx
- MoneroTxSet parsedTxSet = multisigWallet.describeTxSet(new MoneroTxSet().setMultisigTxHex(payoutTxHex));
- if (parsedTxSet.getTxs() == null || parsedTxSet.getTxs().size() != 1) throw new RuntimeException("Bad payout tx"); // TODO (woodser): test nack
- MoneroTxWallet payoutTx = parsedTxSet.getTxs().get(0);
+ 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);
// verify payout tx has exactly 2 destinations
if (payoutTx.getOutgoingTransfer() == null || payoutTx.getOutgoingTransfer().getDestinations() == null || payoutTx.getOutgoingTransfer().getDestinations().size() != 2) throw new RuntimeException("Payout tx does not have exactly two destinations");
@@ -819,21 +820,25 @@ public abstract class Trade implements Tradable, Model {
if (!sellerPayoutDestination.getAmount().equals(expectedSellerPayout)) throw new RuntimeException("Seller destination amount is not deposit amount - trade amount - 1/2 tx costs, " + sellerPayoutDestination.getAmount() + " vs " + expectedSellerPayout);
// TODO (woodser): verify fee is reasonable (e.g. within 2x of fee estimate tx)
-
+
// sign payout tx
- MoneroMultisigSignResult result = multisigWallet.signMultisigTxHex(payoutTxHex);
- if (result.getSignedMultisigTxHex() == null) throw new RuntimeException("Error signing payout tx");
- String signedPayoutTxHex = result.getSignedMultisigTxHex();
-
- // submit payout tx
- multisigWallet.submitMultisigTxHex(signedPayoutTxHex);
- walletService.closeMultisigWallet(getId());
-
+ if (sign) {
+ MoneroMultisigSignResult result = multisigWallet.signMultisigTxHex(payoutTxHex);
+ if (result.getSignedMultisigTxHex() == null) throw new RuntimeException("Error signing payout tx");
+ payoutTxHex = result.getSignedMultisigTxHex();
+ }
+
// update trade state
- getSelf().setPayoutTxHex(signedPayoutTxHex);
- setPayoutTx(parsedTxSet.getTxs().get(0));
- setPayoutTxId(parsedTxSet.getTxs().get(0).getHash());
- setState(isBuyer() ? Trade.State.BUYER_PUBLISHED_PAYOUT_TX : Trade.State.SELLER_PUBLISHED_PAYOUT_TX);
+ getSelf().setPayoutTxHex(payoutTxHex);
+ setPayoutTx(describedTxSet.getTxs().get(0));
+ setPayoutTxId(describedTxSet.getTxs().get(0).getHash());
+
+ // submit payout tx
+ if (publish) {
+ multisigWallet.submitMultisigTxHex(payoutTxHex);
+ setState(isArbitrator() ? Trade.State.WITHDRAW_COMPLETED : isBuyer() ? Trade.State.BUYER_PUBLISHED_PAYOUT_TX : Trade.State.SELLER_PUBLISHED_PAYOUT_TX);
+ }
+ walletService.closeMultisigWallet(getId());
}
/**
@@ -1114,6 +1119,10 @@ public abstract class Trade implements Tradable, Model {
// Getter
///////////////////////////////////////////////////////////////////////////////////////////
+ public boolean isArbitrator() {
+ return this instanceof ArbitratorTrade;
+ }
+
public boolean isBuyer() {
return getBuyer() == getSelf();
}
diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java
index d33051aaea..28d0db3935 100644
--- a/core/src/main/java/bisq/core/trade/TradeManager.java
+++ b/core/src/main/java/bisq/core/trade/TradeManager.java
@@ -541,7 +541,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
//System.out.println("TradeManager trade.setTradingPeerNodeAddress(): " + sender);
//trade.setTradingPeerNodeAddress(sender);
// TODO (woodser): what if maker's address changes while offer open, or taker's address changes after multisig deposit available? need to verify and update. see OpenOfferManager.maybeUpdatePersistedOffers()
- trade.setArbitratorPubKeyRing(user.getAcceptedArbitratorByAddress(sender).getPubKeyRing());
+ trade.setArbitratorPubKeyRing(arbitrator.getPubKeyRing());
trade.setMakerPubKeyRing(trade.getOffer().getPubKeyRing());
initTradeAndProtocol(trade, getTradeProtocol(trade));
trade.getSelf().setReserveTxHash(openOffer.getReserveTxHash()); // TODO (woodser): initialize in initTradeAndProtocol?
diff --git a/core/src/main/java/bisq/core/trade/TradeUtils.java b/core/src/main/java/bisq/core/trade/TradeUtils.java
index cec9f24ca2..808c4b9df8 100644
--- a/core/src/main/java/bisq/core/trade/TradeUtils.java
+++ b/core/src/main/java/bisq/core/trade/TradeUtils.java
@@ -74,8 +74,8 @@ public class TradeUtils {
/**
* Check if the arbitrator signature for an offer is valid.
*
- * @param arbitrator is the possible original arbitrator
* @param signedOfferPayload is a signed offer payload
+ * @param arbitrator is the possible original arbitrator
* @return true if the arbitrator's signature is valid for the offer
*/
public static boolean isArbitratorSignatureValid(OfferPayload signedOfferPayload, Arbitrator arbitrator) {
diff --git a/core/src/main/java/bisq/core/trade/messages/PayoutTxPublishedMessage.java b/core/src/main/java/bisq/core/trade/messages/PayoutTxPublishedMessage.java
index 596e11a4e7..97c12e1ccd 100644
--- a/core/src/main/java/bisq/core/trade/messages/PayoutTxPublishedMessage.java
+++ b/core/src/main/java/bisq/core/trade/messages/PayoutTxPublishedMessage.java
@@ -38,7 +38,7 @@ import javax.annotation.Nullable;
@Value
public final class PayoutTxPublishedMessage extends TradeMailboxMessage {
private final NodeAddress senderNodeAddress;
- private final String payoutTxHex;
+ private final String signedPayoutTxHex;
// Added in v1.4.0
@Nullable
@@ -47,13 +47,13 @@ public final class PayoutTxPublishedMessage extends TradeMailboxMessage {
public PayoutTxPublishedMessage(String tradeId,
NodeAddress senderNodeAddress,
@Nullable SignedWitness signedWitness,
- String payoutTxHex) {
+ String signedPayoutTxHex) {
this(tradeId,
senderNodeAddress,
signedWitness,
UUID.randomUUID().toString(),
Version.getP2PMessageVersion(),
- payoutTxHex);
+ signedPayoutTxHex);
}
@@ -66,11 +66,11 @@ public final class PayoutTxPublishedMessage extends TradeMailboxMessage {
@Nullable SignedWitness signedWitness,
String uid,
String messageVersion,
- String payoutTxHex) {
+ String signedPayoutTxHex) {
super(messageVersion, tradeId, uid);
this.senderNodeAddress = senderNodeAddress;
this.signedWitness = signedWitness;
- this.payoutTxHex = payoutTxHex;
+ this.signedPayoutTxHex = signedPayoutTxHex;
}
@Override
@@ -79,7 +79,7 @@ public final class PayoutTxPublishedMessage extends TradeMailboxMessage {
.setTradeId(tradeId)
.setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setUid(uid)
- .setPayoutTxHex(payoutTxHex);
+ .setSignedPayoutTxHex(signedPayoutTxHex);
Optional.ofNullable(signedWitness).ifPresent(signedWitness -> builder.setSignedWitness(signedWitness.toProtoSignedWitness()));
return getNetworkEnvelopeBuilder().setPayoutTxPublishedMessage(builder).build();
}
@@ -96,7 +96,7 @@ public final class PayoutTxPublishedMessage extends TradeMailboxMessage {
signedWitness,
proto.getUid(),
messageVersion,
- proto.getPayoutTxHex());
+ proto.getSignedPayoutTxHex());
}
@Override
@@ -104,7 +104,7 @@ public final class PayoutTxPublishedMessage extends TradeMailboxMessage {
return "PayoutTxPublishedMessage{" +
"\n senderNodeAddress=" + senderNodeAddress +
",\n signedWitness=" + signedWitness +
- ",\n payoutTxHex=" + payoutTxHex +
+ ",\n signedPayoutTxHex=" + signedPayoutTxHex +
"\n} " + super.toString();
}
}
diff --git a/core/src/main/java/bisq/core/trade/protocol/ArbitratorProtocol.java b/core/src/main/java/bisq/core/trade/protocol/ArbitratorProtocol.java
index a1f33c6cd9..ef04322837 100644
--- a/core/src/main/java/bisq/core/trade/protocol/ArbitratorProtocol.java
+++ b/core/src/main/java/bisq/core/trade/protocol/ArbitratorProtocol.java
@@ -1,5 +1,6 @@
package bisq.core.trade.protocol;
+import bisq.common.handlers.ErrorMessageHandler;
import bisq.core.trade.ArbitratorTrade;
import bisq.core.trade.Trade;
import bisq.core.trade.messages.DepositRequest;
@@ -7,17 +8,18 @@ import bisq.core.trade.messages.DepositResponse;
import bisq.core.trade.messages.InitTradeRequest;
import bisq.core.trade.messages.PaymentAccountKeyRequest;
import bisq.core.trade.messages.SignContractResponse;
+import bisq.core.trade.messages.PayoutTxPublishedMessage;
+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.ArbitratorSendsInitTradeOrMultisigRequests;
import bisq.core.trade.protocol.tasks.ArbitratorProcessesDepositRequest;
import bisq.core.trade.protocol.tasks.ArbitratorProcessesPaymentAccountKeyRequest;
import bisq.core.trade.protocol.tasks.ArbitratorProcessesReserveTx;
+import bisq.core.trade.protocol.tasks.ArbitratorProcessPayoutTxPublishedMessage;
+import bisq.core.trade.protocol.tasks.ArbitratorSendsInitTradeOrMultisigRequests;
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
import bisq.core.util.Validator;
import bisq.network.p2p.NodeAddress;
-import bisq.common.handlers.ErrorMessageHandler;
-
import lombok.extern.slf4j.Slf4j;
@Slf4j
@@ -27,6 +29,22 @@ public class ArbitratorProtocol extends DisputeProtocol {
super(trade);
}
+ @Override
+ protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
+ super.onTradeMessage(message, peer);
+ if (message instanceof PayoutTxPublishedMessage) {
+ handle((PayoutTxPublishedMessage) message, peer);
+ }
+ }
+
+ @Override
+ public void onMailboxMessage(TradeMessage message, NodeAddress peer) {
+ super.onMailboxMessage(message, peer);
+ if (message instanceof PayoutTxPublishedMessage) {
+ handle((PayoutTxPublishedMessage) message, peer);
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////////////////////
// Incoming messages
///////////////////////////////////////////////////////////////////////////////////////////
@@ -94,7 +112,7 @@ public class ArbitratorProtocol extends DisputeProtocol {
@Override
public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
- log.warn("Arbitrator ignoring DepositResponse");
+ log.warn("Arbitrator ignoring DepositResponse for trade " + response.getTradeId());
}
public void handlePaymentAccountKeyRequest(PaymentAccountKeyRequest request, NodeAddress sender) {
@@ -121,15 +139,30 @@ public class ArbitratorProtocol extends DisputeProtocol {
awaitTradeLatch();
}
}
-
- ///////////////////////////////////////////////////////////////////////////////////////////
- // Message dispatcher
- ///////////////////////////////////////////////////////////////////////////////////////////
-
-// @Override
-// protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
-// if (message instanceof InitTradeRequest) {
-// handleInitTradeRequest((InitTradeRequest) message, peer);
-// }
-// }
+
+ protected void handle(PayoutTxPublishedMessage request, NodeAddress peer) {
+ System.out.println("ArbitratorProtocol.handle(PayoutTxPublishedMessage)");
+ new Thread(() -> {
+ synchronized (trade) {
+ if (trade.isCompleted()) return; // ignore subsequent requests
+ latchTrade();
+ Validator.checkTradeId(processModel.getOfferId(), request);
+ processModel.setTradeMessage(request);
+ expect(phase(Trade.Phase.DEPOSITS_PUBLISHED)
+ .with(request)
+ .from(peer))
+ .setup(tasks(
+ ArbitratorProcessPayoutTxPublishedMessage.class)
+ .using(new TradeTaskRunner(trade,
+ () -> {
+ handleTaskRunnerSuccess(peer, request);
+ },
+ errorMessage -> {
+ handleTaskRunnerFault(peer, request, errorMessage);
+ })))
+ .executeTasks(true);
+ awaitTradeLatch();
+ }
+ }).start();
+ }
}
diff --git a/core/src/main/java/bisq/core/trade/protocol/BuyerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/BuyerProtocol.java
index e0a6143a56..2f4a2baa5f 100644
--- a/core/src/main/java/bisq/core/trade/protocol/BuyerProtocol.java
+++ b/core/src/main/java/bisq/core/trade/protocol/BuyerProtocol.java
@@ -25,6 +25,7 @@ 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.BuyerSendsPayoutTxPublishedMessage;
import bisq.core.trade.protocol.tasks.BuyerPreparesPaymentSentMessage;
import bisq.core.trade.protocol.tasks.BuyerProcessesPaymentReceivedMessage;
import bisq.core.trade.protocol.tasks.BuyerSendsPaymentAccountKeyRequestToArbitrator;
@@ -189,7 +190,8 @@ public abstract class BuyerProtocol extends DisputeProtocol {
.with(message)
.from(peer))
.setup(tasks(
- BuyerProcessesPaymentReceivedMessage.class)
+ BuyerProcessesPaymentReceivedMessage.class,
+ BuyerSendsPayoutTxPublishedMessage.class)
.using(new TradeTaskRunner(trade,
() -> {
handleTaskRunnerSuccess(peer, message);
diff --git a/core/src/main/java/bisq/core/trade/protocol/DisputeProtocol.java b/core/src/main/java/bisq/core/trade/protocol/DisputeProtocol.java
index 3b77fb2baa..4129e468d5 100644
--- a/core/src/main/java/bisq/core/trade/protocol/DisputeProtocol.java
+++ b/core/src/main/java/bisq/core/trade/protocol/DisputeProtocol.java
@@ -51,6 +51,31 @@ public abstract class DisputeProtocol extends TradeProtocol {
}
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Dispatcher
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ @Override
+ protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
+ super.onTradeMessage(message, peer);
+ if (message instanceof MediatedPayoutTxSignatureMessage) {
+ handle((MediatedPayoutTxSignatureMessage) message, peer);
+ } else if (message instanceof MediatedPayoutTxPublishedMessage) {
+ handle((MediatedPayoutTxPublishedMessage) message, peer);
+ }
+ }
+
+ @Override
+ protected void onMailboxMessage(TradeMessage message, NodeAddress peer) {
+ super.onMailboxMessage(message, peer);
+ if (message instanceof MediatedPayoutTxSignatureMessage) {
+ handle((MediatedPayoutTxSignatureMessage) message, peer);
+ } else if (message instanceof MediatedPayoutTxPublishedMessage) {
+ handle((MediatedPayoutTxPublishedMessage) message, peer);
+ }
+ }
+
+
///////////////////////////////////////////////////////////////////////////////////////////
// User interaction: Trader accepts mediation result
///////////////////////////////////////////////////////////////////////////////////////////
@@ -131,53 +156,4 @@ public abstract class DisputeProtocol extends TradeProtocol {
.setup(tasks(ProcessMediatedPayoutTxPublishedMessage.class))
.executeTasks();
}
-
- ///////////////////////////////////////////////////////////////////////////////////////////
- // Delayed payout tx
- ///////////////////////////////////////////////////////////////////////////////////////////
-
-// public void onPublishDelayedPayoutTx(ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
-// DisputeEvent event = DisputeEvent.ARBITRATION_REQUESTED;
-// expect(anyPhase(Trade.Phase.DEPOSITS_CONFIRMED,
-// Trade.Phase.FIAT_SENT,
-// Trade.Phase.FIAT_RECEIVED)
-// .with(event)
-// .preCondition(trade.getDelayedPayoutTx() != null))
-// .setup(tasks(PublishedDelayedPayoutTx.class,
-// SendPeerPublishedDelayedPayoutTxMessage.class)
-// .using(new TradeTaskRunner(trade,
-// () -> {
-// resultHandler.handleResult();
-// handleTaskRunnerSuccess(event);
-// },
-// errorMessage -> {
-// errorMessageHandler.handleErrorMessage(errorMessage);
-// handleTaskRunnerFault(event, errorMessage);
-// })))
-// .executeTasks();
-// }
-
-
- ///////////////////////////////////////////////////////////////////////////////////////////
- // Dispatcher
- ///////////////////////////////////////////////////////////////////////////////////////////
-
- @Override
- protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
- if (message instanceof MediatedPayoutTxSignatureMessage) {
- handle((MediatedPayoutTxSignatureMessage) message, peer);
- } else if (message instanceof MediatedPayoutTxPublishedMessage) {
- handle((MediatedPayoutTxPublishedMessage) message, peer);
- }
- }
-
- @Override
- protected void onMailboxMessage(TradeMessage message, NodeAddress peer) {
- super.onMailboxMessage(message, peer);
- if (message instanceof MediatedPayoutTxSignatureMessage) {
- handle((MediatedPayoutTxSignatureMessage) message, peer);
- } else if (message instanceof MediatedPayoutTxPublishedMessage) {
- handle((MediatedPayoutTxPublishedMessage) message, peer);
- }
- }
}
diff --git a/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java
index 9fc6e6f184..5b3f45b90e 100644
--- a/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java
+++ b/core/src/main/java/bisq/core/trade/protocol/SellerProtocol.java
@@ -19,12 +19,12 @@ package bisq.core.trade.protocol;
import bisq.core.trade.SellerTrade;
import bisq.core.trade.Trade;
-import bisq.core.trade.messages.DepositResponse;
import bisq.core.trade.messages.PaymentSentMessage;
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.SellerMaybeSendsPayoutTxPublishedMessage;
import bisq.core.trade.protocol.tasks.SellerPreparesPaymentReceivedMessage;
import bisq.core.trade.protocol.tasks.SellerProcessesPaymentSentMessage;
import bisq.core.trade.protocol.tasks.SellerSendsPaymentReceivedMessage;
@@ -155,6 +155,7 @@ public abstract class SellerProtocol extends DisputeProtocol {
.setup(tasks(
ApplyFilter.class,
SellerPreparesPaymentReceivedMessage.class,
+ SellerMaybeSendsPayoutTxPublishedMessage.class,
SellerSendsPaymentReceivedMessage.class)
.using(new TradeTaskRunner(trade, () -> {
this.errorMessageHandler = null;
diff --git a/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java b/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java
index 7402d4d3ce..6cf72cad35 100644
--- a/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java
+++ b/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java
@@ -86,6 +86,19 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
}
+ ///////////////////////////////////////////////////////////////////////////////////////////
+ // Dispatcher
+ ///////////////////////////////////////////////////////////////////////////////////////////
+
+ protected void onTradeMessage(TradeMessage message, NodeAddress peerNodeAddress) {
+ log.info("Received {} as TradeMessage from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peerNodeAddress, message.getTradeId(), message.getUid());
+ }
+
+ protected void onMailboxMessage(TradeMessage message, NodeAddress peerNodeAddress) {
+ log.info("Received {} as MailboxMessage from {} with tradeId {} and uid {}", message.getClass().getSimpleName(), peerNodeAddress, message.getTradeId(), message.getUid());
+ }
+
+
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
@@ -116,11 +129,6 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
log.info("Withdraw completed");
}
- protected void onMailboxMessage(TradeMessage message, NodeAddress peerNodeAddress) {
- log.info("Received {} as MailboxMessage from {} with tradeId {} and uid {}",
- message.getClass().getSimpleName(), peerNodeAddress, message.getTradeId(), message.getUid());
- }
-
///////////////////////////////////////////////////////////////////////////////////////////
// DecryptedDirectMessageListener
@@ -218,8 +226,6 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
// Abstract
///////////////////////////////////////////////////////////////////////////////////////////
- protected abstract void onTradeMessage(TradeMessage message, NodeAddress peer);
-
public void handleInitMultisigRequest(InitMultisigRequest request, NodeAddress sender) {
System.out.println(getClass().getCanonicalName() + ".handleInitMultisigRequest()");
synchronized (trade) {
diff --git a/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java b/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java
index eaa67fe9e1..792b1e6f5d 100644
--- a/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java
+++ b/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java
@@ -147,8 +147,7 @@ public final class TradingPeer implements PersistablePayload {
Optional.ofNullable(signature).ifPresent(e -> builder.setSignature(ByteString.copyFrom(e)));
Optional.ofNullable(pubKeyRing).ifPresent(e -> builder.setPubKeyRing(e.toProtoMessage()));
Optional.ofNullable(multiSigPubKey).ifPresent(e -> builder.setMultiSigPubKey(ByteString.copyFrom(e)));
- Optional.ofNullable(rawTransactionInputs).ifPresent(e -> builder.addAllRawTransactionInputs(
- ProtoUtil.collectionToProto(e, protobuf.RawTransactionInput.class)));
+ Optional.ofNullable(rawTransactionInputs).ifPresent(e -> builder.addAllRawTransactionInputs(ProtoUtil.collectionToProto(e, protobuf.RawTransactionInput.class)));
Optional.ofNullable(changeOutputAddress).ifPresent(builder::setChangeOutputAddress);
Optional.ofNullable(accountAgeWitnessNonce).ifPresent(e -> builder.setAccountAgeWitnessNonce(ByteString.copyFrom(e)));
Optional.ofNullable(accountAgeWitnessSignature).ifPresent(e -> builder.setAccountAgeWitnessSignature(ByteString.copyFrom(e)));
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessPayoutTxPublishedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessPayoutTxPublishedMessage.java
new file mode 100644
index 0000000000..db1091866d
--- /dev/null
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorProcessPayoutTxPublishedMessage.java
@@ -0,0 +1,53 @@
+/*
+ * 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 .
+ */
+
+package bisq.core.trade.protocol.tasks;
+
+
+import bisq.common.taskrunner.TaskRunner;
+import bisq.core.trade.Trade;
+import bisq.core.trade.messages.PayoutTxPublishedMessage;
+import lombok.extern.slf4j.Slf4j;
+
+@Slf4j
+public class ArbitratorProcessPayoutTxPublishedMessage extends TradeTask {
+
+ @SuppressWarnings({"unused"})
+ public ArbitratorProcessPayoutTxPublishedMessage(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+ PayoutTxPublishedMessage request = (PayoutTxPublishedMessage) processModel.getTradeMessage();
+
+ // verify and publish payout tx
+ trade.verifyPayoutTx(request.getSignedPayoutTxHex(), false, true);
+
+ // TODO: publish signed witness data?
+ //request.getSignedWitness()
+
+ // close arbitrator trade
+ processModel.getTradeManager().onTradeCompleted(trade);
+ complete();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+}
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerProcessesPaymentReceivedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerProcessesPaymentReceivedMessage.java
index 37cd304d3f..079c5c60d1 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerProcessesPaymentReceivedMessage.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerProcessesPaymentReceivedMessage.java
@@ -71,7 +71,7 @@ public class BuyerProcessesPaymentReceivedMessage extends TradeTask {
walletService.closeMultisigWallet(trade.getId());
} else {
log.info("Buyer verifying, signing, and publishing seller's payout tx");
- trade.verifySignAndPublishPayoutTx(message.getPayoutTxHex());
+ trade.verifyPayoutTx(message.getPayoutTxHex(), true, true);
trade.setState(Trade.State.BUYER_PUBLISHED_PAYOUT_TX);
// TODO (woodser): send PayoutTxPublishedMessage to arbitrator and seller
}
@@ -79,6 +79,7 @@ public class BuyerProcessesPaymentReceivedMessage extends TradeTask {
log.info("We got the payout tx already set from BuyerSetupPayoutTxListener and do nothing here. trade ID={}", trade.getId());
}
+ // TODO: remove witness
SignedWitness signedWitness = message.getSignedWitness();
if (signedWitness != null) {
// We received the signedWitness from the seller and publish the data to the network.
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerSendsPayoutTxPublishedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerSendsPayoutTxPublishedMessage.java
new file mode 100644
index 0000000000..75d7d36561
--- /dev/null
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerSendsPayoutTxPublishedMessage.java
@@ -0,0 +1,81 @@
+/*
+ * 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 .
+ */
+
+package bisq.core.trade.protocol.tasks;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import bisq.common.crypto.PubKeyRing;
+import bisq.common.taskrunner.TaskRunner;
+import bisq.core.payment.PaymentAccount;
+import bisq.core.trade.Trade;
+import bisq.core.trade.messages.PayoutTxPublishedMessage;
+import bisq.core.trade.messages.TradeMailboxMessage;
+import bisq.network.p2p.NodeAddress;
+import java.util.UUID;
+import lombok.EqualsAndHashCode;
+import lombok.extern.slf4j.Slf4j;
+
+@EqualsAndHashCode(callSuper = true)
+@Slf4j
+public class BuyerSendsPayoutTxPublishedMessage extends SendMailboxMessageTask {
+
+ public BuyerSendsPayoutTxPublishedMessage(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected NodeAddress getReceiverNodeAddress() {
+ return trade.getArbitratorNodeAddress();
+ }
+
+ @Override
+ protected PubKeyRing getReceiverPubKeyRing() {
+ return trade.getArbitratorPubKeyRing();
+ }
+
+ @Override
+ protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) {
+ checkNotNull(trade.getSelf().getPayoutTxHex(), "Payout tx must not be null");
+ return new PayoutTxPublishedMessage(
+ tradeId,
+ processModel.getMyNodeAddress(),
+ null, // TODO: send witness data?
+ trade.getSelf().getPayoutTxHex()
+ );
+ }
+
+ @Override
+ protected void setStateSent() {
+ log.info("Buyer sent PayoutTxPublishedMessage: tradeId={} at arbitrator {}", trade.getId(), getReceiverNodeAddress());
+ }
+
+ @Override
+ protected void setStateArrived() {
+ log.info("Buyer's PayoutTxPublishedMessage arrived: tradeId={} at arbitrator {}", trade.getId(), getReceiverNodeAddress());
+ }
+
+ @Override
+ protected void setStateStoredInMailbox() {
+ log.info("Buyer's PayoutTxPublishedMessage stored in mailbox: tradeId={} at arbitrator {}", trade.getId(), getReceiverNodeAddress());
+ }
+
+ @Override
+ protected void setStateFault() {
+ log.error("Buyer's PayoutTxPublishedMessage failed: tradeId={} at arbitrator {}", trade.getId(), getReceiverNodeAddress());
+ }
+}
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/SellerMaybeSendsPayoutTxPublishedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/SellerMaybeSendsPayoutTxPublishedMessage.java
new file mode 100644
index 0000000000..9cf343a5e3
--- /dev/null
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/SellerMaybeSendsPayoutTxPublishedMessage.java
@@ -0,0 +1,96 @@
+/*
+ * 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 .
+ */
+
+package bisq.core.trade.protocol.tasks;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import bisq.common.crypto.PubKeyRing;
+import bisq.common.taskrunner.TaskRunner;
+import bisq.core.trade.Trade;
+import bisq.core.trade.messages.PayoutTxPublishedMessage;
+import bisq.core.trade.messages.TradeMailboxMessage;
+import bisq.network.p2p.NodeAddress;
+import lombok.EqualsAndHashCode;
+import lombok.extern.slf4j.Slf4j;
+
+@EqualsAndHashCode(callSuper = true)
+@Slf4j
+public class SellerMaybeSendsPayoutTxPublishedMessage extends SendMailboxMessageTask {
+
+ public SellerMaybeSendsPayoutTxPublishedMessage(TaskRunner taskHandler, Trade trade) {
+ super(taskHandler, trade);
+ }
+
+ @Override
+ protected void run() {
+ try {
+ runInterceptHook();
+
+ // skip if payout tx not published
+ if (trade.getPhase().ordinal() < Trade.Phase.PAYOUT_PUBLISHED.ordinal()) {
+ complete();
+ return;
+ }
+
+ super.run();
+ } catch (Throwable t) {
+ failed(t);
+ }
+ }
+
+ @Override
+ protected NodeAddress getReceiverNodeAddress() {
+ return trade.getArbitratorNodeAddress();
+ }
+
+ @Override
+ protected PubKeyRing getReceiverPubKeyRing() {
+ return trade.getArbitratorPubKeyRing();
+ }
+
+ @Override
+ protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) {
+ checkNotNull(trade.getSelf().getPayoutTxHex(), "Payout tx must not be null");
+ return new PayoutTxPublishedMessage(
+ tradeId,
+ processModel.getMyNodeAddress(),
+ null, // TODO: send witness data?
+ trade.getSelf().getPayoutTxHex()
+ );
+ }
+
+ @Override
+ protected void setStateSent() {
+ log.info("Seller sent PayoutTxPublishedMessage: tradeId={} at arbitrator {}", trade.getId(), getReceiverNodeAddress());
+ }
+
+ @Override
+ protected void setStateArrived() {
+ log.info("Seller's PayoutTxPublishedMessage arrived: tradeId={} at arbitrator {}", trade.getId(), getReceiverNodeAddress());
+ }
+
+ @Override
+ protected void setStateStoredInMailbox() {
+ log.info("Seller's PayoutTxPublishedMessage stored in mailbox: tradeId={} at arbitrator {}", trade.getId(), getReceiverNodeAddress());
+ }
+
+ @Override
+ protected void setStateFault() {
+ log.error("Seller's PayoutTxPublishedMessage failed: tradeId={} at arbitrator {}", trade.getId(), getReceiverNodeAddress());
+ }
+}
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/SellerPreparesPaymentReceivedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/SellerPreparesPaymentReceivedMessage.java
index e3e3e21e83..7ebede4fcf 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/SellerPreparesPaymentReceivedMessage.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/SellerPreparesPaymentReceivedMessage.java
@@ -40,7 +40,7 @@ public class SellerPreparesPaymentReceivedMessage extends TradeTask {
// verify, sign, and publish payout tx if given. otherwise create payout tx
if (trade.getBuyer().getPayoutTxHex() != null) {
log.info("Seller verifying, signing, and publishing payout tx");
- trade.verifySignAndPublishPayoutTx(trade.getBuyer().getPayoutTxHex());
+ trade.verifyPayoutTx(trade.getBuyer().getPayoutTxHex(), true, true);
} else {
log.info("Seller creating unsigned payout tx");
MoneroTxWallet payoutTx = trade.createPayoutTx();
diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/SendMailboxMessageTask.java b/core/src/main/java/bisq/core/trade/protocol/tasks/SendMailboxMessageTask.java
index 7c73999b31..4eb7d7c568 100644
--- a/core/src/main/java/bisq/core/trade/protocol/tasks/SendMailboxMessageTask.java
+++ b/core/src/main/java/bisq/core/trade/protocol/tasks/SendMailboxMessageTask.java
@@ -23,7 +23,7 @@ import bisq.core.trade.messages.TradeMessage;
import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.SendMailboxMessageListener;
-
+import bisq.common.crypto.PubKeyRing;
import bisq.common.taskrunner.TaskRunner;
import lombok.extern.slf4j.Slf4j;
@@ -34,7 +34,15 @@ public abstract class SendMailboxMessageTask extends TradeTask {
super(taskHandler, trade);
}
- protected abstract TradeMailboxMessage getTradeMailboxMessage(String id);
+ protected NodeAddress getReceiverNodeAddress() {
+ return trade.getTradingPeerNodeAddress();
+ }
+
+ protected PubKeyRing getReceiverPubKeyRing() {
+ return trade.getTradingPeer().getPubKeyRing();
+ }
+
+ protected abstract TradeMailboxMessage getTradeMailboxMessage(String tradeId);
protected abstract void setStateSent();
@@ -51,34 +59,31 @@ public abstract class SendMailboxMessageTask extends TradeTask {
String id = processModel.getOfferId();
TradeMailboxMessage message = getTradeMailboxMessage(id);
setStateSent();
- NodeAddress peersNodeAddress = trade.getTradingPeerNodeAddress();
+ NodeAddress peersNodeAddress = getReceiverNodeAddress();
log.info("Send {} to peer {}. tradeId={}, uid={}",
message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid());
processModel.getP2PService().getMailboxMessageService().sendEncryptedMailboxMessage(
peersNodeAddress,
- trade.getTradingPeer().getPubKeyRing(),
+ getReceiverPubKeyRing(),
message,
new SendMailboxMessageListener() {
@Override
public void onArrived() {
- log.info("{} arrived at peer {}. tradeId={}, uid={}",
- message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid());
+ log.info("{} arrived at peer {}. tradeId={}, uid={}", message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid());
setStateArrived();
complete();
}
@Override
public void onStoredInMailbox() {
- log.info("{} stored in mailbox for peer {}. tradeId={}, uid={}",
- message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid());
+ log.info("{} stored in mailbox for peer {}. tradeId={}, uid={}", message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid());
SendMailboxMessageTask.this.onStoredInMailbox();
}
@Override
public void onFault(String errorMessage) {
- log.error("{} failed: Peer {}. tradeId={}, uid={}, errorMessage={}",
- message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid(), errorMessage);
+ log.error("{} failed: Peer {}. tradeId={}, uid={}, errorMessage={}", message.getClass().getSimpleName(), peersNodeAddress, message.getTradeId(), message.getUid(), errorMessage);
SendMailboxMessageTask.this.onFault(errorMessage, message);
}
}
diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto
index e1426d36a5..4f99ce0782 100644
--- a/proto/src/main/proto/pb.proto
+++ b/proto/src/main/proto/pb.proto
@@ -79,10 +79,12 @@ message NetworkEnvelope {
PaymentSentMessage payment_sent_message = 1011;
PaymentReceivedMessage payment_received_message = 1012;
PayoutTxPublishedMessage payout_tx_published_message = 1013;
- UpdateMultisigRequest update_multisig_request = 1014;
- UpdateMultisigResponse update_multisig_response = 1015;
ArbitratorPayoutTxRequest arbitrator_payout_tx_request = 1016;
ArbitratorPayoutTxResponse arbitrator_payout_tx_response = 1017;
+
+ // TODO: delete these
+ UpdateMultisigRequest update_multisig_request = 1018;
+ UpdateMultisigResponse update_multisig_response = 1019;
}
}
@@ -459,8 +461,8 @@ message PayoutTxPublishedMessage {
string trade_id = 1;
NodeAddress sender_node_address = 2;
string uid = 3;
- SignedWitness signed_witness = 4; // Added in v1.4.0
- string payout_tx_hex = 5;
+ SignedWitness signed_witness = 4;
+ string signed_payout_tx_hex = 5;
}
message ArbitratorPayoutTxRequest {