From 6a010b8b64835e9ca2ba5e49d3711172038a04c8 Mon Sep 17 00:00:00 2001 From: HenrikJannsen Date: Wed, 21 Dec 2022 19:11:26 -0500 Subject: [PATCH] Use lists instead of hashsets in data or messages to ensure that we have deterministic behaviour. The proof or work check compares the payload bytes and not deterministic sorting in collections would cause random failures. We use generic sorting of the byte array of the serialized data. --- .../java/bisq/account/accounts/Account.java | 4 ++++ .../main/java/bisq/chat/channel/Channel.java | 6 +++--- .../java/bisq/chat/channel/PrivateChannel.java | 11 +++++++---- .../java/bisq/chat/channel/PublicChannel.java | 4 ++-- .../discuss/priv/PrivateDiscussionChannel.java | 10 +++++----- .../discuss/pub/PublicDiscussionChannel.java | 17 ++++++++++------- .../chat/events/priv/PrivateEventsChannel.java | 10 +++++----- .../chat/events/pub/PublicEventsChannel.java | 17 ++++++++++------- .../support/priv/PrivateSupportChannel.java | 10 +++++----- .../chat/support/pub/PublicSupportChannel.java | 18 +++++++++++------- .../chat/trade/priv/PrivateTradeChannel.java | 10 +++++----- .../bisq/chat/trade/pub/TradeChatOffer.java | 14 ++++++++------ .../review/ReviewOfferController.java | 4 ++-- .../src/main/java/bisq/network/NetworkId.java | 4 ++-- .../java/bisq/network/p2p/node/Capability.java | 14 ++++++++------ .../main/java/bisq/network/p2p/node/Node.java | 3 ++- .../network/p2p/services/data/DataService.java | 8 ++++---- .../p2p/services/data/filter/DataFilter.java | 12 ++++++++---- .../p2p/services/data/inventory/Inventory.java | 2 +- .../exchange/PeerExchangeRequest.java | 16 +++++++++++----- .../exchange/PeerExchangeRequestHandler.java | 6 ++++-- .../exchange/PeerExchangeResponse.java | 16 +++++++++++----- .../exchange/PeerExchangeService.java | 8 +++----- .../dto/PublicDiscussionChannelDto.java | 4 ++-- .../java/bisq/support/MediationRequest.java | 13 +++++++++---- .../java/bisq/support/MediationService.java | 3 +-- 26 files changed, 143 insertions(+), 101 deletions(-) diff --git a/account/src/main/java/bisq/account/accounts/Account.java b/account/src/main/java/bisq/account/accounts/Account.java index 066b10cdad..e149293c53 100644 --- a/account/src/main/java/bisq/account/accounts/Account.java +++ b/account/src/main/java/bisq/account/accounts/Account.java @@ -19,6 +19,7 @@ import bisq.account.settlement.SettlementMethod; import bisq.common.currency.TradeCurrency; +import bisq.common.data.ByteArray; import bisq.common.proto.Proto; import bisq.common.proto.UnresolvableProtobufMessageException; import bisq.common.util.StringUtils; @@ -27,6 +28,7 @@ import lombok.ToString; import lombok.extern.slf4j.Slf4j; +import java.util.Comparator; import java.util.Date; import java.util.List; import java.util.Set; @@ -63,6 +65,8 @@ public Account(String id, long creationDate, this.payload = payload; this.settlementMethod = settlementMethod; this.tradeCurrencies = tradeCurrencies; + // We need to sort deterministically as the data is used in the proof of work check + this.tradeCurrencies.sort(Comparator.comparing((TradeCurrency e) -> new ByteArray(e.serialize()))); } diff --git a/chat/src/main/java/bisq/chat/channel/Channel.java b/chat/src/main/java/bisq/chat/channel/Channel.java index 90f3d9a925..ebc16ecd97 100644 --- a/chat/src/main/java/bisq/chat/channel/Channel.java +++ b/chat/src/main/java/bisq/chat/channel/Channel.java @@ -27,7 +27,7 @@ import bisq.chat.trade.priv.PrivateTradeChannel; import bisq.chat.trade.pub.PublicTradeChannel; import bisq.common.observable.Observable; -import bisq.common.observable.ObservableSet; +import bisq.common.observable.ObservableArray; import bisq.common.proto.Proto; import bisq.common.proto.UnresolvableProtobufMessageException; import lombok.EqualsAndHashCode; @@ -86,7 +86,7 @@ public static Channel fromProto(bisq.chat.protobuf.Channe case PUBLICSUPPORTCHANNEL: { return PublicSupportChannel.fromProto(proto, proto.getPublicSupportChannel()); } - + case MESSAGE_NOT_SET: { throw new UnresolvableProtobufMessageException(proto); } @@ -94,7 +94,7 @@ public static Channel fromProto(bisq.chat.protobuf.Channe throw new UnresolvableProtobufMessageException(proto); } - abstract public ObservableSet getChatMessages(); + abstract public ObservableArray getChatMessages(); abstract public void addChatMessage(T chatMessage); diff --git a/chat/src/main/java/bisq/chat/channel/PrivateChannel.java b/chat/src/main/java/bisq/chat/channel/PrivateChannel.java index bfa9a883c8..f8a7877b30 100644 --- a/chat/src/main/java/bisq/chat/channel/PrivateChannel.java +++ b/chat/src/main/java/bisq/chat/channel/PrivateChannel.java @@ -18,14 +18,16 @@ package bisq.chat.channel; import bisq.chat.message.PrivateChatMessage; -import bisq.common.observable.ObservableSet; +import bisq.common.data.ByteArray; +import bisq.common.observable.ObservableArray; import bisq.user.identity.UserIdentity; import bisq.user.profile.UserProfile; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; -import java.util.Set; +import java.util.Comparator; +import java.util.List; @Getter @ToString(callSuper = true) @@ -35,17 +37,18 @@ public abstract class PrivateChannel extends Chann protected final UserIdentity myUserIdentity; // We persist the messages as they are NOT persisted in the P2P data store. - protected final ObservableSet chatMessages = new ObservableSet<>(); + protected final ObservableArray chatMessages = new ObservableArray<>(); public PrivateChannel(String id, UserProfile peer, UserIdentity myUserIdentity, - Set chatMessages, + List chatMessages, ChannelNotificationType channelNotificationType) { super(id, channelNotificationType); this.peer = peer; this.myUserIdentity = myUserIdentity; this.chatMessages.addAll(chatMessages); + this.chatMessages.sort(Comparator.comparing((T e) -> new ByteArray(e.serialize()))); } public static String createChannelId(String peersId, String myId) { diff --git a/chat/src/main/java/bisq/chat/channel/PublicChannel.java b/chat/src/main/java/bisq/chat/channel/PublicChannel.java index bb8b36ac5b..b4efff17ee 100644 --- a/chat/src/main/java/bisq/chat/channel/PublicChannel.java +++ b/chat/src/main/java/bisq/chat/channel/PublicChannel.java @@ -18,7 +18,7 @@ package bisq.chat.channel; import bisq.chat.message.PublicChatMessage; -import bisq.common.observable.ObservableSet; +import bisq.common.observable.ObservableArray; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; @@ -28,7 +28,7 @@ @EqualsAndHashCode(callSuper = true, onlyExplicitlyIncluded = true) public abstract class PublicChannel extends Channel { // We do not persist the messages as they are persisted in the P2P data store. - protected transient final ObservableSet chatMessages = new ObservableSet<>(); + protected transient final ObservableArray chatMessages = new ObservableArray<>(); public PublicChannel(String id, ChannelNotificationType channelNotificationType) { super(id, channelNotificationType); diff --git a/chat/src/main/java/bisq/chat/discuss/priv/PrivateDiscussionChannel.java b/chat/src/main/java/bisq/chat/discuss/priv/PrivateDiscussionChannel.java index 9e48cff5ab..120f2c9fb4 100644 --- a/chat/src/main/java/bisq/chat/discuss/priv/PrivateDiscussionChannel.java +++ b/chat/src/main/java/bisq/chat/discuss/priv/PrivateDiscussionChannel.java @@ -25,9 +25,9 @@ import lombok.Getter; import lombok.ToString; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.List; import java.util.stream.Collectors; @ToString(callSuper = true) @@ -39,14 +39,14 @@ public PrivateDiscussionChannel(UserProfile peer, UserIdentity myProfile) { this(PrivateChannel.createChannelId(peer.getId(), myProfile.getId()), peer, myProfile, - new HashSet<>(), + new ArrayList<>(), ChannelNotificationType.ALL); } private PrivateDiscussionChannel(String id, UserProfile peer, UserIdentity myProfile, - Set chatMessages, + List chatMessages, ChannelNotificationType channelNotificationType) { super(id, peer, myProfile, chatMessages, channelNotificationType); } @@ -70,7 +70,7 @@ public static PrivateDiscussionChannel fromProto(bisq.chat.protobuf.Channel base UserIdentity.fromProto(proto.getMyUserIdentity()), proto.getChatMessagesList().stream() .map(PrivateDiscussionChatMessage::fromProto) - .collect(Collectors.toSet()), + .collect(Collectors.toList()), ChannelNotificationType.fromProto(baseProto.getChannelNotificationType())); } diff --git a/chat/src/main/java/bisq/chat/discuss/pub/PublicDiscussionChannel.java b/chat/src/main/java/bisq/chat/discuss/pub/PublicDiscussionChannel.java index a62a7cb9c2..e2f6efe423 100644 --- a/chat/src/main/java/bisq/chat/discuss/pub/PublicDiscussionChannel.java +++ b/chat/src/main/java/bisq/chat/discuss/pub/PublicDiscussionChannel.java @@ -24,9 +24,10 @@ import lombok.Getter; import lombok.ToString; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.Comparator; +import java.util.List; @Getter @ToString(callSuper = true) @@ -35,14 +36,14 @@ public final class PublicDiscussionChannel extends PublicChannel channelModeratorIds; + private final List channelModeratorIds; public PublicDiscussionChannel(String id) { this(id, Res.get("discussion." + id + ".name"), Res.get("discussion." + id + ".description"), "", - new HashSet<>(), + new ArrayList<>(), ChannelNotificationType.MENTION); } @@ -50,7 +51,7 @@ public PublicDiscussionChannel(String id, String channelName, String description, String channelAdminId, - Set channelModeratorIds) { + List channelModeratorIds) { this(id, channelName, description, @@ -63,7 +64,7 @@ private PublicDiscussionChannel(String id, String channelName, String description, String channelAdminId, - Set channelModeratorIds, + List channelModeratorIds, ChannelNotificationType channelNotificationType) { super(id, channelNotificationType); @@ -71,6 +72,8 @@ private PublicDiscussionChannel(String id, this.description = description; this.channelAdminId = channelAdminId; this.channelModeratorIds = channelModeratorIds; + // We need to sort deterministically as the data is used in the proof of work check + this.channelModeratorIds.sort(Comparator.comparing((String e) -> e)); } public bisq.chat.protobuf.Channel toProto() { @@ -90,7 +93,7 @@ public static PublicDiscussionChannel fromProto(bisq.chat.protobuf.Channel baseP proto.getChannelName(), proto.getDescription(), proto.getChannelAdminId(), - new HashSet<>(proto.getChannelModeratorIdsList()), + new ArrayList<>(proto.getChannelModeratorIdsList()), ChannelNotificationType.fromProto(baseProto.getChannelNotificationType())); } diff --git a/chat/src/main/java/bisq/chat/events/priv/PrivateEventsChannel.java b/chat/src/main/java/bisq/chat/events/priv/PrivateEventsChannel.java index 5693cd2efd..df99ee8f56 100644 --- a/chat/src/main/java/bisq/chat/events/priv/PrivateEventsChannel.java +++ b/chat/src/main/java/bisq/chat/events/priv/PrivateEventsChannel.java @@ -25,9 +25,9 @@ import lombok.Getter; import lombok.ToString; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.List; import java.util.stream.Collectors; @ToString(callSuper = true) @@ -39,14 +39,14 @@ public PrivateEventsChannel(UserProfile peer, UserIdentity myProfile) { this(PrivateChannel.createChannelId(peer.getId(), myProfile.getId()), peer, myProfile, - new HashSet<>(), + new ArrayList<>(), ChannelNotificationType.ALL); } private PrivateEventsChannel(String id, UserProfile peer, UserIdentity myProfile, - Set chatMessages, + List chatMessages, ChannelNotificationType channelNotificationType) { super(id, peer, myProfile, chatMessages, channelNotificationType); } @@ -70,7 +70,7 @@ public static PrivateEventsChannel fromProto(bisq.chat.protobuf.Channel baseProt UserIdentity.fromProto(proto.getMyUserIdentity()), proto.getChatMessagesList().stream() .map(PrivateEventsChatMessage::fromProto) - .collect(Collectors.toSet()), + .collect(Collectors.toList()), ChannelNotificationType.fromProto(baseProto.getChannelNotificationType())); } diff --git a/chat/src/main/java/bisq/chat/events/pub/PublicEventsChannel.java b/chat/src/main/java/bisq/chat/events/pub/PublicEventsChannel.java index bb93547e71..bf9c32cb77 100644 --- a/chat/src/main/java/bisq/chat/events/pub/PublicEventsChannel.java +++ b/chat/src/main/java/bisq/chat/events/pub/PublicEventsChannel.java @@ -24,9 +24,10 @@ import lombok.Getter; import lombok.ToString; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.Comparator; +import java.util.List; @Getter @ToString(callSuper = true) @@ -35,14 +36,14 @@ public final class PublicEventsChannel extends PublicChannel channelModeratorIds; + private final List channelModeratorIds; public PublicEventsChannel(String id) { this(id, Res.get("events." + id + ".name"), Res.get("events." + id + ".description"), "", - new HashSet<>(), + new ArrayList<>(), ChannelNotificationType.MENTION); } @@ -50,7 +51,7 @@ public PublicEventsChannel(String id, String channelName, String description, String channelAdminId, - Set channelModeratorIds) { + List channelModeratorIds) { this(id, channelName, description, @@ -63,7 +64,7 @@ private PublicEventsChannel(String id, String channelName, String description, String channelAdminId, - Set channelModeratorIds, + List channelModeratorIds, ChannelNotificationType channelNotificationType) { super(id, channelNotificationType); @@ -71,6 +72,8 @@ private PublicEventsChannel(String id, this.description = description; this.channelAdminId = channelAdminId; this.channelModeratorIds = channelModeratorIds; + // We need to sort deterministically as the data is used in the proof of work check + this.channelModeratorIds.sort(Comparator.comparing((String e) -> e)); } public bisq.chat.protobuf.Channel toProto() { @@ -90,7 +93,7 @@ public static PublicEventsChannel fromProto(bisq.chat.protobuf.Channel baseProto proto.getChannelName(), proto.getDescription(), proto.getChannelAdminId(), - new HashSet<>(proto.getChannelModeratorIdsList()), + new ArrayList<>(proto.getChannelModeratorIdsList()), ChannelNotificationType.fromProto(baseProto.getChannelNotificationType())); } diff --git a/chat/src/main/java/bisq/chat/support/priv/PrivateSupportChannel.java b/chat/src/main/java/bisq/chat/support/priv/PrivateSupportChannel.java index 2393243fe5..814c4659b5 100644 --- a/chat/src/main/java/bisq/chat/support/priv/PrivateSupportChannel.java +++ b/chat/src/main/java/bisq/chat/support/priv/PrivateSupportChannel.java @@ -25,9 +25,9 @@ import lombok.Getter; import lombok.ToString; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.List; import java.util.stream.Collectors; @ToString(callSuper = true) @@ -39,14 +39,14 @@ public PrivateSupportChannel(UserProfile peer, UserIdentity myProfile) { this(PrivateChannel.createChannelId(peer.getId(), myProfile.getId()), peer, myProfile, - new HashSet<>(), + new ArrayList<>(), ChannelNotificationType.ALL); } private PrivateSupportChannel(String id, UserProfile peer, UserIdentity myProfile, - Set chatMessages, + List chatMessages, ChannelNotificationType channelNotificationType) { super(id, peer, myProfile, chatMessages, channelNotificationType); } @@ -70,7 +70,7 @@ public static PrivateSupportChannel fromProto(bisq.chat.protobuf.Channel basePro UserIdentity.fromProto(proto.getMyUserIdentity()), proto.getChatMessagesList().stream() .map(PrivateSupportChatMessage::fromProto) - .collect(Collectors.toSet()), + .collect(Collectors.toList()), ChannelNotificationType.fromProto(baseProto.getChannelNotificationType())); } diff --git a/chat/src/main/java/bisq/chat/support/pub/PublicSupportChannel.java b/chat/src/main/java/bisq/chat/support/pub/PublicSupportChannel.java index 1ff46bfaa2..0674312188 100644 --- a/chat/src/main/java/bisq/chat/support/pub/PublicSupportChannel.java +++ b/chat/src/main/java/bisq/chat/support/pub/PublicSupportChannel.java @@ -24,9 +24,10 @@ import lombok.Getter; import lombok.ToString; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; -import java.util.Set; +import java.util.Comparator; +import java.util.List; @Getter @ToString(callSuper = true) @@ -35,14 +36,14 @@ public final class PublicSupportChannel extends PublicChannel channelModeratorIds; + private final List channelModeratorIds; public PublicSupportChannel(String id) { this(id, Res.get("support." + id + ".name"), Res.get("support." + id + ".description"), "", - new HashSet<>(), + new ArrayList<>(), ChannelNotificationType.MENTION); } @@ -50,7 +51,7 @@ public PublicSupportChannel(String id, String channelName, String description, String channelAdminId, - Set channelModeratorIds) { + List channelModeratorIds) { this(id, channelName, description, @@ -63,7 +64,7 @@ private PublicSupportChannel(String id, String channelName, String description, String channelAdminId, - Set channelModeratorIds, + List channelModeratorIds, ChannelNotificationType channelNotificationType) { super(id, channelNotificationType); @@ -71,9 +72,12 @@ private PublicSupportChannel(String id, this.description = description; this.channelAdminId = channelAdminId; this.channelModeratorIds = channelModeratorIds; + // We need to sort deterministically as the data is used in the proof of work check + this.channelModeratorIds.sort(Comparator.comparing((String e) -> e)); } public bisq.chat.protobuf.Channel toProto() { + return getChannelBuilder() .setPublicSupportChannel(bisq.chat.protobuf.PublicSupportChannel.newBuilder() .setChannelName(channelName) @@ -90,7 +94,7 @@ public static PublicSupportChannel fromProto(bisq.chat.protobuf.Channel baseProt proto.getChannelName(), proto.getDescription(), proto.getChannelAdminId(), - new HashSet<>(proto.getChannelModeratorIdsList()), + new ArrayList<>(proto.getChannelModeratorIdsList()), ChannelNotificationType.fromProto(baseProto.getChannelNotificationType())); } diff --git a/chat/src/main/java/bisq/chat/trade/priv/PrivateTradeChannel.java b/chat/src/main/java/bisq/chat/trade/priv/PrivateTradeChannel.java index add9e6b135..d56f245b74 100644 --- a/chat/src/main/java/bisq/chat/trade/priv/PrivateTradeChannel.java +++ b/chat/src/main/java/bisq/chat/trade/priv/PrivateTradeChannel.java @@ -27,10 +27,10 @@ import lombok.Getter; import lombok.ToString; +import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; +import java.util.List; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; @ToString(callSuper = true) @@ -47,7 +47,7 @@ public PrivateTradeChannel(UserIdentity myUserIdentity, UserProfile trader1, Use super(PrivateChannel.createChannelId(trader1.getId(), trader2.getId()), trader1, myUserIdentity, - new HashSet<>(), + new ArrayList<>(), ChannelNotificationType.ALL); this.trader1 = trader1; this.trader2 = trader2; @@ -59,7 +59,7 @@ private PrivateTradeChannel(String id, UserProfile peer, UserIdentity myUserIdentity, Optional mediator, - Set chatMessages, + List chatMessages, ChannelNotificationType channelNotificationType) { super(id, peer, myUserIdentity, chatMessages, channelNotificationType); @@ -91,7 +91,7 @@ public static PrivateTradeChannel fromProto(bisq.chat.protobuf.Channel baseProto proto.hasMediator() ? Optional.of(UserProfile.fromProto(proto.getMediator())) : Optional.empty(), proto.getChatMessagesList().stream() .map(PrivateTradeChatMessage::fromProto) - .collect(Collectors.toSet()), + .collect(Collectors.toList()), ChannelNotificationType.fromProto(baseProto.getChannelNotificationType())); privateTradeChannel.getMediationActivated().set(proto.getMediationActivated()); return privateTradeChannel; diff --git a/chat/src/main/java/bisq/chat/trade/pub/TradeChatOffer.java b/chat/src/main/java/bisq/chat/trade/pub/TradeChatOffer.java index 0fb8a1ad4c..218935199d 100644 --- a/chat/src/main/java/bisq/chat/trade/pub/TradeChatOffer.java +++ b/chat/src/main/java/bisq/chat/trade/pub/TradeChatOffer.java @@ -14,8 +14,8 @@ import lombok.extern.slf4j.Slf4j; import java.util.ArrayList; -import java.util.HashSet; -import java.util.Set; +import java.util.Comparator; +import java.util.List; @ToString @EqualsAndHashCode @@ -26,7 +26,7 @@ public final class TradeChatOffer implements Proto { private final long baseSideAmount; private final Market market; private final long quoteSideAmount; - private final Set paymentMethods; + private final List paymentMethods; private final String makersTradeTerms; private final long requiredTotalReputationScore; @@ -36,7 +36,7 @@ public TradeChatOffer(Direction direction, Market market, long baseSideAmount, long quoteSideAmount, - Set paymentMethods, + List paymentMethods, String makersTradeTerms, long requiredTotalReputationScore) { this.direction = direction; @@ -47,6 +47,8 @@ public TradeChatOffer(Direction direction, this.makersTradeTerms = makersTradeTerms; this.requiredTotalReputationScore = requiredTotalReputationScore; + // We need to sort deterministically as the data is used in the proof of work check + this.paymentMethods.sort(Comparator.comparing((String e) -> e)); chatMessageText = Res.get("createOffer.tradeChatOffer.chatMessage", Res.get(direction.name().toLowerCase()).toUpperCase(), AmountFormatter.formatAmountWithCode(Fiat.of(quoteSideAmount, market.getQuoteCurrencyCode()), true), @@ -60,7 +62,7 @@ public bisq.chat.protobuf.TradeChatOffer toProto() { .setMarket(market.toProto()) .setBaseSideAmount(baseSideAmount) .setQuoteSideAmount(quoteSideAmount) - .addAllPaymentMethods(new ArrayList<>(paymentMethods)) + .addAllPaymentMethods(paymentMethods) .setMakersTradeTerms(makersTradeTerms) .setRequiredTotalReputationScore(requiredTotalReputationScore) .build(); @@ -71,7 +73,7 @@ public static TradeChatOffer fromProto(bisq.chat.protobuf.TradeChatOffer proto) Market.fromProto(proto.getMarket()), proto.getBaseSideAmount(), proto.getQuoteSideAmount(), - new HashSet<>(proto.getPaymentMethodsList()), + new ArrayList<>(proto.getPaymentMethodsList()), proto.getMakersTradeTerms(), proto.getRequiredTotalReputationScore()); } diff --git a/desktop/src/main/java/bisq/desktop/primary/overlay/createOffer/review/ReviewOfferController.java b/desktop/src/main/java/bisq/desktop/primary/overlay/createOffer/review/ReviewOfferController.java index ef4c836128..688ac711df 100644 --- a/desktop/src/main/java/bisq/desktop/primary/overlay/createOffer/review/ReviewOfferController.java +++ b/desktop/src/main/java/bisq/desktop/primary/overlay/createOffer/review/ReviewOfferController.java @@ -135,7 +135,7 @@ public void onActivate() { model.getMarket(), model.getBaseSideAmount().getValue(), model.getQuoteSideAmount().getValue(), - new HashSet<>(model.getPaymentMethods()), + new ArrayList<>(model.getPaymentMethods()), userIdentity.getUserProfile().getTerms(), settingsService.getRequiredTotalReputationScore().get()); model.setMyOfferText(StringUtils.truncate(tradeChatOffer.getChatMessageText(), 100)); @@ -263,7 +263,7 @@ private Predicate getTakeOfferPredicate() { return false; } - Set paymentMethods = peersOffer.getPaymentMethods(); + List paymentMethods = peersOffer.getPaymentMethods(); if (myChatOffer.getPaymentMethods().stream().noneMatch(paymentMethods::contains)) { return false; } diff --git a/network/network/src/main/java/bisq/network/NetworkId.java b/network/network/src/main/java/bisq/network/NetworkId.java index 5556d33b51..9b565d59d5 100644 --- a/network/network/src/main/java/bisq/network/NetworkId.java +++ b/network/network/src/main/java/bisq/network/NetworkId.java @@ -61,8 +61,8 @@ public bisq.network.protobuf.NetworkId toProto() { } public static NetworkId fromProto(bisq.network.protobuf.NetworkId proto) { - Map addressByNetworkType = proto.getAddressNetworkTypeTupleList().stream(). - map(AddressTransportTypeTuple::fromProto) + Map addressByNetworkType = proto.getAddressNetworkTypeTupleList().stream() + .map(AddressTransportTypeTuple::fromProto) .collect(Collectors.toMap(e -> e.transportType, e -> e.address)); return new NetworkId(addressByNetworkType, PubKey.fromProto(proto.getPubKey()), proto.getNodeId()); } diff --git a/network/network/src/main/java/bisq/network/p2p/node/Capability.java b/network/network/src/main/java/bisq/network/p2p/node/Capability.java index 4b6f28d428..7e60a41b84 100644 --- a/network/network/src/main/java/bisq/network/p2p/node/Capability.java +++ b/network/network/src/main/java/bisq/network/p2p/node/Capability.java @@ -24,7 +24,8 @@ import lombok.Getter; import lombok.ToString; -import java.util.Set; +import java.util.Comparator; +import java.util.List; import java.util.stream.Collectors; @Getter @@ -32,27 +33,28 @@ @EqualsAndHashCode public final class Capability implements Proto { private final Address address; - private final Set supportedTransportTypes; + private final List supportedTransportTypes; - public Capability(Address address, Set supportedTransportTypes) { + public Capability(Address address, List supportedTransportTypes) { this.address = address; this.supportedTransportTypes = supportedTransportTypes; + // We need to sort deterministically as the data is used in the proof of work check + this.supportedTransportTypes.sort(Comparator.comparing(Enum::ordinal)); } public bisq.network.protobuf.Capability toProto() { return bisq.network.protobuf.Capability.newBuilder() .setAddress(address.toProto()) .addAllSupportedTransportTypes(supportedTransportTypes.stream() - .sorted(Enum::compareTo) .map(Enum::name) .collect(Collectors.toList())) .build(); } public static Capability fromProto(bisq.network.protobuf.Capability proto) { - Set supportedTransportTypes = proto.getSupportedTransportTypesList().stream() + List supportedTransportTypes = proto.getSupportedTransportTypesList().stream() .map(e -> ProtobufUtils.enumFromProto(Transport.Type.class, e)) - .collect(Collectors.toSet()); + .collect(Collectors.toList()); return new Capability(Address.fromProto(proto.getAddress()), supportedTransportTypes); } } diff --git a/network/network/src/main/java/bisq/network/p2p/node/Node.java b/network/network/src/main/java/bisq/network/p2p/node/Node.java index cdbdc7375a..00887b925c 100644 --- a/network/network/src/main/java/bisq/network/p2p/node/Node.java +++ b/network/network/src/main/java/bisq/network/p2p/node/Node.java @@ -45,6 +45,7 @@ import java.net.SocketTimeoutException; import java.net.UnknownHostException; import java.time.Duration; +import java.util.ArrayList; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -203,7 +204,7 @@ private void doInitialize(int port) { private void createServerAndListen(int port) { Transport.ServerSocketResult serverSocketResult = transport.getServerSocket(port, nodeId); - myCapability = Optional.of(new Capability(serverSocketResult.getAddress(), config.getSupportedTransportTypes())); + myCapability = Optional.of(new Capability(serverSocketResult.getAddress(), new ArrayList<>(config.getSupportedTransportTypes()))); server = Optional.of(new Server(serverSocketResult, socket -> onClientSocket(socket, serverSocketResult, myCapability.get()), exception -> { diff --git a/network/network/src/main/java/bisq/network/p2p/services/data/DataService.java b/network/network/src/main/java/bisq/network/p2p/services/data/DataService.java index 5e7d0705e8..7d8da0c5c2 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/data/DataService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/data/DataService.java @@ -42,8 +42,8 @@ import java.security.GeneralSecurityException; import java.security.KeyPair; import java.security.PublicKey; +import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -292,11 +292,11 @@ public void requestInventory() { } public void requestInventory(StorageService.StoreType storeType) { - requestInventory(new DataFilter(new HashSet<>(storageService.getFilterEntries(storeType)))); + requestInventory(new DataFilter(new ArrayList<>(storageService.getFilterEntries(storeType)))); } public void requestInventory(String storeName) { - requestInventory(new DataFilter(new HashSet<>(storageService.getFilterEntries(storeName)))); + requestInventory(new DataFilter(new ArrayList<>(storageService.getFilterEntries(storeName)))); } public void requestInventory(DataFilter dataFilter) { @@ -372,6 +372,6 @@ private void processRemoveDataRequest(RemoveDataRequest removeDataRequest, boole } private void doRequestInventory(DataNetworkService dataNetworkService) { - requestInventory(new DataFilter(new HashSet<>(storageService.getFilterEntries(StorageService.StoreType.ALL))), dataNetworkService); + requestInventory(new DataFilter(new ArrayList<>(storageService.getFilterEntries(StorageService.StoreType.ALL))), dataNetworkService); } } \ No newline at end of file diff --git a/network/network/src/main/java/bisq/network/p2p/services/data/filter/DataFilter.java b/network/network/src/main/java/bisq/network/p2p/services/data/filter/DataFilter.java index 2df9299bbd..09a86cec44 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/data/filter/DataFilter.java +++ b/network/network/src/main/java/bisq/network/p2p/services/data/filter/DataFilter.java @@ -18,22 +18,26 @@ package bisq.network.p2p.services.data.filter; +import bisq.common.data.ByteArray; import bisq.common.proto.Proto; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; -import java.util.Set; +import java.util.Comparator; +import java.util.List; import java.util.stream.Collectors; @Getter @ToString @EqualsAndHashCode public final class DataFilter implements Proto { - private final Set filterEntries; + private final List filterEntries; - public DataFilter(Set filterEntries) { + public DataFilter(List filterEntries) { this.filterEntries = filterEntries; + // We need to sort deterministically as the data is used in the proof of work check + this.filterEntries.sort(Comparator.comparing((FilterEntry e) -> new ByteArray(e.serialize()))); } public bisq.network.protobuf.DataFilter toProto() { @@ -47,6 +51,6 @@ public bisq.network.protobuf.DataFilter toProto() { public static DataFilter fromProto(bisq.network.protobuf.DataFilter proto) { return new DataFilter(proto.getFilterEntriesList().stream() .map(FilterEntry::fromProto) - .collect(Collectors.toSet())); + .collect(Collectors.toList())); } } \ No newline at end of file diff --git a/network/network/src/main/java/bisq/network/p2p/services/data/inventory/Inventory.java b/network/network/src/main/java/bisq/network/p2p/services/data/inventory/Inventory.java index 33a2407cef..dbdb3ab4b4 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/data/inventory/Inventory.java +++ b/network/network/src/main/java/bisq/network/p2p/services/data/inventory/Inventory.java @@ -44,7 +44,7 @@ public Inventory(Collection entries, int numDropped) { this.numDropped = numDropped; // We need to sort deterministically as the data is used in the proof of work check - this.entries.sort(Comparator.comparing((DataRequest o) -> new ByteArray(o.serialize()))); + this.entries.sort(Comparator.comparing((DataRequest e) -> new ByteArray(e.serialize()))); } public bisq.network.protobuf.Inventory toProto() { diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequest.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequest.java index 0b6acebf7b..a6d9b77a3b 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequest.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequest.java @@ -17,13 +17,15 @@ package bisq.network.p2p.services.peergroup.exchange; +import bisq.common.data.ByteArray; import bisq.network.p2p.message.NetworkMessage; import bisq.network.p2p.services.peergroup.Peer; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; -import java.util.Set; +import java.util.Comparator; +import java.util.List; import java.util.stream.Collectors; @Getter @@ -31,11 +33,13 @@ @EqualsAndHashCode public final class PeerExchangeRequest implements NetworkMessage { private final int nonce; - private final Set peers; + private final List peers; - public PeerExchangeRequest(int nonce, Set peers) { + public PeerExchangeRequest(int nonce, List peers) { this.nonce = nonce; this.peers = peers; + // We need to sort deterministically as the data is used in the proof of work check + this.peers.sort(Comparator.comparing((Peer e) -> new ByteArray(e.serialize()))); } @Override @@ -43,12 +47,14 @@ public bisq.network.protobuf.NetworkMessage toProto() { return getNetworkMessageBuilder().setPeerExchangeRequest( bisq.network.protobuf.PeerExchangeRequest.newBuilder() .setNonce(nonce) - .addAllPeers(peers.stream().map(Peer::toProto).collect(Collectors.toSet()))) + .addAllPeers(peers.stream() + .map(Peer::toProto) + .collect(Collectors.toList()))) .build(); } public static PeerExchangeRequest fromProto(bisq.network.protobuf.PeerExchangeRequest proto) { return new PeerExchangeRequest(proto.getNonce(), - proto.getPeersList().stream().map(Peer::fromProto).collect(Collectors.toSet())); + proto.getPeersList().stream().map(Peer::fromProto).collect(Collectors.toList())); } } \ No newline at end of file diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequestHandler.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequestHandler.java index 131755b9e8..538f7932d3 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequestHandler.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeRequestHandler.java @@ -25,6 +25,8 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; +import java.util.ArrayList; +import java.util.HashSet; import java.util.Random; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -51,7 +53,7 @@ CompletableFuture> request(Set peersForPeerExchange) { ts = System.currentTimeMillis(); try { // We get called from the IO thread, so we do not use the async send method - node.send(new PeerExchangeRequest(nonce, peersForPeerExchange), connection); + node.send(new PeerExchangeRequest(nonce, new ArrayList<>(peersForPeerExchange)), connection); } catch (Throwable throwable) { future.completeExceptionally(throwable); dispose(); @@ -73,7 +75,7 @@ public void onNetworkMessage(NetworkMessage networkMessage) { node, connection.getPeerAddress(), response.getPeers().size()); connection.getMetrics().addRtt(ts = System.currentTimeMillis() - ts); removeListeners(); - future.complete(response.getPeers()); + future.complete(new HashSet<>(response.getPeers())); } else { log.warn("Node {} received a PeerExchangeResponse from {} with an invalid nonce. response.nonce()={}, nonce={}", node, connection.getPeerAddress(), response.getNonce(), nonce); diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeResponse.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeResponse.java index 97cc67c8b8..8b6acb0cb4 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeResponse.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeResponse.java @@ -17,13 +17,15 @@ package bisq.network.p2p.services.peergroup.exchange; +import bisq.common.data.ByteArray; import bisq.network.p2p.message.NetworkMessage; import bisq.network.p2p.services.peergroup.Peer; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.ToString; -import java.util.Set; +import java.util.Comparator; +import java.util.List; import java.util.stream.Collectors; @Getter @@ -31,11 +33,13 @@ @EqualsAndHashCode public final class PeerExchangeResponse implements NetworkMessage { private final int nonce; - private final Set peers; + private final List peers; - public PeerExchangeResponse(int nonce, Set peers) { + public PeerExchangeResponse(int nonce, List peers) { this.nonce = nonce; this.peers = peers; + // We need to sort deterministically as the data is used in the proof of work check + this.peers.sort(Comparator.comparing((Peer e) -> new ByteArray(e.serialize()))); } @Override @@ -43,12 +47,14 @@ public bisq.network.protobuf.NetworkMessage toProto() { return getNetworkMessageBuilder().setPeerExchangeResponse( bisq.network.protobuf.PeerExchangeResponse.newBuilder() .setNonce(nonce) - .addAllPeers(peers.stream().map(Peer::toProto).collect(Collectors.toSet()))) + .addAllPeers(peers.stream() + .map(Peer::toProto) + .collect(Collectors.toList()))) .build(); } public static PeerExchangeResponse fromProto(bisq.network.protobuf.PeerExchangeResponse proto) { return new PeerExchangeResponse(proto.getNonce(), - proto.getPeersList().stream().map(Peer::fromProto).collect(Collectors.toSet())); + proto.getPeersList().stream().map(Peer::fromProto).collect(Collectors.toList())); } } \ No newline at end of file diff --git a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java index 3df2c4dde2..03ac95a031 100644 --- a/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java +++ b/network/network/src/main/java/bisq/network/p2p/services/peergroup/exchange/PeerExchangeService.java @@ -29,9 +29,7 @@ import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import java.util.Map; -import java.util.Optional; -import java.util.Set; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -181,8 +179,8 @@ public void onMessage(NetworkMessage networkMessage, Connection connection, Stri PeerExchangeRequest request = (PeerExchangeRequest) networkMessage; //log.debug("Node {} received PeerExchangeRequest with myPeers {}", node, request.peers()); Address peerAddress = connection.getPeerAddress(); - peerExchangeStrategy.addReportedPeers(request.getPeers(), peerAddress); - Set myPeers = peerExchangeStrategy.getPeers(peerAddress); + peerExchangeStrategy.addReportedPeers(new HashSet<>(request.getPeers()), peerAddress); + List myPeers = new ArrayList<>(peerExchangeStrategy.getPeers(peerAddress)); NETWORK_IO_POOL.submit(() -> node.send(new PeerExchangeResponse(request.getNonce(), myPeers), connection)); log.debug("Node {} sent PeerExchangeResponse with my myPeers {}", node, myPeers); } diff --git a/restApi/src/main/java/bisq/restApi/dto/PublicDiscussionChannelDto.java b/restApi/src/main/java/bisq/restApi/dto/PublicDiscussionChannelDto.java index 14bae019b2..851f8b7948 100644 --- a/restApi/src/main/java/bisq/restApi/dto/PublicDiscussionChannelDto.java +++ b/restApi/src/main/java/bisq/restApi/dto/PublicDiscussionChannelDto.java @@ -6,7 +6,7 @@ import lombok.Getter; import lombok.ToString; -import java.util.Set; +import java.util.List; @EqualsAndHashCode(onlyExplicitlyIncluded = true) @@ -19,7 +19,7 @@ public final class PublicDiscussionChannelDto { private String channelName; private String description; private String channelAdminId; - private Set channelModeratorIds; + private List channelModeratorIds; public static PublicDiscussionChannelDto from(PublicDiscussionChannel publicDiscussionChannel) { PublicDiscussionChannelDto dto = new PublicDiscussionChannelDto(); diff --git a/support/src/main/java/bisq/support/MediationRequest.java b/support/src/main/java/bisq/support/MediationRequest.java index d3c6825e4c..0fe2938f27 100644 --- a/support/src/main/java/bisq/support/MediationRequest.java +++ b/support/src/main/java/bisq/support/MediationRequest.java @@ -18,6 +18,7 @@ package bisq.support; import bisq.chat.trade.priv.PrivateTradeChatMessage; +import bisq.common.data.ByteArray; import bisq.common.proto.ProtoResolver; import bisq.common.proto.UnresolvableProtobufMessageException; import bisq.network.p2p.services.data.storage.MetaData; @@ -30,7 +31,8 @@ import lombok.Getter; import lombok.ToString; -import java.util.Set; +import java.util.Comparator; +import java.util.List; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; @@ -43,12 +45,15 @@ public final class MediationRequest implements MailboxMessage { MediationRequest.class.getSimpleName()); private final UserProfile requester; private final UserProfile peer; - private final Set chatMessages; + private final List chatMessages; - public MediationRequest(Set chatMessages, UserProfile requester, UserProfile peer) { + public MediationRequest(List chatMessages, UserProfile requester, UserProfile peer) { this.chatMessages = chatMessages; this.requester = requester; this.peer = peer; + + // We need to sort deterministically as the data is used in the proof of work check + this.chatMessages.sort(Comparator.comparing((PrivateTradeChatMessage e) -> new ByteArray(e.serialize()))); } @Override @@ -72,7 +77,7 @@ private bisq.support.protobuf.MediationRequest toMediationRequestProto() { public static MediationRequest fromProto(bisq.support.protobuf.MediationRequest proto) { return new MediationRequest(proto.getChatMessagesList().stream() .map(PrivateTradeChatMessage::fromProto) - .collect(Collectors.toSet()), + .collect(Collectors.toList()), UserProfile.fromProto(proto.getRequester()), UserProfile.fromProto(proto.getPeer())); } diff --git a/support/src/main/java/bisq/support/MediationService.java b/support/src/main/java/bisq/support/MediationService.java index 67d44da899..ba716284eb 100644 --- a/support/src/main/java/bisq/support/MediationService.java +++ b/support/src/main/java/bisq/support/MediationService.java @@ -40,7 +40,6 @@ import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.util.ArrayList; -import java.util.HashSet; import java.util.Optional; import java.util.Set; import java.util.concurrent.CompletableFuture; @@ -124,7 +123,7 @@ public void onMessage(NetworkMessage networkMessage) { public void requestMediation(UserIdentity myProfile, UserProfile peer, UserProfile mediator) { PrivateTradeChannel channel = (PrivateTradeChannel) tradeChannelSelectionService.getSelectedChannel().get(); - MediationRequest networkMessage = new MediationRequest(new HashSet<>(channel.getChatMessages()), + MediationRequest networkMessage = new MediationRequest(new ArrayList<>(channel.getChatMessages()), myProfile.getUserProfile(), peer); networkService.confidentialSend(networkMessage, mediator.getNetworkId(), myProfile.getNodeIdAndKeyPair());