diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitness.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitness.java index eeaf646e4a..69dc824794 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitness.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitness.java @@ -64,7 +64,7 @@ public class AccountAgeWitness implements ProcessOncePersistableNetworkPayload, return protobuf.PersistableNetworkPayload.newBuilder().setAccountAgeWitness(builder).build(); } - protobuf.AccountAgeWitness toProtoAccountAgeWitness() { + public protobuf.AccountAgeWitness toProtoAccountAgeWitness() { return toProtoMessage().getAccountAgeWitness(); } diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index 1615502105..08c9450e5c 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -531,6 +531,15 @@ public class AccountAgeWitnessService { byte[] nonce, byte[] signature, ErrorMessageHandler errorMessageHandler) { + + log.info("Verifying account age witness for {} {}, payment account payload hash={}, peers current date={}, nonce={}, signature={}", + trade.getClass().getSimpleName(), + trade.getId(), + Utilities.bytesAsHexString(peersPaymentAccountPayload.getHash()), + peersCurrentDate, + Utilities.bytesAsHexString(nonce), + Utilities.bytesAsHexString(signature)); + final Optional accountAgeWitnessOptional = findWitness(peersPaymentAccountPayload, peersPubKeyRing); // If we don't find a stored witness data we create a new dummy object which makes is easier to reuse the diff --git a/core/src/main/java/bisq/core/trade/Contract.java b/core/src/main/java/bisq/core/trade/Contract.java index e11741651a..fa2bc3f084 100644 --- a/core/src/main/java/bisq/core/trade/Contract.java +++ b/core/src/main/java/bisq/core/trade/Contract.java @@ -31,6 +31,7 @@ import com.google.protobuf.ByteString; import bisq.common.crypto.PubKeyRing; import bisq.common.proto.network.NetworkPayload; import bisq.common.util.JsonExclude; +import bisq.common.util.Utilities; import org.bitcoinj.core.Coin; @@ -289,8 +290,8 @@ public final class Contract implements NetworkPayload { ",\n takerAccountId='" + takerAccountId + '\'' + ",\n makerPaymentMethodId='" + makerPaymentMethodId + '\'' + ",\n takerPaymentMethodId='" + takerPaymentMethodId + '\'' + - ",\n makerPaymentAccountPayloadHash=" + makerPaymentAccountPayloadHash + - ",\n takerPaymentAccountPayloadHash=" + takerPaymentAccountPayloadHash + + ",\n makerPaymentAccountPayloadHash=" + Utilities.bytesAsHexString(makerPaymentAccountPayloadHash) + + ",\n takerPaymentAccountPayloadHash=" + Utilities.bytesAsHexString(takerPaymentAccountPayloadHash) + ",\n makerPubKeyRing=" + makerPubKeyRing + ",\n takerPubKeyRing=" + takerPubKeyRing + ",\n makerPayoutAddressString='" + makerPayoutAddressString + '\'' + diff --git a/core/src/main/java/bisq/core/trade/TradeManager.java b/core/src/main/java/bisq/core/trade/TradeManager.java index 20ac1a9a96..872e5afd11 100644 --- a/core/src/main/java/bisq/core/trade/TradeManager.java +++ b/core/src/main/java/bisq/core/trade/TradeManager.java @@ -1020,7 +1020,9 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi } public Optional getOpenTrade(String tradeId) { - return tradableList.stream().filter(e -> e.getId().equals(tradeId)).findFirst(); + synchronized (tradableList) { + return tradableList.stream().filter(e -> e.getId().equals(tradeId)).findFirst(); + } } public List getOpenTrades() { diff --git a/core/src/main/java/bisq/core/trade/messages/PaymentReceivedMessage.java b/core/src/main/java/bisq/core/trade/messages/PaymentReceivedMessage.java index c255517532..ae8e163134 100644 --- a/core/src/main/java/bisq/core/trade/messages/PaymentReceivedMessage.java +++ b/core/src/main/java/bisq/core/trade/messages/PaymentReceivedMessage.java @@ -18,7 +18,7 @@ package bisq.core.trade.messages; import bisq.core.account.sign.SignedWitness; - +import bisq.core.account.witness.AccountAgeWitness; import bisq.network.p2p.NodeAddress; import bisq.common.app.Version; @@ -31,7 +31,6 @@ import java.util.UUID; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.Setter; -import lombok.Value; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; @@ -49,32 +48,34 @@ public final class PaymentReceivedMessage extends TradeMailboxMessage { private final String signedPayoutTxHex; private final String updatedMultisigHex; private final boolean deferPublishPayout; + @Nullable + private final AccountAgeWitness buyerAccountAgeWitness; + @Nullable + private final SignedWitness buyerSignedWitness; private final PaymentSentMessage paymentSentMessage; @Setter @Nullable private byte[] sellerSignature; - // Added in v1.4.0 - @Nullable - private final SignedWitness signedWitness; - public PaymentReceivedMessage(String tradeId, NodeAddress senderNodeAddress, - @Nullable SignedWitness signedWitness, String unsignedPayoutTxHex, String signedPayoutTxHex, String updatedMultisigHex, boolean deferPublishPayout, + AccountAgeWitness buyerAccountAgeWitness, + @Nullable SignedWitness buyerSignedWitness, PaymentSentMessage paymentSentMessage) { this(tradeId, senderNodeAddress, - signedWitness, UUID.randomUUID().toString(), Version.getP2PMessageVersion(), unsignedPayoutTxHex, signedPayoutTxHex, updatedMultisigHex, deferPublishPayout, + buyerAccountAgeWitness, + buyerSignedWitness, paymentSentMessage); } @@ -85,22 +86,24 @@ public final class PaymentReceivedMessage extends TradeMailboxMessage { private PaymentReceivedMessage(String tradeId, NodeAddress senderNodeAddress, - @Nullable SignedWitness signedWitness, String uid, String messageVersion, String unsignedPayoutTxHex, String signedPayoutTxHex, String updatedMultisigHex, boolean deferPublishPayout, + AccountAgeWitness buyerAccountAgeWitness, + @Nullable SignedWitness buyerSignedWitness, PaymentSentMessage paymentSentMessage) { super(messageVersion, tradeId, uid); this.senderNodeAddress = senderNodeAddress; - this.signedWitness = signedWitness; this.unsignedPayoutTxHex = unsignedPayoutTxHex; this.signedPayoutTxHex = signedPayoutTxHex; this.updatedMultisigHex = updatedMultisigHex; this.deferPublishPayout = deferPublishPayout; this.paymentSentMessage = paymentSentMessage; + this.buyerAccountAgeWitness = buyerAccountAgeWitness; + this.buyerSignedWitness = buyerSignedWitness; } @Override @@ -110,10 +113,11 @@ public final class PaymentReceivedMessage extends TradeMailboxMessage { .setSenderNodeAddress(senderNodeAddress.toProtoMessage()) .setUid(uid) .setDeferPublishPayout(deferPublishPayout); - Optional.ofNullable(signedWitness).ifPresent(signedWitness -> builder.setSignedWitness(signedWitness.toProtoSignedWitness())); Optional.ofNullable(updatedMultisigHex).ifPresent(e -> builder.setUpdatedMultisigHex(updatedMultisigHex)); Optional.ofNullable(unsignedPayoutTxHex).ifPresent(e -> builder.setUnsignedPayoutTxHex(unsignedPayoutTxHex)); Optional.ofNullable(signedPayoutTxHex).ifPresent(e -> builder.setSignedPayoutTxHex(signedPayoutTxHex)); + Optional.ofNullable(buyerAccountAgeWitness).ifPresent(buyerAccountAgeWitness -> builder.setBuyerAccountAgeWitness(buyerAccountAgeWitness.toProtoAccountAgeWitness())); + Optional.ofNullable(buyerSignedWitness).ifPresent(buyerSignedWitness -> builder.setBuyerSignedWitness(buyerSignedWitness.toProtoSignedWitness())); Optional.ofNullable(paymentSentMessage).ifPresent(e -> builder.setPaymentSentMessage(paymentSentMessage.toProtoNetworkEnvelope().getPaymentSentMessage())); Optional.ofNullable(sellerSignature).ifPresent(e -> builder.setSellerSignature(ByteString.copyFrom(e))); return getNetworkEnvelopeBuilder().setPaymentReceivedMessage(builder).build(); @@ -121,20 +125,23 @@ public final class PaymentReceivedMessage extends TradeMailboxMessage { public static NetworkEnvelope fromProto(protobuf.PaymentReceivedMessage proto, String messageVersion) { // There is no method to check for a nullable non-primitive data type object but we know that all fields - // are empty/null, so we check for the signature to see if we got a valid signedWitness. - protobuf.SignedWitness protoSignedWitness = proto.getSignedWitness(); - SignedWitness signedWitness = !protoSignedWitness.getSignature().isEmpty() ? + // are empty/null, so we check for the signature to see if we got a valid buyerSignedWitness. + protobuf.AccountAgeWitness protoAccountAgeWitness = proto.getBuyerAccountAgeWitness(); + AccountAgeWitness buyerAccountAgeWitness = protoAccountAgeWitness.getHash().isEmpty() ? null : AccountAgeWitness.fromProto(protoAccountAgeWitness); + protobuf.SignedWitness protoSignedWitness = proto.getBuyerSignedWitness(); + SignedWitness buyerSignedWitness = !protoSignedWitness.getSignature().isEmpty() ? SignedWitness.fromProto(protoSignedWitness) : null; PaymentReceivedMessage message = new PaymentReceivedMessage(proto.getTradeId(), NodeAddress.fromProto(proto.getSenderNodeAddress()), - signedWitness, proto.getUid(), messageVersion, ProtoUtil.stringOrNullFromProto(proto.getUnsignedPayoutTxHex()), ProtoUtil.stringOrNullFromProto(proto.getSignedPayoutTxHex()), ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()), proto.getDeferPublishPayout(), + buyerAccountAgeWitness, + buyerSignedWitness, proto.hasPaymentSentMessage() ? PaymentSentMessage.fromProto(proto.getPaymentSentMessage(), messageVersion) : null); message.setSellerSignature(ProtoUtil.byteArrayOrNullFromProto(proto.getSellerSignature())); return message; @@ -144,7 +151,7 @@ public final class PaymentReceivedMessage extends TradeMailboxMessage { public String toString() { return "PaymentReceivedMessage{" + "\n senderNodeAddress=" + senderNodeAddress + - ",\n signedWitness=" + signedWitness + + ",\n buyerSignedWitness=" + buyerSignedWitness + ",\n unsignedPayoutTxHex=" + unsignedPayoutTxHex + ",\n signedPayoutTxHex=" + signedPayoutTxHex + ",\n updatedMultisigHex=" + (updatedMultisigHex == null ? null : updatedMultisigHex.substring(0, Math.max(updatedMultisigHex.length(), 1000))) + diff --git a/core/src/main/java/bisq/core/trade/messages/PaymentSentMessage.java b/core/src/main/java/bisq/core/trade/messages/PaymentSentMessage.java index 8e6d6e908e..49dd4e941b 100644 --- a/core/src/main/java/bisq/core/trade/messages/PaymentSentMessage.java +++ b/core/src/main/java/bisq/core/trade/messages/PaymentSentMessage.java @@ -21,6 +21,7 @@ import bisq.network.p2p.NodeAddress; import com.google.protobuf.ByteString; import bisq.common.app.Version; import bisq.common.proto.ProtoUtil; +import bisq.core.account.witness.AccountAgeWitness; import java.util.Optional; @@ -42,6 +43,8 @@ public final class PaymentSentMessage extends TradeMailboxMessage { private final String updatedMultisigHex; @Nullable private final byte[] paymentAccountKey; + @Nullable + private AccountAgeWitness sellerAccountAgeWitness; @Setter @Nullable private byte[] buyerSignature; @@ -58,7 +61,8 @@ public final class PaymentSentMessage extends TradeMailboxMessage { String uid, @Nullable String signedPayoutTxHex, @Nullable String updatedMultisigHex, - @Nullable byte[] paymentAccountKey) { + @Nullable byte[] paymentAccountKey, + AccountAgeWitness sellerAccountAgeWitness) { this(tradeId, senderNodeAddress, counterCurrencyTxId, @@ -67,7 +71,8 @@ public final class PaymentSentMessage extends TradeMailboxMessage { Version.getP2PMessageVersion(), signedPayoutTxHex, updatedMultisigHex, - paymentAccountKey); + paymentAccountKey, + sellerAccountAgeWitness); } @@ -83,7 +88,8 @@ public final class PaymentSentMessage extends TradeMailboxMessage { String messageVersion, @Nullable String signedPayoutTxHex, @Nullable String updatedMultisigHex, - @Nullable byte[] paymentAccountKey) { + @Nullable byte[] paymentAccountKey, + AccountAgeWitness sellerAccountAgeWitness) { super(messageVersion, tradeId, uid); this.senderNodeAddress = senderNodeAddress; this.counterCurrencyTxId = counterCurrencyTxId; @@ -91,6 +97,7 @@ public final class PaymentSentMessage extends TradeMailboxMessage { this.payoutTxHex = signedPayoutTxHex; this.updatedMultisigHex = updatedMultisigHex; this.paymentAccountKey = paymentAccountKey; + this.sellerAccountAgeWitness = sellerAccountAgeWitness; } @Override @@ -106,12 +113,17 @@ public final class PaymentSentMessage extends TradeMailboxMessage { Optional.ofNullable(updatedMultisigHex).ifPresent(e -> builder.setUpdatedMultisigHex(updatedMultisigHex)); Optional.ofNullable(paymentAccountKey).ifPresent(e -> builder.setPaymentAccountKey(ByteString.copyFrom(e))); Optional.ofNullable(buyerSignature).ifPresent(e -> builder.setBuyerSignature(ByteString.copyFrom(e))); + Optional.ofNullable(sellerAccountAgeWitness).ifPresent(e -> builder.setSellerAccountAgeWitness(sellerAccountAgeWitness.toProtoAccountAgeWitness())); return getNetworkEnvelopeBuilder().setPaymentSentMessage(builder).build(); } public static PaymentSentMessage fromProto(protobuf.PaymentSentMessage proto, String messageVersion) { + + protobuf.AccountAgeWitness protoAccountAgeWitness = proto.getSellerAccountAgeWitness(); + AccountAgeWitness accountAgeWitness = protoAccountAgeWitness.getHash().isEmpty() ? null : AccountAgeWitness.fromProto(protoAccountAgeWitness); + PaymentSentMessage message = new PaymentSentMessage(proto.getTradeId(), NodeAddress.fromProto(proto.getSenderNodeAddress()), ProtoUtil.stringOrNullFromProto(proto.getCounterCurrencyTxId()), @@ -120,7 +132,8 @@ public final class PaymentSentMessage extends TradeMailboxMessage { messageVersion, ProtoUtil.stringOrNullFromProto(proto.getPayoutTxHex()), ProtoUtil.stringOrNullFromProto(proto.getUpdatedMultisigHex()), - ProtoUtil.byteArrayOrNullFromProto(proto.getPaymentAccountKey()) + ProtoUtil.byteArrayOrNullFromProto(proto.getPaymentAccountKey()), + accountAgeWitness ); message.setBuyerSignature(ProtoUtil.byteArrayOrNullFromProto(proto.getBuyerSignature())); return message; diff --git a/core/src/main/java/bisq/core/trade/messages/SignContractRequest.java b/core/src/main/java/bisq/core/trade/messages/SignContractRequest.java index 81dd69808f..fc76758335 100644 --- a/core/src/main/java/bisq/core/trade/messages/SignContractRequest.java +++ b/core/src/main/java/bisq/core/trade/messages/SignContractRequest.java @@ -21,9 +21,15 @@ import bisq.core.proto.CoreProtoResolver; import bisq.network.p2p.DirectMessage; import bisq.network.p2p.NodeAddress; + +import java.util.Optional; + +import javax.annotation.Nullable; + import com.google.protobuf.ByteString; import bisq.common.crypto.PubKeyRing; - +import bisq.common.proto.ProtoUtil; +import bisq.common.util.Utilities; import lombok.EqualsAndHashCode; import lombok.Value; @@ -37,6 +43,7 @@ public final class SignContractRequest extends TradeMessage implements DirectMes private final byte[] paymentAccountPayloadHash; private final String payoutAddress; private final String depositTxHash; + private final byte[] accountAgeWitnessSignatureOfDepositHash; public SignContractRequest(String tradeId, NodeAddress senderNodeAddress, @@ -47,7 +54,8 @@ public final class SignContractRequest extends TradeMessage implements DirectMes String accountId, byte[] paymentAccountPayloadHash, String payoutAddress, - String depositTxHash) { + String depositTxHash, + @Nullable byte[] accountAgeWitnessSignatureOfDepositHash) { super(messageVersion, tradeId, uid); this.senderNodeAddress = senderNodeAddress; this.pubKeyRing = pubKeyRing; @@ -56,6 +64,7 @@ public final class SignContractRequest extends TradeMessage implements DirectMes this.paymentAccountPayloadHash = paymentAccountPayloadHash; this.payoutAddress = payoutAddress; this.depositTxHash = depositTxHash; + this.accountAgeWitnessSignatureOfDepositHash = accountAgeWitnessSignatureOfDepositHash; } @@ -75,6 +84,7 @@ public final class SignContractRequest extends TradeMessage implements DirectMes .setPayoutAddress(payoutAddress) .setDepositTxHash(depositTxHash); + Optional.ofNullable(accountAgeWitnessSignatureOfDepositHash).ifPresent(e -> builder.setAccountAgeWitnessSignatureOfDepositHash(ByteString.copyFrom(e))); builder.setCurrentDate(currentDate); return getNetworkEnvelopeBuilder().setSignContractRequest(builder).build(); @@ -92,7 +102,8 @@ public final class SignContractRequest extends TradeMessage implements DirectMes proto.getAccountId(), proto.getPaymentAccountPayloadHash().toByteArray(), proto.getPayoutAddress(), - proto.getDepositTxHash()); + proto.getDepositTxHash(), + ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignatureOfDepositHash())); } @Override @@ -102,9 +113,10 @@ public final class SignContractRequest extends TradeMessage implements DirectMes ",\n pubKeyRing=" + pubKeyRing + ",\n currentDate=" + currentDate + ",\n accountId=" + accountId + - ",\n paymentAccountPayloadHash='" + paymentAccountPayloadHash + + ",\n paymentAccountPayloadHash='" + Utilities.bytesAsHexString(paymentAccountPayloadHash) + ",\n payoutAddress='" + payoutAddress + ",\n depositTxHash='" + depositTxHash + + ",\n accountAgeWitnessSignatureOfDepositHash='" + Utilities.bytesAsHexString(accountAgeWitnessSignatureOfDepositHash) + "\n} " + super.toString(); } } diff --git a/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java b/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java index f55a971111..b2aa44fda9 100644 --- a/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/BuyerAsTakerProtocol.java @@ -22,19 +22,10 @@ import bisq.core.offer.Offer; import bisq.core.trade.BuyerAsTakerTrade; import bisq.core.trade.Trade; import bisq.core.trade.handlers.TradeResultHandler; -import bisq.core.trade.messages.DepositResponse; -import bisq.core.trade.messages.InitMultisigRequest; -import bisq.core.trade.messages.DepositsConfirmedMessage; -import bisq.core.trade.messages.PaymentReceivedMessage; -import bisq.core.trade.messages.SignContractRequest; -import bisq.core.trade.messages.SignContractResponse; -import bisq.core.trade.messages.TradeMessage; import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.TakerReserveTradeFunds; import bisq.core.trade.protocol.tasks.TakerSendInitTradeRequestToArbitrator; -import bisq.network.p2p.NodeAddress; import bisq.common.handlers.ErrorMessageHandler; -import bisq.common.handlers.ResultHandler; import lombok.extern.slf4j.Slf4j; diff --git a/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java b/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java index e13515241a..236f7209c0 100644 --- a/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java +++ b/core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java @@ -37,6 +37,7 @@ import bisq.core.trade.messages.TradeMessage; import bisq.core.trade.protocol.tasks.RemoveOffer; import bisq.core.trade.protocol.tasks.ProcessPaymentSentMessage; import bisq.core.trade.protocol.tasks.TradeTask; +import bisq.core.trade.protocol.tasks.VerifyPeersAccountAgeWitness; import bisq.core.trade.protocol.FluentProtocol.Condition; import bisq.core.trade.protocol.tasks.ApplyFilter; import bisq.core.trade.protocol.tasks.MaybeSendSignContractRequest; @@ -387,7 +388,9 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D expect(new Condition(trade) .with(response) .from(sender)) - .setup(tasks(ProcessDepositsConfirmedMessage.class) + .setup(tasks( + ProcessDepositsConfirmedMessage.class, + VerifyPeersAccountAgeWitness.class) .using(new TradeTaskRunner(trade, () -> { handleTaskRunnerSuccess(sender, response); @@ -431,7 +434,8 @@ public abstract class TradeProtocol implements DecryptedDirectMessageListener, D })) .setup(tasks( ApplyFilter.class, - ProcessPaymentSentMessage.class) + ProcessPaymentSentMessage.class, + VerifyPeersAccountAgeWitness.class) .using(new TradeTaskRunner(trade, () -> { handleTaskRunnerSuccess(peer, message); diff --git a/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java b/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java index 8ed0489468..68f8a45fa3 100644 --- a/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java +++ b/core/src/main/java/bisq/core/trade/protocol/TradingPeer.java @@ -17,6 +17,7 @@ package bisq.core.trade.protocol; +import bisq.core.account.witness.AccountAgeWitness; import bisq.core.btc.model.RawTransactionInput; import bisq.core.payment.payload.PaymentAccountPayload; import bisq.core.proto.CoreProtoResolver; @@ -97,6 +98,10 @@ public final class TradingPeer implements PersistablePayload { private byte[] accountAgeWitnessNonce; @Nullable private byte[] accountAgeWitnessSignature; + @Getter + @Setter + @Nullable + private AccountAgeWitness accountAgeWitness; private long currentDate; // Added in v.1.1.6 @@ -155,6 +160,7 @@ public final class TradingPeer implements PersistablePayload { Optional.ofNullable(changeOutputAddress).ifPresent(builder::setChangeOutputAddress); Optional.ofNullable(accountAgeWitnessNonce).ifPresent(e -> builder.setAccountAgeWitnessNonce(ByteString.copyFrom(e))); Optional.ofNullable(accountAgeWitnessSignature).ifPresent(e -> builder.setAccountAgeWitnessSignature(ByteString.copyFrom(e))); + Optional.ofNullable(accountAgeWitness).ifPresent(e -> builder.setAccountAgeWitness(accountAgeWitness.toProtoAccountAgeWitness())); Optional.ofNullable(mediatedPayoutTxSignature).ifPresent(e -> builder.setMediatedPayoutTxSignature(ByteString.copyFrom(e))); Optional.ofNullable(reserveTxHash).ifPresent(e -> builder.setReserveTxHash(reserveTxHash)); Optional.ofNullable(reserveTxHex).ifPresent(e -> builder.setReserveTxHex(reserveTxHex)); @@ -203,6 +209,8 @@ public final class TradingPeer implements PersistablePayload { tradingPeer.setChangeOutputAddress(ProtoUtil.stringOrNullFromProto(proto.getChangeOutputAddress())); tradingPeer.setAccountAgeWitnessNonce(ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessNonce())); tradingPeer.setAccountAgeWitnessSignature(ProtoUtil.byteArrayOrNullFromProto(proto.getAccountAgeWitnessSignature())); + protobuf.AccountAgeWitness protoAccountAgeWitness = proto.getAccountAgeWitness(); + tradingPeer.setAccountAgeWitness(protoAccountAgeWitness.getHash().isEmpty() ? null : AccountAgeWitness.fromProto(protoAccountAgeWitness)); tradingPeer.setCurrentDate(proto.getCurrentDate()); tradingPeer.setMediatedPayoutTxSignature(ProtoUtil.byteArrayOrNullFromProto(proto.getMediatedPayoutTxSignature())); tradingPeer.setReserveTxHash(ProtoUtil.stringOrNullFromProto(proto.getReserveTxHash())); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java index d28a56ab57..cdca88c825 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ArbitratorSendInitTradeOrMultisigRequests.java @@ -54,9 +54,6 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask { runInterceptHook(); InitTradeRequest request = (InitTradeRequest) processModel.getTradeMessage(); - // arbitrator signs offer id as nonce to avoid challenge protocol - byte[] sig = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), processModel.getOfferId().getBytes(Charsets.UTF_8)); - // handle request from taker if (request.getSenderNodeAddress().equals(trade.getTaker().getNodeAddress())) { @@ -73,7 +70,7 @@ public class ArbitratorSendInitTradeOrMultisigRequests extends TradeTask { request.getPaymentMethodId(), UUID.randomUUID().toString(), Version.getP2PMessageVersion(), - sig, + request.getAccountAgeWitnessSignatureOfOfferId(), new Date().getTime(), trade.getMaker().getNodeAddress(), trade.getTaker().getNodeAddress(), diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerSendPaymentSentMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerSendPaymentSentMessage.java index f00d079615..60ddac9d76 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerSendPaymentSentMessage.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/BuyerSendPaymentSentMessage.java @@ -89,7 +89,8 @@ public abstract class BuyerSendPaymentSentMessage extends SendMailboxMessageTask deterministicId, trade.getPayoutTxHex(), trade.getSelf().getUpdatedMultisigHex(), - trade.getSelf().getPaymentAccountKey() + trade.getSelf().getPaymentAccountKey(), + trade.getTradingPeer().getAccountAgeWitness() ); // sign message diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/MakerSendInitTradeRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/MakerSendInitTradeRequest.java index 59ddbb4e7d..33b0b95a44 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/MakerSendInitTradeRequest.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/MakerSendInitTradeRequest.java @@ -53,11 +53,8 @@ public class MakerSendInitTradeRequest extends TradeTask { checkNotNull(makerRequest); checkTradeId(processModel.getOfferId(), makerRequest); - // maker signs offer id as nonce to avoid challenge protocol // TODO: how is this used? - Offer offer = processModel.getOffer(); - byte[] sig = Sig.sign(processModel.getKeyRing().getSignatureKeyPair().getPrivate(), offer.getId().getBytes(Charsets.UTF_8)); - // create request to arbitrator + Offer offer = processModel.getOffer(); InitTradeRequest arbitratorRequest = new InitTradeRequest( offer.getId(), processModel.getMyNodeAddress(), @@ -70,7 +67,7 @@ public class MakerSendInitTradeRequest extends TradeTask { offer.getOfferPayload().getPaymentMethodId(), UUID.randomUUID().toString(), Version.getP2PMessageVersion(), - sig, + null, makerRequest.getCurrentDate(), trade.getMaker().getNodeAddress(), trade.getTaker().getNodeAddress(), diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/MaybeSendSignContractRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/MaybeSendSignContractRequest.java index 89e7e30453..77a7df737c 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/MaybeSendSignContractRequest.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/MaybeSendSignContractRequest.java @@ -18,17 +18,21 @@ package bisq.core.trade.protocol.tasks; import bisq.common.app.Version; +import bisq.common.crypto.Sig; import bisq.common.taskrunner.TaskRunner; import bisq.core.btc.model.XmrAddressEntry; import bisq.core.trade.ArbitratorTrade; +import bisq.core.trade.MakerTrade; import bisq.core.trade.Trade; import bisq.core.trade.Trade.State; import bisq.core.trade.messages.SignContractRequest; import bisq.network.p2p.SendDirectMessageListener; import java.util.Date; import java.util.UUID; + +import com.google.common.base.Charsets; + import lombok.extern.slf4j.Slf4j; -import monero.wallet.MoneroWallet; import monero.wallet.model.MoneroTxWallet; // TODO (woodser): separate classes for deposit tx creation and contract request, or combine into ProcessInitMultisigRequest @@ -75,6 +79,12 @@ public class MaybeSendSignContractRequest extends TradeTask { 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)); + // maker signs deposit hash nonce to avoid challenge protocol + byte[] sig = null; + if (trade instanceof MakerTrade) { + sig = Sig.sign(processModel.getP2PService().getKeyRing().getSignatureKeyPair().getPrivate(), depositTx.getHash().getBytes(Charsets.UTF_8)); + } + // create request for peer and arbitrator to sign contract SignContractRequest request = new SignContractRequest( trade.getOffer().getId(), @@ -86,7 +96,8 @@ public class MaybeSendSignContractRequest extends TradeTask { trade.getProcessModel().getAccountId(), trade.getSelf().getPaymentAccountPayload().getHash(), trade.getSelf().getPayoutAddressString(), - depositTx.getHash()); + depositTx.getHash(), + sig); // send request to trading peer processModel.getP2PService().sendEncryptedDirectMessage(trade.getTradingPeer().getNodeAddress(), trade.getTradingPeer().getPubKeyRing(), request, new SendDirectMessageListener() { diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessDepositsConfirmedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessDepositsConfirmedMessage.java index ac55b6b466..771d48848f 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessDepositsConfirmedMessage.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessDepositsConfirmedMessage.java @@ -53,7 +53,7 @@ public class ProcessDepositsConfirmedMessage extends TradeTask { // decrypt seller payment account payload if key given if (request.getSellerPaymentAccountKey() != null && trade.getTradingPeer().getPaymentAccountPayload() == null) { - log.info(trade.getClass().getSimpleName() + " decryping using seller payment account key: " + request.getSellerPaymentAccountKey()); + log.info(trade.getClass().getSimpleName() + " decrypting using seller payment account key"); trade.decryptPeerPaymentAccountPayload(request.getSellerPaymentAccountKey()); } diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessInitTradeRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessInitTradeRequest.java index 58e3f512c6..d9e734558c 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessInitTradeRequest.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessInitTradeRequest.java @@ -95,7 +95,7 @@ public class ProcessInitTradeRequest extends TradeTask { } } - // handle maker trade + // handle request as maker else if (trade instanceof MakerTrade) { multisigParticipant = processModel.getTaker(); trade.getTaker().setNodeAddress(request.getSenderNodeAddress()); // arbitrator sends maker InitTradeRequest with taker's node address and pub key ring diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java index 4b6af55823..202bd6e6f9 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessPaymentReceivedMessage.java @@ -20,6 +20,7 @@ package bisq.core.trade.protocol.tasks; import bisq.core.account.sign.SignedWitness; import bisq.core.support.dispute.Dispute; import bisq.core.trade.ArbitratorTrade; +import bisq.core.trade.BuyerTrade; import bisq.core.trade.HavenoUtils; import bisq.core.trade.Trade; import bisq.core.trade.messages.PaymentReceivedMessage; @@ -55,6 +56,7 @@ public class ProcessPaymentReceivedMessage extends TradeTask { HavenoUtils.verifyPaymentReceivedMessage(trade, message); trade.getSeller().setUpdatedMultisigHex(message.getUpdatedMultisigHex()); trade.getBuyer().setUpdatedMultisigHex(message.getPaymentSentMessage().getUpdatedMultisigHex()); + trade.getBuyer().setAccountAgeWitness(message.getBuyerAccountAgeWitness()); // update to the latest peer address of our peer if message is correct trade.getSeller().setNodeAddress(processModel.getTempTradingPeerNodeAddress()); @@ -71,8 +73,8 @@ public class ProcessPaymentReceivedMessage extends TradeTask { // process payout tx unless already unlocked if (!trade.isPayoutUnlocked()) processPayoutTx(message); - SignedWitness signedWitness = message.getSignedWitness(); - if (signedWitness != null) { + SignedWitness signedWitness = message.getBuyerSignedWitness(); + if (signedWitness != null && trade instanceof BuyerTrade) { // We received the signedWitness from the seller and publish the data to the network. // The signer has published it as well but we prefer to re-do it on our side as well to achieve higher // resilience. diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessPaymentSentMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessPaymentSentMessage.java index 3498887474..3ec860d264 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessPaymentSentMessage.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessPaymentSentMessage.java @@ -48,6 +48,7 @@ public class ProcessPaymentSentMessage extends TradeTask { trade.setPayoutTxHex(message.getPayoutTxHex()); trade.getBuyer().setUpdatedMultisigHex(message.getUpdatedMultisigHex()); trade.getBuyer().setPaymentSentMessage(message); + trade.getSeller().setAccountAgeWitness(message.getSellerAccountAgeWitness()); // if seller, decrypt buyer's payment account payload if (trade.isSeller()) trade.decryptPeerPaymentAccountPayload(message.getPaymentAccountKey()); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessSignContractRequest.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessSignContractRequest.java index 43992d4b5d..97ba94e734 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessSignContractRequest.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ProcessSignContractRequest.java @@ -37,9 +37,13 @@ import bisq.core.trade.protocol.TradingPeer; import bisq.core.util.JsonUtil; import bisq.network.p2p.NodeAddress; import bisq.network.p2p.SendDirectMessageListener; + import java.util.Date; import java.util.UUID; import javax.crypto.SecretKey; + +import com.google.common.base.Charsets; + import lombok.extern.slf4j.Slf4j; @Slf4j @@ -66,6 +70,12 @@ public class ProcessSignContractRequest extends TradeTask { trader.setAccountId(request.getAccountId()); trader.setPaymentAccountPayloadHash(request.getPaymentAccountPayloadHash()); trader.setPayoutAddressString(request.getPayoutAddress()); + + // maker sends witness signature of deposit tx hash + if (trader == trade.getMaker()) { + trader.setAccountAgeWitnessNonce(request.getDepositTxHash().getBytes(Charsets.UTF_8)); + trader.setAccountAgeWitnessSignature(request.getAccountAgeWitnessSignatureOfDepositHash()); + } // sign contract only when both deposit txs hashes known // TODO (woodser): remove makerDepositTxId and takerDepositTxId from Trade diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java b/core/src/main/java/bisq/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java index 62bdfedf1a..43af1cb2cb 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/SellerSendPaymentReceivedMessage.java @@ -18,6 +18,7 @@ package bisq.core.trade.protocol.tasks; import bisq.core.account.sign.SignedWitness; +import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.trade.Trade; import bisq.core.trade.messages.PaymentReceivedMessage; import bisq.core.trade.messages.TradeMailboxMessage; @@ -63,22 +64,23 @@ public abstract class SellerSendPaymentReceivedMessage extends SendMailboxMessag checkNotNull(trade.getPayoutTxHex(), "Payout tx must not be null"); if (message == null) { - // TODO: sign witness - // AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService(); - // if (accountAgeWitnessService.isSignWitnessTrade(trade)) { - // // Broadcast is done in accountAgeWitness domain. - // accountAgeWitnessService.traderSignAndPublishPeersAccountAgeWitness(trade).ifPresent(witness -> signedWitness = witness); - // } + // sign account witness + AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService(); + if (accountAgeWitnessService.isSignWitnessTrade(trade)) { + accountAgeWitnessService.traderSignAndPublishPeersAccountAgeWitness(trade).ifPresent(witness -> signedWitness = witness); + log.info("{} {} signed and published peers account age witness", trade.getClass().getSimpleName(), trade.getId()); + } // TODO: create with deterministic id like BuyerSendPaymentSentMessage message = new PaymentReceivedMessage( tradeId, processModel.getMyNodeAddress(), - signedWitness, trade.isPayoutPublished() ? null : trade.getPayoutTxHex(), // unsigned trade.isPayoutPublished() ? trade.getPayoutTxHex() : null, // signed trade.getSelf().getUpdatedMultisigHex(), trade.getState().ordinal() >= Trade.State.SELLER_SAW_ARRIVED_PAYMENT_RECEIVED_MSG.ordinal(), // informs to expect payout + trade.getTradingPeer().getAccountAgeWitness(), + signedWitness, trade.getBuyer().getPaymentSentMessage() ); diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/VerifyPeersAccountAgeWitness.java b/core/src/main/java/bisq/core/trade/protocol/tasks/VerifyPeersAccountAgeWitness.java index f6819d25d9..12a57ca121 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/VerifyPeersAccountAgeWitness.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/VerifyPeersAccountAgeWitness.java @@ -21,6 +21,7 @@ import bisq.core.account.witness.AccountAgeWitnessService; import bisq.core.locale.CurrencyUtil; import bisq.core.offer.Offer; import bisq.core.payment.payload.PaymentAccountPayload; +import bisq.core.trade.ArbitratorTrade; import bisq.core.trade.Trade; import bisq.core.trade.protocol.TradingPeer; @@ -46,14 +47,27 @@ public class VerifyPeersAccountAgeWitness extends TradeTask { try { runInterceptHook(); + // only verify fiat offer Offer offer = checkNotNull(trade.getOffer()); if (CurrencyUtil.isCryptoCurrency(offer.getCurrencyCode())) { complete(); return; } - AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService(); + // skip if arbitrator + if (trade instanceof ArbitratorTrade) { + complete(); + return; + } + + // skip if payment account payload is null TradingPeer tradingPeer = trade.getTradingPeer(); + if (tradingPeer.getPaymentAccountPayload() == null) { + complete(); + return; + } + + AccountAgeWitnessService accountAgeWitnessService = processModel.getAccountAgeWitnessService(); PaymentAccountPayload peersPaymentAccountPayload = checkNotNull(tradingPeer.getPaymentAccountPayload(), "Peers peersPaymentAccountPayload must not be null"); PubKeyRing peersPubKeyRing = checkNotNull(tradingPeer.getPubKeyRing(), "peersPubKeyRing must not be null"); @@ -71,6 +85,8 @@ public class VerifyPeersAccountAgeWitness extends TradeTask { signature, errorMsg::set); if (isValid) { + trade.getTradingPeer().setAccountAgeWitness(processModel.getAccountAgeWitnessService().findWitness(trade.getTradingPeer().getPaymentAccountPayload(), trade.getTradingPeer().getPubKeyRing()).orElse(null)); + log.info("{} {} verified witness data of peer {}", trade.getClass().getSimpleName(), trade.getId(), tradingPeer.getNodeAddress()); complete(); } else { failed(errorMsg.get()); diff --git a/core/src/main/java/bisq/core/user/User.java b/core/src/main/java/bisq/core/user/User.java index e1f05fa5b5..ff7171ac2c 100644 --- a/core/src/main/java/bisq/core/user/User.java +++ b/core/src/main/java/bisq/core/user/User.java @@ -119,8 +119,10 @@ public class User implements PersistedDataHost { userPayload.getAcceptedLanguageLocaleCodes().add(english); paymentAccountsAsObservable.addListener((SetChangeListener) change -> { - userPayload.setPaymentAccounts(new HashSet<>(paymentAccountsAsObservable)); - requestPersistence(); + synchronized (paymentAccountsAsObservable) { + userPayload.setPaymentAccounts(new HashSet<>(paymentAccountsAsObservable)); + requestPersistence(); + } }); currentPaymentAccountProperty.addListener((ov) -> { userPayload.setCurrentPaymentAccount(currentPaymentAccountProperty.get()); @@ -212,28 +214,33 @@ public class User implements PersistedDataHost { public void addPaymentAccount(PaymentAccount paymentAccount) { paymentAccount.onAddToUser(); - - boolean changed = paymentAccountsAsObservable.add(paymentAccount); - setCurrentPaymentAccount(paymentAccount); - if (changed) - requestPersistence(); + synchronized (paymentAccountsAsObservable) { + boolean changed = paymentAccountsAsObservable.add(paymentAccount); + setCurrentPaymentAccount(paymentAccount); + if (changed) + requestPersistence(); + } } public void addImportedPaymentAccounts(Collection paymentAccounts) { - isPaymentAccountImport = true; + synchronized (paymentAccountsAsObservable) { + isPaymentAccountImport = true; - boolean changed = paymentAccountsAsObservable.addAll(paymentAccounts); - paymentAccounts.stream().findFirst().ifPresent(this::setCurrentPaymentAccount); - if (changed) - requestPersistence(); - - isPaymentAccountImport = false; + boolean changed = paymentAccountsAsObservable.addAll(paymentAccounts); + paymentAccounts.stream().findFirst().ifPresent(this::setCurrentPaymentAccount); + if (changed) + requestPersistence(); + + isPaymentAccountImport = false; + } } public void removePaymentAccount(PaymentAccount paymentAccount) { - boolean changed = paymentAccountsAsObservable.remove(paymentAccount); - if (changed) - requestPersistence(); + synchronized (paymentAccountsAsObservable) { + boolean changed = paymentAccountsAsObservable.remove(paymentAccount); + if (changed) + requestPersistence(); + } } public boolean addAcceptedArbitrator(Arbitrator arbitrator) { @@ -513,7 +520,9 @@ public class User implements PersistedDataHost { } private boolean paymentAccountExists(PaymentAccount paymentAccount) { - return getPaymentAccountsAsObservable().stream().anyMatch(e -> e.equals(paymentAccount)); + synchronized (paymentAccountsAsObservable) { + return getPaymentAccountsAsObservable().stream().anyMatch(e -> e.equals(paymentAccount)); + } } public Cookie getCookie() { diff --git a/desktop/src/main/java/bisq/desktop/components/controlsfx/control/PopOver.java b/desktop/src/main/java/bisq/desktop/components/controlsfx/control/PopOver.java index 0b2c8790bd..b5b6a5268c 100644 --- a/desktop/src/main/java/bisq/desktop/components/controlsfx/control/PopOver.java +++ b/desktop/src/main/java/bisq/desktop/components/controlsfx/control/PopOver.java @@ -523,7 +523,7 @@ public class PopOver extends PopupControl { skinNode); fadeOut.setFromValue(skinNode.getOpacity()); fadeOut.setToValue(0); - fadeOut.setOnFinished(evt -> super.hide()); + fadeOut.setOnFinished(evt -> { if (super.isShowing()) super.hide(); }); fadeOut.play(); } else { super.hide(); diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignPaymentAccountsWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignPaymentAccountsWindow.java index d63a8d0a95..3f6427f412 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignPaymentAccountsWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/SignPaymentAccountsWindow.java @@ -200,9 +200,7 @@ public class SignPaymentAccountsWindow extends Overlay disputesAsObservableList = useDevPrivilegeKeys ? - mediationManager.getDisputesAsObservableList() - : arbitrationManager.getDisputesAsObservableList(); + ObservableList disputesAsObservableList = arbitrationManager.getDisputesAsObservableList(); long safeDate = datePicker.getValue().atStartOfDay().toEpochSecond(ZoneOffset.UTC) * 1000; List traderDataItemList; StringBuilder sb = new StringBuilder("Summary for ").append(appName).append("\n"); diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/TradeDetailsWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/TradeDetailsWindow.java index a5da25ca40..452da7f719 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/TradeDetailsWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/TradeDetailsWindow.java @@ -43,6 +43,7 @@ import bisq.network.p2p.NodeAddress; import bisq.common.UserThread; import bisq.common.util.Tuple3; +import bisq.common.util.Utilities; import javax.inject.Inject; import javax.inject.Named; @@ -313,6 +314,11 @@ public class TradeDetailsWindow extends Overlay { HBox.setHgrow(spacer, Priority.ALWAYS); hBox.getChildren().add(0, spacer); + String buyerWitnessHash = trade.getBuyer().getAccountAgeWitness() == null ? "null" : Utilities.bytesAsHexString(trade.getBuyer().getAccountAgeWitness().getHash()); + String buyerPubKeyRingHash = Utilities.bytesAsHexString(trade.getBuyer().getPubKeyRing().getSignaturePubKeyBytes()); + String sellerWitnessHash = trade.getSeller().getAccountAgeWitness() == null ? "null" : Utilities.bytesAsHexString(trade.getSeller().getAccountAgeWitness().getHash()); + String sellerPubKeyRingHash = Utilities.bytesAsHexString(trade.getSeller().getPubKeyRing().getSignaturePubKeyBytes()); + if (contract != null) { viewContractButton.setOnAction(e -> { TextArea textArea = new HavenoTextArea(); @@ -321,8 +327,10 @@ public class TradeDetailsWindow extends Overlay { data += trade.getContractAsJson(); data += "\n\nOther detail data:"; if (offer.isFiatOffer()) { - data += "\n\nBuyersAccountAge: " + buyersAccountAge; - data += "\nSellersAccountAge: " + sellersAccountAge; + data += "\n\nBuyers witness hash,pub key ring hash: " + buyerWitnessHash + "," + buyerPubKeyRingHash; + data += "\nBuyers account age: " + buyersAccountAge; + data += "\nSellers witness hash,pub key ring hash: " + sellerWitnessHash + "," + sellerPubKeyRingHash; + data += "\nSellers account age: " + sellersAccountAge; } // TODO (woodser): include maker and taker deposit tx hex in contract? diff --git a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java index 0ded6580e6..487b5db0d9 100644 --- a/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java +++ b/desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java @@ -448,7 +448,7 @@ public class SellerStep3View extends TradeStepView { } if (model.dataModel.isSignWitnessTrade()) { - message += Res.get("portfolio.pending.step3_seller.onPaymentReceived.signer"); + message += "\n\n" + Res.get("portfolio.pending.step3_seller.onPaymentReceived.signer"); } } if (!DevEnv.isDevMode() && DontShowAgainLookup.showAgain(key)) { diff --git a/docs/developer-guide.md b/docs/developer-guide.md index fe5168a568..b0ff573471 100644 --- a/docs/developer-guide.md +++ b/docs/developer-guide.md @@ -34,6 +34,15 @@ Follow [instructions](https://github.com/haveno-dex/haveno-ts#run-tests) to run 9. Run the tests with `npm run test -- -t 'my test'` to run tests by name and `npm test` to run all tests together. Ensure all tests pass and there are no exception stacktraces in the terminals of Alice, Bob, or the arbitrator. 10. Open pull requests to the haveno and haveno-ts projects for the backend and frontend implementations. +## How to manually sign accounts as the arbitrator + +1. Open legacy UI as the arbitrator. +2. Go to the 'Account' tab. +3. Open Signing tab: `ctrl+i` + a. Sign payment account: `ctrl+s`, select payment accounts to sign (sourced from disputes). + b. Sign account age witness: `ctrl+p` then enter , (from past trade details) and click the "Import unsigned account age witness" button. + c. Sign unsigned witness pub keys: `ctrl+o` + ## How to rebase and squash your commits When submitting a pull request for review, please first rebase and squash your commits. diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 423ae8321c..ff1d5744ee 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -312,6 +312,7 @@ message SignContractRequest { bytes payment_account_payload_hash = 7; string payout_address = 8;; string deposit_tx_hash = 9; + bytes account_age_witness_signature_of_deposit_hash = 10; } message SignContractResponse { @@ -404,20 +405,22 @@ message PaymentSentMessage { string payout_tx_hex = 6; string updated_multisig_hex = 7; bytes payment_account_key = 8; - bytes buyer_signature = 9; + AccountAgeWitness seller_account_age_witness = 9; + bytes buyer_signature = 10; } message PaymentReceivedMessage { string trade_id = 1; NodeAddress sender_node_address = 2; string uid = 3; - SignedWitness signed_witness = 4; // Added in v1.4.0 - string unsigned_payout_tx_hex = 5; - string signed_payout_tx_hex = 6; - string updated_multisig_hex = 7; - bool defer_publish_payout = 8; - PaymentSentMessage payment_sent_message = 9; - bytes seller_signature = 10; + string unsigned_payout_tx_hex = 4; + string signed_payout_tx_hex = 5; + string updated_multisig_hex = 6; + bool defer_publish_payout = 7; + AccountAgeWitness buyer_account_age_witness = 8; + SignedWitness buyer_signed_witness = 9; + PaymentSentMessage payment_sent_message = 10; + bytes seller_signature = 11; } message MediatedPayoutTxPublishedMessage { @@ -1732,8 +1735,9 @@ message TradingPeer { string change_output_address = 17; bytes account_age_witness_nonce = 18; bytes account_age_witness_signature = 19; - int64 current_date = 20; - bytes mediated_payout_tx_signature = 21; + AccountAgeWitness account_age_witness = 20; + int64 current_date = 21; + bytes mediated_payout_tx_signature = 22; string reserve_tx_hash = 1001; string reserve_tx_hex = 1002;