Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Signer send witness to signee #4353

Merged
merged 5 commits into from
Jul 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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