close arbitrator trade by sending PayoutTxPublishedMessage
This commit is contained in:
parent
9975d7398b
commit
64925d0137
@ -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();
|
||||
}
|
||||
|
@ -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?
|
||||
|
@ -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) {
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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)));
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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.
|
||||
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Trade> 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());
|
||||
}
|
||||
}
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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<Trade> 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());
|
||||
}
|
||||
}
|
@ -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();
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user