Skip to content

Commit

Permalink
Merge pull request #4353 from sqrrm/signer-send-witness-to-signee
Browse files Browse the repository at this point in the history
Signer send witness to signee
  • Loading branch information
ripcurlx authored Jul 3, 2020
2 parents c190f42 + 4e7283c commit 592cfa1
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ public protobuf.PersistableNetworkPayload toProtoMessage() {
return protobuf.PersistableNetworkPayload.newBuilder().setSignedWitness(builder).build();
}

protobuf.SignedWitness toProtoSignedWitness() {
public protobuf.SignedWitness toProtoSignedWitness() {
return toProtoMessage().getSignedWitness();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.Stack;
import java.util.stream.Collectors;
Expand Down Expand Up @@ -203,6 +204,17 @@ public Set<SignedWitness> getSignedWitnessSetByOwnerPubKey(byte[] ownerPubKey) {
.collect(Collectors.toSet());
}

public boolean publishOwnSignedWitness(SignedWitness signedWitness) {
if (!Arrays.equals(signedWitness.getWitnessOwnerPubKey(), keyRing.getPubKeyRing().getSignaturePubKeyBytes()) ||
!verifySigner(signedWitness)) {
return false;
}

log.info("Publish own signedWitness {}", signedWitness);
publishSignedWitness(signedWitness);
return true;
}

// Arbitrators sign with EC key
public void signAccountAgeWitness(Coin tradeAmount,
AccountAgeWitness accountAgeWitness,
Expand Down Expand Up @@ -267,17 +279,17 @@ public void selfSignAccountAgeWitness(AccountAgeWitness accountAgeWitness) throw
}

// Any peer can sign with DSA key
public void signAccountAgeWitness(Coin tradeAmount,
AccountAgeWitness accountAgeWitness,
PublicKey peersPubKey) throws CryptoException {
public Optional<SignedWitness> signAccountAgeWitness(Coin tradeAmount,
AccountAgeWitness accountAgeWitness,
PublicKey peersPubKey) throws CryptoException {
if (isSignedAccountAgeWitness(accountAgeWitness)) {
log.warn("Trader trying to sign already signed accountagewitness {}", accountAgeWitness.toString());
return;
return Optional.empty();
}

if (!isSufficientTradeAmountForSigning(tradeAmount)) {
log.warn("Trader tried to sign account with too little trade amount");
return;
return Optional.empty();
}

byte[] signature = Sig.sign(keyRing.getSignatureKeyPair().getPrivate(), accountAgeWitness.getHash());
Expand All @@ -290,6 +302,7 @@ public void signAccountAgeWitness(Coin tradeAmount,
tradeAmount.value);
publishSignedWitness(signedWitness);
log.info("Trader signed witness {}", signedWitness.toString());
return Optional.of(signedWitness);
}

public boolean verifySignature(SignedWitness signedWitness) {
Expand Down Expand Up @@ -366,8 +379,8 @@ public Set<SignedWitness> getUnsignedSignerPubKeys() {
var oldestUnsignedSigners = new HashMap<P2PDataStorage.ByteArray, SignedWitness>();
getRootSignedWitnessSet(false).forEach(signedWitness ->
oldestUnsignedSigners.compute(new P2PDataStorage.ByteArray(signedWitness.getSignerPubKey()),
(key, oldValue) -> oldValue == null ? signedWitness :
oldValue.getDate() > signedWitness.getDate() ? signedWitness : oldValue));
(key, oldValue) -> oldValue == null ? signedWitness :
oldValue.getDate() > signedWitness.getDate() ? signedWitness : oldValue));
return new HashSet<>(oldestUnsignedSigners.values());
}

Expand All @@ -393,6 +406,11 @@ public boolean isSufficientTradeAmountForSigning(Coin tradeAmount) {
return !tradeAmount.isLessThan(MINIMUM_TRADE_AMOUNT_FOR_SIGNING);
}

private boolean verifySigner(SignedWitness signedWitness) {
return getSignedWitnessSetByOwnerPubKey(signedWitness.getWitnessOwnerPubKey(), new Stack<>()).stream()
.anyMatch(w -> isValidSignerWitnessInternal(w, signedWitness.getDate(), new Stack<>()));
}

/**
* Checks whether the accountAgeWitness has a valid signature from a peer/arbitrator and is allowed to sign
* other accounts.
Expand All @@ -417,7 +435,7 @@ private boolean isSignerAccountAgeWitness(AccountAgeWitness accountAgeWitness, l
* Helper to isValidAccountAgeWitness(accountAgeWitness)
*
* @param signedWitness the signedWitness to validate
* @param childSignedWitnessDateMillis the date the child SignedWitness was signed or current time if it is a leave.
* @param childSignedWitnessDateMillis the date the child SignedWitness was signed or current time if it is a leaf.
* @param excludedPubKeys stack to prevent recursive loops
* @return true if signedWitness is valid, false otherwise.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -668,7 +668,7 @@ public void arbitratorSignAccountAgeWitness(AccountAgeWitness accountAgeWitness,
signedWitnessService.signAccountAgeWitness(accountAgeWitness, key, tradersPubKey, time);
}

public void traderSignPeersAccountAgeWitness(Trade trade) {
public Optional<SignedWitness> traderSignPeersAccountAgeWitness(Trade trade) {
AccountAgeWitness peersWitness = findTradePeerWitness(trade).orElse(null);
Coin tradeAmount = trade.getTradeAmount();
checkNotNull(trade.getProcessModel().getTradingPeer().getPubKeyRing(), "Peer must have a keyring");
Expand All @@ -678,10 +678,15 @@ public void traderSignPeersAccountAgeWitness(Trade trade) {
checkNotNull(peersPubKey, "Peers pub key must not be null");

try {
signedWitnessService.signAccountAgeWitness(tradeAmount, peersWitness, peersPubKey);
return signedWitnessService.signAccountAgeWitness(tradeAmount, peersWitness, peersPubKey);
} catch (CryptoException e) {
log.warn("Trader failed to sign witness, exception {}", e.toString());
}
return Optional.empty();
}

public boolean publishOwnSignedWitness(SignedWitness signedWitness) {
return signedWitnessService.publishOwnSignedWitness(signedWitness);
}

// Arbitrator signing
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import bisq.core.trade.messages.PayoutTxPublishedMessage;
import bisq.core.trade.messages.PeerPublishedDelayedPayoutTxMessage;
import bisq.core.trade.messages.RefreshTradeStateRequest;
import bisq.core.trade.messages.TraderSignedWitnessMessage;
import bisq.core.trade.statistics.TradeStatistics;

import bisq.network.p2p.AckMessage;
Expand Down Expand Up @@ -165,6 +166,8 @@ public NetworkEnvelope fromProto(protobuf.NetworkEnvelope proto) throws Protobuf
return PayoutTxPublishedMessage.fromProto(proto.getPayoutTxPublishedMessage(), messageVersion);
case PEER_PUBLISHED_DELAYED_PAYOUT_TX_MESSAGE:
return PeerPublishedDelayedPayoutTxMessage.fromProto(proto.getPeerPublishedDelayedPayoutTxMessage(), messageVersion);
case TRADER_SIGNED_WITNESS_MESSAGE:
return TraderSignedWitnessMessage.fromProto(proto.getTraderSignedWitnessMessage(), messageVersion);

case MEDIATED_PAYOUT_TX_SIGNATURE_MESSAGE:
return MediatedPayoutTxSignatureMessage.fromProto(proto.getMediatedPayoutTxSignatureMessage(), messageVersion);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
* This file is part of Bisq.
*
* Bisq is free software: you can redistribute it and/or modify it
* under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or (at
* your option) any later version.
*
* Bisq is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
* License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
*/

package bisq.core.trade.messages;

import bisq.core.account.sign.SignedWitness;

import bisq.network.p2p.MailboxMessage;
import bisq.network.p2p.NodeAddress;

import bisq.common.app.Version;

import lombok.EqualsAndHashCode;
import lombok.Value;

@EqualsAndHashCode(callSuper = true)
@Value
public class TraderSignedWitnessMessage extends TradeMessage implements MailboxMessage {
private final NodeAddress senderNodeAddress;
private final SignedWitness signedWitness;

public TraderSignedWitnessMessage(String uid,
String tradeId,
NodeAddress senderNodeAddress,
SignedWitness signedWitness) {
this(Version.getP2PMessageVersion(),
uid,
tradeId,
senderNodeAddress,
signedWitness);
}


///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////

private TraderSignedWitnessMessage(int messageVersion,
String uid,
String tradeId,
NodeAddress senderNodeAddress,
SignedWitness signedWitness) {
super(messageVersion, tradeId, uid);
this.senderNodeAddress = senderNodeAddress;
this.signedWitness = signedWitness;
}

@Override
public protobuf.NetworkEnvelope toProtoNetworkEnvelope() {
final protobuf.TraderSignedWitnessMessage.Builder builder = protobuf.TraderSignedWitnessMessage.newBuilder();
builder.setUid(uid)
.setTradeId(tradeId)
.setSenderNodeAddress(senderNodeAddress.toProtoMessage())
.setSignedWitness(signedWitness.toProtoSignedWitness());
return getNetworkEnvelopeBuilder().setTraderSignedWitnessMessage(builder).build();
}

public static TraderSignedWitnessMessage fromProto(protobuf.TraderSignedWitnessMessage proto, int messageVersion) {
return new TraderSignedWitnessMessage(messageVersion,
proto.getUid(),
proto.getTradeId(),
NodeAddress.fromProto(proto.getSenderNodeAddress()),
SignedWitness.fromProto(proto.getSignedWitness()));
}

@Override
public String toString() {
return "TraderSignedWitnessMessage{" +
"\n senderNodeAddress=" + senderNodeAddress +
"\n signedWitness=" + signedWitness +
"\n} " + super.toString();
}

}
14 changes: 14 additions & 0 deletions core/src/main/java/bisq/core/trade/protocol/TradeProtocol.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import bisq.core.trade.messages.MediatedPayoutTxSignatureMessage;
import bisq.core.trade.messages.PeerPublishedDelayedPayoutTxMessage;
import bisq.core.trade.messages.TradeMessage;
import bisq.core.trade.messages.TraderSignedWitnessMessage;
import bisq.core.trade.protocol.tasks.ApplyFilter;
import bisq.core.trade.protocol.tasks.ProcessPeerPublishedDelayedPayoutTxMessage;
import bisq.core.trade.protocol.tasks.mediation.BroadcastMediatedPayoutTx;
Expand Down Expand Up @@ -228,6 +229,15 @@ private void handle(PeerPublishedDelayedPayoutTxMessage tradeMessage, NodeAddres
taskRunner.run();
}

///////////////////////////////////////////////////////////////////////////////////////////
// Peer has sent a SignedWitness
///////////////////////////////////////////////////////////////////////////////////////////

private void handle(TraderSignedWitnessMessage tradeMessage) {
// Publish signed witness, if it is valid and ours
processModel.getAccountAgeWitnessService().publishOwnSignedWitness(tradeMessage.getSignedWitness());
}


///////////////////////////////////////////////////////////////////////////////////////////
// Dispatcher
Expand All @@ -240,6 +250,8 @@ protected void doHandleDecryptedMessage(TradeMessage tradeMessage, NodeAddress s
handle((MediatedPayoutTxPublishedMessage) tradeMessage, sender);
} else if (tradeMessage instanceof PeerPublishedDelayedPayoutTxMessage) {
handle((PeerPublishedDelayedPayoutTxMessage) tradeMessage, sender);
} else if (tradeMessage instanceof TraderSignedWitnessMessage) {
handle((TraderSignedWitnessMessage) tradeMessage);
}
}

Expand Down Expand Up @@ -287,6 +299,8 @@ protected void doApplyMailboxTradeMessage(TradeMessage tradeMessage, NodeAddress
handle((MediatedPayoutTxPublishedMessage) tradeMessage, peerNodeAddress);
} else if (tradeMessage instanceof PeerPublishedDelayedPayoutTxMessage) {
handle((PeerPublishedDelayedPayoutTxMessage) tradeMessage, peerNodeAddress);
} else if (tradeMessage instanceof TraderSignedWitnessMessage) {
handle((TraderSignedWitnessMessage) tradeMessage);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import bisq.desktop.util.DisplayUtils;
import bisq.desktop.util.GUIUtil;

import bisq.core.account.sign.SignedWitness;
import bisq.core.account.witness.AccountAgeWitness;
import bisq.core.account.witness.AccountAgeWitnessService;
import bisq.core.btc.wallet.Restrictions;
Expand All @@ -33,13 +34,17 @@
import bisq.core.trade.Contract;
import bisq.core.trade.Trade;
import bisq.core.trade.closed.ClosedTradableManager;
import bisq.core.trade.messages.RefreshTradeStateRequest;
import bisq.core.trade.messages.TraderSignedWitnessMessage;
import bisq.core.user.User;
import bisq.core.util.FormattingUtils;
import bisq.core.util.coin.BsqFormatter;
import bisq.core.util.coin.CoinFormatter;
import bisq.core.util.validation.BtcAddressValidator;

import bisq.network.p2p.NodeAddress;
import bisq.network.p2p.P2PService;
import bisq.network.p2p.SendMailboxMessageListener;

import bisq.common.ClockWatcher;
import bisq.common.app.DevEnv;
Expand All @@ -58,6 +63,7 @@
import javafx.beans.property.SimpleObjectProperty;

import java.util.Date;
import java.util.UUID;
import java.util.stream.Collectors;

import lombok.Getter;
Expand Down Expand Up @@ -383,10 +389,45 @@ public boolean isSignWitnessTrade() {

public void maybeSignWitness() {
if (isSignWitnessTrade()) {
accountAgeWitnessService.traderSignPeersAccountAgeWitness(trade);
var signedWitness = accountAgeWitnessService.traderSignPeersAccountAgeWitness(trade);
signedWitness.ifPresent(this::sendSignedWitnessToPeer);
}
}

private void sendSignedWitnessToPeer(SignedWitness signedWitness) {
Trade trade = getTrade();
if (trade == null) return;

NodeAddress tradingPeerNodeAddress = trade.getTradingPeerNodeAddress();
var traderSignedWitnessMessage = new TraderSignedWitnessMessage(UUID.randomUUID().toString(), trade.getId(),
tradingPeerNodeAddress, signedWitness);

p2PService.sendEncryptedMailboxMessage(
tradingPeerNodeAddress,
trade.getProcessModel().getTradingPeer().getPubKeyRing(),
traderSignedWitnessMessage,
new SendMailboxMessageListener() {
@Override
public void onArrived() {
log.info("SendMailboxMessageListener onArrived tradeId={} at peer {} SignedWitness {}",
trade.getId(), tradingPeerNodeAddress, signedWitness);
}

@Override
public void onStoredInMailbox() {
log.info("SendMailboxMessageListener onStoredInMailbox tradeId={} at peer {} SignedWitness {}",
trade.getId(), tradingPeerNodeAddress, signedWitness);
}

@Override
public void onFault(String errorMessage) {
log.error("SendMailboxMessageListener onFault tradeId={} at peer {} SignedWitness {}",
trade.getId(), tradingPeerNodeAddress, signedWitness);
}
}
);
}

///////////////////////////////////////////////////////////////////////////////////////////
// States
///////////////////////////////////////////////////////////////////////////////////////////
Expand Down
8 changes: 8 additions & 0 deletions proto/src/main/proto/pb.proto
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ message NetworkEnvelope {
PeerPublishedDelayedPayoutTxMessage peer_published_delayed_payout_tx_message = 49;

RefreshTradeStateRequest refresh_trade_state_request = 50;
TraderSignedWitnessMessage trader_signed_witness_message = 51;
}
}

Expand Down Expand Up @@ -329,6 +330,13 @@ message RefreshTradeStateRequest {
NodeAddress sender_node_address = 3;
}

message TraderSignedWitnessMessage {
string uid = 1;
string trade_id = 2;
NodeAddress sender_node_address = 3;
SignedWitness signed_witness = 4;
}

// dispute

enum SupportType {
Expand Down

0 comments on commit 592cfa1

Please sign in to comment.