fix peer deleting unique payment sent, received, dispute messages

This commit is contained in:
woodser 2023-11-20 09:08:30 -05:00
parent 546e260cd0
commit fc396f7478
17 changed files with 103 additions and 110 deletions

View file

@ -365,7 +365,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
UUID.randomUUID().toString(), UUID.randomUUID().toString(),
getSupportType(), getSupportType(),
updatedMultisigHex, updatedMultisigHex,
trade.getProcessModel().getPaymentSentMessage()); trade.getArbitrator().getPaymentSentMessage());
log.info("Send {} to peer {}. tradeId={}, openNewDisputeMessage.uid={}, " + log.info("Send {} to peer {}. tradeId={}, openNewDisputeMessage.uid={}, " +
"chatMessage.uid={}", "chatMessage.uid={}",
disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress, disputeOpenedMessage.getClass().getSimpleName(), agentNodeAddress,
@ -647,7 +647,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
UUID.randomUUID().toString(), UUID.randomUUID().toString(),
getSupportType(), getSupportType(),
updatedMultisigHex, updatedMultisigHex,
trade.getProcessModel().getPaymentSentMessage()); trade.getArbitrator().getPaymentSentMessage());
log.info("Send {} to peer {}. tradeId={}, peerOpenedDisputeMessage.uid={}, chatMessage.uid={}", log.info("Send {} to peer {}. tradeId={}, peerOpenedDisputeMessage.uid={}, chatMessage.uid={}",
peerOpenedDisputeMessage.getClass().getSimpleName(), peersNodeAddress, peerOpenedDisputeMessage.getClass().getSimpleName(), peersNodeAddress,

View file

@ -224,13 +224,13 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
else DisputeSummaryVerification.verifySignature(summaryText, arbitratorManager); // verify using registered arbitrator (will fail is arbitrator is unregistered) else DisputeSummaryVerification.verifySignature(summaryText, arbitratorManager); // verify using registered arbitrator (will fail is arbitrator is unregistered)
// save dispute closed message for reprocessing // save dispute closed message for reprocessing
trade.getProcessModel().setDisputeClosedMessage(disputeClosedMessage); trade.getArbitrator().setDisputeClosedMessage(disputeClosedMessage);
requestPersistence(); requestPersistence();
// verify arbitrator does not receive DisputeClosedMessage // verify arbitrator does not receive DisputeClosedMessage
if (keyRing.getPubKeyRing().equals(dispute.getAgentPubKeyRing())) { if (keyRing.getPubKeyRing().equals(dispute.getAgentPubKeyRing())) {
log.error("Arbitrator received disputeResultMessage. That should never happen."); log.error("Arbitrator received disputeResultMessage. That should never happen.");
trade.getProcessModel().setDisputeClosedMessage(null); // don't reprocess trade.getArbitrator().setDisputeClosedMessage(null); // don't reprocess
return; return;
} }
@ -303,14 +303,14 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
// nack bad message and do not reprocess // nack bad message and do not reprocess
if (e instanceof IllegalArgumentException) { if (e instanceof IllegalArgumentException) {
trade.getProcessModel().setPaymentReceivedMessage(null); // message is processed trade.getArbitrator().setDisputeClosedMessage(null); // message is processed
sendAckMessage(chatMessage, dispute.getAgentPubKeyRing(), false, e.getMessage()); sendAckMessage(chatMessage, dispute.getAgentPubKeyRing(), false, e.getMessage());
requestPersistence(); requestPersistence();
throw e; throw e;
} }
// schedule to reprocess message unless deleted // schedule to reprocess message unless deleted
if (trade.getProcessModel().getDisputeClosedMessage() != null) { if (trade.getArbitrator().getDisputeClosedMessage() != null) {
if (!reprocessDisputeClosedMessageCounts.containsKey(trade.getId())) reprocessDisputeClosedMessageCounts.put(trade.getId(), 0); if (!reprocessDisputeClosedMessageCounts.containsKey(trade.getId())) reprocessDisputeClosedMessageCounts.put(trade.getId(), 0);
UserThread.runAfter(() -> { UserThread.runAfter(() -> {
reprocessDisputeClosedMessageCounts.put(trade.getId(), reprocessDisputeClosedMessageCounts.get(trade.getId()) + 1); // increment reprocess count reprocessDisputeClosedMessageCounts.put(trade.getId(), reprocessDisputeClosedMessageCounts.get(trade.getId()) + 1); // increment reprocess count
@ -325,12 +325,12 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
synchronized (trade) { synchronized (trade) {
// skip if no need to reprocess // skip if no need to reprocess
if (trade.isArbitrator() || trade.getProcessModel().getDisputeClosedMessage() == null || trade.getProcessModel().getDisputeClosedMessage().getUnsignedPayoutTxHex() == null || trade.getDisputeState().ordinal() >= Trade.DisputeState.DISPUTE_CLOSED.ordinal()) { if (trade.isArbitrator() || trade.getArbitrator().getDisputeClosedMessage() == null || trade.getArbitrator().getDisputeClosedMessage().getUnsignedPayoutTxHex() == null || trade.getDisputeState().ordinal() >= Trade.DisputeState.DISPUTE_CLOSED.ordinal()) {
return; return;
} }
log.warn("Reprocessing dispute closed message for {} {}", trade.getClass().getSimpleName(), trade.getId()); log.warn("Reprocessing dispute closed message for {} {}", trade.getClass().getSimpleName(), trade.getId());
new Thread(() -> handleDisputeClosedMessage(trade.getProcessModel().getDisputeClosedMessage(), reprocessOnError)).start(); new Thread(() -> handleDisputeClosedMessage(trade.getArbitrator().getDisputeClosedMessage(), reprocessOnError)).start();
} }
} }
@ -343,7 +343,7 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
Dispute dispute = disputeOptional.get(); Dispute dispute = disputeOptional.get();
Contract contract = dispute.getContract(); Contract contract = dispute.getContract();
DisputeResult disputeResult = dispute.getDisputeResultProperty().get(); DisputeResult disputeResult = dispute.getDisputeResultProperty().get();
String unsignedPayoutTxHex = trade.getProcessModel().getDisputeClosedMessage().getUnsignedPayoutTxHex(); String unsignedPayoutTxHex = trade.getArbitrator().getDisputeClosedMessage().getUnsignedPayoutTxHex();
// Offer offer = checkNotNull(trade.getOffer(), "offer must not be null"); // Offer offer = checkNotNull(trade.getOffer(), "offer must not be null");
// BigInteger sellerDepositAmount = multisigWallet.getTx(trade instanceof MakerTrade ? trade.getMaker().getDepositTxHash() : trade.getTaker().getDepositTxHash()).getIncomingAmount(); // TODO (woodser): use contract instead of trade to get deposit tx ids when contract has deposit tx ids // BigInteger sellerDepositAmount = multisigWallet.getTx(trade instanceof MakerTrade ? trade.getMaker().getDepositTxHash() : trade.getTaker().getDepositTxHash()).getIncomingAmount(); // TODO (woodser): use contract instead of trade to get deposit tx ids when contract has deposit tx ids
@ -420,10 +420,10 @@ public final class ArbitrationManager extends DisputeManager<ArbitrationDisputeL
// determine if we already signed dispute payout tx // determine if we already signed dispute payout tx
// TODO: better way, such as by saving signed dispute payout tx hex in designated field instead of shared payoutTxHex field? // TODO: better way, such as by saving signed dispute payout tx hex in designated field instead of shared payoutTxHex field?
Set<String> nonSignedDisputePayoutTxHexes = new HashSet<String>(); Set<String> nonSignedDisputePayoutTxHexes = new HashSet<String>();
if (trade.getProcessModel().getPaymentSentMessage() != null) nonSignedDisputePayoutTxHexes.add(trade.getProcessModel().getPaymentSentMessage().getPayoutTxHex()); if (trade.getTradePeer().getPaymentSentMessage() != null) nonSignedDisputePayoutTxHexes.add(trade.getTradePeer().getPaymentSentMessage().getPayoutTxHex());
if (trade.getProcessModel().getPaymentReceivedMessage() != null) { if (trade.getTradePeer().getPaymentReceivedMessage() != null) {
nonSignedDisputePayoutTxHexes.add(trade.getProcessModel().getPaymentReceivedMessage().getUnsignedPayoutTxHex()); nonSignedDisputePayoutTxHexes.add(trade.getTradePeer().getPaymentReceivedMessage().getUnsignedPayoutTxHex());
nonSignedDisputePayoutTxHexes.add(trade.getProcessModel().getPaymentReceivedMessage().getSignedPayoutTxHex()); nonSignedDisputePayoutTxHexes.add(trade.getTradePeer().getPaymentReceivedMessage().getSignedPayoutTxHex());
} }
boolean signed = trade.getPayoutTxHex() != null && !nonSignedDisputePayoutTxHexes.contains(trade.getPayoutTxHex()); boolean signed = trade.getPayoutTxHex() != null && !nonSignedDisputePayoutTxHexes.contains(trade.getPayoutTxHex());

View file

@ -1859,7 +1859,7 @@ public abstract class Trade implements Tradable, Model {
if (isDepositsUnlocked() && !isPayoutPublished()) wallet.rescanSpent(); if (isDepositsUnlocked() && !isPayoutPublished()) wallet.rescanSpent();
// get txs from trade wallet // get txs from trade wallet
boolean payoutExpected = isPaymentReceived() || processModel.getPaymentReceivedMessage() != null || disputeState.ordinal() > DisputeState.ARBITRATOR_SENT_DISPUTE_CLOSED_MSG.ordinal() || processModel.getDisputeClosedMessage() != null; boolean payoutExpected = isPaymentReceived() || getSeller().getPaymentReceivedMessage() != null || disputeState.ordinal() >= DisputeState.ARBITRATOR_SENT_DISPUTE_CLOSED_MSG.ordinal() || getArbitrator().getDisputeClosedMessage() != null;
boolean checkPool = !isDepositsConfirmed() || (!isPayoutConfirmed() && payoutExpected); boolean checkPool = !isDepositsConfirmed() || (!isPayoutConfirmed() && payoutExpected);
MoneroTxQuery query = new MoneroTxQuery().setIncludeOutputs(true); MoneroTxQuery query = new MoneroTxQuery().setIncludeOutputs(true);
if (!checkPool) query.setInTxPool(false); // avoid pool check if possible if (!checkPool) query.setInTxPool(false); // avoid pool check if possible

View file

@ -18,7 +18,6 @@
package haveno.core.trade.protocol; package haveno.core.trade.protocol;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import haveno.common.app.Version;
import haveno.common.crypto.KeyRing; import haveno.common.crypto.KeyRing;
import haveno.common.crypto.PubKeyRing; import haveno.common.crypto.PubKeyRing;
import haveno.common.proto.ProtoUtil; import haveno.common.proto.ProtoUtil;
@ -34,12 +33,9 @@ import haveno.core.payment.payload.PaymentAccountPayload;
import haveno.core.proto.CoreProtoResolver; import haveno.core.proto.CoreProtoResolver;
import haveno.core.support.dispute.arbitration.arbitrator.ArbitratorManager; import haveno.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
import haveno.core.support.dispute.mediation.mediator.MediatorManager; import haveno.core.support.dispute.mediation.mediator.MediatorManager;
import haveno.core.support.dispute.messages.DisputeClosedMessage;
import haveno.core.support.dispute.refund.refundagent.RefundAgentManager; import haveno.core.support.dispute.refund.refundagent.RefundAgentManager;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.TradeManager; import haveno.core.trade.TradeManager;
import haveno.core.trade.messages.PaymentReceivedMessage;
import haveno.core.trade.messages.PaymentSentMessage;
import haveno.core.trade.messages.TradeMessage; import haveno.core.trade.messages.TradeMessage;
import haveno.core.trade.statistics.ReferralIdService; import haveno.core.trade.statistics.ReferralIdService;
import haveno.core.trade.statistics.TradeStatisticsManager; import haveno.core.trade.statistics.TradeStatisticsManager;
@ -139,18 +135,6 @@ public class ProcessModel implements Model, PersistablePayload {
@Getter @Getter
@Setter @Setter
private String multisigAddress; private String multisigAddress;
@Nullable
@Setter
@Getter
private PaymentSentMessage paymentSentMessage;
@Nullable
@Setter
@Getter
private PaymentReceivedMessage paymentReceivedMessage;
@Nullable
@Setter
@Getter
private DisputeClosedMessage disputeClosedMessage;
// We want to indicate the user the state of the message delivery of the // We want to indicate the user the state of the message delivery of the
// PaymentSentMessage. As well we do an automatic re-send in case it was not ACKed yet. // PaymentSentMessage. As well we do an automatic re-send in case it was not ACKed yet.
@ -204,9 +188,6 @@ public class ProcessModel implements Model, PersistablePayload {
Optional.ofNullable(mediatedPayoutTxSignature).ifPresent(e -> builder.setMediatedPayoutTxSignature(ByteString.copyFrom(e))); Optional.ofNullable(mediatedPayoutTxSignature).ifPresent(e -> builder.setMediatedPayoutTxSignature(ByteString.copyFrom(e)));
Optional.ofNullable(makerSignature).ifPresent(e -> builder.setMakerSignature(ByteString.copyFrom(e))); Optional.ofNullable(makerSignature).ifPresent(e -> builder.setMakerSignature(ByteString.copyFrom(e)));
Optional.ofNullable(multisigAddress).ifPresent(e -> builder.setMultisigAddress(multisigAddress)); Optional.ofNullable(multisigAddress).ifPresent(e -> builder.setMultisigAddress(multisigAddress));
Optional.ofNullable(paymentSentMessage).ifPresent(e -> builder.setPaymentSentMessage(paymentSentMessage.toProtoNetworkEnvelope().getPaymentSentMessage()));
Optional.ofNullable(paymentReceivedMessage).ifPresent(e -> builder.setPaymentReceivedMessage(paymentReceivedMessage.toProtoNetworkEnvelope().getPaymentReceivedMessage()));
Optional.ofNullable(disputeClosedMessage).ifPresent(e -> builder.setDisputeClosedMessage(disputeClosedMessage.toProtoNetworkEnvelope().getDisputeClosedMessage()));
return builder.build(); return builder.build();
} }
@ -232,9 +213,6 @@ public class ProcessModel implements Model, PersistablePayload {
MessageState paymentSentMessageState = ProtoUtil.enumFromProto(MessageState.class, paymentSentMessageStateString); MessageState paymentSentMessageState = ProtoUtil.enumFromProto(MessageState.class, paymentSentMessageStateString);
processModel.setPaymentSentMessageState(paymentSentMessageState); processModel.setPaymentSentMessageState(paymentSentMessageState);
processModel.setPaymentSentMessage(proto.hasPaymentSentMessage() ? PaymentSentMessage.fromProto(proto.getPaymentSentMessage(), Version.getP2PMessageVersion()) : null);
processModel.setPaymentReceivedMessage(proto.hasPaymentReceivedMessage() ? PaymentReceivedMessage.fromProto(proto.getPaymentReceivedMessage(), Version.getP2PMessageVersion()) : null);
processModel.setDisputeClosedMessage(proto.hasDisputeClosedMessage() ? DisputeClosedMessage.fromProto(proto.getDisputeClosedMessage(), Version.getP2PMessageVersion()) : null);
return processModel; return processModel;
} }

View file

@ -19,12 +19,16 @@ package haveno.core.trade.protocol;
import com.google.protobuf.ByteString; import com.google.protobuf.ByteString;
import com.google.protobuf.Message; import com.google.protobuf.Message;
import haveno.common.app.Version;
import haveno.common.crypto.PubKeyRing; import haveno.common.crypto.PubKeyRing;
import haveno.common.proto.ProtoUtil; import haveno.common.proto.ProtoUtil;
import haveno.common.proto.persistable.PersistablePayload; import haveno.common.proto.persistable.PersistablePayload;
import haveno.core.account.witness.AccountAgeWitness; import haveno.core.account.witness.AccountAgeWitness;
import haveno.core.payment.payload.PaymentAccountPayload; import haveno.core.payment.payload.PaymentAccountPayload;
import haveno.core.proto.CoreProtoResolver; import haveno.core.proto.CoreProtoResolver;
import haveno.core.support.dispute.messages.DisputeClosedMessage;
import haveno.core.trade.messages.PaymentReceivedMessage;
import haveno.core.trade.messages.PaymentSentMessage;
import haveno.network.p2p.NodeAddress; import haveno.network.p2p.NodeAddress;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
@ -79,6 +83,19 @@ public final class TradePeer implements PersistablePayload {
private String contractAsJson; private String contractAsJson;
@Nullable @Nullable
private byte[] contractSignature; private byte[] contractSignature;
@Nullable
@Setter
@Getter
private PaymentSentMessage paymentSentMessage;
@Nullable
@Setter
@Getter
private PaymentReceivedMessage paymentReceivedMessage;
@Nullable
@Setter
@Getter
private DisputeClosedMessage disputeClosedMessage;
// added in v 0.6 // added in v 0.6
@Nullable @Nullable
@ -164,6 +181,9 @@ public final class TradePeer implements PersistablePayload {
Optional.ofNullable(accountAgeWitnessSignature).ifPresent(e -> builder.setAccountAgeWitnessSignature(ByteString.copyFrom(e))); Optional.ofNullable(accountAgeWitnessSignature).ifPresent(e -> builder.setAccountAgeWitnessSignature(ByteString.copyFrom(e)));
Optional.ofNullable(accountAgeWitness).ifPresent(e -> builder.setAccountAgeWitness(accountAgeWitness.toProtoAccountAgeWitness())); Optional.ofNullable(accountAgeWitness).ifPresent(e -> builder.setAccountAgeWitness(accountAgeWitness.toProtoAccountAgeWitness()));
Optional.ofNullable(mediatedPayoutTxSignature).ifPresent(e -> builder.setMediatedPayoutTxSignature(ByteString.copyFrom(e))); Optional.ofNullable(mediatedPayoutTxSignature).ifPresent(e -> builder.setMediatedPayoutTxSignature(ByteString.copyFrom(e)));
Optional.ofNullable(paymentSentMessage).ifPresent(e -> builder.setPaymentSentMessage(paymentSentMessage.toProtoNetworkEnvelope().getPaymentSentMessage()));
Optional.ofNullable(paymentReceivedMessage).ifPresent(e -> builder.setPaymentReceivedMessage(paymentReceivedMessage.toProtoNetworkEnvelope().getPaymentReceivedMessage()));
Optional.ofNullable(disputeClosedMessage).ifPresent(e -> builder.setDisputeClosedMessage(disputeClosedMessage.toProtoNetworkEnvelope().getDisputeClosedMessage()));
Optional.ofNullable(reserveTxHash).ifPresent(e -> builder.setReserveTxHash(reserveTxHash)); Optional.ofNullable(reserveTxHash).ifPresent(e -> builder.setReserveTxHash(reserveTxHash));
Optional.ofNullable(reserveTxHex).ifPresent(e -> builder.setReserveTxHex(reserveTxHex)); Optional.ofNullable(reserveTxHex).ifPresent(e -> builder.setReserveTxHex(reserveTxHex));
Optional.ofNullable(reserveTxKey).ifPresent(e -> builder.setReserveTxKey(reserveTxKey)); Optional.ofNullable(reserveTxKey).ifPresent(e -> builder.setReserveTxKey(reserveTxKey));
@ -207,6 +227,9 @@ public final class TradePeer implements PersistablePayload {
tradePeer.setAccountAgeWitness(protoAccountAgeWitness.getHash().isEmpty() ? null : AccountAgeWitness.fromProto(protoAccountAgeWitness)); tradePeer.setAccountAgeWitness(protoAccountAgeWitness.getHash().isEmpty() ? null : AccountAgeWitness.fromProto(protoAccountAgeWitness));
tradePeer.setCurrentDate(proto.getCurrentDate()); tradePeer.setCurrentDate(proto.getCurrentDate());
tradePeer.setMediatedPayoutTxSignature(ProtoUtil.byteArrayOrNullFromProto(proto.getMediatedPayoutTxSignature())); tradePeer.setMediatedPayoutTxSignature(ProtoUtil.byteArrayOrNullFromProto(proto.getMediatedPayoutTxSignature()));
tradePeer.setPaymentSentMessage(proto.hasPaymentSentMessage() ? PaymentSentMessage.fromProto(proto.getPaymentSentMessage(), Version.getP2PMessageVersion()) : null);
tradePeer.setPaymentReceivedMessage(proto.hasPaymentReceivedMessage() ? PaymentReceivedMessage.fromProto(proto.getPaymentReceivedMessage(), Version.getP2PMessageVersion()) : null);
tradePeer.setDisputeClosedMessage(proto.hasDisputeClosedMessage() ? DisputeClosedMessage.fromProto(proto.getDisputeClosedMessage(), Version.getP2PMessageVersion()) : null);
tradePeer.setReserveTxHash(ProtoUtil.stringOrNullFromProto(proto.getReserveTxHash())); tradePeer.setReserveTxHash(ProtoUtil.stringOrNullFromProto(proto.getReserveTxHash()));
tradePeer.setReserveTxHex(ProtoUtil.stringOrNullFromProto(proto.getReserveTxHex())); tradePeer.setReserveTxHex(ProtoUtil.stringOrNullFromProto(proto.getReserveTxHex()));
tradePeer.setReserveTxKey(ProtoUtil.stringOrNullFromProto(proto.getReserveTxKey())); tradePeer.setReserveTxKey(ProtoUtil.stringOrNullFromProto(proto.getReserveTxKey()));

View file

@ -274,12 +274,12 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
synchronized (trade) { synchronized (trade) {
// skip if no need to reprocess // skip if no need to reprocess
if (trade.isSeller() || trade.getProcessModel().getPaymentReceivedMessage() == null || trade.getState().ordinal() >= Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal()) { if (trade.isSeller() || trade.getSeller().getPaymentReceivedMessage() == null || trade.getState().ordinal() >= Trade.State.SELLER_SENT_PAYMENT_RECEIVED_MSG.ordinal()) {
return; return;
} }
log.warn("Reprocessing payment received message for {} {}", trade.getClass().getSimpleName(), trade.getId()); log.warn("Reprocessing payment received message for {} {}", trade.getClass().getSimpleName(), trade.getId());
new Thread(() -> handle(trade.getProcessModel().getPaymentReceivedMessage(), trade.getProcessModel().getPaymentReceivedMessage().getSenderNodeAddress(), reprocessOnError)).start(); new Thread(() -> handle(trade.getSeller().getPaymentReceivedMessage(), trade.getSeller().getPaymentReceivedMessage().getSenderNodeAddress(), reprocessOnError)).start();
} }
} }
@ -552,7 +552,7 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
processModel.getTradeManager().requestPersistence(); processModel.getTradeManager().requestPersistence();
// schedule to reprocess message unless deleted // schedule to reprocess message unless deleted
if (trade.getProcessModel().getPaymentReceivedMessage() != null) { if (trade.getSeller().getPaymentReceivedMessage() != null) {
UserThread.runAfter(() -> { UserThread.runAfter(() -> {
reprocessPaymentReceivedMessageCount++; reprocessPaymentReceivedMessageCount++;
maybeReprocessPaymentReceivedMessage(reprocessOnError); maybeReprocessPaymentReceivedMessage(reprocessOnError);

View file

@ -45,9 +45,9 @@ public class BuyerPreparePaymentSentMessage extends TradeTask {
try { try {
runInterceptHook(); runInterceptHook();
// skip if already created // skip if payout tx already created
if (processModel.getPaymentSentMessage() != null) { if (trade.getPayoutTxHex() != null) {
log.warn("Skipping preparation of payment sent message since it's already created for {} {}", trade.getClass().getSimpleName(), trade.getId()); log.warn("Skipping preparation of payment sent message because payout tx is already created for {} {}", trade.getClass().getSimpleName(), trade.getId());
complete(); complete();
return; return;
} }

View file

@ -28,6 +28,7 @@ import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.messages.PaymentSentMessage; import haveno.core.trade.messages.PaymentSentMessage;
import haveno.core.trade.messages.TradeMailboxMessage; import haveno.core.trade.messages.TradeMailboxMessage;
import haveno.core.trade.protocol.TradePeer;
import haveno.core.util.JsonUtil; import haveno.core.util.JsonUtil;
import haveno.network.p2p.NodeAddress; import haveno.network.p2p.NodeAddress;
import javafx.beans.value.ChangeListener; import javafx.beans.value.ChangeListener;
@ -56,9 +57,17 @@ public abstract class BuyerSendPaymentSentMessage extends SendMailboxMessageTask
super(taskHandler, trade); super(taskHandler, trade);
} }
protected abstract NodeAddress getReceiverNodeAddress(); protected abstract TradePeer getReceiver();
protected abstract PubKeyRing getReceiverPubKeyRing(); @Override
protected NodeAddress getReceiverNodeAddress() {
return getReceiver().getNodeAddress();
}
@Override
protected PubKeyRing getReceiverPubKeyRing() {
return getReceiver().getPubKeyRing();
}
@Override @Override
protected void run() { protected void run() {
@ -72,7 +81,7 @@ public abstract class BuyerSendPaymentSentMessage extends SendMailboxMessageTask
@Override @Override
protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) { protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) {
if (processModel.getPaymentSentMessage() == null) { if (getReceiver().getPaymentSentMessage() == null) {
// We do not use a real unique ID here as we want to be able to re-send the exact same message in case the // We do not use a real unique ID here as we want to be able to re-send the exact same message in case the
// peer does not respond with an ACK msg in a certain time interval. To avoid that we get dangling mailbox // peer does not respond with an ACK msg in a certain time interval. To avoid that we get dangling mailbox
@ -98,13 +107,13 @@ public abstract class BuyerSendPaymentSentMessage extends SendMailboxMessageTask
String messageAsJson = JsonUtil.objectToJson(message); String messageAsJson = JsonUtil.objectToJson(message);
byte[] sig = HavenoUtils.sign(processModel.getP2PService().getKeyRing(), messageAsJson); byte[] sig = HavenoUtils.sign(processModel.getP2PService().getKeyRing(), messageAsJson);
message.setBuyerSignature(sig); message.setBuyerSignature(sig);
processModel.setPaymentSentMessage(message); getReceiver().setPaymentSentMessage(message);
trade.requestPersistence(); trade.requestPersistence();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException (e); throw new RuntimeException (e);
} }
} }
return processModel.getPaymentSentMessage(); return getReceiver().getPaymentSentMessage();
} }
@Override @Override

View file

@ -17,10 +17,9 @@
package haveno.core.trade.protocol.tasks; package haveno.core.trade.protocol.tasks;
import haveno.common.crypto.PubKeyRing;
import haveno.common.taskrunner.TaskRunner; import haveno.common.taskrunner.TaskRunner;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.network.p2p.NodeAddress; import haveno.core.trade.protocol.TradePeer;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -32,12 +31,9 @@ public class BuyerSendPaymentSentMessageToArbitrator extends BuyerSendPaymentSen
super(taskHandler, trade); super(taskHandler, trade);
} }
protected NodeAddress getReceiverNodeAddress() { @Override
return trade.getArbitrator().getNodeAddress(); protected TradePeer getReceiver() {
} return trade.getArbitrator();
protected PubKeyRing getReceiverPubKeyRing() {
return trade.getArbitrator().getPubKeyRing();
} }
@Override @Override

View file

@ -17,11 +17,10 @@
package haveno.core.trade.protocol.tasks; package haveno.core.trade.protocol.tasks;
import haveno.common.crypto.PubKeyRing;
import haveno.common.taskrunner.TaskRunner; import haveno.common.taskrunner.TaskRunner;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.messages.TradeMessage; import haveno.core.trade.messages.TradeMessage;
import haveno.network.p2p.NodeAddress; import haveno.core.trade.protocol.TradePeer;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -33,12 +32,9 @@ public class BuyerSendPaymentSentMessageToSeller extends BuyerSendPaymentSentMes
super(taskHandler, trade); super(taskHandler, trade);
} }
protected NodeAddress getReceiverNodeAddress() { @Override
return trade.getSeller().getNodeAddress(); protected TradePeer getReceiver() {
} return trade.getSeller();
protected PubKeyRing getReceiverPubKeyRing() {
return trade.getSeller().getPubKeyRing();
} }
// continue execution on fault so payment sent message is sent to arbitrator // continue execution on fault so payment sent message is sent to arbitrator

View file

@ -26,6 +26,7 @@ import haveno.core.trade.BuyerTrade;
import haveno.core.trade.HavenoUtils; import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.messages.PaymentReceivedMessage; import haveno.core.trade.messages.PaymentReceivedMessage;
import haveno.core.trade.messages.PaymentSentMessage;
import haveno.core.util.Validator; import haveno.core.util.Validator;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@ -65,13 +66,13 @@ public class ProcessPaymentReceivedMessage extends TradeTask {
} }
// save message for reprocessing // save message for reprocessing
processModel.setPaymentReceivedMessage(message); trade.getSeller().setPaymentReceivedMessage(message);
trade.requestPersistence();
// set state // set state
trade.getSeller().setUpdatedMultisigHex(message.getUpdatedMultisigHex()); trade.getSeller().setUpdatedMultisigHex(message.getUpdatedMultisigHex());
trade.getBuyer().setUpdatedMultisigHex(message.getPaymentSentMessage().getUpdatedMultisigHex()); trade.getBuyer().setUpdatedMultisigHex(message.getPaymentSentMessage().getUpdatedMultisigHex());
trade.getBuyer().setAccountAgeWitness(message.getBuyerAccountAgeWitness()); trade.getBuyer().setAccountAgeWitness(message.getBuyerAccountAgeWitness());
trade.requestPersistence();
// close open disputes // close open disputes
if (trade.getDisputeState().ordinal() >= Trade.DisputeState.DISPUTE_REQUESTED.ordinal()) { if (trade.getDisputeState().ordinal() >= Trade.DisputeState.DISPUTE_REQUESTED.ordinal()) {
@ -100,7 +101,7 @@ public class ProcessPaymentReceivedMessage extends TradeTask {
// do not reprocess illegal argument // do not reprocess illegal argument
if (t instanceof IllegalArgumentException) { if (t instanceof IllegalArgumentException) {
processModel.setPaymentReceivedMessage(null); // do not reprocess trade.getSeller().setPaymentReceivedMessage(null); // do not reprocess
trade.requestPersistence(); trade.requestPersistence();
} }
@ -134,8 +135,9 @@ public class ProcessPaymentReceivedMessage extends TradeTask {
trade.verifyPayoutTx(message.getSignedPayoutTxHex(), false, true); trade.verifyPayoutTx(message.getSignedPayoutTxHex(), false, true);
} else { } else {
try { try {
if (trade.getProcessModel().getPaymentSentMessage() == null) throw new RuntimeException("Process model does not have payment sent message for " + trade.getClass().getSimpleName() + " " + trade.getId()); PaymentSentMessage paymentSentMessage = (trade.isArbitrator() ? trade.getBuyer() : trade.getArbitrator()).getPaymentSentMessage();
if (StringUtils.equals(trade.getPayoutTxHex(), trade.getProcessModel().getPaymentSentMessage().getPayoutTxHex())) { // unsigned if (paymentSentMessage == null) throw new RuntimeException("Process model does not have payment sent message for " + trade.getClass().getSimpleName() + " " + trade.getId());
if (StringUtils.equals(trade.getPayoutTxHex(), paymentSentMessage.getPayoutTxHex())) { // unsigned
log.info("{} {} verifying, signing, and publishing seller's payout tx", trade.getClass().getSimpleName(), trade.getId()); log.info("{} {} verifying, signing, and publishing seller's payout tx", trade.getClass().getSimpleName(), trade.getId());
trade.verifyPayoutTx(message.getUnsignedPayoutTxHex(), true, true); trade.verifyPayoutTx(message.getUnsignedPayoutTxHex(), true, true);
} else { } else {

View file

@ -48,7 +48,7 @@ public class ProcessPaymentSentMessage extends TradeTask {
trade.getBuyer().setNodeAddress(processModel.getTempTradePeerNodeAddress()); trade.getBuyer().setNodeAddress(processModel.getTempTradePeerNodeAddress());
// update state from message // update state from message
processModel.setPaymentSentMessage(message); trade.getBuyer().setPaymentSentMessage(message);
trade.setPayoutTxHex(message.getPayoutTxHex()); trade.setPayoutTxHex(message.getPayoutTxHex());
trade.getBuyer().setUpdatedMultisigHex(message.getUpdatedMultisigHex()); trade.getBuyer().setUpdatedMultisigHex(message.getUpdatedMultisigHex());
trade.getSeller().setAccountAgeWitness(message.getSellerAccountAgeWitness()); trade.getSeller().setAccountAgeWitness(message.getSellerAccountAgeWitness());

View file

@ -39,7 +39,7 @@ public class SellerPreparePaymentReceivedMessage extends TradeTask {
trade.checkDaemonConnection(); trade.checkDaemonConnection();
// handle first time preparation // handle first time preparation
if (processModel.getPaymentReceivedMessage() == null) { if (trade.getArbitrator().getPaymentReceivedMessage() == null) {
// import multisig hex // import multisig hex
trade.importMultisigHex(); trade.importMultisigHex();
@ -56,11 +56,11 @@ public class SellerPreparePaymentReceivedMessage extends TradeTask {
} else { } else {
createUnsignedPayoutTx(); createUnsignedPayoutTx();
} }
} else if (processModel.getPaymentReceivedMessage().getSignedPayoutTxHex() != null && !trade.isPayoutPublished()) { } else if (trade.getArbitrator().getPaymentReceivedMessage().getSignedPayoutTxHex() != null && !trade.isPayoutPublished()) {
// republish payout tx from previous message // republish payout tx from previous message
log.info("Seller re-verifying and publishing payout tx for trade {}", trade.getId()); log.info("Seller re-verifying and publishing payout tx for trade {}", trade.getId());
trade.verifyPayoutTx(processModel.getPaymentReceivedMessage().getSignedPayoutTxHex(), false, true); trade.verifyPayoutTx(trade.getArbitrator().getPaymentReceivedMessage().getSignedPayoutTxHex(), false, true);
} }
processModel.getTradeManager().requestPersistence(); processModel.getTradeManager().requestPersistence();

View file

@ -27,6 +27,7 @@ import haveno.core.trade.HavenoUtils;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.messages.PaymentReceivedMessage; import haveno.core.trade.messages.PaymentReceivedMessage;
import haveno.core.trade.messages.TradeMailboxMessage; import haveno.core.trade.messages.TradeMailboxMessage;
import haveno.core.trade.protocol.TradePeer;
import haveno.core.util.JsonUtil; import haveno.core.util.JsonUtil;
import haveno.network.p2p.NodeAddress; import haveno.network.p2p.NodeAddress;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
@ -37,16 +38,23 @@ import static com.google.common.base.Preconditions.checkNotNull;
@Slf4j @Slf4j
@EqualsAndHashCode(callSuper = true) @EqualsAndHashCode(callSuper = true)
public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessageTask { public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessageTask {
PaymentReceivedMessage message = null;
SignedWitness signedWitness = null; SignedWitness signedWitness = null;
public SellerSendPaymentReceivedMessage(TaskRunner<Trade> taskHandler, Trade trade) { public SellerSendPaymentReceivedMessage(TaskRunner<Trade> taskHandler, Trade trade) {
super(taskHandler, trade); super(taskHandler, trade);
} }
protected abstract TradePeer getReceiver();
@Override
protected NodeAddress getReceiverNodeAddress() {
return getReceiver().getNodeAddress();
}
protected abstract NodeAddress getReceiverNodeAddress(); @Override
protected PubKeyRing getReceiverPubKeyRing() {
protected abstract PubKeyRing getReceiverPubKeyRing(); return getReceiver().getPubKeyRing();
}
@Override @Override
protected void run() { protected void run() {
@ -61,7 +69,7 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag
@Override @Override
protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) { protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) {
checkNotNull(trade.getPayoutTxHex(), "Payout tx must not be null"); checkNotNull(trade.getPayoutTxHex(), "Payout tx must not be null");
if (message == null) { if (getReceiver().getPaymentReceivedMessage() == null) {
// sign account witness // sign account witness
AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService(); AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService();
@ -75,7 +83,7 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag
// messages where only the one which gets processed by the peer would be removed we use the same uid. All // messages where only the one which gets processed by the peer would be removed we use the same uid. All
// other data stays the same when we re-send the message at any time later. // other data stays the same when we re-send the message at any time later.
String deterministicId = HavenoUtils.getDeterministicId(trade, PaymentReceivedMessage.class, getReceiverNodeAddress()); String deterministicId = HavenoUtils.getDeterministicId(trade, PaymentReceivedMessage.class, getReceiverNodeAddress());
message = new PaymentReceivedMessage( PaymentReceivedMessage message = new PaymentReceivedMessage(
tradeId, tradeId,
processModel.getMyNodeAddress(), processModel.getMyNodeAddress(),
deterministicId, deterministicId,
@ -85,7 +93,7 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag
trade.getState().ordinal() >= Trade.State.SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG.ordinal(), // informs to expect payout trade.getState().ordinal() >= Trade.State.SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG.ordinal(), // informs to expect payout
trade.getTradePeer().getAccountAgeWitness(), trade.getTradePeer().getAccountAgeWitness(),
signedWitness, signedWitness,
processModel.getPaymentSentMessage() trade.getBuyer().getPaymentSentMessage()
); );
// sign message // sign message
@ -93,13 +101,13 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag
String messageAsJson = JsonUtil.objectToJson(message); String messageAsJson = JsonUtil.objectToJson(message);
byte[] sig = Sig.sign(processModel.getP2PService().getKeyRing().getSignatureKeyPair().getPrivate(), messageAsJson.getBytes(Charsets.UTF_8)); byte[] sig = Sig.sign(processModel.getP2PService().getKeyRing().getSignatureKeyPair().getPrivate(), messageAsJson.getBytes(Charsets.UTF_8));
message.setSellerSignature(sig); message.setSellerSignature(sig);
processModel.setPaymentReceivedMessage(message); getReceiver().setPaymentReceivedMessage(message);
trade.requestPersistence(); trade.requestPersistence();
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
return message; return getReceiver().getPaymentReceivedMessage();
} }
@Override @Override

View file

@ -17,10 +17,9 @@
package haveno.core.trade.protocol.tasks; package haveno.core.trade.protocol.tasks;
import haveno.common.crypto.PubKeyRing;
import haveno.common.taskrunner.TaskRunner; import haveno.common.taskrunner.TaskRunner;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.network.p2p.NodeAddress; import haveno.core.trade.protocol.TradePeer;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -32,11 +31,8 @@ public class SellerSendPaymentReceivedMessageToArbitrator extends SellerSendPaym
super(taskHandler, trade); super(taskHandler, trade);
} }
protected NodeAddress getReceiverNodeAddress() { @Override
return trade.getArbitrator().getNodeAddress(); protected TradePeer getReceiver() {
} return trade.getArbitrator();
protected PubKeyRing getReceiverPubKeyRing() {
return trade.getArbitrator().getPubKeyRing();
} }
} }

View file

@ -17,13 +17,10 @@
package haveno.core.trade.protocol.tasks; package haveno.core.trade.protocol.tasks;
import haveno.common.crypto.PubKeyRing;
import haveno.common.taskrunner.TaskRunner; import haveno.common.taskrunner.TaskRunner;
import haveno.core.trade.Trade; import haveno.core.trade.Trade;
import haveno.core.trade.messages.PaymentReceivedMessage;
import haveno.core.trade.messages.TradeMailboxMessage;
import haveno.core.trade.messages.TradeMessage; import haveno.core.trade.messages.TradeMessage;
import haveno.network.p2p.NodeAddress; import haveno.core.trade.protocol.TradePeer;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -35,21 +32,9 @@ public class SellerSendPaymentReceivedMessageToBuyer extends SellerSendPaymentRe
super(taskHandler, trade); super(taskHandler, trade);
} }
@Override @Override
protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) { protected TradePeer getReceiver() {
if (processModel.getPaymentReceivedMessage() == null) { return trade.getBuyer();
processModel.setPaymentReceivedMessage((PaymentReceivedMessage) super.getTradeMailboxMessage(tradeId)); // save payment received message for buyer
}
return processModel.getPaymentReceivedMessage();
}
protected NodeAddress getReceiverNodeAddress() {
return trade.getBuyer().getNodeAddress();
}
protected PubKeyRing getReceiverPubKeyRing() {
return trade.getBuyer().getPubKeyRing();
} }
// continue execution on fault so payment received message is sent to arbitrator // continue execution on fault so payment received message is sent to arbitrator

View file

@ -1546,9 +1546,6 @@ message ProcessModel {
TradePeer arbitrator = 11; TradePeer arbitrator = 11;
NodeAddress temp_trade_peer_node_address = 12; NodeAddress temp_trade_peer_node_address = 12;
string multisig_address = 13; string multisig_address = 13;
PaymentSentMessage payment_sent_message = 14;
PaymentReceivedMessage payment_received_message = 15;
DisputeClosedMessage dispute_closed_message = 16;
bytes mediated_payout_tx_signature = 17; // placeholder if mediation used in future bytes mediated_payout_tx_signature = 17; // placeholder if mediation used in future
int64 buyer_payout_amount_from_mediation = 18; int64 buyer_payout_amount_from_mediation = 18;
int64 seller_payout_amount_from_mediation = 19; int64 seller_payout_amount_from_mediation = 19;
@ -1572,6 +1569,9 @@ message TradePeer {
AccountAgeWitness account_age_witness = 20; AccountAgeWitness account_age_witness = 20;
int64 current_date = 21; int64 current_date = 21;
bytes mediated_payout_tx_signature = 22; bytes mediated_payout_tx_signature = 22;
PaymentSentMessage payment_sent_message = 23;
PaymentReceivedMessage payment_received_message = 24;
DisputeClosedMessage dispute_closed_message = 25;
string reserve_tx_hash = 1001; string reserve_tx_hash = 1001;
string reserve_tx_hex = 1002; string reserve_tx_hex = 1002;