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..d2f89824321 100644 --- a/core/src/main/java/bisq/core/filter/Filter.java +++ b/core/src/main/java/bisq/core/filter/Filter.java @@ -21,8 +21,10 @@ 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; @@ -30,147 +32,135 @@ 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; + private final String signatureAsBase64; + // The pub EC key from the dev who has signed and published the filter (different to ownerPubKeyBytes) + private final String signerPubKeyAsHex; - // added in v0.9.4 + // The pub key used for the data protection in the 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; + private final List bannedAccountWitnessSignerPubKeys; - // 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; + private final List bannedPrivilegedDevPubKeys; - /////////////////////////////////////////////////////////////////////////////////////////// - // PROTO BUFFER - /////////////////////////////////////////////////////////////////////////////////////////// + // 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; + + 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.getBannedAccountWitnessSignerPubKeys(), + filter.getBtcFeeReceiverAddresses(), + filter.getOwnerPubKeyBytes(), + filter.getCreationDate(), + filter.getExtraDataMap(), + signatureAsBase64, + filter.getSignerPubKeyAsHex(), + filter.getBannedPrivilegedDevPubKeys()); + } + + // 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.getBannedAccountWitnessSignerPubKeys(), + filter.getBtcFeeReceiverAddresses(), + filter.getOwnerPubKeyBytes(), + filter.getCreationDate(), + filter.getExtraDataMap(), + null, + filter.getSignerPubKeyAsHex(), + filter.getBannedPrivilegedDevPubKeys()); + } - @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 bannedAccountWitnessSignerPubKeys, + List btcFeeReceiverAddresses, + PublicKey ownerPubKey, + String signerPubKeyAsHex, + List bannedPrivilegedDevPubKeys) { this(bannedOfferIds, bannedNodeAddress, bannedPaymentAccounts, @@ -186,73 +176,141 @@ public Filter(List bannedOfferIds, disableTradeBelowVersion, mediators, refundAgents, - bannedSignerPubKeys, - btcFeeReceiverAddresses); - this.signatureAsBase64 = signatureAsBase64; + bannedAccountWitnessSignerPubKeys, + btcFeeReceiverAddresses, + Sig.getPublicKeyBytes(ownerPubKey), + System.currentTimeMillis(), + null, + null, + signerPubKeyAsHex, + bannedPrivilegedDevPubKeys); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // PROTO BUFFER + /////////////////////////////////////////////////////////////////////////////////////////// + + @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 bannedAccountWitnessSignerPubKeys, + List btcFeeReceiverAddresses, + byte[] ownerPubKeyBytes, + long creationDate, + @Nullable Map extraDataMap, + @Nullable String signatureAsBase64, + String signerPubKeyAsHex, + List bannedPrivilegedDevPubKeys) { + 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.bannedAccountWitnessSignerPubKeys = bannedAccountWitnessSignerPubKeys; + this.btcFeeReceiverAddresses = btcFeeReceiverAddresses; this.ownerPubKeyBytes = ownerPubKeyBytes; + this.creationDate = creationDate; this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap); - - ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyBytes); + this.signatureAsBase64 = signatureAsBase64; + this.signerPubKeyAsHex = signerPubKeyAsHex; + this.bannedPrivilegedDevPubKeys = bannedPrivilegedDevPubKeys; + + // ownerPubKeyBytes can be null when called from tests + if (ownerPubKeyBytes != null) { + ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyBytes); + } else { + ownerPubKey = null; + } } @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(bannedAccountWitnessSignerPubKeys) + .addAllBtcFeeReceiverAddresses(btcFeeReceiverAddresses) + .setOwnerPubKeyBytes(ByteString.copyFrom(ownerPubKeyBytes)) + .setSignerPubKeyAsHex(signerPubKeyAsHex) + .setCreationDate(creationDate) + .addAllBannedPrivilegedDevPubKeys(bannedPrivilegedDevPubKeys); + + 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(), + proto.getSignerPubKeyAsHex(), + ProtoUtil.protocolStringListToList(proto.getBannedPrivilegedDevPubKeysList()) + ); } @@ -265,13 +323,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,14 +336,19 @@ public String toString() { ",\n priceRelayNodes=" + priceRelayNodes + ",\n preventPublicBtcNetwork=" + preventPublicBtcNetwork + ",\n btcNodes=" + btcNodes + - ",\n extraDataMap=" + extraDataMap + + ",\n signatureAsBase64='" + signatureAsBase64 + '\'' + + ",\n signerPubKeyAsHex='" + signerPubKeyAsHex + '\'' + + ",\n ownerPubKeyBytes=" + Utilities.bytesAsHexString(ownerPubKeyBytes) + ",\n disableDao=" + disableDao + ",\n disableDaoBelowVersion='" + disableDaoBelowVersion + '\'' + ",\n disableTradeBelowVersion='" + disableTradeBelowVersion + '\'' + ",\n mediators=" + mediators + ",\n refundAgents=" + refundAgents + - ",\n bannedSignerPubKeys=" + bannedSignerPubKeys + + ",\n bannedAccountWitnessSignerPubKeys=" + bannedAccountWitnessSignerPubKeys + + ",\n bannedPrivilegedDevPubKeys=" + bannedPrivilegedDevPubKeys + ",\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..0302fc24f78 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,7 +37,7 @@ import bisq.common.crypto.KeyRing; import org.bitcoinj.core.ECKey; -import org.bitcoinj.core.Utils; +import org.bitcoinj.core.Sha256Hash; import javax.inject.Inject; import javax.inject.Named; @@ -47,33 +45,36 @@ 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.Collections; 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,15 @@ 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 final List publicKeys; private ECKey filterSigningKey; /////////////////////////////////////////////////////////////////////////////////////////// - // Constructor, Initialization + // Constructor /////////////////////////////////////////////////////////////////////////////////////////// @Inject @@ -118,47 +118,52 @@ public FilterManager(P2PService p2PService, this.configFileEditor = new ConfigFileEditor(config.configFile); this.providersRepository = providersRepository; this.ignoreDevMsg = ignoreDevMsg; - pubKeyAsHex = useDevPrivilegeKeys ? - DevEnv.DEV_PRIVILEGE_PUB_KEY : - "022ac7b7766b0aedff82962522c2c14fb8d1961dabef6e5cfd10edc679456a32f1"; + + publicKeys = useDevPrivilegeKeys ? + Collections.singletonList(DevEnv.DEV_PRIVILEGE_PUB_KEY) : + List.of("0358d47858acdc41910325fce266571540681ef83a0d6fedce312bef9810793a27", + "029340c3e7d4bb0f9e651b5f590b434fecb6175aeaa57145c7804ff05d210e534f", + "034dc7530bf66ffd9580aa98031ea9a18ac2d269f7c56c0e71eca06105b9ed69f9"); + } + + /////////////////////////////////////////////////////////////////////////////////////////// + // 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) + .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); - } - } - }); - } + 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(); - } - }); - } - }); - } + onFilterRemovedFromNetwork(filter); + }); + } + }); p2PService.addP2PServiceListener(new P2PServiceListener() { @Override @@ -175,12 +180,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,147 +206,117 @@ 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); + public boolean isPrivilegedDevPubKeyBanned(String pubKeyAsHex) { + Filter filter = getFilter(); + if (filter == null) { + return false; + } - filterProperty.set(null); + return filter.getBannedPrivilegedDevPubKeys().contains(pubKeyAsHex); } - 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()); - - // Banned price relay nodes we can apply at runtime - final List priceRelayNodes = filter.getPriceRelayNodes(); - saveBannedNodes(BANNED_PRICE_RELAY_NODES, priceRelayNodes); - - providersRepository.applyBannedNodes(priceRelayNodes); - - filterProperty.set(filter); - listeners.forEach(e -> e.onFilterAdded(filter)); - - if (filter.isPreventPublicBtcNetwork() && - preferences.getBitcoinNodesOptionOrdinal() == BtcNodes.BitcoinNodesOption.PUBLIC.ordinal()) - preferences.setBitcoinNodesOptionOrdinal(BtcNodes.BitcoinNodesOption.PROVIDED.ordinal()); - return true; - } else { + public boolean canAddDevFilter(String privKeyString) { + if (privKeyString == null || privKeyString.isEmpty()) { + return false; + } + if (!isValidDevPrivilegeKey(privKeyString)) { + log.warn("Key in invalid"); return false; } - } - private void saveBannedNodes(String optionName, List bannedNodes) { - if (bannedNodes != null) - configFileEditor.setOption(optionName, String.join(",", bannedNodes)); - else - configFileEditor.clearOption(optionName); + ECKey ecKeyFromPrivate = toECKey(privKeyString); + String pubKeyAsHex = getPubKeyAsHex(ecKeyFromPrivate); + if (isPrivilegedDevPubKeyBanned(pubKeyAsHex)) { + log.warn("Pub key is banned."); + return false; + } + return true; } - - /////////////////////////////////////////////////////////////////////////////////////////// - // API - /////////////////////////////////////////////////////////////////////////////////////////// - - public void addListener(Listener listener) { - listeners.add(listener); + public String getSignerPubKeyAsHex(String privKeyString) { + ECKey ecKey = toECKey(privKeyString); + return getPubKeyAsHex(ecKey); } - public ObjectProperty filterProperty() { - return filterProperty; - } + public void addDevFilter(Filter filterWithoutSig, String privKeyString) { + setFilterSigningKey(privKeyString); + String signatureAsBase64 = getSignature(filterWithoutSig); + Filter filterWithSig = Filter.cloneWithSig(filterWithoutSig, signatureAsBase64); + user.setDevelopersFilter(filterWithSig); - @Nullable - public Filter getFilter() { - return filterProperty.get(); + p2PService.addProtectedStorageEntry(filterWithSig); } - 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); + public boolean canRemoveDevFilter(String privKeyString) { + if (privKeyString == null || privKeyString.isEmpty()) { + return false; + } - boolean result = p2PService.addProtectedStorageEntry(filter); - if (result) - log.trace("Add filter to network was successful. FilterMessage = {}", filter); + Filter developersFilter = getDevFilter(); + if (developersFilter == null) { + log.warn("There is no persisted dev filter to be removed."); + return false; + } + if (!isValidDevPrivilegeKey(privKeyString)) { + log.warn("Key in invalid."); + return false; } - 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 { + + 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; } - } - 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) { + if (isPrivilegedDevPubKeyBanned(pubKeyAsHex)) { + log.warn("Pub key is banned."); return false; } - } - private void signAndAddSignatureToFilter(Filter filter) { - filter.setSigAndPubKey(filterSigningKey.signMessage(getHexFromData(filter)), keyRing.getSignatureKeyPair().getPublic()); + return true; } - 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; + 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 + return; + } + + if (p2PService.removeData(filterWithSig)) { + user.setDevelopersFilter(null); + } else { + log.warn("Removing dev filter from network failed"); } } - // 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())); + public void addListener(Listener listener) { + listeners.add(listener); + } - Optional.ofNullable(filter.getBannedCurrencies()).ifPresent(builder::addAllBannedCurrencies); - Optional.ofNullable(filter.getBannedPaymentMethods()).ifPresent(builder::addAllBannedPaymentMethods); - Optional.ofNullable(filter.getBannedSignerPubKeys()).ifPresent(builder::addAllBannedSignerPubKeys); + public ObjectProperty filterProperty() { + return filterProperty; + } - return Utils.HEX.encode(builder.build().toByteArray()); + @Nullable + public Filter getFilter() { + return filterProperty.get(); } @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 && @@ -396,8 +371,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 +394,183 @@ 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)); + getFilter().getBannedAccountWitnessSignerPubKeys() != null && + getFilter().getBannedAccountWitnessSignerPubKeys().stream() + .anyMatch(e -> e.equals(witnessSignerPubKeyAsHex)); + } + + + /////////////////////////////////////////////////////////////////////////////////////////// + // Private + /////////////////////////////////////////////////////////////////////////////////////////// + + private void onFilterAddedFromNetwork(Filter newFilter) { + Filter currentFilter = getFilter(); + + if (!isFilterPublicKeyInList(newFilter)) { + log.warn("isFilterPublicKeyInList failed. Filter={}", newFilter); + return; + } + if (!isSignatureValid(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(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, newFilter.getSeedNodes()); + saveBannedNodes(BANNED_BTC_NODES, newFilter.getBtcNodes()); + + // Banned price relay nodes we can apply at runtime + List priceRelayNodes = newFilter.getPriceRelayNodes(); + saveBannedNodes(BANNED_PRICE_RELAY_NODES, priceRelayNodes); + + //TODO should be moved to client with listening on onFilterAdded + providersRepository.applyBannedNodes(priceRelayNodes); + + //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(newFilter)); + } + + private void onFilterRemovedFromNetwork(Filter filter) { + if (!isFilterPublicKeyInList(filter)) { + log.warn("isFilterPublicKeyInList failed. Filter={}", filter); + return; + } + if (!isSignatureValid(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; + } + + 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 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); + byte[] encodeToDER = ecdsaSignature.encodeToDER(); + 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 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 isSignatureValid(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); + + 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); + return false; + } + } + + 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); + } + + private String getPubKeyAsHex(ECKey ecKey) { + return HEX.encode(ecKey.getPubKey()); + } } 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/main/resources/i18n/displayStrings.properties b/core/src/main/resources/i18n/displayStrings.properties index 276eecf83b1..4ecd97e304f 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 @@ -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/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/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java b/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java index 67d09bb6586..b2bf0fc6450 100644 --- a/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java +++ b/core/src/test/java/bisq/core/user/UserPayloadModelVOTest.java @@ -53,13 +53,17 @@ 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, + 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..a8488e9c1e3 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,28 @@ 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, + 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 50dbf04f182..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 @@ -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(); @@ -112,90 +112,123 @@ private void addContent() { gridPane.getColumnConstraints().remove(1); gridPane.getColumnConstraints().get(0).setHalignment(HPos.LEFT); - InputTextField keyInputTextField = addInputTextField(gridPane, ++rowIndex, Res.get("shared.unlock"), 10); - 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; - 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 bannedSignerPubKeysInputTextField = addTopLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.bannedSignerPubKeys")).second; - bannedSignerPubKeysInputTextField.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")); - - final Filter filter = filterManager.getDevelopersFilter(); + InputTextField keyTF = addInputTextField(gridPane, ++rowIndex, + Res.get("shared.unlock"), 10); + if (useDevPrivilegeKeys) { + keyTF.setText(DevEnv.DEV_PRIVILEGE_PRIV_KEY); + } + + 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(bannedSignerPubKeysInputTextField, 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(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(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")); + removeFilterMessageButton.setDisable(filterManager.getDevFilter() == 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()) - ) + String privKeyString = keyTF.getText(); + if (filterManager.canAddDevFilter(privKeyString)) { + String signerPubKeyAsHex = filterManager.getSignerPubKeyAsHex(privKeyString); + Filter newFilter = new Filter( + readAsList(offerIdsTF), + readAsList(nodesTF), + readAsPaymentAccountFiltersList(paymentAccountFilterTF), + readAsList(bannedCurrenciesTF), + readAsList(bannedPaymentMethodsTF), + readAsList(arbitratorsTF), + readAsList(seedNodesTF), + readAsList(priceRelayNodesTF), + preventPublicBtcNetworkCheckBox.isSelected(), + readAsList(btcNodesTF), + disableDaoCheckBox.isSelected(), + disableDaoBelowVersionTF.getText(), + disableTradeBelowVersionTF.getText(), + readAsList(mediatorsTF), + readAsList(refundAgentsTF), + readAsList(bannedAccountWitnessSignerPubKeysTF), + readAsList(btcFeeReceiverAddressesTF), + filterManager.getOwnerPubKey(), + signerPubKeyAsHex, + readAsList(bannedPrivilegedDevPubKeysTF) + ); + + filterManager.addDevFilter(newFilter, privKeyString); + removeFilterMessageButton.setDisable(filterManager.getDevFilter() == null); hide(); - else - new Popup().warning(Res.get("shared.invalidKey")).width(300).onClose(this::blurAgain).show(); + } else { + new Popup().warning(Res.get("shared.invalidKey")).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(); + String privKeyString = keyTF.getText(); + if (filterManager.canRemoveDevFilter(privKeyString)) { + filterManager.removeDevFilter(privKeyString); + hide(); + } else { + new Popup().warning(Res.get("shared.invalidKey")).onClose(this::blurAgain).show(); } }); @@ -215,13 +248,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 b581cf4009f..b90433bd8ed 100644 --- a/proto/src/main/proto/pb.proto +++ b/proto/src/main/proto/pb.proto @@ -638,6 +638,9 @@ message Filter { repeated string refundAgents = 18; repeated string bannedSignerPubKeys = 19; 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