mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-12-22 11:39:29 +00:00
decrypt payment info after confirmation for double spend protection
retrieve decryption key from arbitrator if peer fails to send
This commit is contained in:
parent
355a6146b6
commit
3f5fe671cd
38 changed files with 706 additions and 238 deletions
|
@ -46,7 +46,8 @@ import bisq.core.trade.messages.InitMultisigRequest;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.messages.MediatedPayoutTxPublishedMessage;
|
import bisq.core.trade.messages.MediatedPayoutTxPublishedMessage;
|
||||||
import bisq.core.trade.messages.MediatedPayoutTxSignatureMessage;
|
import bisq.core.trade.messages.MediatedPayoutTxSignatureMessage;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
import bisq.core.trade.messages.PaymentAccountKeyRequest;
|
||||||
|
import bisq.core.trade.messages.PaymentAccountKeyResponse;
|
||||||
import bisq.core.trade.messages.PaymentReceivedMessage;
|
import bisq.core.trade.messages.PaymentReceivedMessage;
|
||||||
import bisq.core.trade.messages.RefreshTradeStateRequest;
|
import bisq.core.trade.messages.RefreshTradeStateRequest;
|
||||||
import bisq.core.trade.messages.SignContractRequest;
|
import bisq.core.trade.messages.SignContractRequest;
|
||||||
|
@ -157,8 +158,10 @@ public class CoreNetworkProtoResolver extends CoreProtoResolver implements Netwo
|
||||||
return DepositRequest.fromProto(proto.getDepositRequest(), this, messageVersion);
|
return DepositRequest.fromProto(proto.getDepositRequest(), this, messageVersion);
|
||||||
case DEPOSIT_RESPONSE:
|
case DEPOSIT_RESPONSE:
|
||||||
return DepositResponse.fromProto(proto.getDepositResponse(), this, messageVersion);
|
return DepositResponse.fromProto(proto.getDepositResponse(), this, messageVersion);
|
||||||
case PAYMENT_ACCOUNT_PAYLOAD_REQUEST:
|
case PAYMENT_ACCOUNT_KEY_REQUEST:
|
||||||
return PaymentAccountPayloadRequest.fromProto(proto.getPaymentAccountPayloadRequest(), this, messageVersion);
|
return PaymentAccountKeyRequest.fromProto(proto.getPaymentAccountKeyRequest(), this, messageVersion);
|
||||||
|
case PAYMENT_ACCOUNT_KEY_RESPONSE:
|
||||||
|
return PaymentAccountKeyResponse.fromProto(proto.getPaymentAccountKeyResponse(), this, messageVersion);
|
||||||
case UPDATE_MULTISIG_REQUEST:
|
case UPDATE_MULTISIG_REQUEST:
|
||||||
return UpdateMultisigRequest.fromProto(proto.getUpdateMultisigRequest(), this, messageVersion);
|
return UpdateMultisigRequest.fromProto(proto.getUpdateMultisigRequest(), this, messageVersion);
|
||||||
case UPDATE_MULTISIG_RESPONSE:
|
case UPDATE_MULTISIG_RESPONSE:
|
||||||
|
|
|
@ -506,6 +506,8 @@ public final class Dispute implements NetworkPayload, PersistablePayload {
|
||||||
",\n mediatorsDisputeResult='" + mediatorsDisputeResult + '\'' +
|
",\n mediatorsDisputeResult='" + mediatorsDisputeResult + '\'' +
|
||||||
",\n delayedPayoutTxId='" + delayedPayoutTxId + '\'' +
|
",\n delayedPayoutTxId='" + delayedPayoutTxId + '\'' +
|
||||||
",\n donationAddressOfDelayedPayoutTx='" + donationAddressOfDelayedPayoutTx + '\'' +
|
",\n donationAddressOfDelayedPayoutTx='" + donationAddressOfDelayedPayoutTx + '\'' +
|
||||||
|
",\n makerPaymentAccountPayload='" + makerPaymentAccountPayload + '\'' +
|
||||||
|
",\n takerPaymentAccountPayload='" + takerPaymentAccountPayload + '\'' +
|
||||||
"\n}";
|
"\n}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,6 @@ import javafx.collections.ObservableList;
|
||||||
|
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -373,7 +372,7 @@ public abstract class DisputeManager<T extends DisputeList<Dispute>> extends Sup
|
||||||
addMediationResultMessage(dispute);
|
addMediationResultMessage(dispute);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
TradeDataValidation.validatePaymentAccountPayloads(dispute);
|
TradeDataValidation.validatePaymentAccountPayload(dispute);
|
||||||
TradeDataValidation.validateDonationAddress(dispute.getDonationAddressOfDelayedPayoutTx());
|
TradeDataValidation.validateDonationAddress(dispute.getDonationAddressOfDelayedPayoutTx());
|
||||||
//TradeDataValidation.testIfDisputeTriesReplay(dispute, disputeList.getList()); // TODO (woodser): disabled for xmr, needed?
|
//TradeDataValidation.testIfDisputeTriesReplay(dispute, disputeList.getList()); // TODO (woodser): disabled for xmr, needed?
|
||||||
TradeDataValidation.validateNodeAddress(dispute, dispute.getContract().getBuyerNodeAddress(), config);
|
TradeDataValidation.validateNodeAddress(dispute, dispute.getContract().getBuyerNodeAddress(), config);
|
||||||
|
|
|
@ -75,6 +75,7 @@ import lombok.Setter;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
@ -117,14 +118,14 @@ public abstract class Trade implements Tradable, Model {
|
||||||
// deposit requested
|
// deposit requested
|
||||||
SENT_PUBLISH_DEPOSIT_TX_REQUEST(Phase.DEPOSIT_REQUESTED),
|
SENT_PUBLISH_DEPOSIT_TX_REQUEST(Phase.DEPOSIT_REQUESTED),
|
||||||
SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST(Phase.DEPOSIT_REQUESTED),
|
SAW_ARRIVED_PUBLISH_DEPOSIT_TX_REQUEST(Phase.DEPOSIT_REQUESTED),
|
||||||
STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST(Phase.DEPOSIT_REQUESTED), //not a mailbox msg, not used...
|
STORED_IN_MAILBOX_PUBLISH_DEPOSIT_TX_REQUEST(Phase.DEPOSIT_REQUESTED), // not a mailbox msg, not used... remove
|
||||||
SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST(Phase.DEPOSIT_REQUESTED),
|
SEND_FAILED_PUBLISH_DEPOSIT_TX_REQUEST(Phase.DEPOSIT_REQUESTED),
|
||||||
|
|
||||||
// deposit published
|
// deposit published
|
||||||
DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN(Phase.DEPOSITS_PUBLISHED), // TODO: seeing in network usually happens after arbitrator publishes
|
DEPOSIT_TXS_SEEN_IN_BLOCKCHAIN(Phase.DEPOSITS_PUBLISHED), // TODO: seeing in network usually happens after arbitrator publishes
|
||||||
ARBITRATOR_PUBLISHED_DEPOSIT_TXS(Phase.DEPOSITS_PUBLISHED),
|
ARBITRATOR_PUBLISHED_DEPOSIT_TXS(Phase.DEPOSITS_PUBLISHED),
|
||||||
|
|
||||||
// deposit confirmed (TODO)
|
// deposit confirmed
|
||||||
DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN(Phase.DEPOSITS_CONFIRMED),
|
DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN(Phase.DEPOSITS_CONFIRMED),
|
||||||
|
|
||||||
// deposit unlocked
|
// deposit unlocked
|
||||||
|
@ -1114,11 +1115,11 @@ public abstract class Trade implements Tradable, Model {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public boolean isBuyer() {
|
public boolean isBuyer() {
|
||||||
return offer.getDirection() == OfferDirection.BUY;
|
return getBuyer() == getSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isSeller() {
|
public boolean isSeller() {
|
||||||
return offer.getDirection() == OfferDirection.SELL;
|
return getSeller() == getSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMaker() {
|
public boolean isMaker() {
|
||||||
|
|
|
@ -54,9 +54,9 @@ import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class TradeDataValidation {
|
public class TradeDataValidation {
|
||||||
|
|
||||||
public static void validatePaymentAccountPayloads(Dispute dispute) throws InvalidPaymentAccountPayloadException {
|
public static void validatePaymentAccountPayload(Dispute dispute) throws InvalidPaymentAccountPayloadException {
|
||||||
if (!Arrays.equals(dispute.getMakerPaymentAccountPayload().getHash(), dispute.getContract().getMakerPaymentAccountPayloadHash())) throw new InvalidPaymentAccountPayloadException(dispute, "Hash of maker's payment account payload does not match contract");
|
if (dispute.getSellerPaymentAccountPayload() == null) throw new InvalidPaymentAccountPayloadException(dispute, "Seller's payment account payload is null in dispute opened for trade " + dispute.getTradeId());
|
||||||
if (!Arrays.equals(dispute.getTakerPaymentAccountPayload().getHash(), dispute.getContract().getTakerPaymentAccountPayloadHash())) throw new InvalidPaymentAccountPayloadException(dispute, "Hash of taker's payment account payload does not match contract");
|
if (!Arrays.equals(dispute.getSellerPaymentAccountPayload().getHash(), dispute.getContract().getSellerPaymentAccountPayloadHash())) throw new InvalidPaymentAccountPayloadException(dispute, "Hash of maker's payment account payload does not match contract");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void validateDonationAddress(String addressAsString)
|
public static void validateDonationAddress(String addressAsString)
|
||||||
|
|
|
@ -41,7 +41,7 @@ import bisq.core.trade.messages.DepositRequest;
|
||||||
import bisq.core.trade.messages.DepositResponse;
|
import bisq.core.trade.messages.DepositResponse;
|
||||||
import bisq.core.trade.messages.InitMultisigRequest;
|
import bisq.core.trade.messages.InitMultisigRequest;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
import bisq.core.trade.messages.PaymentAccountKeyRequest;
|
||||||
import bisq.core.trade.messages.SignContractRequest;
|
import bisq.core.trade.messages.SignContractRequest;
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.core.trade.messages.UpdateMultisigRequest;
|
import bisq.core.trade.messages.UpdateMultisigRequest;
|
||||||
|
@ -253,8 +253,8 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
handleDepositRequest((DepositRequest) networkEnvelope, peer);
|
handleDepositRequest((DepositRequest) networkEnvelope, peer);
|
||||||
} else if (networkEnvelope instanceof DepositResponse) {
|
} else if (networkEnvelope instanceof DepositResponse) {
|
||||||
handleDepositResponse((DepositResponse) networkEnvelope, peer);
|
handleDepositResponse((DepositResponse) networkEnvelope, peer);
|
||||||
} else if (networkEnvelope instanceof PaymentAccountPayloadRequest) {
|
} else if (networkEnvelope instanceof PaymentAccountKeyRequest) {
|
||||||
handlePaymentAccountPayloadRequest((PaymentAccountPayloadRequest) networkEnvelope, peer);
|
handlePaymentAccountKeyRequest((PaymentAccountKeyRequest) networkEnvelope, peer);
|
||||||
} else if (networkEnvelope instanceof UpdateMultisigRequest) {
|
} else if (networkEnvelope instanceof UpdateMultisigRequest) {
|
||||||
handleUpdateMultisigRequest((UpdateMultisigRequest) networkEnvelope, peer);
|
handleUpdateMultisigRequest((UpdateMultisigRequest) networkEnvelope, peer);
|
||||||
}
|
}
|
||||||
|
@ -666,13 +666,13 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
((TraderProtocol) getTradeProtocol(trade)).handleDepositResponse(response, peer);
|
((TraderProtocol) getTradeProtocol(trade)).handleDepositResponse(response, peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress peer) {
|
private void handlePaymentAccountKeyRequest(PaymentAccountKeyRequest request, NodeAddress peer) {
|
||||||
log.info("Received PaymentAccountPayloadRequest from {} with tradeId {} and uid {}", peer, request.getTradeId(), request.getUid());
|
log.info("Received PaymentAccountKeyRequest from {} with tradeId {} and uid {}", peer, request.getTradeId(), request.getUid());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
Validator.nonEmptyStringOf(request.getTradeId());
|
Validator.nonEmptyStringOf(request.getTradeId());
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
log.warn("Invalid PaymentAccountPayloadRequest message " + request.toString());
|
log.warn("Invalid PaymentAccountKeyRequest message " + request.toString());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -682,7 +682,7 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Trade trade = tradeOptional.get();
|
Trade trade = tradeOptional.get();
|
||||||
((TraderProtocol) getTradeProtocol(trade)).handlePaymentAccountPayloadRequest(request, peer);
|
((ArbitratorProtocol) getTradeProtocol(trade)).handlePaymentAccountKeyRequest(request, peer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleUpdateMultisigRequest(UpdateMultisigRequest request, NodeAddress peer) {
|
private void handleUpdateMultisigRequest(UpdateMultisigRequest request, NodeAddress peer) {
|
||||||
|
|
|
@ -22,8 +22,10 @@ import bisq.core.proto.CoreProtoResolver;
|
||||||
import bisq.network.p2p.DirectMessage;
|
import bisq.network.p2p.DirectMessage;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import bisq.common.crypto.PubKeyRing;
|
import bisq.common.crypto.PubKeyRing;
|
||||||
|
import bisq.common.proto.ProtoUtil;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
|
@ -36,6 +38,8 @@ public final class DepositRequest extends TradeMessage implements DirectMessage
|
||||||
private final String contractSignature;
|
private final String contractSignature;
|
||||||
private final String depositTxHex;
|
private final String depositTxHex;
|
||||||
private final String depositTxKey;
|
private final String depositTxKey;
|
||||||
|
@Nullable
|
||||||
|
private final byte[] paymentAccountKey;
|
||||||
|
|
||||||
public DepositRequest(String tradeId,
|
public DepositRequest(String tradeId,
|
||||||
NodeAddress senderNodeAddress,
|
NodeAddress senderNodeAddress,
|
||||||
|
@ -45,7 +49,8 @@ public final class DepositRequest extends TradeMessage implements DirectMessage
|
||||||
long currentDate,
|
long currentDate,
|
||||||
String contractSignature,
|
String contractSignature,
|
||||||
String depositTxHex,
|
String depositTxHex,
|
||||||
String depositTxKey) {
|
String depositTxKey,
|
||||||
|
@Nullable byte[] paymentAccountKey) {
|
||||||
super(messageVersion, tradeId, uid);
|
super(messageVersion, tradeId, uid);
|
||||||
this.senderNodeAddress = senderNodeAddress;
|
this.senderNodeAddress = senderNodeAddress;
|
||||||
this.pubKeyRing = pubKeyRing;
|
this.pubKeyRing = pubKeyRing;
|
||||||
|
@ -53,6 +58,7 @@ public final class DepositRequest extends TradeMessage implements DirectMessage
|
||||||
this.contractSignature = contractSignature;
|
this.contractSignature = contractSignature;
|
||||||
this.depositTxHex = depositTxHex;
|
this.depositTxHex = depositTxHex;
|
||||||
this.depositTxKey = depositTxKey;
|
this.depositTxKey = depositTxKey;
|
||||||
|
this.paymentAccountKey = paymentAccountKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -71,6 +77,7 @@ public final class DepositRequest extends TradeMessage implements DirectMessage
|
||||||
.setDepositTxHex(depositTxHex)
|
.setDepositTxHex(depositTxHex)
|
||||||
.setDepositTxKey(depositTxKey);
|
.setDepositTxKey(depositTxKey);
|
||||||
builder.setCurrentDate(currentDate);
|
builder.setCurrentDate(currentDate);
|
||||||
|
Optional.ofNullable(paymentAccountKey).ifPresent(e -> builder.setPaymentAccountKey(ByteString.copyFrom(e)));
|
||||||
|
|
||||||
return getNetworkEnvelopeBuilder().setDepositRequest(builder).build();
|
return getNetworkEnvelopeBuilder().setDepositRequest(builder).build();
|
||||||
}
|
}
|
||||||
|
@ -86,7 +93,8 @@ public final class DepositRequest extends TradeMessage implements DirectMessage
|
||||||
proto.getCurrentDate(),
|
proto.getCurrentDate(),
|
||||||
proto.getContractSignature(),
|
proto.getContractSignature(),
|
||||||
proto.getDepositTxHex(),
|
proto.getDepositTxHex(),
|
||||||
proto.getDepositTxKey());
|
proto.getDepositTxKey(),
|
||||||
|
ProtoUtil.byteArrayOrNullFromProto(proto.getPaymentAccountKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -98,6 +106,7 @@ public final class DepositRequest extends TradeMessage implements DirectMessage
|
||||||
",\n contractSignature=" + contractSignature +
|
",\n contractSignature=" + contractSignature +
|
||||||
",\n depositTxHex='" + depositTxHex +
|
",\n depositTxHex='" + depositTxHex +
|
||||||
",\n depositTxKey='" + depositTxKey +
|
",\n depositTxKey='" + depositTxKey +
|
||||||
|
",\n paymentAccountKey='" + paymentAccountKey +
|
||||||
"\n} " + super.toString();
|
"\n} " + super.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* 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.messages;
|
||||||
|
|
||||||
|
import bisq.core.proto.CoreProtoResolver;
|
||||||
|
|
||||||
|
import bisq.network.p2p.DirectMessage;
|
||||||
|
import bisq.network.p2p.NodeAddress;
|
||||||
|
import bisq.common.crypto.PubKeyRing;
|
||||||
|
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import lombok.Value;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@Value
|
||||||
|
public final class PaymentAccountKeyRequest extends TradeMessage implements DirectMessage {
|
||||||
|
private final NodeAddress senderNodeAddress;
|
||||||
|
private final PubKeyRing pubKeyRing;
|
||||||
|
|
||||||
|
public PaymentAccountKeyRequest(String tradeId,
|
||||||
|
NodeAddress senderNodeAddress,
|
||||||
|
PubKeyRing pubKeyRing,
|
||||||
|
String uid,
|
||||||
|
String messageVersion) {
|
||||||
|
super(messageVersion, tradeId, uid);
|
||||||
|
this.senderNodeAddress = senderNodeAddress;
|
||||||
|
this.pubKeyRing = pubKeyRing;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// PROTO BUFFER
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
|
||||||
|
protobuf.PaymentAccountKeyRequest.Builder builder = protobuf.PaymentAccountKeyRequest.newBuilder()
|
||||||
|
.setTradeId(tradeId)
|
||||||
|
.setSenderNodeAddress(senderNodeAddress.toProtoMessage())
|
||||||
|
.setPubKeyRing(pubKeyRing.toProtoMessage())
|
||||||
|
.setUid(uid);
|
||||||
|
return getNetworkEnvelopeBuilder().setPaymentAccountKeyRequest(builder).build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PaymentAccountKeyRequest fromProto(protobuf.PaymentAccountKeyRequest proto,
|
||||||
|
CoreProtoResolver coreProtoResolver,
|
||||||
|
String messageVersion) {
|
||||||
|
return new PaymentAccountKeyRequest(proto.getTradeId(),
|
||||||
|
NodeAddress.fromProto(proto.getSenderNodeAddress()),
|
||||||
|
PubKeyRing.fromProto(proto.getPubKeyRing()),
|
||||||
|
proto.getUid(),
|
||||||
|
messageVersion);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "PaymentAccountKeyRequest {" +
|
||||||
|
"\n senderNodeAddress=" + senderNodeAddress +
|
||||||
|
",\n pubKeyRing=" + pubKeyRing +
|
||||||
|
"\n} " + super.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,37 +17,40 @@
|
||||||
|
|
||||||
package bisq.core.trade.messages;
|
package bisq.core.trade.messages;
|
||||||
|
|
||||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
|
||||||
import bisq.core.proto.CoreProtoResolver;
|
import bisq.core.proto.CoreProtoResolver;
|
||||||
|
|
||||||
import bisq.network.p2p.DirectMessage;
|
import bisq.network.p2p.DirectMessage;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import com.google.protobuf.ByteString;
|
import com.google.protobuf.ByteString;
|
||||||
|
import java.util.Optional;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import bisq.common.crypto.PubKeyRing;
|
import bisq.common.crypto.PubKeyRing;
|
||||||
|
import bisq.common.proto.ProtoUtil;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
@EqualsAndHashCode(callSuper = true)
|
@EqualsAndHashCode(callSuper = true)
|
||||||
@Value
|
@Value
|
||||||
public final class PaymentAccountPayloadRequest extends TradeMessage implements DirectMessage {
|
public final class PaymentAccountKeyResponse extends TradeMailboxMessage implements DirectMessage {
|
||||||
private final NodeAddress senderNodeAddress;
|
private final NodeAddress senderNodeAddress;
|
||||||
private final PubKeyRing pubKeyRing;
|
private final PubKeyRing pubKeyRing;
|
||||||
private final long currentDate;
|
@Nullable
|
||||||
private final PaymentAccountPayload paymentAccountPayload;
|
private final byte[] paymentAccountKey;
|
||||||
|
@Nullable
|
||||||
|
private final String updatedMultisigHex;
|
||||||
|
|
||||||
public PaymentAccountPayloadRequest(String tradeId,
|
public PaymentAccountKeyResponse(String tradeId,
|
||||||
NodeAddress senderNodeAddress,
|
NodeAddress senderNodeAddress,
|
||||||
PubKeyRing pubKeyRing,
|
PubKeyRing pubKeyRing,
|
||||||
String uid,
|
String uid,
|
||||||
String messageVersion,
|
String messageVersion,
|
||||||
long currentDate,
|
@Nullable byte[] paymentAccountKey,
|
||||||
PaymentAccountPayload paymentAccountPayload) {
|
@Nullable String updatedMultisigHex) {
|
||||||
super(messageVersion, tradeId, uid);
|
super(messageVersion, tradeId, uid);
|
||||||
this.senderNodeAddress = senderNodeAddress;
|
this.senderNodeAddress = senderNodeAddress;
|
||||||
this.pubKeyRing = pubKeyRing;
|
this.pubKeyRing = pubKeyRing;
|
||||||
this.currentDate = currentDate;
|
this.paymentAccountKey = paymentAccountKey;
|
||||||
this.paymentAccountPayload = paymentAccountPayload;
|
this.updatedMultisigHex = updatedMultisigHex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -57,36 +60,34 @@ public final class PaymentAccountPayloadRequest extends TradeMessage implements
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
|
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
|
||||||
protobuf.PaymentAccountPayloadRequest.Builder builder = protobuf.PaymentAccountPayloadRequest.newBuilder()
|
protobuf.PaymentAccountKeyResponse.Builder builder = protobuf.PaymentAccountKeyResponse.newBuilder()
|
||||||
.setTradeId(tradeId)
|
.setTradeId(tradeId)
|
||||||
.setSenderNodeAddress(senderNodeAddress.toProtoMessage())
|
.setSenderNodeAddress(senderNodeAddress.toProtoMessage())
|
||||||
.setPubKeyRing(pubKeyRing.toProtoMessage())
|
.setPubKeyRing(pubKeyRing.toProtoMessage())
|
||||||
.setUid(uid)
|
.setUid(uid);
|
||||||
.setPaymentAccountPayload((protobuf.PaymentAccountPayload) paymentAccountPayload.toProtoMessage());
|
Optional.ofNullable(paymentAccountKey).ifPresent(e -> builder.setPaymentAccountKey(ByteString.copyFrom(e)));
|
||||||
builder.setCurrentDate(currentDate);
|
Optional.ofNullable(updatedMultisigHex).ifPresent(e -> builder.setUpdatedMultisigHex(updatedMultisigHex));
|
||||||
|
return getNetworkEnvelopeBuilder().setPaymentAccountKeyResponse(builder).build();
|
||||||
return getNetworkEnvelopeBuilder().setPaymentAccountPayloadRequest(builder).build();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PaymentAccountPayloadRequest fromProto(protobuf.PaymentAccountPayloadRequest proto,
|
public static PaymentAccountKeyResponse fromProto(protobuf.PaymentAccountKeyResponse proto,
|
||||||
CoreProtoResolver coreProtoResolver,
|
CoreProtoResolver coreProtoResolver,
|
||||||
String messageVersion) {
|
String messageVersion) {
|
||||||
return new PaymentAccountPayloadRequest(proto.getTradeId(),
|
return new PaymentAccountKeyResponse(proto.getTradeId(),
|
||||||
NodeAddress.fromProto(proto.getSenderNodeAddress()),
|
NodeAddress.fromProto(proto.getSenderNodeAddress()),
|
||||||
PubKeyRing.fromProto(proto.getPubKeyRing()),
|
PubKeyRing.fromProto(proto.getPubKeyRing()),
|
||||||
proto.getUid(),
|
proto.getUid(),
|
||||||
messageVersion,
|
messageVersion,
|
||||||
proto.getCurrentDate(),
|
ProtoUtil.byteArrayOrNullFromProto(proto.getPaymentAccountKey()),
|
||||||
coreProtoResolver.fromProto(proto.getPaymentAccountPayload()));
|
ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "PaymentAccountPayloadRequest {" +
|
return "PaymentAccountKeyResponse {" +
|
||||||
"\n senderNodeAddress=" + senderNodeAddress +
|
"\n senderNodeAddress=" + senderNodeAddress +
|
||||||
",\n pubKeyRing=" + pubKeyRing +
|
",\n pubKeyRing=" + pubKeyRing +
|
||||||
",\n currentDate=" + currentDate +
|
",\n paymentAccountKey=" + paymentAccountKey +
|
||||||
",\n paymentAccountPayload=" + paymentAccountPayload +
|
|
||||||
"\n} " + super.toString();
|
"\n} " + super.toString();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,12 +21,12 @@ import bisq.core.proto.CoreProtoResolver;
|
||||||
|
|
||||||
import bisq.network.p2p.DirectMessage;
|
import bisq.network.p2p.DirectMessage;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
import bisq.common.crypto.PubKeyRing;
|
import bisq.common.crypto.PubKeyRing;
|
||||||
import bisq.common.proto.ProtoUtil;
|
import bisq.common.proto.ProtoUtil;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import lombok.EqualsAndHashCode;
|
import lombok.EqualsAndHashCode;
|
||||||
import lombok.Value;
|
import lombok.Value;
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ public final class SignContractResponse extends TradeMessage implements DirectMe
|
||||||
private final long currentDate;
|
private final long currentDate;
|
||||||
private final String contractAsJson;
|
private final String contractAsJson;
|
||||||
private final String contractSignature;
|
private final String contractSignature;
|
||||||
|
private final byte[] encryptedPaymentAccountPayload;
|
||||||
|
|
||||||
public SignContractResponse(String tradeId,
|
public SignContractResponse(String tradeId,
|
||||||
NodeAddress senderNodeAddress,
|
NodeAddress senderNodeAddress,
|
||||||
|
@ -46,13 +47,15 @@ public final class SignContractResponse extends TradeMessage implements DirectMe
|
||||||
String messageVersion,
|
String messageVersion,
|
||||||
long currentDate,
|
long currentDate,
|
||||||
String contractAsJson,
|
String contractAsJson,
|
||||||
String contractSignature) {
|
String contractSignature,
|
||||||
|
@Nullable byte[] encryptedPaymentAccountPayload) {
|
||||||
super(messageVersion, tradeId, uid);
|
super(messageVersion, tradeId, uid);
|
||||||
this.senderNodeAddress = senderNodeAddress;
|
this.senderNodeAddress = senderNodeAddress;
|
||||||
this.pubKeyRing = pubKeyRing;
|
this.pubKeyRing = pubKeyRing;
|
||||||
this.currentDate = currentDate;
|
this.currentDate = currentDate;
|
||||||
this.contractAsJson = contractAsJson;
|
this.contractAsJson = contractAsJson;
|
||||||
this.contractSignature = contractSignature;
|
this.contractSignature = contractSignature;
|
||||||
|
this.encryptedPaymentAccountPayload = encryptedPaymentAccountPayload;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -70,6 +73,7 @@ public final class SignContractResponse extends TradeMessage implements DirectMe
|
||||||
|
|
||||||
Optional.ofNullable(contractAsJson).ifPresent(e -> builder.setContractAsJson(contractAsJson));
|
Optional.ofNullable(contractAsJson).ifPresent(e -> builder.setContractAsJson(contractAsJson));
|
||||||
Optional.ofNullable(contractSignature).ifPresent(e -> builder.setContractSignature(contractSignature));
|
Optional.ofNullable(contractSignature).ifPresent(e -> builder.setContractSignature(contractSignature));
|
||||||
|
Optional.ofNullable(encryptedPaymentAccountPayload).ifPresent(e -> builder.setEncryptedPaymentAccountPayload(ByteString.copyFrom(e)));
|
||||||
|
|
||||||
builder.setCurrentDate(currentDate);
|
builder.setCurrentDate(currentDate);
|
||||||
|
|
||||||
|
@ -86,7 +90,8 @@ public final class SignContractResponse extends TradeMessage implements DirectMe
|
||||||
messageVersion,
|
messageVersion,
|
||||||
proto.getCurrentDate(),
|
proto.getCurrentDate(),
|
||||||
ProtoUtil.stringOrNullFromProto(proto.getContractAsJson()),
|
ProtoUtil.stringOrNullFromProto(proto.getContractAsJson()),
|
||||||
ProtoUtil.stringOrNullFromProto(proto.getContractSignature()));
|
ProtoUtil.stringOrNullFromProto(proto.getContractSignature()),
|
||||||
|
proto.getEncryptedPaymentAccountPayload().toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -5,11 +5,13 @@ import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.messages.DepositRequest;
|
import bisq.core.trade.messages.DepositRequest;
|
||||||
import bisq.core.trade.messages.DepositResponse;
|
import bisq.core.trade.messages.DepositResponse;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
import bisq.core.trade.messages.PaymentAccountKeyRequest;
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
|
import bisq.core.trade.protocol.FluentProtocol.Condition;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
import bisq.core.trade.protocol.tasks.ArbitratorSendsInitTradeOrMultisigRequests;
|
import bisq.core.trade.protocol.tasks.ArbitratorSendsInitTradeOrMultisigRequests;
|
||||||
import bisq.core.trade.protocol.tasks.ArbitratorProcessesDepositRequest;
|
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.ArbitratorProcessesReserveTx;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
||||||
import bisq.core.util.Validator;
|
import bisq.core.util.Validator;
|
||||||
|
@ -94,10 +96,30 @@ public class ArbitratorProtocol extends DisputeProtocol {
|
||||||
public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
|
public void handleDepositResponse(DepositResponse response, NodeAddress sender) {
|
||||||
log.warn("Arbitrator ignoring DepositResponse");
|
log.warn("Arbitrator ignoring DepositResponse");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void handlePaymentAccountKeyRequest(PaymentAccountKeyRequest request, NodeAddress sender) {
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
|
System.out.println("ArbitratorProtocol.handlePaymentAccountKeyRequest() " + trade.getId());
|
||||||
log.warn("Arbitrator ignoring PaymentAccountPayloadRequest");
|
synchronized (trade) {
|
||||||
|
latchTrade();
|
||||||
|
Validator.checkTradeId(processModel.getOfferId(), request);
|
||||||
|
processModel.setTradeMessage(request);
|
||||||
|
expect(new Condition(trade)
|
||||||
|
.with(request)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(
|
||||||
|
ArbitratorProcessesPaymentAccountKeyRequest.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
stopTimeout();
|
||||||
|
handleTaskRunnerSuccess(sender, request);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
handleTaskRunnerFault(sender, request, errorMessage);
|
||||||
|
}))
|
||||||
|
.withTimeout(TRADE_TIMEOUT))
|
||||||
|
.executeTasks(true);
|
||||||
|
awaitTradeLatch();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -22,7 +22,7 @@ import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.messages.DepositResponse;
|
import bisq.core.trade.messages.DepositResponse;
|
||||||
import bisq.core.trade.messages.InitMultisigRequest;
|
import bisq.core.trade.messages.InitMultisigRequest;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
import bisq.core.trade.messages.PaymentAccountKeyResponse;
|
||||||
import bisq.core.trade.messages.PaymentReceivedMessage;
|
import bisq.core.trade.messages.PaymentReceivedMessage;
|
||||||
import bisq.core.trade.messages.SignContractRequest;
|
import bisq.core.trade.messages.SignContractRequest;
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
|
@ -100,8 +100,8 @@ public class BuyerAsMakerProtocol extends BuyerProtocol implements MakerProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
|
public void handle(PaymentAccountKeyResponse request, NodeAddress sender) {
|
||||||
super.handlePaymentAccountPayloadRequest(request, sender);
|
super.handle(request, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -24,12 +24,14 @@ import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.handlers.TradeResultHandler;
|
import bisq.core.trade.handlers.TradeResultHandler;
|
||||||
import bisq.core.trade.messages.DepositResponse;
|
import bisq.core.trade.messages.DepositResponse;
|
||||||
import bisq.core.trade.messages.InitMultisigRequest;
|
import bisq.core.trade.messages.InitMultisigRequest;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
import bisq.core.trade.messages.PaymentAccountKeyResponse;
|
||||||
import bisq.core.trade.messages.PaymentReceivedMessage;
|
import bisq.core.trade.messages.PaymentReceivedMessage;
|
||||||
import bisq.core.trade.messages.SignContractRequest;
|
import bisq.core.trade.messages.SignContractRequest;
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.core.trade.messages.TradeMessage;
|
import bisq.core.trade.messages.TradeMessage;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
|
import bisq.core.trade.protocol.tasks.TakerReservesTradeFunds;
|
||||||
|
import bisq.core.trade.protocol.tasks.TakerSendsInitTradeRequestToArbitrator;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.common.handlers.ErrorMessageHandler;
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
import bisq.common.handlers.ResultHandler;
|
import bisq.common.handlers.ResultHandler;
|
||||||
|
@ -109,8 +111,8 @@ public class BuyerAsTakerProtocol extends BuyerProtocol implements TakerProtocol
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
|
public void handle(PaymentAccountKeyResponse request, NodeAddress sender) {
|
||||||
super.handlePaymentAccountPayloadRequest(request, sender);
|
super.handle(request, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -19,25 +19,36 @@ package bisq.core.trade.protocol;
|
||||||
|
|
||||||
import bisq.core.trade.BuyerTrade;
|
import bisq.core.trade.BuyerTrade;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
|
import bisq.core.trade.messages.PaymentAccountKeyResponse;
|
||||||
import bisq.core.trade.messages.PaymentReceivedMessage;
|
import bisq.core.trade.messages.PaymentReceivedMessage;
|
||||||
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.core.trade.messages.TradeMessage;
|
import bisq.core.trade.messages.TradeMessage;
|
||||||
|
import bisq.core.trade.protocol.FluentProtocol.Condition;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerPreparesPaymentSentMessage;
|
import bisq.core.trade.protocol.tasks.BuyerPreparesPaymentSentMessage;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerProcessesPaymentReceivedMessage;
|
import bisq.core.trade.protocol.tasks.BuyerProcessesPaymentReceivedMessage;
|
||||||
|
import bisq.core.trade.protocol.tasks.BuyerSendsPaymentAccountKeyRequestToArbitrator;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerSendsPaymentSentMessage;
|
import bisq.core.trade.protocol.tasks.BuyerSendsPaymentSentMessage;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerSetupPayoutTxListener;
|
import bisq.core.trade.protocol.tasks.BuyerSetupPayoutTxListener;
|
||||||
|
import bisq.core.trade.protocol.tasks.BuyerProcessesPaymentAccountKeyResponse;
|
||||||
import bisq.core.trade.protocol.tasks.SetupDepositTxsListener;
|
import bisq.core.trade.protocol.tasks.SetupDepositTxsListener;
|
||||||
import bisq.core.util.Validator;
|
import bisq.core.util.Validator;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
|
import bisq.common.UserThread;
|
||||||
import bisq.common.handlers.ErrorMessageHandler;
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
import bisq.common.handlers.ResultHandler;
|
import bisq.common.handlers.ResultHandler;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class BuyerProtocol extends DisputeProtocol {
|
public abstract class BuyerProtocol extends DisputeProtocol {
|
||||||
|
|
||||||
|
private boolean listeningToSendPaymentAccountKey;
|
||||||
|
private boolean paymentAccountPayloadKeyRequestSent;
|
||||||
enum BuyerEvent implements FluentProtocol.Event {
|
enum BuyerEvent implements FluentProtocol.Event {
|
||||||
STARTUP,
|
STARTUP,
|
||||||
|
DEPOSIT_TXS_CONFIRMED,
|
||||||
PAYMENT_SENT
|
PAYMENT_SENT
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,6 +65,9 @@ public abstract class BuyerProtocol extends DisputeProtocol {
|
||||||
super.onInitialized();
|
super.onInitialized();
|
||||||
|
|
||||||
// TODO: run with trade lock and latch, otherwise getting invalid transition warnings on startup after offline trades
|
// TODO: run with trade lock and latch, otherwise getting invalid transition warnings on startup after offline trades
|
||||||
|
|
||||||
|
// request key to decrypt seller's payment account payload after first confirmation
|
||||||
|
sendPaymentAccountKeyRequestIfWhenNeeded(BuyerEvent.STARTUP, false);
|
||||||
|
|
||||||
given(anyPhase(Trade.Phase.DEPOSIT_REQUESTED, Trade.Phase.DEPOSITS_PUBLISHED, Trade.Phase.DEPOSITS_CONFIRMED)
|
given(anyPhase(Trade.Phase.DEPOSIT_REQUESTED, Trade.Phase.DEPOSITS_PUBLISHED, Trade.Phase.DEPOSITS_CONFIRMED)
|
||||||
.with(BuyerEvent.STARTUP))
|
.with(BuyerEvent.STARTUP))
|
||||||
|
@ -66,21 +80,60 @@ public abstract class BuyerProtocol extends DisputeProtocol {
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
|
|
||||||
given(anyPhase(Trade.Phase.PAYMENT_SENT, Trade.Phase.PAYMENT_RECEIVED)
|
given(anyPhase(Trade.Phase.PAYMENT_SENT, Trade.Phase.PAYMENT_RECEIVED)
|
||||||
.anyState(Trade.State.BUYER_STORED_IN_MAILBOX_PAYMENT_SENT_MSG,
|
.anyState(Trade.State.BUYER_STORED_IN_MAILBOX_PAYMENT_SENT_MSG, Trade.State.BUYER_SEND_FAILED_PAYMENT_SENT_MSG)
|
||||||
Trade.State.BUYER_SEND_FAILED_PAYMENT_SENT_MSG)
|
|
||||||
.with(BuyerEvent.STARTUP))
|
.with(BuyerEvent.STARTUP))
|
||||||
.setup(tasks(BuyerSendsPaymentSentMessage.class))
|
.setup(tasks(BuyerSendsPaymentSentMessage.class))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
|
||||||
|
super.onTradeMessage(message, peer);
|
||||||
|
if (message instanceof PaymentReceivedMessage) {
|
||||||
|
handle((PaymentReceivedMessage) message, peer);
|
||||||
|
} if (message instanceof PaymentAccountKeyResponse) {
|
||||||
|
handle((PaymentAccountKeyResponse) message, peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMailboxMessage(TradeMessage message, NodeAddress peer) {
|
public void onMailboxMessage(TradeMessage message, NodeAddress peer) {
|
||||||
super.onMailboxMessage(message, peer);
|
super.onMailboxMessage(message, peer);
|
||||||
if (message instanceof PaymentReceivedMessage) {
|
if (message instanceof PaymentReceivedMessage) {
|
||||||
handle((PaymentReceivedMessage) message, peer);
|
handle((PaymentReceivedMessage) message, peer);
|
||||||
|
} else if (message instanceof PaymentAccountKeyResponse) {
|
||||||
|
handle((PaymentAccountKeyResponse) message, peer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSignContractResponse(SignContractResponse response, NodeAddress sender) {
|
||||||
|
sendPaymentAccountKeyRequestIfWhenNeeded(BuyerEvent.DEPOSIT_TXS_CONFIRMED, true);
|
||||||
|
super.handleSignContractResponse(response, sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void handle(PaymentAccountKeyResponse response, NodeAddress sender) {
|
||||||
|
System.out.println(getClass().getCanonicalName() + ".handlePaymentAccountKeyResponse()");
|
||||||
|
new Thread(() -> {
|
||||||
|
synchronized (trade) {
|
||||||
|
latchTrade();
|
||||||
|
expect(new Condition(trade)
|
||||||
|
.with(response)
|
||||||
|
.from(sender))
|
||||||
|
.setup(tasks(BuyerProcessesPaymentAccountKeyResponse.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(sender, response);
|
||||||
|
},
|
||||||
|
errorMessage -> {
|
||||||
|
handleTaskRunnerFault(sender, response, errorMessage);
|
||||||
|
})))
|
||||||
|
.executeTasks();
|
||||||
|
awaitTradeLatch();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// User interaction
|
// User interaction
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -126,7 +179,7 @@ public abstract class BuyerProtocol extends DisputeProtocol {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
protected void handle(PaymentReceivedMessage message, NodeAddress peer) {
|
protected void handle(PaymentReceivedMessage message, NodeAddress peer) {
|
||||||
log.info("BuyerProtocol.handle(PaymentReceivedMessage)");
|
System.out.println("BuyerProtocol.handle(PaymentReceivedMessage)");
|
||||||
new Thread(() -> {
|
new Thread(() -> {
|
||||||
synchronized (trade) {
|
synchronized (trade) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
|
@ -150,16 +203,54 @@ public abstract class BuyerProtocol extends DisputeProtocol {
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendPaymentAccountKeyRequestIfWhenNeeded(BuyerEvent event, boolean waitForSellerOnConfirm) {
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
// skip if payment account payload already decrypted or not enough progress
|
||||||
// Message dispatcher
|
if (trade.getSeller().getPaymentAccountPayload() != null) return;
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
if (trade.getPhase().ordinal() < Trade.Phase.DEPOSIT_REQUESTED.ordinal()) return;
|
||||||
|
|
||||||
@Override
|
// if confirmed and waiting for seller, recheck later
|
||||||
protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
|
if (trade.getState() == Trade.State.DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN && waitForSellerOnConfirm) {
|
||||||
super.onTradeMessage(message, peer);
|
UserThread.runAfter(() -> {
|
||||||
if (message instanceof PaymentReceivedMessage) {
|
sendPaymentAccountKeyRequestIfWhenNeeded(event, false);
|
||||||
handle((PaymentReceivedMessage) message, peer);
|
}, TRADE_TIMEOUT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// else if confirmed send request and return
|
||||||
|
else if (trade.getState().ordinal() >= Trade.State.DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN.ordinal()) {
|
||||||
|
sendPaymentAccountKeyRequest(event);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// register for state changes once
|
||||||
|
if (!listeningToSendPaymentAccountKey) {
|
||||||
|
listeningToSendPaymentAccountKey = true;
|
||||||
|
EasyBind.subscribe(trade.stateProperty(), state -> {
|
||||||
|
sendPaymentAccountKeyRequestIfWhenNeeded(event, waitForSellerOnConfirm);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void sendPaymentAccountKeyRequest(BuyerEvent event) {
|
||||||
|
new Thread(() -> {
|
||||||
|
synchronized (trade) {
|
||||||
|
if (paymentAccountPayloadKeyRequestSent) return;
|
||||||
|
if (trade.getSeller().getPaymentAccountPayload() != null) return; // skip if initialized
|
||||||
|
latchTrade();
|
||||||
|
expect(new Condition(trade))
|
||||||
|
.setup(tasks(BuyerSendsPaymentAccountKeyRequestToArbitrator.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(event);
|
||||||
|
},
|
||||||
|
(errorMessage) -> {
|
||||||
|
handleTaskRunnerFault(event, errorMessage);
|
||||||
|
})))
|
||||||
|
.executeTasks(true);
|
||||||
|
awaitTradeLatch();
|
||||||
|
paymentAccountPayloadKeyRequestSent = true;
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.core.trade.messages.DepositResponse;
|
import bisq.core.trade.messages.DepositResponse;
|
||||||
import bisq.core.trade.messages.InitMultisigRequest;
|
import bisq.core.trade.messages.InitMultisigRequest;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
|
||||||
import bisq.core.trade.messages.TradeMessage;
|
import bisq.core.trade.messages.TradeMessage;
|
||||||
import bisq.core.trade.protocol.tasks.MakerSendsInitTradeRequest;
|
import bisq.core.trade.protocol.tasks.MakerSendsInitTradeRequest;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
import bisq.core.trade.protocol.tasks.ProcessInitTradeRequest;
|
||||||
|
@ -101,11 +100,6 @@ public class SellerAsMakerProtocol extends SellerProtocol implements MakerProtoc
|
||||||
super.handleDepositResponse(response, sender);
|
super.handleDepositResponse(response, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
|
|
||||||
super.handlePaymentAccountPayloadRequest(request, sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// User interaction
|
// User interaction
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -27,9 +27,10 @@ import bisq.core.trade.messages.SignContractRequest;
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.core.trade.messages.DepositResponse;
|
import bisq.core.trade.messages.DepositResponse;
|
||||||
import bisq.core.trade.messages.InitMultisigRequest;
|
import bisq.core.trade.messages.InitMultisigRequest;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
|
||||||
import bisq.core.trade.messages.TradeMessage;
|
import bisq.core.trade.messages.TradeMessage;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
|
import bisq.core.trade.protocol.tasks.TakerReservesTradeFunds;
|
||||||
|
import bisq.core.trade.protocol.tasks.TakerSendsInitTradeRequestToArbitrator;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.common.handlers.ErrorMessageHandler;
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
import bisq.common.handlers.ResultHandler;
|
import bisq.common.handlers.ResultHandler;
|
||||||
|
@ -107,11 +108,6 @@ public class SellerAsTakerProtocol extends SellerProtocol implements TakerProtoc
|
||||||
super.handleDepositResponse(response, sender);
|
super.handleDepositResponse(response, sender);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
|
|
||||||
super.handlePaymentAccountPayloadRequest(request, sender);
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Incoming message when buyer has clicked payment started button
|
// Incoming message when buyer has clicked payment started button
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
|
@ -19,24 +19,29 @@ package bisq.core.trade.protocol;
|
||||||
|
|
||||||
import bisq.core.trade.SellerTrade;
|
import bisq.core.trade.SellerTrade;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
|
import bisq.core.trade.messages.DepositResponse;
|
||||||
import bisq.core.trade.messages.PaymentSentMessage;
|
import bisq.core.trade.messages.PaymentSentMessage;
|
||||||
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.core.trade.messages.TradeMessage;
|
import bisq.core.trade.messages.TradeMessage;
|
||||||
import bisq.core.trade.protocol.BuyerProtocol.BuyerEvent;
|
import bisq.core.trade.protocol.FluentProtocol.Condition;
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
import bisq.core.trade.protocol.tasks.SellerPreparesPaymentReceivedMessage;
|
import bisq.core.trade.protocol.tasks.SellerPreparesPaymentReceivedMessage;
|
||||||
import bisq.core.trade.protocol.tasks.SellerProcessesPaymentSentMessage;
|
import bisq.core.trade.protocol.tasks.SellerProcessesPaymentSentMessage;
|
||||||
import bisq.core.trade.protocol.tasks.SellerSendsPaymentReceivedMessage;
|
import bisq.core.trade.protocol.tasks.SellerSendsPaymentReceivedMessage;
|
||||||
|
import bisq.core.trade.protocol.tasks.SellerSendsPaymentAccountPayloadKey;
|
||||||
import bisq.core.trade.protocol.tasks.SetupDepositTxsListener;
|
import bisq.core.trade.protocol.tasks.SetupDepositTxsListener;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.common.handlers.ErrorMessageHandler;
|
import bisq.common.handlers.ErrorMessageHandler;
|
||||||
import bisq.common.handlers.ResultHandler;
|
import bisq.common.handlers.ResultHandler;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.fxmisc.easybind.EasyBind;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public abstract class SellerProtocol extends DisputeProtocol {
|
public abstract class SellerProtocol extends DisputeProtocol {
|
||||||
enum SellerEvent implements FluentProtocol.Event {
|
enum SellerEvent implements FluentProtocol.Event {
|
||||||
STARTUP,
|
STARTUP,
|
||||||
|
DEPOSIT_TXS_CONFIRMED,
|
||||||
PAYMENT_RECEIVED
|
PAYMENT_RECEIVED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,16 +55,25 @@ public abstract class SellerProtocol extends DisputeProtocol {
|
||||||
|
|
||||||
// TODO: run with trade lock and latch, otherwise getting invalid transition warnings on startup after offline trades
|
// TODO: run with trade lock and latch, otherwise getting invalid transition warnings on startup after offline trades
|
||||||
|
|
||||||
|
// send payment account payload key when trade state is confirmed
|
||||||
|
if (trade.getPhase() == Trade.Phase.DEPOSIT_REQUESTED || trade.getPhase() == Trade.Phase.DEPOSITS_PUBLISHED) {
|
||||||
|
sendPaymentAccountPayloadKeyWhenConfirmed(SellerEvent.STARTUP);
|
||||||
|
}
|
||||||
|
|
||||||
|
// listen for changes to deposit txs
|
||||||
given(anyPhase(Trade.Phase.DEPOSIT_REQUESTED, Trade.Phase.DEPOSITS_PUBLISHED, Trade.Phase.DEPOSITS_CONFIRMED)
|
given(anyPhase(Trade.Phase.DEPOSIT_REQUESTED, Trade.Phase.DEPOSITS_PUBLISHED, Trade.Phase.DEPOSITS_CONFIRMED)
|
||||||
.with(BuyerEvent.STARTUP))
|
.with(SellerEvent.STARTUP))
|
||||||
.setup(tasks(SetupDepositTxsListener.class))
|
.setup(tasks(SetupDepositTxsListener.class))
|
||||||
.executeTasks();
|
.executeTasks();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
|
||||||
// Mailbox
|
super.onTradeMessage(message, peer);
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
if (message instanceof PaymentSentMessage) {
|
||||||
|
handle((PaymentSentMessage) message, peer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMailboxMessage(TradeMessage message, NodeAddress peerNodeAddress) {
|
public void onMailboxMessage(TradeMessage message, NodeAddress peerNodeAddress) {
|
||||||
|
@ -70,6 +84,12 @@ public abstract class SellerProtocol extends DisputeProtocol {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSignContractResponse(SignContractResponse response, NodeAddress sender) {
|
||||||
|
sendPaymentAccountPayloadKeyWhenConfirmed(SellerEvent.DEPOSIT_TXS_CONFIRMED);
|
||||||
|
super.handleSignContractResponse(response, sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Incoming message when buyer has clicked payment started button
|
// Incoming message when buyer has clicked payment started button
|
||||||
|
@ -154,12 +174,26 @@ public abstract class SellerProtocol extends DisputeProtocol {
|
||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void sendPaymentAccountPayloadKeyWhenConfirmed(SellerEvent event) {
|
||||||
protected void onTradeMessage(TradeMessage message, NodeAddress peer) {
|
EasyBind.subscribe(trade.stateProperty(), state -> {
|
||||||
super.onTradeMessage(message, peer);
|
if (state == Trade.State.DEPOSIT_TXS_CONFIRMED_IN_BLOCKCHAIN) {
|
||||||
|
new Thread(() -> {
|
||||||
if (message instanceof PaymentSentMessage) {
|
synchronized (trade) {
|
||||||
handle((PaymentSentMessage) message, peer);
|
latchTrade();
|
||||||
}
|
expect(new Condition(trade))
|
||||||
|
.setup(tasks(SellerSendsPaymentAccountPayloadKey.class)
|
||||||
|
.using(new TradeTaskRunner(trade,
|
||||||
|
() -> {
|
||||||
|
handleTaskRunnerSuccess(event);
|
||||||
|
},
|
||||||
|
(errorMessage) -> {
|
||||||
|
handleTaskRunnerFault(event, errorMessage);
|
||||||
|
})))
|
||||||
|
.executeTasks(true);
|
||||||
|
awaitTradeLatch();
|
||||||
|
}
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,16 +25,13 @@ import bisq.core.trade.handlers.TradeResultHandler;
|
||||||
import bisq.core.trade.messages.PaymentSentMessage;
|
import bisq.core.trade.messages.PaymentSentMessage;
|
||||||
import bisq.core.trade.messages.DepositResponse;
|
import bisq.core.trade.messages.DepositResponse;
|
||||||
import bisq.core.trade.messages.InitMultisigRequest;
|
import bisq.core.trade.messages.InitMultisigRequest;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
|
||||||
import bisq.core.trade.messages.SignContractRequest;
|
import bisq.core.trade.messages.SignContractRequest;
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.core.trade.messages.TradeMessage;
|
import bisq.core.trade.messages.TradeMessage;
|
||||||
import bisq.core.trade.messages.UpdateMultisigRequest;
|
import bisq.core.trade.messages.UpdateMultisigRequest;
|
||||||
import bisq.core.trade.protocol.tasks.MaybeRemoveOpenOffer;
|
|
||||||
import bisq.core.trade.protocol.tasks.MaybeSendSignContractRequest;
|
import bisq.core.trade.protocol.tasks.MaybeSendSignContractRequest;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessDepositResponse;
|
import bisq.core.trade.protocol.tasks.ProcessDepositResponse;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessInitMultisigRequest;
|
import bisq.core.trade.protocol.tasks.ProcessInitMultisigRequest;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessPaymentAccountPayloadRequest;
|
|
||||||
import bisq.core.trade.protocol.tasks.ProcessSignContractRequest;
|
import bisq.core.trade.protocol.tasks.ProcessSignContractRequest;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessSignContractResponse;
|
import bisq.core.trade.protocol.tasks.ProcessSignContractResponse;
|
||||||
import bisq.core.trade.protocol.tasks.ProcessUpdateMultisigRequest;
|
import bisq.core.trade.protocol.tasks.ProcessUpdateMultisigRequest;
|
||||||
|
@ -331,8 +328,10 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
||||||
ProcessDepositResponse.class)
|
ProcessDepositResponse.class)
|
||||||
.using(new TradeTaskRunner(trade,
|
.using(new TradeTaskRunner(trade,
|
||||||
() -> {
|
() -> {
|
||||||
startTimeout(TRADE_TIMEOUT);
|
stopTimeout();
|
||||||
|
this.errorMessageHandler = null;
|
||||||
handleTaskRunnerSuccess(sender, response);
|
handleTaskRunnerSuccess(sender, response);
|
||||||
|
if (tradeResultHandler != null) tradeResultHandler.handleResult(trade); // trade is initialized
|
||||||
},
|
},
|
||||||
errorMessage -> {
|
errorMessage -> {
|
||||||
handleTaskRunnerFault(sender, response, errorMessage);
|
handleTaskRunnerFault(sender, response, errorMessage);
|
||||||
|
@ -343,42 +342,6 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress sender) {
|
|
||||||
System.out.println(getClass().getCanonicalName() + ".handlePaymentAccountPayloadRequest()");
|
|
||||||
synchronized (trade) {
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
|
||||||
if (trade.getState() == Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS) {
|
|
||||||
latchTrade();
|
|
||||||
Validator.checkTradeId(processModel.getOfferId(), request);
|
|
||||||
processModel.setTradeMessage(request);
|
|
||||||
expect(state(Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS)
|
|
||||||
.with(request)
|
|
||||||
.from(sender)) // TODO (woodser): ensure this asserts sender == response.getSenderNodeAddress()
|
|
||||||
.setup(tasks(
|
|
||||||
// TODO (woodser): validate request
|
|
||||||
ProcessPaymentAccountPayloadRequest.class,
|
|
||||||
MaybeRemoveOpenOffer.class)
|
|
||||||
.using(new TradeTaskRunner(trade,
|
|
||||||
() -> {
|
|
||||||
stopTimeout();
|
|
||||||
this.errorMessageHandler = null;
|
|
||||||
handleTaskRunnerSuccess(sender, request);
|
|
||||||
if (tradeResultHandler != null) tradeResultHandler.handleResult(trade); // trade is initialized
|
|
||||||
},
|
|
||||||
errorMessage -> {
|
|
||||||
handleTaskRunnerFault(sender, request, errorMessage);
|
|
||||||
}))
|
|
||||||
.withTimeout(TRADE_TIMEOUT))
|
|
||||||
.executeTasks(true);
|
|
||||||
awaitTradeLatch();
|
|
||||||
} else {
|
|
||||||
EasyBind.subscribe(trade.stateProperty(), state -> {
|
|
||||||
if (state == Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS) new Thread(() -> handlePaymentAccountPayloadRequest(request, sender)).start(); // process notification without trade lock
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO (woodser): update to use fluent for consistency
|
// TODO (woodser): update to use fluent for consistency
|
||||||
public void handleUpdateMultisigRequest(UpdateMultisigRequest message, NodeAddress peer, ErrorMessageHandler errorMessageHandler) {
|
public void handleUpdateMultisigRequest(UpdateMultisigRequest message, NodeAddress peer, ErrorMessageHandler errorMessageHandler) {
|
||||||
latchTrade();
|
latchTrade();
|
||||||
|
|
|
@ -19,12 +19,10 @@ package bisq.core.trade.protocol;
|
||||||
|
|
||||||
|
|
||||||
import bisq.core.trade.messages.DepositResponse;
|
import bisq.core.trade.messages.DepositResponse;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
|
||||||
import bisq.core.trade.messages.SignContractResponse;
|
import bisq.core.trade.messages.SignContractResponse;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
|
|
||||||
public interface TraderProtocol {
|
public interface TraderProtocol {
|
||||||
public void handleSignContractResponse(SignContractResponse message, NodeAddress peer);
|
public void handleSignContractResponse(SignContractResponse message, NodeAddress peer);
|
||||||
public void handleDepositResponse(DepositResponse response, NodeAddress peer);
|
public void handleDepositResponse(DepositResponse response, NodeAddress peer);
|
||||||
public void handlePaymentAccountPayloadRequest(PaymentAccountPayloadRequest request, NodeAddress peer);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,6 +64,10 @@ public final class TradingPeer implements PersistablePayload {
|
||||||
@Nullable
|
@Nullable
|
||||||
private byte[] paymentAccountPayloadHash;
|
private byte[] paymentAccountPayloadHash;
|
||||||
@Nullable
|
@Nullable
|
||||||
|
private byte[] encryptedPaymentAccountPayload;
|
||||||
|
@Nullable
|
||||||
|
private byte[] paymentAccountKey;
|
||||||
|
@Nullable
|
||||||
private PaymentAccountPayload paymentAccountPayload;
|
private PaymentAccountPayload paymentAccountPayload;
|
||||||
@Nullable
|
@Nullable
|
||||||
private String payoutAddressString;
|
private String payoutAddressString;
|
||||||
|
@ -134,6 +138,8 @@ public final class TradingPeer implements PersistablePayload {
|
||||||
Optional.ofNullable(paymentAccountId).ifPresent(builder::setPaymentAccountId);
|
Optional.ofNullable(paymentAccountId).ifPresent(builder::setPaymentAccountId);
|
||||||
Optional.ofNullable(paymentMethodId).ifPresent(builder::setPaymentMethodId);
|
Optional.ofNullable(paymentMethodId).ifPresent(builder::setPaymentMethodId);
|
||||||
Optional.ofNullable(paymentAccountPayloadHash).ifPresent(e -> builder.setPaymentAccountPayloadHash(ByteString.copyFrom(paymentAccountPayloadHash)));
|
Optional.ofNullable(paymentAccountPayloadHash).ifPresent(e -> builder.setPaymentAccountPayloadHash(ByteString.copyFrom(paymentAccountPayloadHash)));
|
||||||
|
Optional.ofNullable(encryptedPaymentAccountPayload).ifPresent(e -> builder.setEncryptedPaymentAccountPayload(ByteString.copyFrom(e)));
|
||||||
|
Optional.ofNullable(paymentAccountKey).ifPresent(e -> builder.setPaymentAccountKey(ByteString.copyFrom(e)));
|
||||||
Optional.ofNullable(paymentAccountPayload).ifPresent(e -> builder.setPaymentAccountPayload((protobuf.PaymentAccountPayload) e.toProtoMessage()));
|
Optional.ofNullable(paymentAccountPayload).ifPresent(e -> builder.setPaymentAccountPayload((protobuf.PaymentAccountPayload) e.toProtoMessage()));
|
||||||
Optional.ofNullable(payoutAddressString).ifPresent(builder::setPayoutAddressString);
|
Optional.ofNullable(payoutAddressString).ifPresent(builder::setPayoutAddressString);
|
||||||
Optional.ofNullable(contractAsJson).ifPresent(builder::setContractAsJson);
|
Optional.ofNullable(contractAsJson).ifPresent(builder::setContractAsJson);
|
||||||
|
@ -173,6 +179,8 @@ public final class TradingPeer implements PersistablePayload {
|
||||||
tradingPeer.setPaymentAccountId(ProtoUtil.stringOrNullFromProto(proto.getPaymentAccountId()));
|
tradingPeer.setPaymentAccountId(ProtoUtil.stringOrNullFromProto(proto.getPaymentAccountId()));
|
||||||
tradingPeer.setPaymentMethodId(ProtoUtil.stringOrNullFromProto(proto.getPaymentMethodId()));
|
tradingPeer.setPaymentMethodId(ProtoUtil.stringOrNullFromProto(proto.getPaymentMethodId()));
|
||||||
tradingPeer.setPaymentAccountPayloadHash(proto.getPaymentAccountPayloadHash().toByteArray());
|
tradingPeer.setPaymentAccountPayloadHash(proto.getPaymentAccountPayloadHash().toByteArray());
|
||||||
|
tradingPeer.setEncryptedPaymentAccountPayload(proto.getEncryptedPaymentAccountPayload().toByteArray());
|
||||||
|
tradingPeer.setPaymentAccountKey(ProtoUtil.byteArrayOrNullFromProto(proto.getPaymentAccountKey()));
|
||||||
tradingPeer.setPaymentAccountPayload(proto.hasPaymentAccountPayload() ? coreProtoResolver.fromProto(proto.getPaymentAccountPayload()) : null);
|
tradingPeer.setPaymentAccountPayload(proto.hasPaymentAccountPayload() ? coreProtoResolver.fromProto(proto.getPaymentAccountPayload()) : null);
|
||||||
tradingPeer.setPayoutAddressString(ProtoUtil.stringOrNullFromProto(proto.getPayoutAddressString()));
|
tradingPeer.setPayoutAddressString(ProtoUtil.stringOrNullFromProto(proto.getPayoutAddressString()));
|
||||||
tradingPeer.setContractAsJson(ProtoUtil.stringOrNullFromProto(proto.getContractAsJson()));
|
tradingPeer.setContractAsJson(ProtoUtil.stringOrNullFromProto(proto.getContractAsJson()));
|
||||||
|
|
|
@ -96,6 +96,7 @@ public class ArbitratorProcessesDepositRequest extends TradeTask {
|
||||||
// set deposit info
|
// set deposit info
|
||||||
trader.setDepositTxHex(request.getDepositTxHex());
|
trader.setDepositTxHex(request.getDepositTxHex());
|
||||||
trader.setDepositTxKey(request.getDepositTxKey());
|
trader.setDepositTxKey(request.getDepositTxKey());
|
||||||
|
if (request.getPaymentAccountKey() != null) trader.setPaymentAccountKey(request.getPaymentAccountKey());
|
||||||
|
|
||||||
// relay deposit txs when both available
|
// relay deposit txs when both available
|
||||||
// TODO (woodser): add small delay so tx has head start against double spend attempts?
|
// TODO (woodser): add small delay so tx has head start against double spend attempts?
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* 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.app.Version;
|
||||||
|
import bisq.common.crypto.PubKeyRing;
|
||||||
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
|
import bisq.core.trade.Trade;
|
||||||
|
import bisq.core.trade.messages.PaymentAccountKeyResponse;
|
||||||
|
import bisq.network.p2p.NodeAddress;
|
||||||
|
import bisq.network.p2p.SendDirectMessageListener;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class ArbitratorProcessesPaymentAccountKeyRequest extends TradeTask {
|
||||||
|
|
||||||
|
@SuppressWarnings({"unused"})
|
||||||
|
public ArbitratorProcessesPaymentAccountKeyRequest(TaskRunner taskHandler, Trade trade) {
|
||||||
|
super(taskHandler, trade);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() {
|
||||||
|
try {
|
||||||
|
runInterceptHook();
|
||||||
|
|
||||||
|
// create response for buyer with key to decrypt seller's payment account payload
|
||||||
|
PaymentAccountKeyResponse response = new PaymentAccountKeyResponse(
|
||||||
|
trade.getId(),
|
||||||
|
processModel.getMyNodeAddress(),
|
||||||
|
processModel.getPubKeyRing(),
|
||||||
|
UUID.randomUUID().toString(),
|
||||||
|
Version.getP2PMessageVersion(),
|
||||||
|
trade.getSeller().getPaymentAccountKey(),
|
||||||
|
null
|
||||||
|
);
|
||||||
|
|
||||||
|
// send response to buyer
|
||||||
|
boolean isMakerBuyer = trade.getOffer().isBuyOffer();
|
||||||
|
NodeAddress buyerAddress = isMakerBuyer ? trade.getMakerNodeAddress() : trade.getTakerNodeAddress(); // TODO: trade.getBuyer().getNodeAddress()
|
||||||
|
PubKeyRing buyerPubKeyRing = isMakerBuyer ? trade.getMakerPubKeyRing() : trade.getTakerPubKeyRing();
|
||||||
|
log.info("Arbitrator sending PaymentAccountKeyResponse to buyer={}; offerId={}", buyerAddress, trade.getId());
|
||||||
|
processModel.getP2PService().sendEncryptedDirectMessage(buyerAddress, buyerPubKeyRing, response, new SendDirectMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onArrived() {
|
||||||
|
log.info("{} arrived: trading peer={}; offerId={}; uid={}", response.getClass().getSimpleName(), buyerAddress, trade.getId());
|
||||||
|
complete();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFault(String errorMessage) {
|
||||||
|
log.error("Sending {} failed: uid={}; peer={}; error={}", response.getClass().getSimpleName(), buyerAddress, trade.getId(), errorMessage);
|
||||||
|
appendToErrorMessage("Sending message failed: message=" + response + "\nerrorMessage=" + errorMessage);
|
||||||
|
failed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Throwable t) {
|
||||||
|
failed(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -51,27 +51,29 @@ public class BuyerPreparesPaymentSentMessage extends TradeTask {
|
||||||
protected void run() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
// validate state
|
// validate state
|
||||||
|
Preconditions.checkNotNull(trade.getSeller().getPaymentAccountPayload(), "Seller's payment account payload is null");
|
||||||
Preconditions.checkNotNull(trade.getAmount(), "trade.getTradeAmount() must not be null");
|
Preconditions.checkNotNull(trade.getAmount(), "trade.getTradeAmount() must not be null");
|
||||||
Preconditions.checkNotNull(trade.getMakerDepositTx(), "trade.getMakerDepositTx() must not be null");
|
Preconditions.checkNotNull(trade.getMakerDepositTx(), "trade.getMakerDepositTx() must not be null");
|
||||||
Preconditions.checkNotNull(trade.getTakerDepositTx(), "trade.getTakerDepositTx() must not be null");
|
Preconditions.checkNotNull(trade.getTakerDepositTx(), "trade.getTakerDepositTx() must not be null");
|
||||||
checkNotNull(trade.getOffer(), "offer must not be null");
|
checkNotNull(trade.getOffer(), "offer must not be null");
|
||||||
|
|
||||||
// get multisig wallet
|
// get multisig wallet
|
||||||
XmrWalletService walletService = processModel.getProvider().getXmrWalletService();
|
XmrWalletService walletService = processModel.getProvider().getXmrWalletService();
|
||||||
MoneroWallet multisigWallet = walletService.getMultisigWallet(trade.getId());
|
MoneroWallet multisigWallet = walletService.getMultisigWallet(trade.getId());
|
||||||
|
|
||||||
// create payout tx if we have seller's updated multisig hex
|
// create payout tx if we have seller's updated multisig hex
|
||||||
if (!multisigWallet.isMultisigImportNeeded()) {
|
if (trade.getTradingPeer().getUpdatedMultisigHex() != null) {
|
||||||
log.info("Buyer creating unsigned payout tx");
|
log.info("Buyer creating unsigned payout tx");
|
||||||
|
multisigWallet.importMultisigHex(trade.getTradingPeer().getUpdatedMultisigHex());
|
||||||
MoneroTxWallet payoutTx = trade.createPayoutTx();
|
MoneroTxWallet payoutTx = trade.createPayoutTx();
|
||||||
trade.getBuyer().setPayoutTx(payoutTx);
|
trade.getBuyer().setPayoutTx(payoutTx);
|
||||||
trade.getBuyer().setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
trade.getBuyer().setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||||
} else {
|
} else {
|
||||||
if (trade.getSelf().getUpdatedMultisigHex() == null) trade.getSelf().setUpdatedMultisigHex(multisigWallet.exportMultisigHex()); // only export multisig hex once
|
if (trade.getSelf().getUpdatedMultisigHex() == null) trade.getSelf().setUpdatedMultisigHex(multisigWallet.exportMultisigHex()); // only export multisig hex once
|
||||||
}
|
}
|
||||||
|
|
||||||
// close multisig wallet
|
// close multisig wallet
|
||||||
walletService.closeMultisigWallet(trade.getId());
|
walletService.closeMultisigWallet(trade.getId());
|
||||||
complete();
|
complete();
|
||||||
|
|
|
@ -18,24 +18,23 @@
|
||||||
package bisq.core.trade.protocol.tasks;
|
package bisq.core.trade.protocol.tasks;
|
||||||
|
|
||||||
|
|
||||||
|
import bisq.common.crypto.Encryption;
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
import bisq.core.payment.payload.PaymentAccountPayload;
|
||||||
|
import bisq.core.proto.network.CoreNetworkProtoResolver;
|
||||||
import bisq.core.trade.MakerTrade;
|
import bisq.core.trade.MakerTrade;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.Trade.State;
|
import bisq.core.trade.messages.PaymentAccountKeyResponse;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
import java.time.Clock;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.fxmisc.easybind.EasyBind;
|
|
||||||
import org.fxmisc.easybind.Subscription;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
public class ProcessPaymentAccountPayloadRequest extends TradeTask {
|
public class BuyerProcessesPaymentAccountKeyResponse extends TradeTask {
|
||||||
|
|
||||||
private Subscription tradeStateSubscription;
|
|
||||||
|
|
||||||
@SuppressWarnings({"unused"})
|
@SuppressWarnings({"unused"})
|
||||||
public ProcessPaymentAccountPayloadRequest(TaskRunner taskHandler, Trade trade) {
|
public BuyerProcessesPaymentAccountKeyResponse(TaskRunner taskHandler, Trade trade) {
|
||||||
super(taskHandler, trade);
|
super(taskHandler, trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,11 +42,27 @@ public class ProcessPaymentAccountPayloadRequest extends TradeTask {
|
||||||
protected void run() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
if (trade.getTradingPeer().getPaymentAccountPayload() != null) throw new RuntimeException("Peer's payment account payload has already been set");
|
|
||||||
|
|
||||||
// get peer's payment account payload
|
// update peer node address if not from arbitrator
|
||||||
PaymentAccountPayloadRequest request = (PaymentAccountPayloadRequest) processModel.getTradeMessage(); // TODO (woodser): verify request
|
if (!processModel.getTempTradingPeerNodeAddress().equals(trade.getArbitratorNodeAddress())) {
|
||||||
PaymentAccountPayload paymentAccountPayload = request.getPaymentAccountPayload();
|
trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
// buyer may already have decrypted payment account payload from arbitrator request
|
||||||
|
if (trade.getTradingPeer().getPaymentAccountPayload() != null) {
|
||||||
|
complete();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get peer's payment account payload key
|
||||||
|
PaymentAccountKeyResponse request = (PaymentAccountKeyResponse) processModel.getTradeMessage(); // TODO (woodser): verify request
|
||||||
|
trade.getTradingPeer().setPaymentAccountKey(request.getPaymentAccountKey());
|
||||||
|
|
||||||
|
// decrypt peer's payment account payload
|
||||||
|
SecretKey sk = Encryption.getSecretKeyFromBytes(trade.getTradingPeer().getPaymentAccountKey());
|
||||||
|
byte[] decryptedPaymentAccountPayload = Encryption.decrypt(trade.getTradingPeer().getEncryptedPaymentAccountPayload(), sk);
|
||||||
|
CoreNetworkProtoResolver resolver = new CoreNetworkProtoResolver(Clock.systemDefaultZone()); // TODO: reuse resolver from elsewhere?
|
||||||
|
PaymentAccountPayload paymentAccountPayload = resolver.fromProto(protobuf.PaymentAccountPayload.parseFrom(decryptedPaymentAccountPayload));
|
||||||
|
|
||||||
// verify hash of payment account payload
|
// verify hash of payment account payload
|
||||||
byte[] peerPaymentAccountPayloadHash = trade instanceof MakerTrade ? trade.getContract().getTakerPaymentAccountPayloadHash() : trade.getContract().getMakerPaymentAccountPayloadHash();
|
byte[] peerPaymentAccountPayloadHash = trade instanceof MakerTrade ? trade.getContract().getTakerPaymentAccountPayloadHash() : trade.getContract().getMakerPaymentAccountPayloadHash();
|
||||||
|
@ -56,6 +71,9 @@ public class ProcessPaymentAccountPayloadRequest extends TradeTask {
|
||||||
// set payment account payload
|
// set payment account payload
|
||||||
trade.getTradingPeer().setPaymentAccountPayload(paymentAccountPayload);
|
trade.getTradingPeer().setPaymentAccountPayload(paymentAccountPayload);
|
||||||
|
|
||||||
|
// store updated multisig hex for processing on payment sent
|
||||||
|
trade.getTradingPeer().setUpdatedMultisigHex(request.getUpdatedMultisigHex());
|
||||||
|
|
||||||
// persist and complete
|
// persist and complete
|
||||||
processModel.getTradeManager().requestPersistence();
|
processModel.getTradeManager().requestPersistence();
|
||||||
complete();
|
complete();
|
|
@ -54,7 +54,7 @@ public class BuyerProcessesPaymentReceivedMessage extends TradeTask {
|
||||||
|
|
||||||
// update to the latest peer address of our peer if the message is correct
|
// update to the latest peer address of our peer if the message is correct
|
||||||
trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress());
|
trade.setTradingPeerNodeAddress(processModel.getTempTradingPeerNodeAddress());
|
||||||
|
|
||||||
// handle if payout tx is not seen on network
|
// handle if payout tx is not seen on network
|
||||||
if (trade.getPayoutTx() == null) {
|
if (trade.getPayoutTx() == null) {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* 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.app.Version;
|
||||||
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
|
import bisq.core.trade.Trade;
|
||||||
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
|
import bisq.core.trade.messages.PaymentAccountKeyRequest;
|
||||||
|
import bisq.network.p2p.SendDirectMessageListener;
|
||||||
|
import java.util.UUID;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
public class BuyerSendsPaymentAccountKeyRequestToArbitrator extends TradeTask {
|
||||||
|
|
||||||
|
@SuppressWarnings({"unused"})
|
||||||
|
public BuyerSendsPaymentAccountKeyRequestToArbitrator(TaskRunner taskHandler, Trade trade) {
|
||||||
|
super(taskHandler, trade);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() {
|
||||||
|
try {
|
||||||
|
runInterceptHook();
|
||||||
|
|
||||||
|
// create request to arbitrator
|
||||||
|
PaymentAccountKeyRequest request = new PaymentAccountKeyRequest(
|
||||||
|
trade.getId(),
|
||||||
|
processModel.getMyNodeAddress(),
|
||||||
|
processModel.getPubKeyRing(),
|
||||||
|
UUID.randomUUID().toString(),
|
||||||
|
Version.getP2PMessageVersion()
|
||||||
|
);
|
||||||
|
|
||||||
|
// send request to arbitrator
|
||||||
|
log.info("Sending {} with offerId {} and uid {} to arbitrator {} with pub key ring {}", request.getClass().getSimpleName(), request.getTradeId(), request.getUid(), trade.getArbitratorNodeAddress(), trade.getArbitratorPubKeyRing());
|
||||||
|
processModel.getP2PService().sendEncryptedDirectMessage(
|
||||||
|
trade.getArbitratorNodeAddress(),
|
||||||
|
trade.getArbitratorPubKeyRing(),
|
||||||
|
request,
|
||||||
|
new SendDirectMessageListener() {
|
||||||
|
@Override
|
||||||
|
public void onArrived() {
|
||||||
|
log.info("{} arrived at arbitrator: offerId={}", PaymentAccountKeyRequest.class.getSimpleName(), trade.getId());
|
||||||
|
complete();
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onFault(String errorMessage) {
|
||||||
|
log.warn("Failed to send {} to arbitrator, error={}.", PaymentAccountKeyRequest.class.getSimpleName(), errorMessage);
|
||||||
|
failed();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Throwable t) {
|
||||||
|
failed(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -81,6 +81,7 @@ public class MaybeSendSignContractRequest extends TradeTask {
|
||||||
processModel.setDepositTxXmr(depositTx); // TODO: trade.getSelf().setDepositTx()
|
processModel.setDepositTxXmr(depositTx); // TODO: trade.getSelf().setDepositTx()
|
||||||
trade.getSelf().setDepositTxHash(depositTx.getHash());
|
trade.getSelf().setDepositTxHash(depositTx.getHash());
|
||||||
trade.getSelf().setPayoutAddressString(trade.getXmrWalletService().getAddressEntry(processModel.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).get().getAddressString()); // TODO (woodser): allow custom payout address?
|
trade.getSelf().setPayoutAddressString(trade.getXmrWalletService().getAddressEntry(processModel.getOffer().getId(), XmrAddressEntry.Context.TRADE_PAYOUT).get().getAddressString()); // TODO (woodser): allow custom payout address?
|
||||||
|
trade.getSelf().setPaymentAccountPayload(trade.getProcessModel().getPaymentAccountPayload(trade));
|
||||||
|
|
||||||
// create request for peer and arbitrator to sign contract
|
// create request for peer and arbitrator to sign contract
|
||||||
SignContractRequest request = new SignContractRequest(
|
SignContractRequest request = new SignContractRequest(
|
||||||
|
@ -91,7 +92,7 @@ public class MaybeSendSignContractRequest extends TradeTask {
|
||||||
Version.getP2PMessageVersion(),
|
Version.getP2PMessageVersion(),
|
||||||
new Date().getTime(),
|
new Date().getTime(),
|
||||||
trade.getProcessModel().getAccountId(),
|
trade.getProcessModel().getAccountId(),
|
||||||
trade.getProcessModel().getPaymentAccountPayload(trade).getHash(),
|
trade.getSelf().getPaymentAccountPayload().getHash(),
|
||||||
trade.getSelf().getPayoutAddressString(),
|
trade.getSelf().getPayoutAddressString(),
|
||||||
depositTx.getHash());
|
depositTx.getHash());
|
||||||
|
|
||||||
|
|
|
@ -18,13 +18,8 @@
|
||||||
package bisq.core.trade.protocol.tasks;
|
package bisq.core.trade.protocol.tasks;
|
||||||
|
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.messages.PaymentAccountPayloadRequest;
|
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
|
||||||
import java.util.Date;
|
|
||||||
import java.util.UUID;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -39,37 +34,9 @@ public class ProcessDepositResponse extends TradeTask {
|
||||||
protected void run() {
|
protected void run() {
|
||||||
try {
|
try {
|
||||||
runInterceptHook();
|
runInterceptHook();
|
||||||
|
|
||||||
// arbitrator has broadcast deposit txs
|
|
||||||
trade.setState(Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS);
|
trade.setState(Trade.State.ARBITRATOR_PUBLISHED_DEPOSIT_TXS);
|
||||||
|
processModel.getTradeManager().requestPersistence();
|
||||||
// set payment account payload
|
complete();
|
||||||
trade.getSelf().setPaymentAccountPayload(processModel.getPaymentAccountPayload(trade));
|
|
||||||
|
|
||||||
// create request with payment account payload
|
|
||||||
PaymentAccountPayloadRequest request = new PaymentAccountPayloadRequest(
|
|
||||||
trade.getOffer().getId(),
|
|
||||||
processModel.getMyNodeAddress(),
|
|
||||||
processModel.getPubKeyRing(),
|
|
||||||
UUID.randomUUID().toString(),
|
|
||||||
Version.getP2PMessageVersion(),
|
|
||||||
new Date().getTime(),
|
|
||||||
trade.getSelf().getPaymentAccountPayload());
|
|
||||||
|
|
||||||
// send payment account payload to trading peer
|
|
||||||
processModel.getP2PService().sendEncryptedDirectMessage(trade.getTradingPeerNodeAddress(), trade.getTradingPeerPubKeyRing(), request, new SendDirectMessageListener() {
|
|
||||||
@Override
|
|
||||||
public void onArrived() {
|
|
||||||
log.info("{} arrived: trading peer={}; offerId={}; uid={}", request.getClass().getSimpleName(), trade.getTradingPeerNodeAddress(), trade.getId());
|
|
||||||
complete();
|
|
||||||
}
|
|
||||||
@Override
|
|
||||||
public void onFault(String errorMessage) {
|
|
||||||
log.error("Sending {} failed: uid={}; peer={}; error={}", request.getClass().getSimpleName(), trade.getTradingPeerNodeAddress(), trade.getId(), errorMessage);
|
|
||||||
appendToErrorMessage("Sending message failed: message=" + request + "\nerrorMessage=" + errorMessage);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(t);
|
failed(t);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,10 @@ package bisq.core.trade.protocol.tasks;
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import bisq.common.app.Version;
|
import bisq.common.app.Version;
|
||||||
|
import bisq.common.crypto.Encryption;
|
||||||
import bisq.common.crypto.Hash;
|
import bisq.common.crypto.Hash;
|
||||||
import bisq.common.crypto.PubKeyRing;
|
import bisq.common.crypto.PubKeyRing;
|
||||||
|
import bisq.common.crypto.ScryptUtil;
|
||||||
import bisq.common.crypto.Sig;
|
import bisq.common.crypto.Sig;
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
import bisq.core.trade.ArbitratorTrade;
|
import bisq.core.trade.ArbitratorTrade;
|
||||||
|
@ -37,6 +39,7 @@ import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
import bisq.network.p2p.SendDirectMessageListener;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
|
@ -61,7 +64,7 @@ public class ProcessSignContractRequest extends TradeTask {
|
||||||
TradingPeer trader = trade.getTradingPeer(request.getSenderNodeAddress());
|
TradingPeer trader = trade.getTradingPeer(request.getSenderNodeAddress());
|
||||||
trader.setDepositTxHash(request.getDepositTxHash());
|
trader.setDepositTxHash(request.getDepositTxHash());
|
||||||
trader.setAccountId(request.getAccountId());
|
trader.setAccountId(request.getAccountId());
|
||||||
trader.setPaymentAccountPayloadHash(request.getPaymentAccountPayloadHash());
|
trader.setPaymentAccountPayloadHash(request.getPaymentAccountPayloadHash()); // TODO: only seller's payment account payload is shared, so no need to send payment hash
|
||||||
trader.setPayoutAddressString(request.getPayoutAddress());
|
trader.setPayoutAddressString(request.getPayoutAddress());
|
||||||
|
|
||||||
// sign contract only when both deposit txs hashes known
|
// sign contract only when both deposit txs hashes known
|
||||||
|
@ -82,6 +85,20 @@ public class ProcessSignContractRequest extends TradeTask {
|
||||||
trade.setContractAsJson(contractAsJson);
|
trade.setContractAsJson(contractAsJson);
|
||||||
trade.setContractHash(Hash.getSha256Hash(checkNotNull(contractAsJson)));
|
trade.setContractHash(Hash.getSha256Hash(checkNotNull(contractAsJson)));
|
||||||
trade.getSelf().setContractSignature(signature);
|
trade.getSelf().setContractSignature(signature);
|
||||||
|
|
||||||
|
// seller sends encrypted payment account payload
|
||||||
|
byte[] encryptedPaymentAccountPayload = null;
|
||||||
|
if (trade.isSeller()) {
|
||||||
|
|
||||||
|
// generate random key to encrypt payment account payload
|
||||||
|
byte[] decryptionKey = ScryptUtil.getKeyCrypterScrypt().deriveKey(UUID.randomUUID().toString()).getKey();
|
||||||
|
trade.getSelf().setPaymentAccountKey(decryptionKey);
|
||||||
|
|
||||||
|
// encrypt payment account payload
|
||||||
|
byte[] unencrypted = trade.getSelf().getPaymentAccountPayload().toProtoMessage().toByteArray();
|
||||||
|
SecretKey sk = Encryption.getSecretKeyFromBytes(trade.getSelf().getPaymentAccountKey());
|
||||||
|
encryptedPaymentAccountPayload = Encryption.encrypt(unencrypted, sk);
|
||||||
|
}
|
||||||
|
|
||||||
// create response with contract signature
|
// create response with contract signature
|
||||||
SignContractResponse response = new SignContractResponse(
|
SignContractResponse response = new SignContractResponse(
|
||||||
|
@ -92,7 +109,8 @@ public class ProcessSignContractRequest extends TradeTask {
|
||||||
Version.getP2PMessageVersion(),
|
Version.getP2PMessageVersion(),
|
||||||
new Date().getTime(),
|
new Date().getTime(),
|
||||||
contractAsJson,
|
contractAsJson,
|
||||||
signature);
|
signature,
|
||||||
|
encryptedPaymentAccountPayload);
|
||||||
|
|
||||||
// get response recipients. only arbitrator sends response to both peers
|
// get response recipients. only arbitrator sends response to both peers
|
||||||
NodeAddress recipient1 = trade instanceof ArbitratorTrade ? trade.getMakerNodeAddress() : trade.getTradingPeerNodeAddress();
|
NodeAddress recipient1 = trade instanceof ArbitratorTrade ? trade.getMakerNodeAddress() : trade.getTradingPeerNodeAddress();
|
||||||
|
|
|
@ -61,6 +61,12 @@ public class ProcessSignContractResponse extends TradeTask {
|
||||||
else if (peer == processModel.getTaker()) peerPubKeyRing = trade.getTakerPubKeyRing();
|
else if (peer == processModel.getTaker()) peerPubKeyRing = trade.getTakerPubKeyRing();
|
||||||
else throw new RuntimeException(response.getClass().getSimpleName() + " is not from maker, taker, or arbitrator");
|
else throw new RuntimeException(response.getClass().getSimpleName() + " is not from maker, taker, or arbitrator");
|
||||||
|
|
||||||
|
// buyer saves seller's encrypted payment account payload
|
||||||
|
if (trade.isBuyer() && peer == trade.getSeller()) {
|
||||||
|
peer.setEncryptedPaymentAccountPayload(response.getEncryptedPaymentAccountPayload());
|
||||||
|
if (peer.getEncryptedPaymentAccountPayload() == null) throw new RuntimeException("Seller did not send encrypted payment account payload");
|
||||||
|
}
|
||||||
|
|
||||||
// verify signature
|
// verify signature
|
||||||
// TODO (woodser): transfer contract for convenient comparison?
|
// TODO (woodser): transfer contract for convenient comparison?
|
||||||
String signature = response.getContractSignature();
|
String signature = response.getContractSignature();
|
||||||
|
@ -85,7 +91,8 @@ public class ProcessSignContractResponse extends TradeTask {
|
||||||
new Date().getTime(),
|
new Date().getTime(),
|
||||||
trade.getSelf().getContractSignature(),
|
trade.getSelf().getContractSignature(),
|
||||||
processModel.getDepositTxXmr().getFullHex(),
|
processModel.getDepositTxXmr().getFullHex(),
|
||||||
processModel.getDepositTxXmr().getKey());
|
processModel.getDepositTxXmr().getKey(),
|
||||||
|
trade.getSelf().getPaymentAccountKey());
|
||||||
|
|
||||||
// send request to arbitrator
|
// send request to arbitrator
|
||||||
log.info("Sending {} to arbitrator {}; offerId={}; uid={}", request.getClass().getSimpleName(), trade.getArbitratorNodeAddress(), trade.getId(), request.getUid());
|
log.info("Sending {} to arbitrator {}; offerId={}; uid={}", request.getClass().getSimpleName(), trade.getArbitratorNodeAddress(), trade.getId(), request.getUid());
|
||||||
|
|
|
@ -49,8 +49,6 @@ public class SellerPreparesPaymentReceivedMessage extends TradeTask {
|
||||||
trade.getSeller().setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
trade.getSeller().setPayoutTxHex(payoutTx.getTxSet().getMultisigTxHex());
|
||||||
}
|
}
|
||||||
|
|
||||||
// close multisig wallet
|
|
||||||
processModel.getProvider().getXmrWalletService().closeMultisigWallet(trade.getId());
|
|
||||||
complete();
|
complete();
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
failed(t);
|
failed(t);
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Haveno.
|
||||||
|
*
|
||||||
|
* Haveno is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Haveno is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package bisq.core.trade.protocol.tasks;
|
||||||
|
|
||||||
|
import bisq.core.btc.wallet.XmrWalletService;
|
||||||
|
import bisq.core.trade.Trade;
|
||||||
|
import bisq.core.trade.messages.PaymentAccountKeyResponse;
|
||||||
|
import bisq.core.trade.messages.TradeMailboxMessage;
|
||||||
|
import bisq.common.app.Version;
|
||||||
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
|
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import monero.wallet.MoneroWallet;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send the buyer payment account info when the trade state is confirmed.
|
||||||
|
*/
|
||||||
|
@Slf4j
|
||||||
|
public class SellerSendsPaymentAccountPayloadKey extends SendMailboxMessageTask {
|
||||||
|
private PaymentAccountKeyResponse message;
|
||||||
|
|
||||||
|
public SellerSendsPaymentAccountPayloadKey(TaskRunner<Trade> taskHandler, Trade trade) {
|
||||||
|
super(taskHandler, trade);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void run() {
|
||||||
|
try {
|
||||||
|
runInterceptHook();
|
||||||
|
super.run();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
failed(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected TradeMailboxMessage getTradeMailboxMessage(String tradeId) {
|
||||||
|
if (message == null) {
|
||||||
|
|
||||||
|
// set payment account payload
|
||||||
|
trade.getSelf().setPaymentAccountPayload(processModel.getPaymentAccountPayload(trade));
|
||||||
|
|
||||||
|
// get updated multisig hex
|
||||||
|
if (trade.getSelf().getUpdatedMultisigHex() == null) {
|
||||||
|
XmrWalletService walletService = processModel.getProvider().getXmrWalletService();
|
||||||
|
MoneroWallet multisigWallet = walletService.getMultisigWallet(tradeId);
|
||||||
|
trade.getSelf().setUpdatedMultisigHex(multisigWallet.exportMultisigHex()); // only export multisig hex once
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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
|
||||||
|
// 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.
|
||||||
|
String deterministicId = tradeId + processModel.getMyNodeAddress().getFullAddress();
|
||||||
|
message = new PaymentAccountKeyResponse(
|
||||||
|
trade.getOffer().getId(),
|
||||||
|
processModel.getMyNodeAddress(),
|
||||||
|
processModel.getPubKeyRing(),
|
||||||
|
deterministicId,
|
||||||
|
Version.getP2PMessageVersion(),
|
||||||
|
trade.getSelf().getPaymentAccountKey(),
|
||||||
|
trade.getSelf().getUpdatedMultisigHex());
|
||||||
|
}
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setStateSent() {
|
||||||
|
// no additional handling
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setStateArrived() {
|
||||||
|
// no additional handling
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setStateStoredInMailbox() {
|
||||||
|
// no additional handling
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setStateFault() {
|
||||||
|
// no additional handling
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,7 +18,6 @@
|
||||||
package bisq.core.trade.protocol.tasks;
|
package bisq.core.trade.protocol.tasks;
|
||||||
|
|
||||||
import bisq.core.account.sign.SignedWitness;
|
import bisq.core.account.sign.SignedWitness;
|
||||||
import bisq.core.account.witness.AccountAgeWitnessService;
|
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.messages.PaymentReceivedMessage;
|
import bisq.core.trade.messages.PaymentReceivedMessage;
|
||||||
import bisq.core.trade.messages.TradeMailboxMessage;
|
import bisq.core.trade.messages.TradeMailboxMessage;
|
||||||
|
@ -58,13 +57,6 @@ public class SellerSendsPaymentReceivedMessage extends SendMailboxMessageTask {
|
||||||
@Override
|
@Override
|
||||||
protected TradeMailboxMessage getTradeMailboxMessage(String id) {
|
protected TradeMailboxMessage getTradeMailboxMessage(String id) {
|
||||||
checkNotNull(trade.getSeller().getPayoutTxHex(), "Payout tx must not be null");
|
checkNotNull(trade.getSeller().getPayoutTxHex(), "Payout tx must not be null");
|
||||||
|
|
||||||
AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService();
|
|
||||||
if (accountAgeWitnessService.isSignWitnessTrade(trade)) {
|
|
||||||
// Broadcast is done in accountAgeWitness domain.
|
|
||||||
accountAgeWitnessService.traderSignAndPublishPeersAccountAgeWitness(trade).ifPresent(witness -> signedWitness = witness);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new PaymentReceivedMessage(
|
return new PaymentReceivedMessage(
|
||||||
id,
|
id,
|
||||||
processModel.getMyNodeAddress(),
|
processModel.getMyNodeAddress(),
|
||||||
|
@ -83,7 +75,7 @@ public class SellerSendsPaymentReceivedMessage extends SendMailboxMessageTask {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setStateArrived() {
|
protected void setStateArrived() {
|
||||||
trade.setState(trade.getState() == Trade.State.SELLER_PUBLISHED_PAYOUT_TX ? Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG : Trade.State.SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG);
|
trade.setState(trade.getState() == Trade.State.SELLER_SENT_PAYOUT_TX_PUBLISHED_MSG ? Trade.State.SELLER_SAW_ARRIVED_PAYOUT_TX_PUBLISHED_MSG : Trade.State.SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG);
|
||||||
log.info("Seller's PaymentReceivedMessage arrived: tradeId={} at peer {} SignedWitness {}",
|
log.info("Seller's PaymentReceivedMessage arrived: tradeId={} at peer {} SignedWitness {}",
|
||||||
trade.getId(), trade.getTradingPeerNodeAddress(), signedWitness);
|
trade.getId(), trade.getTradingPeerNodeAddress(), signedWitness);
|
||||||
processModel.getTradeManager().requestPersistence();
|
processModel.getTradeManager().requestPersistence();
|
||||||
|
|
|
@ -15,12 +15,11 @@
|
||||||
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
|
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package bisq.core.trade.protocol;
|
package bisq.core.trade.protocol.tasks;
|
||||||
|
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
import bisq.core.btc.model.XmrAddressEntry;
|
import bisq.core.btc.model.XmrAddressEntry;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.protocol.tasks.TradeTask;
|
|
||||||
import bisq.core.util.ParsingUtils;
|
import bisq.core.util.ParsingUtils;
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
|
@ -15,7 +15,7 @@
|
||||||
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
|
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package bisq.core.trade.protocol;
|
package bisq.core.trade.protocol.tasks;
|
||||||
|
|
||||||
import bisq.core.offer.availability.DisputeAgentSelection;
|
import bisq.core.offer.availability.DisputeAgentSelection;
|
||||||
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
import bisq.core.support.dispute.arbitration.arbitrator.Arbitrator;
|
||||||
|
@ -23,7 +23,6 @@ import bisq.core.support.dispute.arbitration.arbitrator.ArbitratorManager;
|
||||||
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
import bisq.core.support.dispute.mediation.mediator.Mediator;
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.messages.InitTradeRequest;
|
import bisq.core.trade.messages.InitTradeRequest;
|
||||||
import bisq.core.trade.protocol.tasks.TradeTask;
|
|
||||||
import bisq.core.trade.statistics.TradeStatisticsManager;
|
import bisq.core.trade.statistics.TradeStatisticsManager;
|
||||||
import bisq.network.p2p.NodeAddress;
|
import bisq.network.p2p.NodeAddress;
|
||||||
import bisq.network.p2p.SendDirectMessageListener;
|
import bisq.network.p2p.SendDirectMessageListener;
|
|
@ -15,11 +15,9 @@
|
||||||
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
|
* along with Haveno. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package bisq.core.trade.protocol;
|
package bisq.core.trade.protocol.tasks;
|
||||||
|
|
||||||
import bisq.core.trade.Trade;
|
import bisq.core.trade.Trade;
|
||||||
import bisq.core.trade.protocol.tasks.TradeTask;
|
|
||||||
|
|
||||||
import bisq.common.taskrunner.TaskRunner;
|
import bisq.common.taskrunner.TaskRunner;
|
||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
|
@ -26,7 +26,6 @@ import bisq.core.offer.availability.tasks.SendOfferAvailabilityRequest;
|
||||||
import bisq.core.offer.placeoffer.tasks.AddToOfferBook;
|
import bisq.core.offer.placeoffer.tasks.AddToOfferBook;
|
||||||
import bisq.core.offer.placeoffer.tasks.MakerReservesOfferFunds;
|
import bisq.core.offer.placeoffer.tasks.MakerReservesOfferFunds;
|
||||||
import bisq.core.offer.placeoffer.tasks.ValidateOffer;
|
import bisq.core.offer.placeoffer.tasks.ValidateOffer;
|
||||||
import bisq.core.trade.protocol.TakerVerifyMakerFeePayment;
|
|
||||||
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
import bisq.core.trade.protocol.tasks.ApplyFilter;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerPreparesPaymentSentMessage;
|
import bisq.core.trade.protocol.tasks.BuyerPreparesPaymentSentMessage;
|
||||||
import bisq.core.trade.protocol.tasks.BuyerProcessesPaymentReceivedMessage;
|
import bisq.core.trade.protocol.tasks.BuyerProcessesPaymentReceivedMessage;
|
||||||
|
@ -39,6 +38,7 @@ import bisq.core.trade.protocol.tasks.SellerProcessesPaymentSentMessage;
|
||||||
import bisq.core.trade.protocol.tasks.SellerPublishesDepositTx;
|
import bisq.core.trade.protocol.tasks.SellerPublishesDepositTx;
|
||||||
import bisq.core.trade.protocol.tasks.SellerPublishesTradeStatistics;
|
import bisq.core.trade.protocol.tasks.SellerPublishesTradeStatistics;
|
||||||
import bisq.core.trade.protocol.tasks.SellerSendsPaymentReceivedMessage;
|
import bisq.core.trade.protocol.tasks.SellerSendsPaymentReceivedMessage;
|
||||||
|
import bisq.core.trade.protocol.tasks.TakerVerifyMakerFeePayment;
|
||||||
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
|
import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness;
|
||||||
import bisq.common.taskrunner.Task;
|
import bisq.common.taskrunner.Task;
|
||||||
import bisq.common.util.Tuple2;
|
import bisq.common.util.Tuple2;
|
||||||
|
|
|
@ -74,14 +74,15 @@ message NetworkEnvelope {
|
||||||
SignContractResponse sign_contract_response = 1006;
|
SignContractResponse sign_contract_response = 1006;
|
||||||
DepositRequest deposit_request = 1007;
|
DepositRequest deposit_request = 1007;
|
||||||
DepositResponse deposit_response = 1008;
|
DepositResponse deposit_response = 1008;
|
||||||
PaymentAccountPayloadRequest payment_account_payload_request = 1009;
|
PaymentAccountKeyRequest payment_account_key_request = 1009;
|
||||||
PaymentSentMessage payment_sent_message = 1010;
|
PaymentAccountKeyResponse payment_account_key_response = 1010;
|
||||||
PaymentReceivedMessage payment_received_message = 1011;
|
PaymentSentMessage payment_sent_message = 1011;
|
||||||
PayoutTxPublishedMessage payout_tx_published_message = 1012;
|
PaymentReceivedMessage payment_received_message = 1012;
|
||||||
UpdateMultisigRequest update_multisig_request = 1013;
|
PayoutTxPublishedMessage payout_tx_published_message = 1013;
|
||||||
UpdateMultisigResponse update_multisig_response = 1014;
|
UpdateMultisigRequest update_multisig_request = 1014;
|
||||||
ArbitratorPayoutTxRequest arbitrator_payout_tx_request = 1015;
|
UpdateMultisigResponse update_multisig_response = 1015;
|
||||||
ArbitratorPayoutTxResponse arbitrator_payout_tx_response = 1016;
|
ArbitratorPayoutTxRequest arbitrator_payout_tx_request = 1016;
|
||||||
|
ArbitratorPayoutTxResponse arbitrator_payout_tx_response = 1017;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -329,6 +330,7 @@ message SignContractResponse {
|
||||||
int64 current_date = 5;
|
int64 current_date = 5;
|
||||||
string contract_as_json = 6;
|
string contract_as_json = 6;
|
||||||
string contract_signature = 7;
|
string contract_signature = 7;
|
||||||
|
bytes encrypted_payment_account_payload = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DepositRequest {
|
message DepositRequest {
|
||||||
|
@ -340,6 +342,7 @@ message DepositRequest {
|
||||||
string contract_signature = 6;
|
string contract_signature = 6;
|
||||||
string deposit_tx_hex = 7;
|
string deposit_tx_hex = 7;
|
||||||
string deposit_tx_key = 8;
|
string deposit_tx_key = 8;
|
||||||
|
bytes payment_account_key = 9;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DepositResponse {
|
message DepositResponse {
|
||||||
|
@ -350,13 +353,20 @@ message DepositResponse {
|
||||||
int64 current_date = 5;
|
int64 current_date = 5;
|
||||||
}
|
}
|
||||||
|
|
||||||
message PaymentAccountPayloadRequest {
|
message PaymentAccountKeyRequest {
|
||||||
string trade_id = 1;
|
string trade_id = 1;
|
||||||
NodeAddress sender_node_address = 2;
|
NodeAddress sender_node_address = 2;
|
||||||
PubKeyRing pub_key_ring = 3;
|
PubKeyRing pub_key_ring = 3;
|
||||||
string uid = 4;
|
string uid = 4;
|
||||||
int64 current_date = 5;
|
}
|
||||||
PaymentAccountPayload payment_account_payload = 6;
|
|
||||||
|
message PaymentAccountKeyResponse {
|
||||||
|
string trade_id = 1;
|
||||||
|
NodeAddress sender_node_address = 2;
|
||||||
|
PubKeyRing pub_key_ring = 3;
|
||||||
|
string uid = 4;
|
||||||
|
bytes payment_account_key = 5;
|
||||||
|
string updated_multisig_hex = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message UpdateMultisigRequest {
|
message UpdateMultisigRequest {
|
||||||
|
@ -1791,20 +1801,22 @@ message TradingPeer {
|
||||||
string payment_account_id = 2;
|
string payment_account_id = 2;
|
||||||
string payment_method_id = 3;
|
string payment_method_id = 3;
|
||||||
bytes payment_account_payload_hash = 4;
|
bytes payment_account_payload_hash = 4;
|
||||||
PaymentAccountPayload payment_account_payload = 5;
|
bytes encrypted_payment_account_payload = 5;
|
||||||
string payout_address_string = 6;
|
bytes payment_account_key = 6;
|
||||||
string contract_as_json = 7;
|
PaymentAccountPayload payment_account_payload = 7;
|
||||||
string contract_signature = 8;
|
string payout_address_string = 8;
|
||||||
bytes signature = 9; // TODO (woodser): remove unused fields? this was buyer-signed payout tx as bytes
|
string contract_as_json = 9;
|
||||||
PubKeyRing pub_key_ring = 10;
|
string contract_signature = 10;
|
||||||
bytes multi_sig_pub_key = 11;
|
bytes signature = 11; // TODO (woodser): remove unused fields? this was buyer-signed payout tx as bytes
|
||||||
repeated RawTransactionInput raw_transaction_inputs = 12;
|
PubKeyRing pub_key_ring = 12;
|
||||||
int64 change_output_value = 13;
|
bytes multi_sig_pub_key = 13;
|
||||||
string change_output_address = 14;
|
repeated RawTransactionInput raw_transaction_inputs = 14;
|
||||||
bytes account_age_witness_nonce = 15;
|
int64 change_output_value = 15;
|
||||||
bytes account_age_witness_signature = 16;
|
string change_output_address = 16;
|
||||||
int64 current_date = 17;
|
bytes account_age_witness_nonce = 17;
|
||||||
bytes mediated_payout_tx_signature = 18;
|
bytes account_age_witness_signature = 18;
|
||||||
|
int64 current_date = 19;
|
||||||
|
bytes mediated_payout_tx_signature = 20;
|
||||||
|
|
||||||
string reserve_tx_hash = 1001;
|
string reserve_tx_hash = 1001;
|
||||||
string reserve_tx_hex = 1002;
|
string reserve_tx_hex = 1002;
|
||||||
|
|
Loading…
Reference in a new issue