mirror of
https://github.com/boldsuck/haveno.git
synced 2024-12-22 20:19:21 +00:00
seller decrypts buyer payment info on payment sent
This commit is contained in:
parent
64925d0137
commit
1f32fc2cbe
9 changed files with 65 additions and 49 deletions
|
@ -24,8 +24,10 @@ import bisq.core.monetary.Price;
|
|||
import bisq.core.monetary.Volume;
|
||||
import bisq.core.offer.Offer;
|
||||
import bisq.core.offer.OfferDirection;
|
||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
||||
import bisq.core.payment.payload.PaymentMethod;
|
||||
import bisq.core.proto.CoreProtoResolver;
|
||||
import bisq.core.proto.network.CoreNetworkProtoResolver;
|
||||
import bisq.core.support.dispute.mediation.MediationResultState;
|
||||
import bisq.core.support.dispute.refund.RefundResultState;
|
||||
import bisq.core.support.messages.ChatMessage;
|
||||
|
@ -41,6 +43,7 @@ import bisq.network.p2p.AckMessage;
|
|||
import bisq.network.p2p.NodeAddress;
|
||||
import bisq.network.p2p.P2PService;
|
||||
import bisq.common.UserThread;
|
||||
import bisq.common.crypto.Encryption;
|
||||
import bisq.common.crypto.PubKeyRing;
|
||||
import bisq.common.proto.ProtoUtil;
|
||||
import bisq.common.taskrunner.Model;
|
||||
|
@ -63,6 +66,7 @@ import javafx.beans.property.StringProperty;
|
|||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
import java.math.BigInteger;
|
||||
import java.time.Clock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
|
@ -77,6 +81,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
import static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
|
@ -633,11 +638,6 @@ public abstract class Trade implements Tradable, Model {
|
|||
return trade;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// API
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public void initialize(ProcessModelServiceProvider serviceProvider) {
|
||||
serviceProvider.getArbitratorManager().getDisputeAgentByNodeAddress(arbitratorNodeAddress).ifPresent(arbitrator -> {
|
||||
arbitratorPubKeyRing = arbitrator.getPubKeyRing();
|
||||
|
@ -841,6 +841,32 @@ public abstract class Trade implements Tradable, Model {
|
|||
walletService.closeMultisigWallet(getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt the peer's payment account payload using the given key.
|
||||
*
|
||||
* @param paymentAccountKey is the key to decrypt the payment account payload
|
||||
*/
|
||||
public void decryptPeersPaymentAccountPayload(byte[] paymentAccountKey) {
|
||||
try {
|
||||
|
||||
// decrypt payment account payload
|
||||
getTradingPeer().setPaymentAccountKey(paymentAccountKey);
|
||||
SecretKey sk = Encryption.getSecretKeyFromBytes(getTradingPeer().getPaymentAccountKey());
|
||||
byte[] decryptedPaymentAccountPayload = Encryption.decrypt(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
|
||||
byte[] peerPaymentAccountPayloadHash = this instanceof MakerTrade ? getContract().getTakerPaymentAccountPayloadHash() : getContract().getMakerPaymentAccountPayloadHash();
|
||||
if (!Arrays.equals(paymentAccountPayload.getHash(), peerPaymentAccountPayloadHash)) throw new RuntimeException("Hash of peer's payment account payload does not match contract");
|
||||
|
||||
// set payment account payload
|
||||
getTradingPeer().setPaymentAccountPayload(paymentAccountPayload);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for deposit transactions to unlock and then apply the transactions.
|
||||
*
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
package bisq.core.trade.messages;
|
||||
|
||||
import bisq.network.p2p.NodeAddress;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
import bisq.common.app.Version;
|
||||
import bisq.common.proto.ProtoUtil;
|
||||
|
||||
|
@ -40,6 +40,8 @@ public final class PaymentSentMessage extends TradeMailboxMessage {
|
|||
private final String payoutTxHex;
|
||||
@Nullable
|
||||
private final String updatedMultisigHex;
|
||||
@Nullable
|
||||
private final byte[] paymentAccountKey;
|
||||
|
||||
// Added after v1.3.7
|
||||
// We use that for the XMR txKey but want to keep it generic to be flexible for data of other payment methods or assets.
|
||||
|
@ -53,7 +55,8 @@ public final class PaymentSentMessage extends TradeMailboxMessage {
|
|||
@Nullable String counterCurrencyExtraData,
|
||||
String uid,
|
||||
String signedPayoutTxHex,
|
||||
String updatedMultisigHex) {
|
||||
String updatedMultisigHex,
|
||||
@Nullable byte[] paymentAccountKey) {
|
||||
this(tradeId,
|
||||
buyerPayoutAddress,
|
||||
senderNodeAddress,
|
||||
|
@ -62,7 +65,8 @@ public final class PaymentSentMessage extends TradeMailboxMessage {
|
|||
uid,
|
||||
Version.getP2PMessageVersion(),
|
||||
signedPayoutTxHex,
|
||||
updatedMultisigHex);
|
||||
updatedMultisigHex,
|
||||
paymentAccountKey);
|
||||
}
|
||||
|
||||
|
||||
|
@ -78,7 +82,8 @@ public final class PaymentSentMessage extends TradeMailboxMessage {
|
|||
String uid,
|
||||
String messageVersion,
|
||||
@Nullable String signedPayoutTxHex,
|
||||
@Nullable String updatedMultisigHex) {
|
||||
@Nullable String updatedMultisigHex,
|
||||
@Nullable byte[] paymentAccountKey) {
|
||||
super(messageVersion, tradeId, uid);
|
||||
this.buyerPayoutAddress = buyerPayoutAddress;
|
||||
this.senderNodeAddress = senderNodeAddress;
|
||||
|
@ -86,6 +91,7 @@ public final class PaymentSentMessage extends TradeMailboxMessage {
|
|||
this.counterCurrencyExtraData = counterCurrencyExtraData;
|
||||
this.payoutTxHex = signedPayoutTxHex;
|
||||
this.updatedMultisigHex = updatedMultisigHex;
|
||||
this.paymentAccountKey = paymentAccountKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -100,6 +106,7 @@ public final class PaymentSentMessage extends TradeMailboxMessage {
|
|||
Optional.ofNullable(counterCurrencyExtraData).ifPresent(e -> builder.setCounterCurrencyExtraData(counterCurrencyExtraData));
|
||||
Optional.ofNullable(payoutTxHex).ifPresent(e -> builder.setPayoutTxHex(payoutTxHex));
|
||||
Optional.ofNullable(updatedMultisigHex).ifPresent(e -> builder.setUpdatedMultisigHex(updatedMultisigHex));
|
||||
Optional.ofNullable(paymentAccountKey).ifPresent(e -> builder.setPaymentAccountKey(ByteString.copyFrom(e)));
|
||||
|
||||
return getNetworkEnvelopeBuilder().setPaymentSentMessage(builder).build();
|
||||
}
|
||||
|
@ -114,7 +121,9 @@ public final class PaymentSentMessage extends TradeMailboxMessage {
|
|||
proto.getUid(),
|
||||
messageVersion,
|
||||
ProtoUtil.stringOrNullFromProto(proto.getPayoutTxHex()),
|
||||
ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()));
|
||||
ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()),
|
||||
ProtoUtil.byteArrayOrNullFromProto(proto.getPaymentAccountKey())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -128,6 +137,7 @@ public final class PaymentSentMessage extends TradeMailboxMessage {
|
|||
",\n uid='" + uid + '\'' +
|
||||
",\n payoutTxHex=" + payoutTxHex +
|
||||
",\n updatedMultisigHex=" + updatedMultisigHex +
|
||||
",\n paymentAccountKey=" + paymentAccountKey +
|
||||
"\n} " + super.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,16 +18,9 @@
|
|||
package bisq.core.trade.protocol.tasks;
|
||||
|
||||
|
||||
import bisq.common.crypto.Encryption;
|
||||
import bisq.common.taskrunner.TaskRunner;
|
||||
import bisq.core.payment.payload.PaymentAccountPayload;
|
||||
import bisq.core.proto.network.CoreNetworkProtoResolver;
|
||||
import bisq.core.trade.MakerTrade;
|
||||
import bisq.core.trade.Trade;
|
||||
import bisq.core.trade.messages.PaymentAccountKeyResponse;
|
||||
import java.time.Clock;
|
||||
import java.util.Arrays;
|
||||
import javax.crypto.SecretKey;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
@Slf4j
|
||||
|
@ -54,22 +47,9 @@ public class BuyerProcessesPaymentAccountKeyResponse extends TradeTask {
|
|||
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
|
||||
byte[] peerPaymentAccountPayloadHash = trade instanceof MakerTrade ? trade.getContract().getTakerPaymentAccountPayloadHash() : trade.getContract().getMakerPaymentAccountPayloadHash();
|
||||
if (!Arrays.equals(paymentAccountPayload.getHash(), peerPaymentAccountPayloadHash)) throw new RuntimeException("Hash of peer's payment account payload does not match contract");
|
||||
|
||||
// set payment account payload
|
||||
trade.getTradingPeer().setPaymentAccountPayload(paymentAccountPayload);
|
||||
PaymentAccountKeyResponse request = (PaymentAccountKeyResponse) processModel.getTradeMessage();
|
||||
trade.decryptPeersPaymentAccountPayload(request.getPaymentAccountKey());
|
||||
|
||||
// store updated multisig hex for processing on payment sent
|
||||
trade.getTradingPeer().setUpdatedMultisigHex(request.getUpdatedMultisigHex());
|
||||
|
|
|
@ -71,7 +71,8 @@ public class BuyerSendsPaymentSentMessage extends SendMailboxMessageTask {
|
|||
trade.getCounterCurrencyExtraData(),
|
||||
deterministicId,
|
||||
trade.getBuyer().getPayoutTxHex(),
|
||||
trade.getBuyer().getUpdatedMultisigHex()
|
||||
trade.getBuyer().getUpdatedMultisigHex(),
|
||||
trade.getSelf().getPaymentAccountKey()
|
||||
);
|
||||
}
|
||||
return message;
|
||||
|
|
|
@ -57,16 +57,16 @@ public class ProcessSignContractRequest extends TradeTask {
|
|||
protected void run() {
|
||||
try {
|
||||
runInterceptHook();
|
||||
|
||||
|
||||
// extract fields from request
|
||||
// TODO (woodser): verify request and from maker or taker
|
||||
SignContractRequest request = (SignContractRequest) processModel.getTradeMessage();
|
||||
TradingPeer trader = trade.getTradingPeer(request.getSenderNodeAddress());
|
||||
trader.setDepositTxHash(request.getDepositTxHash());
|
||||
trader.setAccountId(request.getAccountId());
|
||||
trader.setPaymentAccountPayloadHash(request.getPaymentAccountPayloadHash()); // TODO: only seller's payment account payload is shared, so no need to send payment hash
|
||||
trader.setPaymentAccountPayloadHash(request.getPaymentAccountPayloadHash());
|
||||
trader.setPayoutAddressString(request.getPayoutAddress());
|
||||
|
||||
|
||||
// sign contract only when both deposit txs hashes known
|
||||
// TODO (woodser): synchronize contract creation; both requests received at the same time
|
||||
// TODO (woodser): remove makerDepositTxId and takerDepositTxId from Trade
|
||||
|
@ -85,10 +85,10 @@ public class ProcessSignContractRequest extends TradeTask {
|
|||
trade.setContractAsJson(contractAsJson);
|
||||
trade.setContractHash(Hash.getSha256Hash(checkNotNull(contractAsJson)));
|
||||
trade.getSelf().setContractSignature(signature);
|
||||
|
||||
// seller sends encrypted payment account payload
|
||||
|
||||
// traders send encrypted payment account payload
|
||||
byte[] encryptedPaymentAccountPayload = null;
|
||||
if (trade.isSeller()) {
|
||||
if (!trade.isArbitrator()) {
|
||||
|
||||
// generate random key to encrypt payment account payload
|
||||
byte[] decryptionKey = ScryptUtil.getKeyCrypterScrypt().deriveKey(UUID.randomUUID().toString()).getKey();
|
||||
|
|
|
@ -61,11 +61,9 @@ public class ProcessSignContractResponse extends TradeTask {
|
|||
else if (peer == processModel.getTaker()) peerPubKeyRing = trade.getTakerPubKeyRing();
|
||||
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");
|
||||
}
|
||||
// save peer's encrypted payment account payload
|
||||
peer.setEncryptedPaymentAccountPayload(response.getEncryptedPaymentAccountPayload());
|
||||
if (peer.getEncryptedPaymentAccountPayload() == null) throw new RuntimeException("Peer did not send encrypted payment account payload");
|
||||
|
||||
// verify signature
|
||||
// TODO (woodser): transfer contract for convenient comparison?
|
||||
|
|
|
@ -45,7 +45,10 @@ public class SellerProcessesPaymentSentMessage extends TradeTask {
|
|||
trade.getBuyer().setPayoutAddressString(Validator.nonEmptyStringOf(message.getBuyerPayoutAddress())); // TODO (woodser): verify against contract
|
||||
trade.getBuyer().setPayoutTxHex(message.getPayoutTxHex());
|
||||
trade.getBuyer().setUpdatedMultisigHex(message.getUpdatedMultisigHex());
|
||||
|
||||
|
||||
// decrypt peer's payment account payload
|
||||
trade.decryptPeersPaymentAccountPayload(message.getPaymentAccountKey());
|
||||
|
||||
// sync and update multisig wallet
|
||||
if (trade.getBuyer().getUpdatedMultisigHex() != null) {
|
||||
XmrWalletService walletService = processModel.getProvider().getXmrWalletService();
|
||||
|
|
|
@ -28,7 +28,7 @@ import lombok.extern.slf4j.Slf4j;
|
|||
import monero.wallet.MoneroWallet;
|
||||
|
||||
/**
|
||||
* Send the buyer payment account info when the trade state is confirmed.
|
||||
* Allow sender's payment account info to be decrypted when trade state is confirmed.
|
||||
*/
|
||||
@Slf4j
|
||||
public class SellerSendsPaymentAccountPayloadKey extends SendMailboxMessageTask {
|
||||
|
@ -52,9 +52,6 @@ public class SellerSendsPaymentAccountPayloadKey extends SendMailboxMessageTask
|
|||
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();
|
||||
|
|
|
@ -447,6 +447,7 @@ message PaymentSentMessage {
|
|||
string counter_currency_extra_data = 6;
|
||||
string payout_tx_hex = 7;
|
||||
string updated_multisig_hex = 8;
|
||||
bytes payment_account_key = 9;
|
||||
}
|
||||
|
||||
message PaymentReceivedMessage {
|
||||
|
|
Loading…
Reference in a new issue