From 2f291a6acb3b33ce79f9b5283d707e0217a5be31 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Thu, 27 Aug 2020 21:23:02 -0500 Subject: [PATCH 01/13] Cleanup Filter value object. Remove nullables from old versions. Keep signatureAsBase64 nullable as it is set null at creation time and set later after signing. We use filter protobuf data as input for signature so we have to set signature to null and apply the signature afterwards. To keep object immutable we clone it with static clone method. Similar we handle signature verification. We clone the filter with a null value for the signature and do the validation with that cloned object. Use now the complete object data for signature creation. We use the protobuf data for creating the signature with DER encoding. We restict ourself more regarding backward compatibility but I think it is not a big problem. If a new field is added to Filter and deployed the maintainer needs to publishes a new filter object. By using the new version he cannot remove the old filter from the network as the protobuf data is different and sig verification on the P2P datastorage level will fail. This is intended to keep the old filter alive for some time to support not updated users. We do not remove invalid filters anymore from our local storage to enable seed nodes to support also old filter objects. For not updated users the new filter will fail at sig verification because the protobuf data is different. So they ignore the new filter and still use the old filter. For updated users the old filter will fail and the new filter is accepted. As it has a newer date it would also replace the old filter anyway. If the maintainer wants to delete the old filter from the network he can use the old app version and remove the filter. It is recommended to keep a copy of the data directory before the update so that the removal of the older filter is possible. Refacorings: - Rename isPeersPaymentAccountDataAreBanned to arePeersPaymentAccountDataBanned - Rename isSignerPubKeyBanned to isWitnessSignerPubKeyBanned --- .../java/bisq/common/proto/ProtoUtil.java | 8 + .../account/sign/SignedWitnessService.java | 4 +- .../witness/AccountAgeWitnessService.java | 8 +- .../main/java/bisq/core/filter/Filter.java | 329 ++++++++-------- .../java/bisq/core/filter/FilterManager.java | 363 +++++++++--------- .../trade/protocol/tasks/ApplyFilter.java | 2 +- .../sign/SignedWitnessServiceTest.java | 22 +- .../witness/AccountAgeWitnessServiceTest.java | 4 +- .../main/overlays/windows/FilterWindow.java | 74 ++-- proto/src/main/proto/pb.proto | 1 + 10 files changed, 441 insertions(+), 374 deletions(-) diff --git a/common/src/main/java/bisq/common/proto/ProtoUtil.java b/common/src/main/java/bisq/common/proto/ProtoUtil.java index 6fd7b0f2e5d..e605d12e330 100644 --- a/common/src/main/java/bisq/common/proto/ProtoUtil.java +++ b/common/src/main/java/bisq/common/proto/ProtoUtil.java @@ -18,12 +18,15 @@ package bisq.common.proto; import bisq.common.Proto; +import bisq.common.util.CollectionUtils; import com.google.protobuf.ByteString; import com.google.protobuf.Message; +import com.google.protobuf.ProtocolStringList; import com.google.common.base.Enums; +import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; @@ -95,4 +98,9 @@ public static Iterable collectionToProto(Collection Iterable collectionToProto(Collection collection, Function extra) { return collection.stream().map(o -> extra.apply(o.toProtoMessage())).collect(Collectors.toList()); } + + public static List protocolStringListToList(ProtocolStringList protocolStringList) { + return CollectionUtils.isEmpty(protocolStringList) ? new ArrayList<>() : new ArrayList<>(protocolStringList); + } + } diff --git a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java index a926045df4e..e27bf2493cd 100644 --- a/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java +++ b/core/src/main/java/bisq/core/account/sign/SignedWitnessService.java @@ -180,7 +180,7 @@ public boolean isSignedByArbitrator(AccountAgeWitness accountAgeWitness) { public boolean isFilteredWitness(AccountAgeWitness accountAgeWitness) { return getSignedWitnessSet(accountAgeWitness).stream() .map(SignedWitness::getWitnessOwnerPubKey) - .anyMatch(ownerPubKey -> filterManager.isSignerPubKeyBanned(Utils.HEX.encode(ownerPubKey))); + .anyMatch(ownerPubKey -> filterManager.isWitnessSignerPubKeyBanned(Utils.HEX.encode(ownerPubKey))); } private byte[] ownerPubKey(AccountAgeWitness accountAgeWitness) { @@ -442,7 +442,7 @@ private boolean isSignerAccountAgeWitness(AccountAgeWitness accountAgeWitness, l private boolean isValidSignerWitnessInternal(SignedWitness signedWitness, long childSignedWitnessDateMillis, Stack excludedPubKeys) { - if (filterManager.isSignerPubKeyBanned(Utils.HEX.encode(signedWitness.getWitnessOwnerPubKey()))) { + if (filterManager.isWitnessSignerPubKeyBanned(Utils.HEX.encode(signedWitness.getWitnessOwnerPubKey()))) { return false; } if (!verifySignature(signedWitness)) { diff --git a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java index f4620c7d0e2..f0256e45d5b 100644 --- a/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java +++ b/core/src/main/java/bisq/core/account/witness/AccountAgeWitnessService.java @@ -713,13 +713,13 @@ private boolean isNotFiltered(Dispute dispute) { filterManager.isCurrencyBanned(dispute.getContract().getOfferPayload().getCurrencyCode()) || filterManager.isPaymentMethodBanned( PaymentMethod.getPaymentMethodById(dispute.getContract().getPaymentMethodId())) || - filterManager.isPeersPaymentAccountDataAreBanned(dispute.getContract().getBuyerPaymentAccountPayload(), + filterManager.arePeersPaymentAccountDataBanned(dispute.getContract().getBuyerPaymentAccountPayload(), new PaymentAccountFilter[1]) || - filterManager.isPeersPaymentAccountDataAreBanned(dispute.getContract().getSellerPaymentAccountPayload(), + filterManager.arePeersPaymentAccountDataBanned(dispute.getContract().getSellerPaymentAccountPayload(), new PaymentAccountFilter[1]) || - filterManager.isSignerPubKeyBanned( + filterManager.isWitnessSignerPubKeyBanned( Utils.HEX.encode(dispute.getContract().getBuyerPubKeyRing().getSignaturePubKeyBytes())) || - filterManager.isSignerPubKeyBanned( + filterManager.isWitnessSignerPubKeyBanned( Utils.HEX.encode(dispute.getContract().getSellerPubKeyRing().getSignaturePubKeyBytes())); return !isFiltered; } diff --git a/core/src/main/java/bisq/core/filter/Filter.java b/core/src/main/java/bisq/core/filter/Filter.java index 0c2e39b0953..9698f8ae3ec 100644 --- a/core/src/main/java/bisq/core/filter/Filter.java +++ b/core/src/main/java/bisq/core/filter/Filter.java @@ -21,156 +21,133 @@ import bisq.network.p2p.storage.payload.ProtectedStoragePayload; import bisq.common.crypto.Sig; +import bisq.common.proto.ProtoUtil; import bisq.common.util.CollectionUtils; import bisq.common.util.ExtraDataMapValidator; +import bisq.common.util.Utilities; import com.google.protobuf.ByteString; -import com.google.common.annotations.VisibleForTesting; - import java.security.PublicKey; -import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.TimeUnit; import java.util.stream.Collectors; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.ToString; +import lombok.Value; import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; -import static com.google.common.base.Preconditions.checkNotNull; - @Slf4j -@Getter -@EqualsAndHashCode -@ToString +@Value public final class Filter implements ProtectedStoragePayload, ExpirablePayload { private final List bannedOfferIds; private final List bannedNodeAddress; private final List bannedPaymentAccounts; - - // Because we added those fields in v 0.5.4 and old versions do not have it we annotate it with @Nullable - @Nullable private final List bannedCurrencies; - @Nullable private final List bannedPaymentMethods; - - // added in v0.6.0 - @Nullable private final List arbitrators; - @Nullable private final List seedNodes; - @Nullable private final List priceRelayNodes; private final boolean preventPublicBtcNetwork; - - // added in v0.6.2 - @Nullable private final List btcNodes; - - - private String signatureAsBase64; - private byte[] ownerPubKeyBytes; - // Should be only used in emergency case if we need to add data but do not want to break backward compatibility - // at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new - // field in a class would break that hash and therefore break the storage mechanism. + // SignatureAsBase64 is not set initially as we use the serialized data for signing. We set it after signature is + // created by cloning the object with a non-null sig. @Nullable - private Map extraDataMap; - private PublicKey ownerPubKey; - - // added in v0.9.4 + private final String signatureAsBase64; + // The pub key used for the data protection in teh p2p storage + private final byte[] ownerPubKeyBytes; private final boolean disableDao; - - // added in v0.9.8 - @Nullable private final String disableDaoBelowVersion; - @Nullable private final String disableTradeBelowVersion; - - // added in v1.1.6 - @Nullable private final List mediators; - - // added in v1.2.0 - @Nullable private final List refundAgents; - // added in v1.2.x - @Nullable private final List bannedSignerPubKeys; - // added in v1.3.2 - @Nullable private final List btcFeeReceiverAddresses; - public Filter(List bannedOfferIds, - List bannedNodeAddress, - List bannedPaymentAccounts, - @Nullable List bannedCurrencies, - @Nullable List bannedPaymentMethods, - @Nullable List arbitrators, - @Nullable List seedNodes, - @Nullable List priceRelayNodes, - boolean preventPublicBtcNetwork, - @Nullable List btcNodes, - boolean disableDao, - @Nullable String disableDaoBelowVersion, - @Nullable String disableTradeBelowVersion, - @Nullable List mediators, - @Nullable List refundAgents, - @Nullable List bannedSignerPubKeys, - @Nullable List btcFeeReceiverAddresses) { - this.bannedOfferIds = bannedOfferIds; - this.bannedNodeAddress = bannedNodeAddress; - this.bannedPaymentAccounts = bannedPaymentAccounts; - this.bannedCurrencies = bannedCurrencies; - this.bannedPaymentMethods = bannedPaymentMethods; - this.arbitrators = arbitrators; - this.seedNodes = seedNodes; - this.priceRelayNodes = priceRelayNodes; - this.preventPublicBtcNetwork = preventPublicBtcNetwork; - this.btcNodes = btcNodes; - this.disableDao = disableDao; - this.disableDaoBelowVersion = disableDaoBelowVersion; - this.disableTradeBelowVersion = disableTradeBelowVersion; - this.mediators = mediators; - this.refundAgents = refundAgents; - this.bannedSignerPubKeys = bannedSignerPubKeys; - this.btcFeeReceiverAddresses = btcFeeReceiverAddresses; - } + private final long creationDate; + // Should be only used in emergency case if we need to add data but do not want to break backward compatibility + // at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new + // field in a class would break that hash and therefore break the storage mechanism. + @Nullable + private Map extraDataMap; - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// + private transient PublicKey ownerPubKey; + + // After we have created the signature from the filter data we clone it and apply the signature + static Filter cloneWithSig(Filter filter, String signatureAsBase64) { + return new Filter(filter.getBannedOfferIds(), + filter.getBannedNodeAddress(), + filter.getBannedPaymentAccounts(), + filter.getBannedCurrencies(), + filter.getBannedPaymentMethods(), + filter.getArbitrators(), + filter.getSeedNodes(), + filter.getPriceRelayNodes(), + filter.isPreventPublicBtcNetwork(), + filter.getBtcNodes(), + filter.isDisableDao(), + filter.getDisableDaoBelowVersion(), + filter.getDisableTradeBelowVersion(), + filter.getMediators(), + filter.getRefundAgents(), + filter.getBannedSignerPubKeys(), + filter.getBtcFeeReceiverAddresses(), + filter.getOwnerPubKeyBytes(), + filter.getCreationDate(), + filter.getExtraDataMap(), + signatureAsBase64); + } + + // Used for signature verification as we created the sig without the signatureAsBase64 field we set it to null again + static Filter cloneWithoutSig(Filter filter) { + return new Filter(filter.getBannedOfferIds(), + filter.getBannedNodeAddress(), + filter.getBannedPaymentAccounts(), + filter.getBannedCurrencies(), + filter.getBannedPaymentMethods(), + filter.getArbitrators(), + filter.getSeedNodes(), + filter.getPriceRelayNodes(), + filter.isPreventPublicBtcNetwork(), + filter.getBtcNodes(), + filter.isDisableDao(), + filter.getDisableDaoBelowVersion(), + filter.getDisableTradeBelowVersion(), + filter.getMediators(), + filter.getRefundAgents(), + filter.getBannedSignerPubKeys(), + filter.getBtcFeeReceiverAddresses(), + filter.getOwnerPubKeyBytes(), + filter.getCreationDate(), + filter.getExtraDataMap(), + null); + } - @VisibleForTesting public Filter(List bannedOfferIds, List bannedNodeAddress, List bannedPaymentAccounts, - @Nullable List bannedCurrencies, - @Nullable List bannedPaymentMethods, - @Nullable List arbitrators, - @Nullable List seedNodes, - @Nullable List priceRelayNodes, + List bannedCurrencies, + List bannedPaymentMethods, + List arbitrators, + List seedNodes, + List priceRelayNodes, boolean preventPublicBtcNetwork, - @Nullable List btcNodes, + List btcNodes, boolean disableDao, - @Nullable String disableDaoBelowVersion, - @Nullable String disableTradeBelowVersion, - String signatureAsBase64, - byte[] ownerPubKeyBytes, - @Nullable Map extraDataMap, - @Nullable List mediators, - @Nullable List refundAgents, - @Nullable List bannedSignerPubKeys, - @Nullable List btcFeeReceiverAddresses) { + String disableDaoBelowVersion, + String disableTradeBelowVersion, + List mediators, + List refundAgents, + List bannedSignerPubKeys, + List btcFeeReceiverAddresses, + PublicKey ownerPubKey) { this(bannedOfferIds, bannedNodeAddress, bannedPaymentAccounts, @@ -187,72 +164,124 @@ public Filter(List bannedOfferIds, mediators, refundAgents, bannedSignerPubKeys, - btcFeeReceiverAddresses); - this.signatureAsBase64 = signatureAsBase64; + btcFeeReceiverAddresses, + Sig.getPublicKeyBytes(ownerPubKey), + System.currentTimeMillis(), + null, + null); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + private Filter(List bannedOfferIds, + List bannedNodeAddress, + List bannedPaymentAccounts, + List bannedCurrencies, + List bannedPaymentMethods, + List arbitrators, + List seedNodes, + List priceRelayNodes, + boolean preventPublicBtcNetwork, + List btcNodes, + boolean disableDao, + String disableDaoBelowVersion, + String disableTradeBelowVersion, + List mediators, + List refundAgents, + List bannedSignerPubKeys, + List btcFeeReceiverAddresses, + byte[] ownerPubKeyBytes, + long creationDate, + @Nullable Map extraDataMap, + @Nullable String signatureAsBase64) { + this.bannedOfferIds = bannedOfferIds; + this.bannedNodeAddress = bannedNodeAddress; + this.bannedPaymentAccounts = bannedPaymentAccounts; + this.bannedCurrencies = bannedCurrencies; + this.bannedPaymentMethods = bannedPaymentMethods; + this.arbitrators = arbitrators; + this.seedNodes = seedNodes; + this.priceRelayNodes = priceRelayNodes; + this.preventPublicBtcNetwork = preventPublicBtcNetwork; + this.btcNodes = btcNodes; + this.disableDao = disableDao; + this.disableDaoBelowVersion = disableDaoBelowVersion; + this.disableTradeBelowVersion = disableTradeBelowVersion; + this.mediators = mediators; + this.refundAgents = refundAgents; + this.bannedSignerPubKeys = bannedSignerPubKeys; + this.btcFeeReceiverAddresses = btcFeeReceiverAddresses; this.ownerPubKeyBytes = ownerPubKeyBytes; + this.creationDate = creationDate; this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); + this.signatureAsBase64 = signatureAsBase64; ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyBytes); } @Override public protobuf.StoragePayload toProtoMessage() { - checkNotNull(signatureAsBase64, "signatureAsBase64 must not be null"); - checkNotNull(ownerPubKeyBytes, "ownerPubKeyBytes must not be null"); List paymentAccountFilterList = bannedPaymentAccounts.stream() .map(PaymentAccountFilter::toProtoMessage) .collect(Collectors.toList()); - final protobuf.Filter.Builder builder = protobuf.Filter.newBuilder() - .addAllBannedOfferIds(bannedOfferIds) + + protobuf.Filter.Builder builder = protobuf.Filter.newBuilder().addAllBannedOfferIds(bannedOfferIds) .addAllBannedNodeAddress(bannedNodeAddress) .addAllBannedPaymentAccounts(paymentAccountFilterList) - .setSignatureAsBase64(signatureAsBase64) - .setOwnerPubKeyBytes(ByteString.copyFrom(ownerPubKeyBytes)) + .addAllBannedCurrencies(bannedCurrencies) + .addAllBannedPaymentMethods(bannedPaymentMethods) + .addAllArbitrators(arbitrators) + .addAllSeedNodes(seedNodes) + .addAllPriceRelayNodes(priceRelayNodes) .setPreventPublicBtcNetwork(preventPublicBtcNetwork) - .setDisableDao(disableDao); - - Optional.ofNullable(bannedCurrencies).ifPresent(builder::addAllBannedCurrencies); - Optional.ofNullable(bannedPaymentMethods).ifPresent(builder::addAllBannedPaymentMethods); - Optional.ofNullable(arbitrators).ifPresent(builder::addAllArbitrators); - Optional.ofNullable(seedNodes).ifPresent(builder::addAllSeedNodes); - Optional.ofNullable(priceRelayNodes).ifPresent(builder::addAllPriceRelayNodes); - Optional.ofNullable(btcNodes).ifPresent(builder::addAllBtcNodes); - Optional.ofNullable(disableDaoBelowVersion).ifPresent(builder::setDisableDaoBelowVersion); - Optional.ofNullable(disableTradeBelowVersion).ifPresent(builder::setDisableTradeBelowVersion); + .addAllBtcNodes(btcNodes) + .setDisableDao(disableDao) + .setDisableDaoBelowVersion(disableDaoBelowVersion) + .setDisableTradeBelowVersion(disableTradeBelowVersion) + .addAllMediators(mediators) + .addAllRefundAgents(refundAgents) + .addAllBannedSignerPubKeys(bannedSignerPubKeys) + .addAllBtcFeeReceiverAddresses(btcFeeReceiverAddresses) + .setOwnerPubKeyBytes(ByteString.copyFrom(ownerPubKeyBytes)) + .setCreationDate(creationDate); + + Optional.ofNullable(signatureAsBase64).ifPresent(builder::setSignatureAsBase64); Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); - Optional.ofNullable(mediators).ifPresent(builder::addAllMediators); - Optional.ofNullable(refundAgents).ifPresent(builder::addAllRefundAgents); - Optional.ofNullable(bannedSignerPubKeys).ifPresent(builder::addAllBannedSignerPubKeys); - Optional.ofNullable(btcFeeReceiverAddresses).ifPresent(builder::addAllBtcFeeReceiverAddresses); return protobuf.StoragePayload.newBuilder().setFilter(builder).build(); } public static Filter fromProto(protobuf.Filter proto) { - return new Filter(new ArrayList<>(proto.getBannedOfferIdsList()), - new ArrayList<>(proto.getBannedNodeAddressList()), - proto.getBannedPaymentAccountsList().stream() - .map(PaymentAccountFilter::fromProto) - .collect(Collectors.toList()), - CollectionUtils.isEmpty(proto.getBannedCurrenciesList()) ? null : new ArrayList<>(proto.getBannedCurrenciesList()), - CollectionUtils.isEmpty(proto.getBannedPaymentMethodsList()) ? null : new ArrayList<>(proto.getBannedPaymentMethodsList()), - CollectionUtils.isEmpty(proto.getArbitratorsList()) ? null : new ArrayList<>(proto.getArbitratorsList()), - CollectionUtils.isEmpty(proto.getSeedNodesList()) ? null : new ArrayList<>(proto.getSeedNodesList()), - CollectionUtils.isEmpty(proto.getPriceRelayNodesList()) ? null : new ArrayList<>(proto.getPriceRelayNodesList()), + List bannedPaymentAccountsList = proto.getBannedPaymentAccountsList().stream() + .map(PaymentAccountFilter::fromProto) + .collect(Collectors.toList()); + + + return new Filter(ProtoUtil.protocolStringListToList(proto.getBannedOfferIdsList()), + ProtoUtil.protocolStringListToList(proto.getBannedNodeAddressList()), + bannedPaymentAccountsList, + ProtoUtil.protocolStringListToList(proto.getBannedCurrenciesList()), + ProtoUtil.protocolStringListToList(proto.getBannedPaymentMethodsList()), + ProtoUtil.protocolStringListToList(proto.getArbitratorsList()), + ProtoUtil.protocolStringListToList(proto.getSeedNodesList()), + ProtoUtil.protocolStringListToList(proto.getPriceRelayNodesList()), proto.getPreventPublicBtcNetwork(), - CollectionUtils.isEmpty(proto.getBtcNodesList()) ? null : new ArrayList<>(proto.getBtcNodesList()), + ProtoUtil.protocolStringListToList(proto.getBtcNodesList()), proto.getDisableDao(), - proto.getDisableDaoBelowVersion().isEmpty() ? null : proto.getDisableDaoBelowVersion(), - proto.getDisableTradeBelowVersion().isEmpty() ? null : proto.getDisableTradeBelowVersion(), - proto.getSignatureAsBase64(), + proto.getDisableDaoBelowVersion(), + proto.getDisableTradeBelowVersion(), + ProtoUtil.protocolStringListToList(proto.getMediatorsList()), + ProtoUtil.protocolStringListToList(proto.getRefundAgentsList()), + ProtoUtil.protocolStringListToList(proto.getBannedSignerPubKeysList()), + ProtoUtil.protocolStringListToList(proto.getBtcFeeReceiverAddressesList()), proto.getOwnerPubKeyBytes().toByteArray(), + proto.getCreationDate(), CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap(), - CollectionUtils.isEmpty(proto.getMediatorsList()) ? null : new ArrayList<>(proto.getMediatorsList()), - CollectionUtils.isEmpty(proto.getRefundAgentsList()) ? null : new ArrayList<>(proto.getRefundAgentsList()), - CollectionUtils.isEmpty(proto.getBannedSignerPubKeysList()) ? - null : new ArrayList<>(proto.getBannedSignerPubKeysList()), - CollectionUtils.isEmpty(proto.getBtcFeeReceiverAddressesList()) ? null : - new ArrayList<>(proto.getBtcFeeReceiverAddressesList())); + proto.getSignatureAsBase64() + ); } @@ -265,13 +294,6 @@ public long getTTL() { return TimeUnit.DAYS.toMillis(180); } - void setSigAndPubKey(String signatureAsBase64, PublicKey ownerPubKey) { - this.signatureAsBase64 = signatureAsBase64; - this.ownerPubKey = ownerPubKey; - - ownerPubKeyBytes = Sig.getPublicKeyBytes(this.ownerPubKey); - } - @Override public String toString() { return "Filter{" + @@ -285,7 +307,8 @@ public String toString() { ",\n priceRelayNodes=" + priceRelayNodes + ",\n preventPublicBtcNetwork=" + preventPublicBtcNetwork + ",\n btcNodes=" + btcNodes + - ",\n extraDataMap=" + extraDataMap + + ",\n signatureAsBase64='" + signatureAsBase64 + '\'' + + ",\n ownerPubKeyBytes=" + Utilities.bytesAsHexString(ownerPubKeyBytes) + ",\n disableDao=" + disableDao + ",\n disableDaoBelowVersion='" + disableDaoBelowVersion + '\'' + ",\n disableTradeBelowVersion='" + disableTradeBelowVersion + '\'' + @@ -293,6 +316,8 @@ public String toString() { ",\n refundAgents=" + refundAgents + ",\n bannedSignerPubKeys=" + bannedSignerPubKeys + ",\n btcFeeReceiverAddresses=" + btcFeeReceiverAddresses + + ",\n creationDate=" + creationDate + + ",\n extraDataMap=" + extraDataMap + "\n}"; } } diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index f903638532c..7b54dc0566d 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -29,9 +29,7 @@ import bisq.network.p2p.P2PServiceListener; import bisq.network.p2p.storage.HashMapChangedListener; import bisq.network.p2p.storage.payload.ProtectedStorageEntry; -import bisq.network.p2p.storage.payload.ProtectedStoragePayload; -import bisq.common.UserThread; import bisq.common.app.DevEnv; import bisq.common.app.Version; import bisq.common.config.Config; @@ -39,6 +37,7 @@ import bisq.common.crypto.KeyRing; import org.bitcoinj.core.ECKey; +import org.bitcoinj.core.Sha256Hash; import org.bitcoinj.core.Utils; import javax.inject.Inject; @@ -47,33 +46,35 @@ import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleObjectProperty; -import java.security.SignatureException; +import org.spongycastle.util.encoders.Base64; + +import java.security.PublicKey; + +import java.nio.charset.StandardCharsets; import java.math.BigInteger; -import java.util.ArrayList; import java.util.Collection; import java.util.List; -import java.util.Optional; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.stream.Collectors; import java.lang.reflect.Method; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; +import static com.google.common.base.Preconditions.checkNotNull; import static org.bitcoinj.core.Utils.HEX; +/** + * We only support one active filter, if we receive multiple we use the one with the more recent creationDate. + */ +@Slf4j public class FilterManager { - - private static final Logger log = LoggerFactory.getLogger(FilterManager.class); - - public static final String BANNED_PRICE_RELAY_NODES = "bannedPriceRelayNodes"; - public static final String BANNED_SEED_NODES = "bannedSeedNodes"; - public static final String BANNED_BTC_NODES = "bannedBtcNodes"; + private static final String BANNED_PRICE_RELAY_NODES = "bannedPriceRelayNodes"; + private static final String BANNED_SEED_NODES = "bannedSeedNodes"; + private static final String BANNED_BTC_NODES = "bannedBtcNodes"; /////////////////////////////////////////////////////////////////////////////////////////// @@ -90,16 +91,16 @@ public interface Listener { private final Preferences preferences; private final ConfigFileEditor configFileEditor; private final ProvidersRepository providersRepository; - private boolean ignoreDevMsg; + private final boolean ignoreDevMsg; private final ObjectProperty filterProperty = new SimpleObjectProperty<>(); private final List listeners = new CopyOnWriteArrayList<>(); - private final String pubKeyAsHex; + private ECKey filterSigningKey; /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, Initialization + // Constructor /////////////////////////////////////////////////////////////////////////////////////////// @Inject @@ -118,47 +119,54 @@ public FilterManager(P2PService p2PService, this.configFileEditor = new ConfigFileEditor(config.configFile); this.providersRepository = providersRepository; this.ignoreDevMsg = ignoreDevMsg; + pubKeyAsHex = useDevPrivilegeKeys ? DevEnv.DEV_PRIVILEGE_PUB_KEY : "022ac7b7766b0aedff82962522c2c14fb8d1961dabef6e5cfd10edc679456a32f1"; } + + /////////////////////////////////////////////////////////////////////////////////////////// + // API + /////////////////////////////////////////////////////////////////////////////////////////// + public void onAllServicesInitialized() { - if (!ignoreDevMsg) { - - final List list = new ArrayList<>(p2PService.getP2PDataStorage().getMap().values()); - list.forEach(e -> { - final ProtectedStoragePayload protectedStoragePayload = e.getProtectedStoragePayload(); - if (protectedStoragePayload instanceof Filter) - addFilter((Filter) protectedStoragePayload); - }); - - p2PService.addHashSetChangedListener(new HashMapChangedListener() { - @Override - public void onAdded(Collection protectedStorageEntries) { - protectedStorageEntries.forEach(protectedStorageEntry -> { - if (protectedStorageEntry.getProtectedStoragePayload() instanceof Filter) { + if (ignoreDevMsg) { + return; + } + + p2PService.getP2PDataStorage().getMap().values().stream() + .map(ProtectedStorageEntry::getProtectedStoragePayload) + .filter(protectedStoragePayload -> protectedStoragePayload instanceof Filter) + .map(protectedStoragePayload -> (Filter) protectedStoragePayload) + .filter(this::verifySignature) + .forEach(this::onFilterAddedFromNetwork); + + p2PService.addHashSetChangedListener(new HashMapChangedListener() { + @Override + public void onAdded(Collection protectedStorageEntries) { + protectedStorageEntries.stream() + .filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof Filter) + .forEach(protectedStorageEntry -> { Filter filter = (Filter) protectedStorageEntry.getProtectedStoragePayload(); - boolean wasValid = addFilter(filter); - if (!wasValid) { - UserThread.runAfter(() -> p2PService.getP2PDataStorage().removeInvalidProtectedStorageEntry(protectedStorageEntry), 1); + if (verifySignature(filter)) { + onFilterAddedFromNetwork(filter); } - } - }); - } + }); + } - @Override - public void onRemoved(Collection protectedStorageEntries) { - protectedStorageEntries.forEach(protectedStorageEntry -> { - if (protectedStorageEntry.getProtectedStoragePayload() instanceof Filter) { + @Override + public void onRemoved(Collection protectedStorageEntries) { + protectedStorageEntries.stream() + .filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof Filter) + .forEach(protectedStorageEntry -> { Filter filter = (Filter) protectedStorageEntry.getProtectedStoragePayload(); - if (verifySignature(filter) && getFilter().equals(filter)) - resetFilters(); - } - }); - } - }); - } + if (verifySignature(filter)) { + onFilterRemovedFromNetwork(filter); + } + }); + } + }); p2PService.addP2PServiceListener(new P2PServiceListener() { @Override @@ -175,12 +183,12 @@ public void onNoPeersAvailable() { @Override public void onUpdatedDataReceived() { - // We should have received all data at that point and if the filers were not set we - // clean up as it might be that we missed the filter remove message if we have not been online. - UserThread.runAfter(() -> { - if (filterProperty.get() == null) - resetFilters(); - }, 1); + // We should have received all data at that point and if the filters were not set we + // clean up the persisted banned nodes in the options file as it might be that we missed the filter + // remove message if we have not been online. + if (filterProperty.get() == null) { + clearBannedNodes(); + } } @Override @@ -201,55 +209,38 @@ public void onRequestCustomBridges() { }); } - private void resetFilters() { - saveBannedNodes(BANNED_BTC_NODES, null); - saveBannedNodes(BANNED_SEED_NODES, null); - saveBannedNodes(BANNED_PRICE_RELAY_NODES, null); - - if (providersRepository.getBannedNodes() != null) - providersRepository.applyBannedNodes(null); - - filterProperty.set(null); + public boolean isValidDevPrivilegeKey(String privKeyString) { + try { + filterSigningKey = ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyString))); + return pubKeyAsHex.equals(Utils.HEX.encode(filterSigningKey.getPubKey())); + } catch (Throwable t) { + return false; + } } - private boolean addFilter(Filter filter) { - if (verifySignature(filter)) { - // Seed nodes are requested at startup before we get the filter so we only apply the banned - // nodes at the next startup and don't update the list in the P2P network domain. - // We persist it to the property file which is read before any other initialisation. - saveBannedNodes(BANNED_SEED_NODES, filter.getSeedNodes()); - saveBannedNodes(BANNED_BTC_NODES, filter.getBtcNodes()); + public void publishFilter(Filter filterWithoutSig) { + String signatureAsBase64 = getSignature(filterWithoutSig); + Filter filterWithSig = Filter.cloneWithSig(filterWithoutSig, signatureAsBase64); + user.setDevelopersFilter(filterWithSig); - // Banned price relay nodes we can apply at runtime - final List priceRelayNodes = filter.getPriceRelayNodes(); - saveBannedNodes(BANNED_PRICE_RELAY_NODES, priceRelayNodes); + p2PService.addProtectedStorageEntry(filterWithSig); + } - providersRepository.applyBannedNodes(priceRelayNodes); - filterProperty.set(filter); - listeners.forEach(e -> e.onFilterAdded(filter)); + public void removeFilter() { + Filter filterWithSig = user.getDevelopersFilter(); + if (filterWithSig == null) { + // Should not happen as UI button is deactivated in that case + return; + } - if (filter.isPreventPublicBtcNetwork() && - preferences.getBitcoinNodesOptionOrdinal() == BtcNodes.BitcoinNodesOption.PUBLIC.ordinal()) - preferences.setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.PROVIDED.ordinal()); - return true; + if (p2PService.removeData(filterWithSig)) { + user.setDevelopersFilter(null); } else { - return false; + log.warn("Removing dev filter from network failed"); } } - private void saveBannedNodes(String optionName, List bannedNodes) { - if (bannedNodes != null) - configFileEditor.setOption(optionName, String.join(",", bannedNodes)); - else - configFileEditor.clearOption(optionName); - } - - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - public void addListener(Listener listener) { listeners.add(listener); } @@ -263,80 +254,6 @@ public Filter getFilter() { return filterProperty.get(); } - public boolean addFilterMessageIfKeyIsValid(Filter filter, String privKeyString) { - // if there is a previous message we remove that first - if (user.getDevelopersFilter() != null) - removeFilterMessageIfKeyIsValid(privKeyString); - - boolean isKeyValid = isKeyValid(privKeyString); - if (isKeyValid) { - signAndAddSignatureToFilter(filter); - user.setDevelopersFilter(filter); - - boolean result = p2PService.addProtectedStorageEntry(filter); - if (result) - log.trace("Add filter to network was successful. FilterMessage = {}", filter); - - } - return isKeyValid; - } - - public boolean removeFilterMessageIfKeyIsValid(String privKeyString) { - if (isKeyValid(privKeyString)) { - Filter filter = user.getDevelopersFilter(); - if (filter == null) { - log.warn("Developers filter is null"); - } else if (p2PService.removeData(filter)) { - log.trace("Remove filter from network was successful. FilterMessage = {}", filter); - user.setDevelopersFilter(null); - } else { - log.warn("Filter remove failed"); - } - return true; - } else { - return false; - } - } - - private boolean isKeyValid(String privKeyString) { - try { - filterSigningKey = ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyString))); - return pubKeyAsHex.equals(Utils.HEX.encode(filterSigningKey.getPubKey())); - } catch (Throwable t) { - return false; - } - } - - private void signAndAddSignatureToFilter(Filter filter) { - filter.setSigAndPubKey(filterSigningKey.signMessage(getHexFromData(filter)), keyRing.getSignatureKeyPair().getPublic()); - } - - private boolean verifySignature(Filter filter) { - try { - ECKey.fromPublicOnly(HEX.decode(pubKeyAsHex)).verifyMessage(getHexFromData(filter), filter.getSignatureAsBase64()); - return true; - } catch (SignatureException e) { - log.warn("verifySignature failed. filter={}", filter); - return false; - } - } - - // We don't use full data from Filter as we are only interested in the filter data not the sig and keys - private String getHexFromData(Filter filter) { - protobuf.Filter.Builder builder = protobuf.Filter.newBuilder() - .addAllBannedOfferIds(filter.getBannedOfferIds()) - .addAllBannedNodeAddress(filter.getBannedNodeAddress()) - .addAllBannedPaymentAccounts(filter.getBannedPaymentAccounts().stream() - .map(PaymentAccountFilter::toProtoMessage) - .collect(Collectors.toList())); - - Optional.ofNullable(filter.getBannedCurrencies()).ifPresent(builder::addAllBannedCurrencies); - Optional.ofNullable(filter.getBannedPaymentMethods()).ifPresent(builder::addAllBannedPaymentMethods); - Optional.ofNullable(filter.getBannedSignerPubKeys()).ifPresent(builder::addAllBannedSignerPubKeys); - - return Utils.HEX.encode(builder.build().toByteArray()); - } - @Nullable public Filter getDevelopersFilter() { return user.getDevelopersFilter(); @@ -396,8 +313,8 @@ public boolean requireUpdateToNewVersionForDAO() { return requireUpdateToNewVersion; } - public boolean isPeersPaymentAccountDataAreBanned(PaymentAccountPayload paymentAccountPayload, - PaymentAccountFilter[] appliedPaymentAccountFilter) { + public boolean arePeersPaymentAccountDataBanned(PaymentAccountPayload paymentAccountPayload, + PaymentAccountFilter[] appliedPaymentAccountFilter) { return getFilter() != null && getFilter().getBannedPaymentAccounts().stream() .anyMatch(paymentAccountFilter -> { @@ -419,11 +336,115 @@ public boolean isPeersPaymentAccountDataAreBanned(PaymentAccountPayload paymentA }); } - public boolean isSignerPubKeyBanned(String signerPubKeyAsHex) { + public boolean isWitnessSignerPubKeyBanned(String witnessSignerPubKeyAsHex) { return getFilter() != null && getFilter().getBannedSignerPubKeys() != null && getFilter().getBannedSignerPubKeys().stream() - .anyMatch(e -> e.equals(signerPubKeyAsHex)); + .anyMatch(e -> e.equals(witnessSignerPubKeyAsHex)); } + public PublicKey getOwnerPubKey() { + return keyRing.getSignatureKeyPair().getPublic(); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + + private void onFilterAddedFromNetwork(Filter filter) { + if (filterProperty.get() != null && filterProperty.get().getCreationDate() > filter.getCreationDate()) { + log.warn("We received a new filter from the network but the creation date is older than the " + + "filter we have already. We ignore the new filter.\n" + + "New filer={}\n" + + "Old filter={}", + filter, filterProperty.get()); + return; + } + + // Our new filter is newer so we apply it. + // We do not require strict guarantees here (e.g. clocks not synced) as only trusted developers have the key + // for deploying filters and this is only in place to avoid unintended situations of multiple filters + // from multiple devs or if same dev publishes new filter from different app without the persisted devFilter. + filterProperty.set(filter); + + // Seed nodes are requested at startup before we get the filter so we only apply the banned + // nodes at the next startup and don't update the list in the P2P network domain. + // We persist it to the property file which is read before any other initialisation. + saveBannedNodes(BANNED_SEED_NODES, filter.getSeedNodes()); + saveBannedNodes(BANNED_BTC_NODES, filter.getBtcNodes()); + + // Banned price relay nodes we can apply at runtime + List priceRelayNodes = filter.getPriceRelayNodes(); + saveBannedNodes(BANNED_PRICE_RELAY_NODES, priceRelayNodes); + providersRepository.applyBannedNodes(priceRelayNodes); + + if (filter.isPreventPublicBtcNetwork() && + preferences.getBitcoinNodesOptionOrdinal() == BtcNodes.BitcoinNodesOption.PUBLIC.ordinal()) { + preferences.setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.PROVIDED.ordinal()); + } + + listeners.forEach(e -> e.onFilterAdded(filter)); + } + + // We clean up potentially banned nodes and set value of filter property to null + private void onFilterRemovedFromNetwork(Filter filter) { + if (!filterProperty.get().equals(filter)) { + return; + } + + clearBannedNodes(); + + if (filter.equals(user.getDevelopersFilter())) { + user.setDevelopersFilter(null); + } + filterProperty.set(null); + } + + // Clears options files from banned nodes + private void clearBannedNodes() { + saveBannedNodes(BANNED_BTC_NODES, null); + saveBannedNodes(BANNED_SEED_NODES, null); + saveBannedNodes(BANNED_PRICE_RELAY_NODES, null); + + if (providersRepository.getBannedNodes() != null) { + providersRepository.applyBannedNodes(null); + } + } + + private void saveBannedNodes(String optionName, List bannedNodes) { + if (bannedNodes != null) + configFileEditor.setOption(optionName, String.join(",", bannedNodes)); + else + configFileEditor.clearOption(optionName); + } + + private String getSignature(Filter filterWithoutSig) { + Sha256Hash hash = getSha256Hash(filterWithoutSig); + ECKey.ECDSASignature ecdsaSignature = filterSigningKey.sign(hash); + byte[] encodeToDER = ecdsaSignature.encodeToDER(); + return new String(Base64.encode(encodeToDER), StandardCharsets.UTF_8); + } + + private boolean verifySignature(Filter filter) { + try { + Filter filterForSigVerification = Filter.cloneWithoutSig(filter); + Sha256Hash hash = getSha256Hash(filterForSigVerification); + + checkNotNull(filter.getSignatureAsBase64(), "filter.getSignatureAsBase64() must not be null"); + byte[] sigData = Base64.decode(filter.getSignatureAsBase64()); + ECKey.ECDSASignature ecdsaSignature = ECKey.ECDSASignature.decodeFromDER(sigData); + + ECKey ecPubKey = ECKey.fromPublicOnly(HEX.decode(pubKeyAsHex)); + return ecPubKey.verify(hash, ecdsaSignature); + } catch (Throwable e) { + log.warn("verifySignature failed. filter={}", filter); + return false; + } + } + + private Sha256Hash getSha256Hash(Filter filter) { + byte[] filterData = filter.toProtoMessage().toByteArray(); + return Sha256Hash.of(filterData); + } } diff --git a/core/src/main/java/bisq/core/trade/protocol/tasks/ApplyFilter.java b/core/src/main/java/bisq/core/trade/protocol/tasks/ApplyFilter.java index a892cb4c11f..55e40c120fc 100644 --- a/core/src/main/java/bisq/core/trade/protocol/tasks/ApplyFilter.java +++ b/core/src/main/java/bisq/core/trade/protocol/tasks/ApplyFilter.java @@ -59,7 +59,7 @@ protected void run() { } else if (filterManager.isPaymentMethodBanned(trade.getOffer().getPaymentMethod())) { failed("Payment method is banned.\n" + "Payment method=" + trade.getOffer().getPaymentMethod().getId()); - } else if (filterManager.isPeersPaymentAccountDataAreBanned(paymentAccountPayload, appliedPaymentAccountFilter)) { + } else if (filterManager.arePeersPaymentAccountDataBanned(paymentAccountPayload, appliedPaymentAccountFilter)) { failed("Other trader is banned by their trading account data.\n" + "paymentAccountPayload=" + paymentAccountPayload.getPaymentDetails() + "\n" + "banFilter=" + appliedPaymentAccountFilter[0].toString()); diff --git a/core/src/test/java/bisq/core/account/sign/SignedWitnessServiceTest.java b/core/src/test/java/bisq/core/account/sign/SignedWitnessServiceTest.java index 10915483157..338a9b3a705 100644 --- a/core/src/test/java/bisq/core/account/sign/SignedWitnessServiceTest.java +++ b/core/src/test/java/bisq/core/account/sign/SignedWitnessServiceTest.java @@ -397,14 +397,14 @@ public void testBanFilterSingleTree() { signedWitnessService.addToMap(sw3); // Second account is banned, first account is still a signer but the other two are no longer signers - when(filterManager.isSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true); + when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true); assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1)); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2)); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3)); // First account is banned, no accounts in the tree below it are signers - when(filterManager.isSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true); - when(filterManager.isSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(false); + when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true); + when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(false); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew1)); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2)); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3)); @@ -434,14 +434,14 @@ public void testBanFilterTwoTrees() { signedWitnessService.addToMap(sw3); // Only second account is banned, first account is still a signer but the other two are no longer signers - when(filterManager.isSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true); + when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true); assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1)); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2)); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3)); // Only first account is banned, account2 and account3 are still signers - when(filterManager.isSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true); - when(filterManager.isSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(false); + when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true); + when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(false); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew1)); assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew2)); assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew3)); @@ -484,21 +484,21 @@ public void testBanFilterJoinedTrees() throws Exception { signedWitnessService.addToMap(sw3p); // First account is banned, the other two are still signers - when(filterManager.isSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true); + when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew1)); assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew2)); assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew3)); // Second account is banned, the other two are still signers - when(filterManager.isSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(false); - when(filterManager.isSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true); + when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(false); + when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true); assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew1)); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2)); assertTrue(signedWitnessService.isSignerAccountAgeWitness(aew3)); // First and second account is banned, the third is no longer a signer - when(filterManager.isSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true); - when(filterManager.isSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true); + when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner1PubKey))).thenReturn(true); + when(filterManager.isWitnessSignerPubKeyBanned(Utilities.bytesAsHexString(witnessOwner2PubKey))).thenReturn(true); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew1)); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew2)); assertFalse(signedWitnessService.isSignerAccountAgeWitness(aew3)); diff --git a/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java b/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java index 36d52df74d3..b37a47d057e 100644 --- a/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java +++ b/core/src/test/java/bisq/core/account/witness/AccountAgeWitnessServiceTest.java @@ -218,8 +218,8 @@ public void testArbitratorSignWitness() { when(filterManager.isNodeAddressBanned(any())).thenReturn(false); when(filterManager.isCurrencyBanned(any())).thenReturn(false); when(filterManager.isPaymentMethodBanned(any())).thenReturn(false); - when(filterManager.isPeersPaymentAccountDataAreBanned(any(), any())).thenReturn(false); - when(filterManager.isSignerPubKeyBanned(any())).thenReturn(false); + when(filterManager.arePeersPaymentAccountDataBanned(any(), any())).thenReturn(false); + when(filterManager.isWitnessSignerPubKeyBanned(any())).thenReturn(false); when(chargeBackRisk.hasChargebackRisk(any(), any())).thenReturn(true); diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java index 50dbf04f182..8baea00fda0 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java @@ -160,42 +160,54 @@ private void addContent() { disableDaoBelowVersionInputTextField.setText(filter.getDisableDaoBelowVersion()); disableTradeBelowVersionInputTextField.setText(filter.getDisableTradeBelowVersion()); } + + Button removeFilterMessageButton = new AutoTooltipButton(Res.get("filterWindow.remove")); + removeFilterMessageButton.setDisable(filterManager.getDevelopersFilter() == null); + Button sendButton = new AutoTooltipButton(Res.get("filterWindow.add")); sendButton.setOnAction(e -> { - if (filterManager.addFilterMessageIfKeyIsValid( - new Filter( - readAsList(offerIdsInputTextField), - readAsList(nodesInputTextField), - readAsPaymentAccountFiltersList(paymentAccountFilterInputTextField), - readAsList(bannedCurrenciesInputTextField), - readAsList(bannedPaymentMethodsInputTextField), - readAsList(arbitratorsInputTextField), - readAsList(seedNodesInputTextField), - readAsList(priceRelayNodesInputTextField), - preventPublicBtcNetworkCheckBox.isSelected(), - readAsList(btcNodesInputTextField), - disableDaoCheckBox.isSelected(), - disableDaoBelowVersionInputTextField.getText(), - disableTradeBelowVersionInputTextField.getText(), - readAsList(mediatorsInputTextField), - readAsList(refundAgentsInputTextField), - readAsList(bannedSignerPubKeysInputTextField), - readAsList(btcFeeReceiverAddressesInputTextField) - ), - keyInputTextField.getText()) - ) + if (keyInputTextField.getText().isEmpty()) { + return; + } + Filter newFilter = new Filter( + readAsList(offerIdsInputTextField), + readAsList(nodesInputTextField), + readAsPaymentAccountFiltersList(paymentAccountFilterInputTextField), + readAsList(bannedCurrenciesInputTextField), + readAsList(bannedPaymentMethodsInputTextField), + readAsList(arbitratorsInputTextField), + readAsList(seedNodesInputTextField), + readAsList(priceRelayNodesInputTextField), + preventPublicBtcNetworkCheckBox.isSelected(), + readAsList(btcNodesInputTextField), + disableDaoCheckBox.isSelected(), + disableDaoBelowVersionInputTextField.getText(), + disableTradeBelowVersionInputTextField.getText(), + readAsList(mediatorsInputTextField), + readAsList(refundAgentsInputTextField), + readAsList(bannedSignerPubKeysInputTextField), + readAsList(btcFeeReceiverAddressesInputTextField), + filterManager.getOwnerPubKey() + ); + if (filterManager.isValidDevPrivilegeKey(keyInputTextField.getText())) { + filterManager.publishFilter(newFilter); + removeFilterMessageButton.setDisable(filterManager.getDevelopersFilter() == null); hide(); - else + } else { new Popup().warning(Res.get("shared.invalidKey")).width(300).onClose(this::blurAgain).show(); + } }); - Button removeFilterMessageButton = new AutoTooltipButton(Res.get("filterWindow.remove")); removeFilterMessageButton.setOnAction(e -> { - if (keyInputTextField.getText().length() > 0) { - if (filterManager.removeFilterMessageIfKeyIsValid(keyInputTextField.getText())) - hide(); - else - new Popup().warning(Res.get("shared.invalidKey")).width(300).onClose(this::blurAgain).show(); + if (keyInputTextField.getText().isEmpty()) { + return; + } + + if (filterManager.isValidDevPrivilegeKey(keyInputTextField.getText())) { + filterManager.removeFilter(); + hide(); + } else { + new Popup().warning(Res.get("shared.invalidKey")).width(300).onClose(this::blurAgain).show(); } }); @@ -215,13 +227,13 @@ private void addContent() { private void setupFieldFromList(InputTextField field, List values) { if (values != null) - field.setText(values.stream().collect(Collectors.joining(", "))); + field.setText(String.join(", ", values)); } private void setupFieldFromPaymentAccountFiltersList(InputTextField field, List values) { if (values != null) { StringBuilder sb = new StringBuilder(); - values.stream().forEach(e -> { + values.forEach(e -> { if (e != null && e.getPaymentMethodId() != null) { sb .append(e.getPaymentMethodId()) diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index 117dbb4cb53..75b73eb7367 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -638,6 +638,7 @@ message Filter { repeated string refundAgents = 18; repeated string bannedSignerPubKeys = 19; repeated string btc_fee_receiver_addresses = 20; + int64 creation_date = 21; } // not used anymore from v0.6 on. But leave it for receiving TradeStatistics objects from older From 9e4e800cb62e40cb53af57932ce767e775e4a7cc Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Sat, 29 Aug 2020 12:53:35 -0500 Subject: [PATCH 02/13] Fix tests --- .../main/java/bisq/core/filter/Filter.java | 52 +++++++++++-------- .../core/user/UserPayloadModelVOTest.java | 10 ++-- .../core/util/FeeReceiverSelectorTest.java | 27 ++++++++-- 3 files changed, 58 insertions(+), 31 deletions(-) diff --git a/core/src/main/java/bisq/core/filter/Filter.java b/core/src/main/java/bisq/core/filter/Filter.java index 9698f8ae3ec..93744ef9e97 100644 --- a/core/src/main/java/bisq/core/filter/Filter.java +++ b/core/src/main/java/bisq/core/filter/Filter.java @@ -28,6 +28,8 @@ import com.google.protobuf.ByteString; +import com.google.common.annotations.VisibleForTesting; + import java.security.PublicKey; import java.util.List; @@ -176,27 +178,28 @@ public Filter(List bannedOfferIds, // PROTO BUFFER /////////////////////////////////////////////////////////////////////////////////////////// - private Filter(List bannedOfferIds, - List bannedNodeAddress, - List bannedPaymentAccounts, - List bannedCurrencies, - List bannedPaymentMethods, - List arbitrators, - List seedNodes, - List priceRelayNodes, - boolean preventPublicBtcNetwork, - List btcNodes, - boolean disableDao, - String disableDaoBelowVersion, - String disableTradeBelowVersion, - List mediators, - List refundAgents, - List bannedSignerPubKeys, - List btcFeeReceiverAddresses, - byte[] ownerPubKeyBytes, - long creationDate, - @Nullable Map extraDataMap, - @Nullable String signatureAsBase64) { + @VisibleForTesting + public Filter(List bannedOfferIds, + List bannedNodeAddress, + List bannedPaymentAccounts, + List bannedCurrencies, + List bannedPaymentMethods, + List arbitrators, + List seedNodes, + List priceRelayNodes, + boolean preventPublicBtcNetwork, + List btcNodes, + boolean disableDao, + String disableDaoBelowVersion, + String disableTradeBelowVersion, + List mediators, + List refundAgents, + List bannedSignerPubKeys, + List btcFeeReceiverAddresses, + byte[] ownerPubKeyBytes, + long creationDate, + @Nullable Map extraDataMap, + @Nullable String signatureAsBase64) { this.bannedOfferIds = bannedOfferIds; this.bannedNodeAddress = bannedNodeAddress; this.bannedPaymentAccounts = bannedPaymentAccounts; @@ -219,7 +222,12 @@ private Filter(List bannedOfferIds, this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); this.signatureAsBase64 = signatureAsBase64; - ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyBytes); + // ownerPubKeyBytes can be null when called from tests + if (ownerPubKeyBytes != null) { + ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyBytes); + } else { + ownerPubKey = null; + } } @Override diff --git a/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java b/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java index 67d09bb6586..de685c9ba4d 100644 --- a/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java +++ b/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java @@ -53,13 +53,15 @@ public void testRoundtripFull() { false, null, null, - "string", - new byte[]{10, 0, 0}, - null, Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), - Lists.newArrayList())); + Lists.newArrayList(), + null, + 0, + null, + null)); + vo.setRegisteredArbitrator(ArbitratorTest.getArbitratorMock()); vo.setRegisteredMediator(MediatorTest.getMediatorMock()); vo.setAcceptedArbitrators(Lists.newArrayList(ArbitratorTest.getArbitratorMock())); diff --git a/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java b/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java index c340c36ecdd..2f059edd849 100644 --- a/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java +++ b/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java @@ -22,6 +22,7 @@ import bisq.core.filter.Filter; import bisq.core.filter.FilterManager; +import com.google.common.collect.Lists; import com.google.common.primitives.Longs; import java.util.HashMap; @@ -98,10 +99,26 @@ public void testWeightedSelection() { } private static Filter filterWithReceivers(List btcFeeReceiverAddresses) { - return new Filter(null, null, null, null, - null, null, null, null, - false, null, false, null, - null, null, null, null, - btcFeeReceiverAddresses); + return new Filter(Lists.newArrayList(), + Lists.newArrayList(), + Lists.newArrayList(), + Lists.newArrayList(), + Lists.newArrayList(), + Lists.newArrayList(), + Lists.newArrayList(), + Lists.newArrayList(), + false, + Lists.newArrayList(), + false, + null, + null, + Lists.newArrayList(), + Lists.newArrayList(), + Lists.newArrayList(), + btcFeeReceiverAddresses, + null, + 0, + null, + null); } } From 01482fb8e623dbf92e54cc878a5e275b23a70914 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Sat, 29 Aug 2020 13:21:19 -0500 Subject: [PATCH 03/13] Refactoring Separate validation from setting key --- .../src/main/java/bisq/core/filter/FilterManager.java | 10 +++++++++- .../desktop/main/overlays/windows/FilterWindow.java | 11 +++++++---- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 7b54dc0566d..8ddaf91d798 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -211,13 +211,17 @@ public void onRequestCustomBridges() { public boolean isValidDevPrivilegeKey(String privKeyString) { try { - filterSigningKey = ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyString))); + ECKey filterSigningKey = toECKey(privKeyString); return pubKeyAsHex.equals(Utils.HEX.encode(filterSigningKey.getPubKey())); } catch (Throwable t) { return false; } } + public void setFilterSigningKey(String privKeyString) { + this.filterSigningKey = toECKey(privKeyString); + } + public void publishFilter(Filter filterWithoutSig) { String signatureAsBase64 = getSignature(filterWithoutSig); Filter filterWithSig = Filter.cloneWithSig(filterWithoutSig, signatureAsBase64); @@ -443,6 +447,10 @@ private boolean verifySignature(Filter filter) { } } + private ECKey toECKey(String privKeyString) { + return ECKey.fromPrivate(new BigInteger(1, HEX.decode(privKeyString))); + } + private Sha256Hash getSha256Hash(Filter filter) { byte[] filterData = filter.toProtoMessage().toByteArray(); return Sha256Hash.of(filterData); diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java index 8baea00fda0..0839cbfb317 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java @@ -165,8 +165,9 @@ private void addContent() { removeFilterMessageButton.setDisable(filterManager.getDevelopersFilter() == null); Button sendButton = new AutoTooltipButton(Res.get("filterWindow.add")); + String privKeyString = keyInputTextField.getText(); sendButton.setOnAction(e -> { - if (keyInputTextField.getText().isEmpty()) { + if (privKeyString.isEmpty()) { return; } Filter newFilter = new Filter( @@ -189,7 +190,8 @@ private void addContent() { readAsList(btcFeeReceiverAddressesInputTextField), filterManager.getOwnerPubKey() ); - if (filterManager.isValidDevPrivilegeKey(keyInputTextField.getText())) { + if (filterManager.isValidDevPrivilegeKey(privKeyString)) { + filterManager.setFilterSigningKey(privKeyString); filterManager.publishFilter(newFilter); removeFilterMessageButton.setDisable(filterManager.getDevelopersFilter() == null); hide(); @@ -199,11 +201,12 @@ private void addContent() { }); removeFilterMessageButton.setOnAction(e -> { - if (keyInputTextField.getText().isEmpty()) { + if (privKeyString.isEmpty()) { return; } - if (filterManager.isValidDevPrivilegeKey(keyInputTextField.getText())) { + if (filterManager.isValidDevPrivilegeKey(privKeyString)) { + filterManager.setFilterSigningKey(privKeyString); filterManager.removeFilter(); hide(); } else { From e2de5221bff01c8c269259b8a68c6c34eb84cdff Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 31 Aug 2020 10:51:21 -0500 Subject: [PATCH 04/13] Fix + prefix in string keys --- core/src/main/resources/i18n/displayStrings.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 276eecf83b1..3702495490a 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -1169,8 +1169,8 @@ setting.about.support=Support Bisq setting.about.def=Bisq is not a company—it is a project open to the community. If you want to participate or support Bisq please follow the links below. setting.about.contribute=Contribute setting.about.providers=Data providers -+setting.about.apisWithFee=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices, and Bisq Mempool Nodes for mining fee estimation. -+setting.about.apis=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices. +setting.about.apisWithFee=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices, and Bisq Mempool Nodes for mining fee estimation. +setting.about.apis=Bisq uses Bisq Price Indices for Fiat and Altcoin market prices. setting.about.pricesProvided=Market prices provided by setting.about.feeEstimation.label=Mining fee estimation provided by setting.about.versionDetails=Version details From cc20a3b6c7c325c7670fd9bb4582b5e887a583cc Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 31 Aug 2020 12:33:45 -0500 Subject: [PATCH 05/13] - Add support for multiple pubKeys - Add more validation - Add SignerPubKeyAsHex to filter - Refactor add/remove filter code --- .../main/java/bisq/core/filter/Filter.java | 26 +++- .../java/bisq/core/filter/FilterManager.java | 124 ++++++++++++++---- .../core/user/UserPayloadModelVOTest.java | 1 + .../core/util/FeeReceiverSelectorTest.java | 1 + .../main/overlays/windows/FilterWindow.java | 77 +++++------ proto/src/main/proto/pb.proto | 1 + 6 files changed, 159 insertions(+), 71 deletions(-) diff --git a/core/src/main/java/bisq/core/filter/Filter.java b/core/src/main/java/bisq/core/filter/Filter.java index 93744ef9e97..f6c28bc5757 100644 --- a/core/src/main/java/bisq/core/filter/Filter.java +++ b/core/src/main/java/bisq/core/filter/Filter.java @@ -60,7 +60,10 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { // created by cloning the object with a non-null sig. @Nullable private final String signatureAsBase64; - // The pub key used for the data protection in teh p2p storage + // The pub EC key from the dev who has signed and published the filter (different to ownerPubKeyBytes) + private final String signerPubKeyAsHex; + + // The pub key used for the data protection in the p2p storage private final byte[] ownerPubKeyBytes; private final boolean disableDao; private final String disableDaoBelowVersion; @@ -104,7 +107,8 @@ static Filter cloneWithSig(Filter filter, String signatureAsBase64) { filter.getOwnerPubKeyBytes(), filter.getCreationDate(), filter.getExtraDataMap(), - signatureAsBase64); + signatureAsBase64, + filter.getSignerPubKeyAsHex()); } // Used for signature verification as we created the sig without the signatureAsBase64 field we set it to null again @@ -129,7 +133,8 @@ static Filter cloneWithoutSig(Filter filter) { filter.getOwnerPubKeyBytes(), filter.getCreationDate(), filter.getExtraDataMap(), - null); + null, + filter.getSignerPubKeyAsHex()); } public Filter(List bannedOfferIds, @@ -149,7 +154,8 @@ public Filter(List bannedOfferIds, List refundAgents, List bannedSignerPubKeys, List btcFeeReceiverAddresses, - PublicKey ownerPubKey) { + PublicKey ownerPubKey, + String signerPubKeyAsHex) { this(bannedOfferIds, bannedNodeAddress, bannedPaymentAccounts, @@ -170,7 +176,8 @@ public Filter(List bannedOfferIds, Sig.getPublicKeyBytes(ownerPubKey), System.currentTimeMillis(), null, - null); + null, + signerPubKeyAsHex); } @@ -199,7 +206,8 @@ public Filter(List bannedOfferIds, byte[] ownerPubKeyBytes, long creationDate, @Nullable Map extraDataMap, - @Nullable String signatureAsBase64) { + @Nullable String signatureAsBase64, + String signerPubKeyAsHex) { this.bannedOfferIds = bannedOfferIds; this.bannedNodeAddress = bannedNodeAddress; this.bannedPaymentAccounts = bannedPaymentAccounts; @@ -221,6 +229,7 @@ public Filter(List bannedOfferIds, this.creationDate = creationDate; this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); this.signatureAsBase64 = signatureAsBase64; + this.signerPubKeyAsHex = signerPubKeyAsHex; // ownerPubKeyBytes can be null when called from tests if (ownerPubKeyBytes != null) { @@ -254,6 +263,7 @@ public protobuf.StoragePayload toProtoMessage() { .addAllBannedSignerPubKeys(bannedSignerPubKeys) .addAllBtcFeeReceiverAddresses(btcFeeReceiverAddresses) .setOwnerPubKeyBytes(ByteString.copyFrom(ownerPubKeyBytes)) + .setSignerPubKeyAsHex(signerPubKeyAsHex) .setCreationDate(creationDate); Optional.ofNullable(signatureAsBase64).ifPresent(builder::setSignatureAsBase64); @@ -288,7 +298,8 @@ public static Filter fromProto(protobuf.Filter proto) { proto.getOwnerPubKeyBytes().toByteArray(), proto.getCreationDate(), CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap(), - proto.getSignatureAsBase64() + proto.getSignatureAsBase64(), + proto.getSignerPubKeyAsHex() ); } @@ -316,6 +327,7 @@ public String toString() { ",\n preventPublicBtcNetwork=" + preventPublicBtcNetwork + ",\n btcNodes=" + btcNodes + ",\n signatureAsBase64='" + signatureAsBase64 + '\'' + + ",\n signerPubKeyAsHex='" + signerPubKeyAsHex + '\'' + ",\n ownerPubKeyBytes=" + Utilities.bytesAsHexString(ownerPubKeyBytes) + ",\n disableDao=" + disableDao + ",\n disableDaoBelowVersion='" + disableDaoBelowVersion + '\'' + diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 8ddaf91d798..816c7796059 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -38,7 +38,6 @@ import org.bitcoinj.core.ECKey; import org.bitcoinj.core.Sha256Hash; -import org.bitcoinj.core.Utils; import javax.inject.Inject; import javax.inject.Named; @@ -55,6 +54,7 @@ import java.math.BigInteger; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -94,8 +94,7 @@ public interface Listener { private final boolean ignoreDevMsg; private final ObjectProperty filterProperty = new SimpleObjectProperty<>(); private final List listeners = new CopyOnWriteArrayList<>(); - private final String pubKeyAsHex; - + private final List publicKeys; private ECKey filterSigningKey; @@ -120,9 +119,12 @@ public FilterManager(P2PService p2PService, this.providersRepository = providersRepository; this.ignoreDevMsg = ignoreDevMsg; - pubKeyAsHex = useDevPrivilegeKeys ? - DevEnv.DEV_PRIVILEGE_PUB_KEY : - "022ac7b7766b0aedff82962522c2c14fb8d1961dabef6e5cfd10edc679456a32f1"; + //todo test + publicKeys = !useDevPrivilegeKeys ? + Collections.singletonList(DevEnv.DEV_PRIVILEGE_PUB_KEY) : + List.of("0358d47858acdc41910325fce266571540681ef83a0d6fedce312bef9810793a27", + "029340c3e7d4bb0f9e651b5f590b434fecb6175aeaa57145c7804ff05d210e534f"); + } @@ -139,6 +141,7 @@ public void onAllServicesInitialized() { .map(ProtectedStorageEntry::getProtectedStoragePayload) .filter(protectedStoragePayload -> protectedStoragePayload instanceof Filter) .map(protectedStoragePayload -> (Filter) protectedStoragePayload) + .filter(this::isFilterPublicKeyInList) .filter(this::verifySignature) .forEach(this::onFilterAddedFromNetwork); @@ -149,7 +152,7 @@ public void onAdded(Collection protectedStorageEntries) { .filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof Filter) .forEach(protectedStorageEntry -> { Filter filter = (Filter) protectedStorageEntry.getProtectedStoragePayload(); - if (verifySignature(filter)) { + if (isFilterPublicKeyInList(filter) && verifySignature(filter)) { onFilterAddedFromNetwork(filter); } }); @@ -161,7 +164,7 @@ public void onRemoved(Collection protectedStorageEntries) .filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof Filter) .forEach(protectedStorageEntry -> { Filter filter = (Filter) protectedStorageEntry.getProtectedStoragePayload(); - if (verifySignature(filter)) { + if (isFilterPublicKeyInList(filter) && verifySignature(filter)) { onFilterRemovedFromNetwork(filter); } }); @@ -209,20 +212,24 @@ public void onRequestCustomBridges() { }); } - public boolean isValidDevPrivilegeKey(String privKeyString) { - try { - ECKey filterSigningKey = toECKey(privKeyString); - return pubKeyAsHex.equals(Utils.HEX.encode(filterSigningKey.getPubKey())); - } catch (Throwable t) { + public boolean canAddDevFilter(String privKeyString) { + if (privKeyString == null || privKeyString.isEmpty()) { + return false; + } + if (!isValidDevPrivilegeKey(privKeyString)) { + log.warn("There is no persisted dev filter to be removed."); return false; } + return true; } - public void setFilterSigningKey(String privKeyString) { - this.filterSigningKey = toECKey(privKeyString); + public String getSignerPubKeyAsHex(String privKeyString) { + ECKey ecKey = toECKey(privKeyString); + return getPubKeyAsHex(ecKey); } - public void publishFilter(Filter filterWithoutSig) { + public void addDevFilter(Filter filterWithoutSig, String privKeyString) { + setFilterSigningKey(privKeyString); String signatureAsBase64 = getSignature(filterWithoutSig); Filter filterWithSig = Filter.cloneWithSig(filterWithoutSig, signatureAsBase64); user.setDevelopersFilter(filterWithSig); @@ -230,8 +237,42 @@ public void publishFilter(Filter filterWithoutSig) { p2PService.addProtectedStorageEntry(filterWithSig); } + public boolean canRemoveDevFilter(String privKeyString) { + if (privKeyString == null || privKeyString.isEmpty()) { + return false; + } + + Filter developersFilter = getDevFilter(); + if (developersFilter == null) { + log.warn("There is no persisted dev filter to be removed."); + return false; + } + + if (!isPublicKeyInList(developersFilter.getSignerPubKeyAsHex())) { + log.warn("The SignerPubKey from the filter is not in our pubKey list. " + + "filterSignerPubKey={}, publicKeys={}", developersFilter.getSignerPubKeyAsHex(), publicKeys); + return false; + } - public void removeFilter() { + if (!isValidDevPrivilegeKey(privKeyString)) { + log.warn("There is no persisted dev filter to be removed."); + return false; + } + + ECKey ecKeyFromPrivate = toECKey(privKeyString); + String pubKeyAsHex = getPubKeyAsHex(ecKeyFromPrivate); + if (!developersFilter.getSignerPubKeyAsHex().equals(pubKeyAsHex)) { + log.warn("pubKeyAsHex derived from private key does not match filterSignerPubKey. " + + "filterSignerPubKey={}, pubKeyAsHex derived from private key={}", + developersFilter.getSignerPubKeyAsHex(), pubKeyAsHex); + return false; + } + + return true; + } + + public void removeDevFilter(String privKeyString) { + setFilterSigningKey(privKeyString); Filter filterWithSig = user.getDevelopersFilter(); if (filterWithSig == null) { // Should not happen as UI button is deactivated in that case @@ -259,10 +300,14 @@ public Filter getFilter() { } @Nullable - public Filter getDevelopersFilter() { + public Filter getDevFilter() { return user.getDevelopersFilter(); } + public PublicKey getOwnerPubKey() { + return keyRing.getSignatureKeyPair().getPublic(); + } + public boolean isCurrencyBanned(String currencyCode) { return getFilter() != null && getFilter().getBannedCurrencies() != null && @@ -347,10 +392,6 @@ public boolean isWitnessSignerPubKeyBanned(String witnessSignerPubKeyAsHex) { .anyMatch(e -> e.equals(witnessSignerPubKeyAsHex)); } - public PublicKey getOwnerPubKey() { - return keyRing.getSignatureKeyPair().getPublic(); - } - /////////////////////////////////////////////////////////////////////////////////////////// // Private @@ -423,6 +464,20 @@ private void saveBannedNodes(String optionName, List bannedNodes) { configFileEditor.clearOption(optionName); } + private boolean isValidDevPrivilegeKey(String privKeyString) { + try { + ECKey filterSigningKey = toECKey(privKeyString); + String pubKeyAsHex = getPubKeyAsHex(filterSigningKey); + return isPublicKeyInList(pubKeyAsHex); + } catch (Throwable t) { + return false; + } + } + + private void setFilterSigningKey(String privKeyString) { + this.filterSigningKey = toECKey(privKeyString); + } + private String getSignature(Filter filterWithoutSig) { Sha256Hash hash = getSha256Hash(filterWithoutSig); ECKey.ECDSASignature ecdsaSignature = filterSigningKey.sign(hash); @@ -430,6 +485,15 @@ private String getSignature(Filter filterWithoutSig) { return new String(Base64.encode(encodeToDER), StandardCharsets.UTF_8); } + private boolean isFilterPublicKeyInList(Filter filter) { + String signerPubKeyAsHex = filter.getSignerPubKeyAsHex(); + if (!isPublicKeyInList(signerPubKeyAsHex)) { + log.warn("signerPubKeyAsHex from filter is not part of our pub key list. filter={}, publicKeys={}", filter, publicKeys); + return false; + } + return true; + } + private boolean verifySignature(Filter filter) { try { Filter filterForSigVerification = Filter.cloneWithoutSig(filter); @@ -439,7 +503,9 @@ private boolean verifySignature(Filter filter) { byte[] sigData = Base64.decode(filter.getSignatureAsBase64()); ECKey.ECDSASignature ecdsaSignature = ECKey.ECDSASignature.decodeFromDER(sigData); - ECKey ecPubKey = ECKey.fromPublicOnly(HEX.decode(pubKeyAsHex)); + String signerPubKeyAsHex = filter.getSignerPubKeyAsHex(); + byte[] decode = HEX.decode(signerPubKeyAsHex); + ECKey ecPubKey = ECKey.fromPublicOnly(decode); return ecPubKey.verify(hash, ecdsaSignature); } catch (Throwable e) { log.warn("verifySignature failed. filter={}", filter); @@ -455,4 +521,16 @@ private Sha256Hash getSha256Hash(Filter filter) { byte[] filterData = filter.toProtoMessage().toByteArray(); return Sha256Hash.of(filterData); } + + private String getPubKeyAsHex(ECKey ecKey) { + return HEX.encode(ecKey.getPubKey()); + } + + private boolean isPublicKeyInList(String pubKeyAsHex) { + boolean isPublicKeyInList = publicKeys.contains(pubKeyAsHex); + if (!isPublicKeyInList) { + log.warn("pubKeyAsHex is not part of our pub key list. pubKeyAsHex={}, publicKeys={}", pubKeyAsHex, publicKeys); + } + return isPublicKeyInList; + } } diff --git a/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java b/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java index de685c9ba4d..050fce18ec0 100644 --- a/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java +++ b/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java @@ -60,6 +60,7 @@ public void testRoundtripFull() { null, 0, null, + null, null)); vo.setRegisteredArbitrator(ArbitratorTest.getArbitratorMock()); diff --git a/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java b/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java index 2f059edd849..e5319f8ecd0 100644 --- a/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java +++ b/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java @@ -119,6 +119,7 @@ private static Filter filterWithReceivers(List btcFeeReceiverAddresses) null, 0, null, + null, null); } } diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java index 0839cbfb317..8ca9aa44847 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java @@ -79,7 +79,7 @@ public void show() { if (headLine == null) headLine = Res.get("filterWindow.headline"); - width = 968; + width = 1000; createGridPane(); @@ -87,7 +87,7 @@ public void show() { scrollPane.setContent(gridPane); scrollPane.setFitToWidth(true); scrollPane.setFitToHeight(true); - scrollPane.setMaxHeight(1000); + scrollPane.setMaxHeight(700); scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); addHeadLine(); @@ -139,7 +139,7 @@ private void addContent() { InputTextField disableDaoBelowVersionInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.disableDaoBelowVersion")); InputTextField disableTradeBelowVersionInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.disableTradeBelowVersion")); - final Filter filter = filterManager.getDevelopersFilter(); + Filter filter = filterManager.getDevFilter(); if (filter != null) { setupFieldFromList(offerIdsInputTextField, filter.getBannedOfferIds()); setupFieldFromList(nodesInputTextField, filter.getBannedNodeAddress()); @@ -162,55 +162,50 @@ private void addContent() { } Button removeFilterMessageButton = new AutoTooltipButton(Res.get("filterWindow.remove")); - removeFilterMessageButton.setDisable(filterManager.getDevelopersFilter() == null); + removeFilterMessageButton.setDisable(filterManager.getDevFilter() == null); Button sendButton = new AutoTooltipButton(Res.get("filterWindow.add")); - String privKeyString = keyInputTextField.getText(); sendButton.setOnAction(e -> { - if (privKeyString.isEmpty()) { - return; - } - Filter newFilter = new Filter( - readAsList(offerIdsInputTextField), - readAsList(nodesInputTextField), - readAsPaymentAccountFiltersList(paymentAccountFilterInputTextField), - readAsList(bannedCurrenciesInputTextField), - readAsList(bannedPaymentMethodsInputTextField), - readAsList(arbitratorsInputTextField), - readAsList(seedNodesInputTextField), - readAsList(priceRelayNodesInputTextField), - preventPublicBtcNetworkCheckBox.isSelected(), - readAsList(btcNodesInputTextField), - disableDaoCheckBox.isSelected(), - disableDaoBelowVersionInputTextField.getText(), - disableTradeBelowVersionInputTextField.getText(), - readAsList(mediatorsInputTextField), - readAsList(refundAgentsInputTextField), - readAsList(bannedSignerPubKeysInputTextField), - readAsList(btcFeeReceiverAddressesInputTextField), - filterManager.getOwnerPubKey() - ); - if (filterManager.isValidDevPrivilegeKey(privKeyString)) { - filterManager.setFilterSigningKey(privKeyString); - filterManager.publishFilter(newFilter); - removeFilterMessageButton.setDisable(filterManager.getDevelopersFilter() == null); + String privKeyString = keyInputTextField.getText(); + if (filterManager.canAddDevFilter(privKeyString)) { + String signerPubKeyAsHex = filterManager.getSignerPubKeyAsHex(privKeyString); + Filter newFilter = new Filter( + readAsList(offerIdsInputTextField), + readAsList(nodesInputTextField), + readAsPaymentAccountFiltersList(paymentAccountFilterInputTextField), + readAsList(bannedCurrenciesInputTextField), + readAsList(bannedPaymentMethodsInputTextField), + readAsList(arbitratorsInputTextField), + readAsList(seedNodesInputTextField), + readAsList(priceRelayNodesInputTextField), + preventPublicBtcNetworkCheckBox.isSelected(), + readAsList(btcNodesInputTextField), + disableDaoCheckBox.isSelected(), + disableDaoBelowVersionInputTextField.getText(), + disableTradeBelowVersionInputTextField.getText(), + readAsList(mediatorsInputTextField), + readAsList(refundAgentsInputTextField), + readAsList(bannedSignerPubKeysInputTextField), + readAsList(btcFeeReceiverAddressesInputTextField), + filterManager.getOwnerPubKey(), + signerPubKeyAsHex + ); + + filterManager.addDevFilter(newFilter, privKeyString); + removeFilterMessageButton.setDisable(filterManager.getDevFilter() == null); hide(); } else { - new Popup().warning(Res.get("shared.invalidKey")).width(300).onClose(this::blurAgain).show(); + new Popup().warning(Res.get("shared.invalidKey")).onClose(this::blurAgain).show(); } }); removeFilterMessageButton.setOnAction(e -> { - if (privKeyString.isEmpty()) { - return; - } - - if (filterManager.isValidDevPrivilegeKey(privKeyString)) { - filterManager.setFilterSigningKey(privKeyString); - filterManager.removeFilter(); + String privKeyString = keyInputTextField.getText(); + if (filterManager.canRemoveDevFilter(privKeyString)) { + filterManager.removeDevFilter(privKeyString); hide(); } else { - new Popup().warning(Res.get("shared.invalidKey")).width(300).onClose(this::blurAgain).show(); + new Popup().warning(Res.get("shared.invalidKey")).onClose(this::blurAgain).show(); } }); diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index cf448e81a5c..c1e8de2f5a4 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -639,6 +639,7 @@ message Filter { repeated string bannedSignerPubKeys = 19; repeated string btc_fee_receiver_addresses = 20; int64 creation_date = 21; + string signer_pub_key_as_hex = 22; } // not used anymore from v0.6 on. But leave it for receiving TradeStatistics objects from older From 1b5e449114dfdc4e073657debfb4fd45adf1985a Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 31 Aug 2020 12:34:16 -0500 Subject: [PATCH 06/13] Remove dev test code --- core/src/main/java/bisq/core/filter/FilterManager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 816c7796059..0ebd0a88dd4 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -119,8 +119,7 @@ public FilterManager(P2PService p2PService, this.providersRepository = providersRepository; this.ignoreDevMsg = ignoreDevMsg; - //todo test - publicKeys = !useDevPrivilegeKeys ? + publicKeys = useDevPrivilegeKeys ? Collections.singletonList(DevEnv.DEV_PRIVILEGE_PUB_KEY) : List.of("0358d47858acdc41910325fce266571540681ef83a0d6fedce312bef9810793a27", "029340c3e7d4bb0f9e651b5f590b434fecb6175aeaa57145c7804ff05d210e534f"); From 9a142be86e14d76f01e9841f13aeef19efdc1811 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 31 Aug 2020 12:51:01 -0500 Subject: [PATCH 07/13] Add bannedPrivilegedDevPubKeys field --- .../main/java/bisq/core/filter/Filter.java | 27 +++++++++++++------ .../resources/i18n/displayStrings.properties | 3 ++- .../main/overlays/windows/FilterWindow.java | 17 +++++++----- proto/src/main/proto/pb.proto | 1 + 4 files changed, 33 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/bisq/core/filter/Filter.java b/core/src/main/java/bisq/core/filter/Filter.java index f6c28bc5757..ff63336364d 100644 --- a/core/src/main/java/bisq/core/filter/Filter.java +++ b/core/src/main/java/bisq/core/filter/Filter.java @@ -77,6 +77,8 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { private final long creationDate; + private final List bannedPrivilegedDevPubKeys; + // Should be only used in emergency case if we need to add data but do not want to break backward compatibility // at the P2P network storage checks. The hash of the object will be used to verify if the data is valid. Any new // field in a class would break that hash and therefore break the storage mechanism. @@ -108,7 +110,8 @@ static Filter cloneWithSig(Filter filter, String signatureAsBase64) { filter.getCreationDate(), filter.getExtraDataMap(), signatureAsBase64, - filter.getSignerPubKeyAsHex()); + filter.getSignerPubKeyAsHex(), + filter.getBannedPrivilegedDevPubKeys()); } // Used for signature verification as we created the sig without the signatureAsBase64 field we set it to null again @@ -134,7 +137,8 @@ static Filter cloneWithoutSig(Filter filter) { filter.getCreationDate(), filter.getExtraDataMap(), null, - filter.getSignerPubKeyAsHex()); + filter.getSignerPubKeyAsHex(), + filter.getBannedPrivilegedDevPubKeys()); } public Filter(List bannedOfferIds, @@ -155,7 +159,8 @@ public Filter(List bannedOfferIds, List bannedSignerPubKeys, List btcFeeReceiverAddresses, PublicKey ownerPubKey, - String signerPubKeyAsHex) { + String signerPubKeyAsHex, + List bannedPrivilegedDevPubKeys) { this(bannedOfferIds, bannedNodeAddress, bannedPaymentAccounts, @@ -177,7 +182,8 @@ public Filter(List bannedOfferIds, System.currentTimeMillis(), null, null, - signerPubKeyAsHex); + signerPubKeyAsHex, + bannedPrivilegedDevPubKeys); } @@ -207,7 +213,8 @@ public Filter(List bannedOfferIds, long creationDate, @Nullable Map extraDataMap, @Nullable String signatureAsBase64, - String signerPubKeyAsHex) { + String signerPubKeyAsHex, + List bannedPrivilegedDevPubKeys) { this.bannedOfferIds = bannedOfferIds; this.bannedNodeAddress = bannedNodeAddress; this.bannedPaymentAccounts = bannedPaymentAccounts; @@ -230,6 +237,7 @@ public Filter(List bannedOfferIds, this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); this.signatureAsBase64 = signatureAsBase64; this.signerPubKeyAsHex = signerPubKeyAsHex; + this.bannedPrivilegedDevPubKeys = bannedPrivilegedDevPubKeys; // ownerPubKeyBytes can be null when called from tests if (ownerPubKeyBytes != null) { @@ -264,7 +272,8 @@ public protobuf.StoragePayload toProtoMessage() { .addAllBtcFeeReceiverAddresses(btcFeeReceiverAddresses) .setOwnerPubKeyBytes(ByteString.copyFrom(ownerPubKeyBytes)) .setSignerPubKeyAsHex(signerPubKeyAsHex) - .setCreationDate(creationDate); + .setCreationDate(creationDate) + .addAllBannedPrivilegedDevPubKeys(bannedPrivilegedDevPubKeys); Optional.ofNullable(signatureAsBase64).ifPresent(builder::setSignatureAsBase64); Optional.ofNullable(extraDataMap).ifPresent(builder::putAllExtraData); @@ -299,7 +308,8 @@ public static Filter fromProto(protobuf.Filter proto) { proto.getCreationDate(), CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap(), proto.getSignatureAsBase64(), - proto.getSignerPubKeyAsHex() + proto.getSignerPubKeyAsHex(), + ProtoUtil.protocolStringListToList(proto.getBannedPrivilegedDevPubKeysList()) ); } @@ -334,7 +344,8 @@ public String toString() { ",\n disableTradeBelowVersion='" + disableTradeBelowVersion + '\'' + ",\n mediators=" + mediators + ",\n refundAgents=" + refundAgents + - ",\n bannedSignerPubKeys=" + bannedSignerPubKeys + + ",\n bannedAccountWitnessSignerPubKeys=" + bannedSignerPubKeys + + ",\n bannedPrivilegedDevPubKeys=" + bannedPrivilegedDevPubKeys + ",\n btcFeeReceiverAddresses=" + btcFeeReceiverAddresses + ",\n creationDate=" + creationDate + ",\n extraDataMap=" + extraDataMap + diff --git a/core/src/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 3702495490a..4ecd97e304f 100644 --- a/core/src/main/resources/i18n/displayStrings.properties +++ b/core/src/main/resources/i18n/displayStrings.properties @@ -2434,7 +2434,8 @@ filterWindow.onions=Filtered onion addresses (comma sep.) filterWindow.accounts=Filtered trading account data:\nFormat: comma sep. list of [payment method id | data field | value] filterWindow.bannedCurrencies=Filtered currency codes (comma sep.) filterWindow.bannedPaymentMethods=Filtered payment method IDs (comma sep.) -filterWindow.bannedSignerPubKeys=Filtered signer pubkeys (comma sep. hex of pubkeys) +filterWindow.bannedAccountWitnessSignerPubKeys=Filtered account witness signer pub keys (comma sep. hex of pub keys) +filterWindow.bannedPrivilegedDevPubKeys=Filtered privileged dev pub keys (comma sep. hex of pub keys) filterWindow.arbitrators=Filtered arbitrators (comma sep. onion addresses) filterWindow.mediators=Filtered mediators (comma sep. onion addresses) filterWindow.refundAgents=Filtered refund agents (comma sep. onion addresses) diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java index 8ca9aa44847..eed9ab5353e 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java @@ -113,8 +113,9 @@ private void addContent() { gridPane.getColumnConstraints().get(0).setHalignment(HPos.LEFT); InputTextField keyInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("shared.unlock"), 10); - if (useDevPrivilegeKeys) + if (useDevPrivilegeKeys) { keyInputTextField.setText(DevEnv.DEV_PRIVILEGE_PRIV_KEY); + } InputTextField offerIdsInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.offers")); InputTextField nodesInputTextField = addTopLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.onions")).second; @@ -125,8 +126,8 @@ private void addContent() { InputTextField bannedCurrenciesInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedCurrencies")); InputTextField bannedPaymentMethodsInputTextField = addTopLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedPaymentMethods")).second; bannedPaymentMethodsInputTextField.setPromptText("E.g. PERFECT_MONEY"); // Do not translate - InputTextField bannedSignerPubKeysInputTextField = addTopLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedSignerPubKeys")).second; - bannedSignerPubKeysInputTextField.setPromptText("E.g. 7f66117aa084e5a2c54fe17d29dd1fee2b241257"); // Do not translate + InputTextField bannedAccountWitnessSignerPubKeysInputTextField = addTopLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedAccountWitnessSignerPubKeys")).second; + bannedAccountWitnessSignerPubKeysInputTextField.setPromptText("E.g. 7f66117aa084e5a2c54fe17d29dd1fee2b241257"); // Do not translate InputTextField arbitratorsInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.arbitrators")); InputTextField mediatorsInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.mediators")); InputTextField refundAgentsInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.refundAgents")); @@ -138,6 +139,7 @@ private void addContent() { CheckBox disableDaoCheckBox = addLabelCheckBox(gridPane, ++rowIndex, Res.get("filterWindow.disableDao")); InputTextField disableDaoBelowVersionInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.disableDaoBelowVersion")); InputTextField disableTradeBelowVersionInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.disableTradeBelowVersion")); + InputTextField bannedPrivilegedDevPubKeysInputTextField = addTopLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedPrivilegedDevPubKeys")).second; Filter filter = filterManager.getDevFilter(); if (filter != null) { @@ -146,7 +148,7 @@ private void addContent() { setupFieldFromPaymentAccountFiltersList(paymentAccountFilterInputTextField, filter.getBannedPaymentAccounts()); setupFieldFromList(bannedCurrenciesInputTextField, filter.getBannedCurrencies()); setupFieldFromList(bannedPaymentMethodsInputTextField, filter.getBannedPaymentMethods()); - setupFieldFromList(bannedSignerPubKeysInputTextField, filter.getBannedSignerPubKeys()); + setupFieldFromList(bannedAccountWitnessSignerPubKeysInputTextField, filter.getBannedSignerPubKeys()); setupFieldFromList(arbitratorsInputTextField, filter.getArbitrators()); setupFieldFromList(mediatorsInputTextField, filter.getMediators()); setupFieldFromList(refundAgentsInputTextField, filter.getRefundAgents()); @@ -154,6 +156,8 @@ private void addContent() { setupFieldFromList(seedNodesInputTextField, filter.getSeedNodes()); setupFieldFromList(priceRelayNodesInputTextField, filter.getPriceRelayNodes()); setupFieldFromList(btcNodesInputTextField, filter.getBtcNodes()); + setupFieldFromList(btcNodesInputTextField, filter.getBtcNodes()); + setupFieldFromList(bannedPrivilegedDevPubKeysInputTextField, filter.getBannedPrivilegedDevPubKeys()); preventPublicBtcNetworkCheckBox.setSelected(filter.isPreventPublicBtcNetwork()); disableDaoCheckBox.setSelected(filter.isDisableDao()); @@ -185,10 +189,11 @@ private void addContent() { disableTradeBelowVersionInputTextField.getText(), readAsList(mediatorsInputTextField), readAsList(refundAgentsInputTextField), - readAsList(bannedSignerPubKeysInputTextField), + readAsList(bannedAccountWitnessSignerPubKeysInputTextField), readAsList(btcFeeReceiverAddressesInputTextField), filterManager.getOwnerPubKey(), - signerPubKeyAsHex + signerPubKeyAsHex, + readAsList(bannedPrivilegedDevPubKeysInputTextField) ); filterManager.addDevFilter(newFilter, privKeyString); diff --git a/proto/src/main/proto/pb.proto b/proto/src/main/proto/pb.proto index c1e8de2f5a4..b90433bd8ed 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -640,6 +640,7 @@ message Filter { repeated string btc_fee_receiver_addresses = 20; int64 creation_date = 21; string signer_pub_key_as_hex = 22; + repeated string bannedPrivilegedDevPubKeys = 23; } // not used anymore from v0.6 on. But leave it for receiving TradeStatistics objects from older From 4a2f7dc92c7d11fc4dd2c07cce1c28ae8f7d91cf Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 31 Aug 2020 13:01:44 -0500 Subject: [PATCH 08/13] Rename bannedSignerPubKeys to bannedAccountWitnessSignerPubKeys We use several signing keys, so better to make it more explicit. protobuf entry is not changed to not risk issues (i guess it would work renaming it) --- .../main/java/bisq/core/filter/Filter.java | 18 +-- .../java/bisq/core/filter/FilterManager.java | 4 +- .../main/overlays/windows/FilterWindow.java | 139 ++++++++++-------- 3 files changed, 90 insertions(+), 71 deletions(-) diff --git a/core/src/main/java/bisq/core/filter/Filter.java b/core/src/main/java/bisq/core/filter/Filter.java index ff63336364d..d2f89824321 100644 --- a/core/src/main/java/bisq/core/filter/Filter.java +++ b/core/src/main/java/bisq/core/filter/Filter.java @@ -71,7 +71,7 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload { private final List mediators; private final List refundAgents; - private final List bannedSignerPubKeys; + private final List bannedAccountWitnessSignerPubKeys; private final List btcFeeReceiverAddresses; @@ -104,7 +104,7 @@ static Filter cloneWithSig(Filter filter, String signatureAsBase64) { filter.getDisableTradeBelowVersion(), filter.getMediators(), filter.getRefundAgents(), - filter.getBannedSignerPubKeys(), + filter.getBannedAccountWitnessSignerPubKeys(), filter.getBtcFeeReceiverAddresses(), filter.getOwnerPubKeyBytes(), filter.getCreationDate(), @@ -131,7 +131,7 @@ static Filter cloneWithoutSig(Filter filter) { filter.getDisableTradeBelowVersion(), filter.getMediators(), filter.getRefundAgents(), - filter.getBannedSignerPubKeys(), + filter.getBannedAccountWitnessSignerPubKeys(), filter.getBtcFeeReceiverAddresses(), filter.getOwnerPubKeyBytes(), filter.getCreationDate(), @@ -156,7 +156,7 @@ public Filter(List bannedOfferIds, String disableTradeBelowVersion, List mediators, List refundAgents, - List bannedSignerPubKeys, + List bannedAccountWitnessSignerPubKeys, List btcFeeReceiverAddresses, PublicKey ownerPubKey, String signerPubKeyAsHex, @@ -176,7 +176,7 @@ public Filter(List bannedOfferIds, disableTradeBelowVersion, mediators, refundAgents, - bannedSignerPubKeys, + bannedAccountWitnessSignerPubKeys, btcFeeReceiverAddresses, Sig.getPublicKeyBytes(ownerPubKey), System.currentTimeMillis(), @@ -207,7 +207,7 @@ public Filter(List bannedOfferIds, String disableTradeBelowVersion, List mediators, List refundAgents, - List bannedSignerPubKeys, + List bannedAccountWitnessSignerPubKeys, List btcFeeReceiverAddresses, byte[] ownerPubKeyBytes, long creationDate, @@ -230,7 +230,7 @@ public Filter(List bannedOfferIds, this.disableTradeBelowVersion = disableTradeBelowVersion; this.mediators = mediators; this.refundAgents = refundAgents; - this.bannedSignerPubKeys = bannedSignerPubKeys; + this.bannedAccountWitnessSignerPubKeys = bannedAccountWitnessSignerPubKeys; this.btcFeeReceiverAddresses = btcFeeReceiverAddresses; this.ownerPubKeyBytes = ownerPubKeyBytes; this.creationDate = creationDate; @@ -268,7 +268,7 @@ public protobuf.StoragePayload toProtoMessage() { .setDisableTradeBelowVersion(disableTradeBelowVersion) .addAllMediators(mediators) .addAllRefundAgents(refundAgents) - .addAllBannedSignerPubKeys(bannedSignerPubKeys) + .addAllBannedSignerPubKeys(bannedAccountWitnessSignerPubKeys) .addAllBtcFeeReceiverAddresses(btcFeeReceiverAddresses) .setOwnerPubKeyBytes(ByteString.copyFrom(ownerPubKeyBytes)) .setSignerPubKeyAsHex(signerPubKeyAsHex) @@ -344,7 +344,7 @@ public String toString() { ",\n disableTradeBelowVersion='" + disableTradeBelowVersion + '\'' + ",\n mediators=" + mediators + ",\n refundAgents=" + refundAgents + - ",\n bannedAccountWitnessSignerPubKeys=" + bannedSignerPubKeys + + ",\n bannedAccountWitnessSignerPubKeys=" + bannedAccountWitnessSignerPubKeys + ",\n bannedPrivilegedDevPubKeys=" + bannedPrivilegedDevPubKeys + ",\n btcFeeReceiverAddresses=" + btcFeeReceiverAddresses + ",\n creationDate=" + creationDate + diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 0ebd0a88dd4..8bf547effd1 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -386,8 +386,8 @@ public boolean arePeersPaymentAccountDataBanned(PaymentAccountPayload paymentAcc public boolean isWitnessSignerPubKeyBanned(String witnessSignerPubKeyAsHex) { return getFilter() != null && - getFilter().getBannedSignerPubKeys() != null && - getFilter().getBannedSignerPubKeys().stream() + getFilter().getBannedAccountWitnessSignerPubKeys() != null && + getFilter().getBannedAccountWitnessSignerPubKeys().stream() .anyMatch(e -> e.equals(witnessSignerPubKeyAsHex)); } diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java index eed9ab5353e..d6a5cd151ee 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java @@ -112,57 +112,76 @@ private void addContent() { gridPane.getColumnConstraints().remove(1); gridPane.getColumnConstraints().get(0).setHalignment(HPos.LEFT); - InputTextField keyInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("shared.unlock"), 10); + InputTextField keyTF = addInputTextField(gridPane, ++rowIndex, + Res.get("shared.unlock"), 10); if (useDevPrivilegeKeys) { - keyInputTextField.setText(DevEnv.DEV_PRIVILEGE_PRIV_KEY); + keyTF.setText(DevEnv.DEV_PRIVILEGE_PRIV_KEY); } - InputTextField offerIdsInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.offers")); - InputTextField nodesInputTextField = addTopLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.onions")).second; - nodesInputTextField.setPromptText("E.g. zqnzx6o3nifef5df.onion:9999"); // Do not translate - InputTextField paymentAccountFilterInputTextField = addTopLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.accounts")).second; - GridPane.setHalignment(paymentAccountFilterInputTextField, HPos.RIGHT); - paymentAccountFilterInputTextField.setPromptText("E.g. PERFECT_MONEY|getAccountNr|12345"); // Do not translate - InputTextField bannedCurrenciesInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedCurrencies")); - InputTextField bannedPaymentMethodsInputTextField = addTopLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedPaymentMethods")).second; - bannedPaymentMethodsInputTextField.setPromptText("E.g. PERFECT_MONEY"); // Do not translate - InputTextField bannedAccountWitnessSignerPubKeysInputTextField = addTopLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedAccountWitnessSignerPubKeys")).second; - bannedAccountWitnessSignerPubKeysInputTextField.setPromptText("E.g. 7f66117aa084e5a2c54fe17d29dd1fee2b241257"); // Do not translate - InputTextField arbitratorsInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.arbitrators")); - InputTextField mediatorsInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.mediators")); - InputTextField refundAgentsInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.refundAgents")); - InputTextField btcFeeReceiverAddressesInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.btcFeeReceiverAddresses")); - InputTextField seedNodesInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.seedNode")); - InputTextField priceRelayNodesInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.priceRelayNode")); - InputTextField btcNodesInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.btcNode")); - CheckBox preventPublicBtcNetworkCheckBox = addLabelCheckBox(gridPane, ++rowIndex, Res.get("filterWindow.preventPublicBtcNetwork")); - CheckBox disableDaoCheckBox = addLabelCheckBox(gridPane, ++rowIndex, Res.get("filterWindow.disableDao")); - InputTextField disableDaoBelowVersionInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.disableDaoBelowVersion")); - InputTextField disableTradeBelowVersionInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.disableTradeBelowVersion")); - InputTextField bannedPrivilegedDevPubKeysInputTextField = addTopLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedPrivilegedDevPubKeys")).second; + InputTextField offerIdsTF = addInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.offers")); + InputTextField nodesTF = addTopLabelInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.onions")).second; + nodesTF.setPromptText("E.g. zqnzx6o3nifef5df.onion:9999"); // Do not translate + InputTextField paymentAccountFilterTF = addTopLabelInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.accounts")).second; + GridPane.setHalignment(paymentAccountFilterTF, HPos.RIGHT); + paymentAccountFilterTF.setPromptText("E.g. PERFECT_MONEY|getAccountNr|12345"); // Do not translate + InputTextField bannedCurrenciesTF = addInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.bannedCurrencies")); + InputTextField bannedPaymentMethodsTF = addTopLabelInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.bannedPaymentMethods")).second; + bannedPaymentMethodsTF.setPromptText("E.g. PERFECT_MONEY"); // Do not translate + InputTextField bannedAccountWitnessSignerPubKeysTF = addTopLabelInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.bannedAccountWitnessSignerPubKeys")).second; + bannedAccountWitnessSignerPubKeysTF.setPromptText("E.g. 7f66117aa084e5a2c54fe17d29dd1fee2b241257"); // Do not translate + InputTextField arbitratorsTF = addInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.arbitrators")); + InputTextField mediatorsTF = addInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.mediators")); + InputTextField refundAgentsTF = addInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.refundAgents")); + InputTextField btcFeeReceiverAddressesTF = addInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.btcFeeReceiverAddresses")); + InputTextField seedNodesTF = addInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.seedNode")); + InputTextField priceRelayNodesTF = addInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.priceRelayNode")); + InputTextField btcNodesTF = addInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.btcNode")); + CheckBox preventPublicBtcNetworkCheckBox = addLabelCheckBox(gridPane, ++rowIndex, + Res.get("filterWindow.preventPublicBtcNetwork")); + CheckBox disableDaoCheckBox = addLabelCheckBox(gridPane, ++rowIndex, + Res.get("filterWindow.disableDao")); + InputTextField disableDaoBelowVersionTF = addInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.disableDaoBelowVersion")); + InputTextField disableTradeBelowVersionTF = addInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.disableTradeBelowVersion")); + InputTextField bannedPrivilegedDevPubKeysTF = addTopLabelInputTextField(gridPane, ++rowIndex, + Res.get("filterWindow.bannedPrivilegedDevPubKeys")).second; Filter filter = filterManager.getDevFilter(); if (filter != null) { - setupFieldFromList(offerIdsInputTextField, filter.getBannedOfferIds()); - setupFieldFromList(nodesInputTextField, filter.getBannedNodeAddress()); - setupFieldFromPaymentAccountFiltersList(paymentAccountFilterInputTextField, filter.getBannedPaymentAccounts()); - setupFieldFromList(bannedCurrenciesInputTextField, filter.getBannedCurrencies()); - setupFieldFromList(bannedPaymentMethodsInputTextField, filter.getBannedPaymentMethods()); - setupFieldFromList(bannedAccountWitnessSignerPubKeysInputTextField, filter.getBannedSignerPubKeys()); - setupFieldFromList(arbitratorsInputTextField, filter.getArbitrators()); - setupFieldFromList(mediatorsInputTextField, filter.getMediators()); - setupFieldFromList(refundAgentsInputTextField, filter.getRefundAgents()); - setupFieldFromList(btcFeeReceiverAddressesInputTextField, filter.getBtcFeeReceiverAddresses()); - setupFieldFromList(seedNodesInputTextField, filter.getSeedNodes()); - setupFieldFromList(priceRelayNodesInputTextField, filter.getPriceRelayNodes()); - setupFieldFromList(btcNodesInputTextField, filter.getBtcNodes()); - setupFieldFromList(btcNodesInputTextField, filter.getBtcNodes()); - setupFieldFromList(bannedPrivilegedDevPubKeysInputTextField, filter.getBannedPrivilegedDevPubKeys()); + setupFieldFromList(offerIdsTF, filter.getBannedOfferIds()); + setupFieldFromList(nodesTF, filter.getBannedNodeAddress()); + setupFieldFromPaymentAccountFiltersList(paymentAccountFilterTF, filter.getBannedPaymentAccounts()); + setupFieldFromList(bannedCurrenciesTF, filter.getBannedCurrencies()); + setupFieldFromList(bannedPaymentMethodsTF, filter.getBannedPaymentMethods()); + setupFieldFromList(bannedAccountWitnessSignerPubKeysTF, filter.getBannedAccountWitnessSignerPubKeys()); + setupFieldFromList(arbitratorsTF, filter.getArbitrators()); + setupFieldFromList(mediatorsTF, filter.getMediators()); + setupFieldFromList(refundAgentsTF, filter.getRefundAgents()); + setupFieldFromList(btcFeeReceiverAddressesTF, filter.getBtcFeeReceiverAddresses()); + setupFieldFromList(seedNodesTF, filter.getSeedNodes()); + setupFieldFromList(priceRelayNodesTF, filter.getPriceRelayNodes()); + setupFieldFromList(btcNodesTF, filter.getBtcNodes()); + setupFieldFromList(btcNodesTF, filter.getBtcNodes()); + setupFieldFromList(bannedPrivilegedDevPubKeysTF, filter.getBannedPrivilegedDevPubKeys()); preventPublicBtcNetworkCheckBox.setSelected(filter.isPreventPublicBtcNetwork()); disableDaoCheckBox.setSelected(filter.isDisableDao()); - disableDaoBelowVersionInputTextField.setText(filter.getDisableDaoBelowVersion()); - disableTradeBelowVersionInputTextField.setText(filter.getDisableTradeBelowVersion()); + disableDaoBelowVersionTF.setText(filter.getDisableDaoBelowVersion()); + disableTradeBelowVersionTF.setText(filter.getDisableTradeBelowVersion()); } Button removeFilterMessageButton = new AutoTooltipButton(Res.get("filterWindow.remove")); @@ -170,30 +189,30 @@ private void addContent() { Button sendButton = new AutoTooltipButton(Res.get("filterWindow.add")); sendButton.setOnAction(e -> { - String privKeyString = keyInputTextField.getText(); + String privKeyString = keyTF.getText(); if (filterManager.canAddDevFilter(privKeyString)) { String signerPubKeyAsHex = filterManager.getSignerPubKeyAsHex(privKeyString); Filter newFilter = new Filter( - readAsList(offerIdsInputTextField), - readAsList(nodesInputTextField), - readAsPaymentAccountFiltersList(paymentAccountFilterInputTextField), - readAsList(bannedCurrenciesInputTextField), - readAsList(bannedPaymentMethodsInputTextField), - readAsList(arbitratorsInputTextField), - readAsList(seedNodesInputTextField), - readAsList(priceRelayNodesInputTextField), + readAsList(offerIdsTF), + readAsList(nodesTF), + readAsPaymentAccountFiltersList(paymentAccountFilterTF), + readAsList(bannedCurrenciesTF), + readAsList(bannedPaymentMethodsTF), + readAsList(arbitratorsTF), + readAsList(seedNodesTF), + readAsList(priceRelayNodesTF), preventPublicBtcNetworkCheckBox.isSelected(), - readAsList(btcNodesInputTextField), + readAsList(btcNodesTF), disableDaoCheckBox.isSelected(), - disableDaoBelowVersionInputTextField.getText(), - disableTradeBelowVersionInputTextField.getText(), - readAsList(mediatorsInputTextField), - readAsList(refundAgentsInputTextField), - readAsList(bannedAccountWitnessSignerPubKeysInputTextField), - readAsList(btcFeeReceiverAddressesInputTextField), + disableDaoBelowVersionTF.getText(), + disableTradeBelowVersionTF.getText(), + readAsList(mediatorsTF), + readAsList(refundAgentsTF), + readAsList(bannedAccountWitnessSignerPubKeysTF), + readAsList(btcFeeReceiverAddressesTF), filterManager.getOwnerPubKey(), signerPubKeyAsHex, - readAsList(bannedPrivilegedDevPubKeysInputTextField) + readAsList(bannedPrivilegedDevPubKeysTF) ); filterManager.addDevFilter(newFilter, privKeyString); @@ -205,7 +224,7 @@ private void addContent() { }); removeFilterMessageButton.setOnAction(e -> { - String privKeyString = keyInputTextField.getText(); + String privKeyString = keyTF.getText(); if (filterManager.canRemoveDevFilter(privKeyString)) { filterManager.removeDevFilter(privKeyString); hide(); From 78142bda7dfcfd86e1ba73244609bf004f228334 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 31 Aug 2020 13:23:51 -0500 Subject: [PATCH 09/13] Fix tests --- core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java | 1 + core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java | 1 + 2 files changed, 2 insertions(+) diff --git a/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java b/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java index 050fce18ec0..b2bf0fc6450 100644 --- a/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java +++ b/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java @@ -61,6 +61,7 @@ public void testRoundtripFull() { 0, null, null, + null, null)); vo.setRegisteredArbitrator(ArbitratorTest.getArbitratorMock()); diff --git a/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java b/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java index e5319f8ecd0..a8488e9c1e3 100644 --- a/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java +++ b/core/src/test/java/bisq/core/util/FeeReceiverSelectorTest.java @@ -120,6 +120,7 @@ private static Filter filterWithReceivers(List btcFeeReceiverAddresses) 0, null, null, + null, null); } } From 5209018f4d719d09046fc45fb751372891ceec48 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 31 Aug 2020 13:24:02 -0500 Subject: [PATCH 10/13] Add pub key --- core/src/main/java/bisq/core/filter/FilterManager.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 8bf547effd1..2c59f7e013e 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -122,7 +122,8 @@ public FilterManager(P2PService p2PService, publicKeys = useDevPrivilegeKeys ? Collections.singletonList(DevEnv.DEV_PRIVILEGE_PUB_KEY) : List.of("0358d47858acdc41910325fce266571540681ef83a0d6fedce312bef9810793a27", - "029340c3e7d4bb0f9e651b5f590b434fecb6175aeaa57145c7804ff05d210e534f"); + "029340c3e7d4bb0f9e651b5f590b434fecb6175aeaa57145c7804ff05d210e534f", + "034dc7530bf66ffd9580aa98031ea9a18ac2d269f7c56c0e71eca06105b9ed69f9"); } From c755fc2592b90d5b17264b333c845142a84c3852 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 31 Aug 2020 13:51:41 -0500 Subject: [PATCH 11/13] Add isPrivilegedDevPubKeyBanned method It is used atm only for filter itself which might have limited effect. Applying it to alert and priv notification requires adding pubKey fields there which is outside of the scope of that PR but should be added soon as well. --- .../java/bisq/core/filter/FilterManager.java | 106 +++++++++++++----- 1 file changed, 76 insertions(+), 30 deletions(-) diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 2c59f7e013e..37cfd90431a 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -141,8 +141,6 @@ public void onAllServicesInitialized() { .map(ProtectedStorageEntry::getProtectedStoragePayload) .filter(protectedStoragePayload -> protectedStoragePayload instanceof Filter) .map(protectedStoragePayload -> (Filter) protectedStoragePayload) - .filter(this::isFilterPublicKeyInList) - .filter(this::verifySignature) .forEach(this::onFilterAddedFromNetwork); p2PService.addHashSetChangedListener(new HashMapChangedListener() { @@ -152,9 +150,7 @@ public void onAdded(Collection protectedStorageEntries) { .filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof Filter) .forEach(protectedStorageEntry -> { Filter filter = (Filter) protectedStorageEntry.getProtectedStoragePayload(); - if (isFilterPublicKeyInList(filter) && verifySignature(filter)) { - onFilterAddedFromNetwork(filter); - } + onFilterAddedFromNetwork(filter); }); } @@ -164,9 +160,7 @@ public void onRemoved(Collection protectedStorageEntries) .filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof Filter) .forEach(protectedStorageEntry -> { Filter filter = (Filter) protectedStorageEntry.getProtectedStoragePayload(); - if (isFilterPublicKeyInList(filter) && verifySignature(filter)) { - onFilterRemovedFromNetwork(filter); - } + onFilterRemovedFromNetwork(filter); }); } }); @@ -212,6 +206,15 @@ public void onRequestCustomBridges() { }); } + public boolean isPrivilegedDevPubKeyBanned(String pubKeyAsHex) { + Filter filter = getFilter(); + if (filter == null) { + return false; + } + + return filter.getBannedPrivilegedDevPubKeys().contains(pubKeyAsHex); + } + public boolean canAddDevFilter(String privKeyString) { if (privKeyString == null || privKeyString.isEmpty()) { return false; @@ -220,6 +223,13 @@ public boolean canAddDevFilter(String privKeyString) { log.warn("There is no persisted dev filter to be removed."); return false; } + + ECKey ecKeyFromPrivate = toECKey(privKeyString); + String pubKeyAsHex = getPubKeyAsHex(ecKeyFromPrivate); + if (isPrivilegedDevPubKeyBanned(pubKeyAsHex)) { + log.warn("Pub key is banned."); + return false; + } return true; } @@ -268,6 +278,11 @@ public boolean canRemoveDevFilter(String privKeyString) { return false; } + if (isPrivilegedDevPubKeyBanned(pubKeyAsHex)) { + log.warn("Pub key is banned."); + return false; + } + return true; } @@ -397,43 +412,74 @@ public boolean isWitnessSignerPubKeyBanned(String witnessSignerPubKeyAsHex) { // Private /////////////////////////////////////////////////////////////////////////////////////////// - private void onFilterAddedFromNetwork(Filter filter) { - if (filterProperty.get() != null && filterProperty.get().getCreationDate() > filter.getCreationDate()) { - log.warn("We received a new filter from the network but the creation date is older than the " + - "filter we have already. We ignore the new filter.\n" + - "New filer={}\n" + - "Old filter={}", - filter, filterProperty.get()); + private void onFilterAddedFromNetwork(Filter newFilter) { + Filter currentFilter = getFilter(); + + if (!isFilterPublicKeyInList(newFilter)) { + log.warn("isFilterPublicKeyInList failed. Filter={}", newFilter); + return; + } + if (!verifySignature(newFilter)) { + log.warn("verifySignature failed. Filter={}", newFilter); return; } + if (currentFilter != null) { + if (currentFilter.getCreationDate() > newFilter.getCreationDate()) { + log.warn("We received a new filter from the network but the creation date is older than the " + + "filter we have already. We ignore the new filter.\n" + + "New filer={}\n" + + "Old filter={}", + newFilter, filterProperty.get()); + return; + } + + if (isPrivilegedDevPubKeyBanned(newFilter.getSignerPubKeyAsHex())) { + log.warn("Pub key of filter is banned. currentFilter={}, newFilter={}", currentFilter, newFilter); + return; + } + } + // Our new filter is newer so we apply it. // We do not require strict guarantees here (e.g. clocks not synced) as only trusted developers have the key // for deploying filters and this is only in place to avoid unintended situations of multiple filters // from multiple devs or if same dev publishes new filter from different app without the persisted devFilter. - filterProperty.set(filter); + filterProperty.set(newFilter); // Seed nodes are requested at startup before we get the filter so we only apply the banned // nodes at the next startup and don't update the list in the P2P network domain. // We persist it to the property file which is read before any other initialisation. - saveBannedNodes(BANNED_SEED_NODES, filter.getSeedNodes()); - saveBannedNodes(BANNED_BTC_NODES, filter.getBtcNodes()); + saveBannedNodes(BANNED_SEED_NODES, newFilter.getSeedNodes()); + saveBannedNodes(BANNED_BTC_NODES, newFilter.getBtcNodes()); // Banned price relay nodes we can apply at runtime - List priceRelayNodes = filter.getPriceRelayNodes(); + List priceRelayNodes = newFilter.getPriceRelayNodes(); saveBannedNodes(BANNED_PRICE_RELAY_NODES, priceRelayNodes); + + //TODO should be moved to client with listening on onFilterAdded providersRepository.applyBannedNodes(priceRelayNodes); - if (filter.isPreventPublicBtcNetwork() && + //TODO should be moved to client with listening on onFilterAdded + if (newFilter.isPreventPublicBtcNetwork() && preferences.getBitcoinNodesOptionOrdinal() == BtcNodes.BitcoinNodesOption.PUBLIC.ordinal()) { preferences.setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.PROVIDED.ordinal()); } - listeners.forEach(e -> e.onFilterAdded(filter)); + listeners.forEach(e -> e.onFilterAdded(newFilter)); } - // We clean up potentially banned nodes and set value of filter property to null private void onFilterRemovedFromNetwork(Filter filter) { + if (!isFilterPublicKeyInList(filter)) { + log.warn("isFilterPublicKeyInList failed. Filter={}", filter); + return; + } + if (!verifySignature(filter)) { + log.warn("verifySignature failed. Filter={}", filter); + return; + } + + // We don't check for banned filter as we want to remove a banned filter anyway. + if (!filterProperty.get().equals(filter)) { return; } @@ -494,6 +540,14 @@ private boolean isFilterPublicKeyInList(Filter filter) { return true; } + private boolean isPublicKeyInList(String pubKeyAsHex) { + boolean isPublicKeyInList = publicKeys.contains(pubKeyAsHex); + if (!isPublicKeyInList) { + log.warn("pubKeyAsHex is not part of our pub key list. pubKeyAsHex={}, publicKeys={}", pubKeyAsHex, publicKeys); + } + return isPublicKeyInList; + } + private boolean verifySignature(Filter filter) { try { Filter filterForSigVerification = Filter.cloneWithoutSig(filter); @@ -525,12 +579,4 @@ private Sha256Hash getSha256Hash(Filter filter) { private String getPubKeyAsHex(ECKey ecKey) { return HEX.encode(ecKey.getPubKey()); } - - private boolean isPublicKeyInList(String pubKeyAsHex) { - boolean isPublicKeyInList = publicKeys.contains(pubKeyAsHex); - if (!isPublicKeyInList) { - log.warn("pubKeyAsHex is not part of our pub key list. pubKeyAsHex={}, publicKeys={}", pubKeyAsHex, publicKeys); - } - return isPublicKeyInList; - } } From 334cb43c3b0805c277bf766204c8c5cd5b17aaa5 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 31 Aug 2020 13:53:17 -0500 Subject: [PATCH 12/13] Rename verifySignature to isSignatureValid --- core/src/main/java/bisq/core/filter/FilterManager.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 37cfd90431a..2ecee114c03 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -419,7 +419,7 @@ private void onFilterAddedFromNetwork(Filter newFilter) { log.warn("isFilterPublicKeyInList failed. Filter={}", newFilter); return; } - if (!verifySignature(newFilter)) { + if (!isSignatureValid(newFilter)) { log.warn("verifySignature failed. Filter={}", newFilter); return; } @@ -473,7 +473,7 @@ private void onFilterRemovedFromNetwork(Filter filter) { log.warn("isFilterPublicKeyInList failed. Filter={}", filter); return; } - if (!verifySignature(filter)) { + if (!isSignatureValid(filter)) { log.warn("verifySignature failed. Filter={}", filter); return; } @@ -548,7 +548,7 @@ private boolean isPublicKeyInList(String pubKeyAsHex) { return isPublicKeyInList; } - private boolean verifySignature(Filter filter) { + private boolean isSignatureValid(Filter filter) { try { Filter filterForSigVerification = Filter.cloneWithoutSig(filter); Sha256Hash hash = getSha256Hash(filterForSigVerification); From 86999e52d1470ea047ebfffa156f15d1e782b654 Mon Sep 17 00:00:00 2001 From: chimp1984 Date: Mon, 31 Aug 2020 17:13:09 -0500 Subject: [PATCH 13/13] - Remove isPublicKeyInList check at remove as its irrelevant (dev could add his key anyway to src code) - Fix wrong logs - Remove duplicated line --- core/src/main/java/bisq/core/filter/FilterManager.java | 10 ++-------- .../desktop/main/overlays/windows/FilterWindow.java | 1 - 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/bisq/core/filter/FilterManager.java b/core/src/main/java/bisq/core/filter/FilterManager.java index 2ecee114c03..0302fc24f78 100644 --- a/core/src/main/java/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/bisq/core/filter/FilterManager.java @@ -220,7 +220,7 @@ public boolean canAddDevFilter(String privKeyString) { return false; } if (!isValidDevPrivilegeKey(privKeyString)) { - log.warn("There is no persisted dev filter to be removed."); + log.warn("Key in invalid"); return false; } @@ -258,14 +258,8 @@ public boolean canRemoveDevFilter(String privKeyString) { return false; } - if (!isPublicKeyInList(developersFilter.getSignerPubKeyAsHex())) { - log.warn("The SignerPubKey from the filter is not in our pubKey list. " + - "filterSignerPubKey={}, publicKeys={}", developersFilter.getSignerPubKeyAsHex(), publicKeys); - return false; - } - if (!isValidDevPrivilegeKey(privKeyString)) { - log.warn("There is no persisted dev filter to be removed."); + log.warn("Key in invalid."); return false; } diff --git a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java index d6a5cd151ee..cad11360b64 100644 --- a/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java +++ b/desktop/src/main/java/bisq/desktop/main/overlays/windows/FilterWindow.java @@ -175,7 +175,6 @@ private void addContent() { setupFieldFromList(seedNodesTF, filter.getSeedNodes()); setupFieldFromList(priceRelayNodesTF, filter.getPriceRelayNodes()); setupFieldFromList(btcNodesTF, filter.getBtcNodes()); - setupFieldFromList(btcNodesTF, filter.getBtcNodes()); setupFieldFromList(bannedPrivilegedDevPubKeysTF, filter.getBannedPrivilegedDevPubKeys()); preventPublicBtcNetworkCheckBox.setSelected(filter.isPreventPublicBtcNetwork());