mirror of
https://github.com/haveno-dex/haveno.git
synced 2024-11-16 15:58:08 +00:00
minimum support for account witness verification & storage based on bisq
verify peer witness data and share with arbitrator for signing arbitrator can sign witness hash, owner pub key (see developer-guide.md) taker signs offer id nonce in SendOfferAvailabilityRequest maker signs deposit tx hash in SendSignContractRequest buyer verifies witness with payload on deposit confirmation seller verifies witness with payload on payment sent message add synchronization to User paymentAccountsAsObservable and elsewhere
This commit is contained in:
parent
963d92478d
commit
646380bc7a
28 changed files with 210 additions and 98 deletions
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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<AccountAgeWitness> 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
|
||||
|
|
|
@ -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 + '\'' +
|
||||
|
|
|
@ -1020,7 +1020,9 @@ public class TradeManager implements PersistedDataHost, DecryptedDirectMessageLi
|
|||
}
|
||||
|
||||
public Optional<Trade> 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<Trade> getOpenTrades() {
|
||||
|
|
|
@ -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))) +
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
);
|
||||
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -119,8 +119,10 @@ public class User implements PersistedDataHost {
|
|||
userPayload.getAcceptedLanguageLocaleCodes().add(english);
|
||||
|
||||
paymentAccountsAsObservable.addListener((SetChangeListener<PaymentAccount>) 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<PaymentAccount> 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() {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -200,9 +200,7 @@ public class SignPaymentAccountsWindow extends Overlay<SignPaymentAccountsWindow
|
|||
++rowIndex, Res.get("popup.accountSigning.confirmSelectedAccounts.headline"));
|
||||
GridPane.setRowSpan(selectedPaymentAccountsTuple.third, 2);
|
||||
selectedPaymentAccountsList = selectedPaymentAccountsTuple.second;
|
||||
ObservableList<Dispute> disputesAsObservableList = useDevPrivilegeKeys ?
|
||||
mediationManager.getDisputesAsObservableList()
|
||||
: arbitrationManager.getDisputesAsObservableList();
|
||||
ObservableList<Dispute> disputesAsObservableList = arbitrationManager.getDisputesAsObservableList();
|
||||
long safeDate = datePicker.getValue().atStartOfDay().toEpochSecond(ZoneOffset.UTC) * 1000;
|
||||
List<TraderDataItem> traderDataItemList;
|
||||
StringBuilder sb = new StringBuilder("Summary for ").append(appName).append("\n");
|
||||
|
|
|
@ -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<TradeDetailsWindow> {
|
|||
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<TradeDetailsWindow> {
|
|||
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?
|
||||
|
|
|
@ -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)) {
|
||||
|
|
|
@ -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 <witness hash>,<pub key hash> (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.
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in a new issue