Merge pull request #4460 from chimp1984/refactor-filter

Refactor filter
This commit is contained in:
sqrrm 2020-09-01 19:48:38 +02:00 committed by GitHub
commit 7830dbf4e4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 723 additions and 452 deletions

View File

@ -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 class ProtoUtil {
public static <T> Iterable<T> collectionToProto(Collection<? extends Proto> collection, Function<? super Message, T> extra) {
return collection.stream().map(o -> extra.apply(o.toProtoMessage())).collect(Collectors.toList());
}
public static List<String> protocolStringListToList(ProtocolStringList protocolStringList) {
return CollectionUtils.isEmpty(protocolStringList) ? new ArrayList<>() : new ArrayList<>(protocolStringList);
}
}

View File

@ -180,7 +180,7 @@ public class SignedWitnessService {
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 @@ public class SignedWitnessService {
private boolean isValidSignerWitnessInternal(SignedWitness signedWitness,
long childSignedWitnessDateMillis,
Stack<P2PDataStorage.ByteArray> excludedPubKeys) {
if (filterManager.isSignerPubKeyBanned(Utils.HEX.encode(signedWitness.getWitnessOwnerPubKey()))) {
if (filterManager.isWitnessSignerPubKeyBanned(Utils.HEX.encode(signedWitness.getWitnessOwnerPubKey()))) {
return false;
}
if (!verifySignature(signedWitness)) {

View File

@ -713,13 +713,13 @@ public class AccountAgeWitnessService {
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;
}

View File

@ -21,8 +21,10 @@ import bisq.network.p2p.storage.payload.ExpirablePayload;
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 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<String> bannedOfferIds;
private final List<String> bannedNodeAddress;
private final List<PaymentAccountFilter> 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<String> bannedCurrencies;
@Nullable
private final List<String> bannedPaymentMethods;
// added in v0.6.0
@Nullable
private final List<String> arbitrators;
@Nullable
private final List<String> seedNodes;
@Nullable
private final List<String> priceRelayNodes;
private final boolean preventPublicBtcNetwork;
// added in v0.6.2
@Nullable
private final List<String> btcNodes;
// 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 final String signatureAsBase64;
// 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;
private final String disableTradeBelowVersion;
private final List<String> mediators;
private final List<String> refundAgents;
private final List<String> bannedAccountWitnessSignerPubKeys;
private final List<String> btcFeeReceiverAddresses;
private final long creationDate;
private final List<String> bannedPrivilegedDevPubKeys;
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.
@Nullable
private Map<String, String> extraDataMap;
private PublicKey ownerPubKey;
// added in v0.9.4
private final boolean disableDao;
private transient PublicKey ownerPubKey;
// added in v0.9.8
@Nullable
private final String disableDaoBelowVersion;
@Nullable
private final String disableTradeBelowVersion;
// added in v1.1.6
@Nullable
private final List<String> mediators;
// added in v1.2.0
@Nullable
private final List<String> refundAgents;
// added in v1.2.x
@Nullable
private final List<String> bannedSignerPubKeys;
// added in v1.3.2
@Nullable
private final List<String> btcFeeReceiverAddresses;
public Filter(List<String> bannedOfferIds,
List<String> bannedNodeAddress,
List<PaymentAccountFilter> bannedPaymentAccounts,
@Nullable List<String> bannedCurrencies,
@Nullable List<String> bannedPaymentMethods,
@Nullable List<String> arbitrators,
@Nullable List<String> seedNodes,
@Nullable List<String> priceRelayNodes,
boolean preventPublicBtcNetwork,
@Nullable List<String> btcNodes,
boolean disableDao,
@Nullable String disableDaoBelowVersion,
@Nullable String disableTradeBelowVersion,
@Nullable List<String> mediators,
@Nullable List<String> refundAgents,
@Nullable List<String> bannedSignerPubKeys,
@Nullable List<String> 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;
// 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());
}
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
@VisibleForTesting
public Filter(List<String> bannedOfferIds,
List<String> bannedNodeAddress,
List<PaymentAccountFilter> bannedPaymentAccounts,
@Nullable List<String> bannedCurrencies,
@Nullable List<String> bannedPaymentMethods,
@Nullable List<String> arbitrators,
@Nullable List<String> seedNodes,
@Nullable List<String> priceRelayNodes,
List<String> bannedCurrencies,
List<String> bannedPaymentMethods,
List<String> arbitrators,
List<String> seedNodes,
List<String> priceRelayNodes,
boolean preventPublicBtcNetwork,
@Nullable List<String> btcNodes,
List<String> btcNodes,
boolean disableDao,
@Nullable String disableDaoBelowVersion,
@Nullable String disableTradeBelowVersion,
String signatureAsBase64,
byte[] ownerPubKeyBytes,
@Nullable Map<String, String> extraDataMap,
@Nullable List<String> mediators,
@Nullable List<String> refundAgents,
@Nullable List<String> bannedSignerPubKeys,
@Nullable List<String> btcFeeReceiverAddresses) {
String disableDaoBelowVersion,
String disableTradeBelowVersion,
List<String> mediators,
List<String> refundAgents,
List<String> bannedAccountWitnessSignerPubKeys,
List<String> btcFeeReceiverAddresses,
PublicKey ownerPubKey,
String signerPubKeyAsHex,
List<String> bannedPrivilegedDevPubKeys) {
this(bannedOfferIds,
bannedNodeAddress,
bannedPaymentAccounts,
@ -186,73 +176,141 @@ public final class Filter implements ProtectedStoragePayload, ExpirablePayload {
disableTradeBelowVersion,
mediators,
refundAgents,
bannedSignerPubKeys,
btcFeeReceiverAddresses);
this.signatureAsBase64 = signatureAsBase64;
this.ownerPubKeyBytes = ownerPubKeyBytes;
this.extraDataMap = ExtraDataMapValidator.getValidatedExtraDataMap(extraDataMap);
bannedAccountWitnessSignerPubKeys,
btcFeeReceiverAddresses,
Sig.getPublicKeyBytes(ownerPubKey),
System.currentTimeMillis(),
null,
null,
signerPubKeyAsHex,
bannedPrivilegedDevPubKeys);
}
ownerPubKey = Sig.getPublicKeyFromBytes(ownerPubKeyBytes);
///////////////////////////////////////////////////////////////////////////////////////////
// PROTO BUFFER
///////////////////////////////////////////////////////////////////////////////////////////
@VisibleForTesting
public Filter(List<String> bannedOfferIds,
List<String> bannedNodeAddress,
List<PaymentAccountFilter> bannedPaymentAccounts,
List<String> bannedCurrencies,
List<String> bannedPaymentMethods,
List<String> arbitrators,
List<String> seedNodes,
List<String> priceRelayNodes,
boolean preventPublicBtcNetwork,
List<String> btcNodes,
boolean disableDao,
String disableDaoBelowVersion,
String disableTradeBelowVersion,
List<String> mediators,
List<String> refundAgents,
List<String> bannedAccountWitnessSignerPubKeys,
List<String> btcFeeReceiverAddresses,
byte[] ownerPubKeyBytes,
long creationDate,
@Nullable Map<String, String> extraDataMap,
@Nullable String signatureAsBase64,
String signerPubKeyAsHex,
List<String> 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);
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<protobuf.PaymentAccountFilter> 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);
.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(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);
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<PaymentAccountFilter> 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 final class Filter implements ProtectedStoragePayload, ExpirablePayload {
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 final class Filter implements ProtectedStoragePayload, ExpirablePayload {
",\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}";
}
}

View File

@ -29,9 +29,7 @@ import bisq.network.p2p.P2PService;
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.config.ConfigFileEditor;
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 javax.inject.Named;
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 class FilterManager {
private final Preferences preferences;
private final ConfigFileEditor configFileEditor;
private final ProvidersRepository providersRepository;
private boolean ignoreDevMsg;
private final boolean ignoreDevMsg;
private final ObjectProperty<Filter> filterProperty = new SimpleObjectProperty<>();
private final List<Listener> listeners = new CopyOnWriteArrayList<>();
private final String pubKeyAsHex;
private final List<String> publicKeys;
private ECKey filterSigningKey;
///////////////////////////////////////////////////////////////////////////////////////////
// Constructor, Initialization
// Constructor
///////////////////////////////////////////////////////////////////////////////////////////
@Inject
@ -118,48 +118,53 @@ public class FilterManager {
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<ProtectedStorageEntry> 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<ProtectedStorageEntry> protectedStorageEntries) {
protectedStorageEntries.forEach(protectedStorageEntry -> {
if (protectedStorageEntry.getProtectedStoragePayload() instanceof Filter) {
Filter filter = (Filter) protectedStorageEntry.getProtectedStoragePayload();
boolean wasValid = addFilter(filter);
if (!wasValid) {
UserThread.runAfter(() -> p2PService.getP2PDataStorage().removeInvalidProtectedStorageEntry(protectedStorageEntry), 1);
}
}
});
}
@Override
public void onRemoved(Collection<ProtectedStorageEntry> protectedStorageEntries) {
protectedStorageEntries.forEach(protectedStorageEntry -> {
if (protectedStorageEntry.getProtectedStoragePayload() instanceof Filter) {
Filter filter = (Filter) protectedStorageEntry.getProtectedStoragePayload();
if (verifySignature(filter) && getFilter().equals(filter))
resetFilters();
}
});
}
});
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<ProtectedStorageEntry> protectedStorageEntries) {
protectedStorageEntries.stream()
.filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof Filter)
.forEach(protectedStorageEntry -> {
Filter filter = (Filter) protectedStorageEntry.getProtectedStoragePayload();
onFilterAddedFromNetwork(filter);
});
}
@Override
public void onRemoved(Collection<ProtectedStorageEntry> protectedStorageEntries) {
protectedStorageEntries.stream()
.filter(protectedStorageEntry -> protectedStorageEntry.getProtectedStoragePayload() instanceof Filter)
.forEach(protectedStorageEntry -> {
Filter filter = (Filter) protectedStorageEntry.getProtectedStoragePayload();
onFilterRemovedFromNetwork(filter);
});
}
});
p2PService.addP2PServiceListener(new P2PServiceListener() {
@Override
public void onDataReceived() {
@ -175,12 +180,12 @@ public class FilterManager {
@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,54 +206,94 @@ public class FilterManager {
});
}
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);
}
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<String> 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 isPrivilegedDevPubKeyBanned(String pubKeyAsHex) {
Filter filter = getFilter();
if (filter == null) {
return false;
}
return filter.getBannedPrivilegedDevPubKeys().contains(pubKeyAsHex);
}
private void saveBannedNodes(String optionName, List<String> bannedNodes) {
if (bannedNodes != null)
configFileEditor.setOption(optionName, String.join(",", bannedNodes));
else
configFileEditor.clearOption(optionName);
public boolean canAddDevFilter(String privKeyString) {
if (privKeyString == null || privKeyString.isEmpty()) {
return false;
}
if (!isValidDevPrivilegeKey(privKeyString)) {
log.warn("Key in invalid");
return false;
}
ECKey ecKeyFromPrivate = toECKey(privKeyString);
String pubKeyAsHex = getPubKeyAsHex(ecKeyFromPrivate);
if (isPrivilegedDevPubKeyBanned(pubKeyAsHex)) {
log.warn("Pub key is banned.");
return false;
}
return true;
}
public String getSignerPubKeyAsHex(String privKeyString) {
ECKey ecKey = toECKey(privKeyString);
return getPubKeyAsHex(ecKey);
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
public void addDevFilter(Filter filterWithoutSig, String privKeyString) {
setFilterSigningKey(privKeyString);
String signatureAsBase64 = getSignature(filterWithoutSig);
Filter filterWithSig = Filter.cloneWithSig(filterWithoutSig, signatureAsBase64);
user.setDevelopersFilter(filterWithSig);
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 (!isValidDevPrivilegeKey(privKeyString)) {
log.warn("Key in invalid.");
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;
}
if (isPrivilegedDevPubKeyBanned(pubKeyAsHex)) {
log.warn("Pub key is banned.");
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
return;
}
if (p2PService.removeData(filterWithSig)) {
user.setDevelopersFilter(null);
} else {
log.warn("Removing dev filter from network failed");
}
}
public void addListener(Listener listener) {
listeners.add(listener);
@ -263,85 +308,15 @@ public class FilterManager {
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() {
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 class FilterManager {
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 class FilterManager {
});
}
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<String> 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<String> 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());
}
}

View File

@ -59,7 +59,7 @@ public class ApplyFilter extends TradeTask {
} 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());

View File

@ -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
@ -2449,7 +2449,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)

View File

@ -397,14 +397,14 @@ public class SignedWitnessServiceTest {
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 class SignedWitnessServiceTest {
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 class SignedWitnessServiceTest {
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));

View File

@ -218,8 +218,8 @@ public class AccountAgeWitnessServiceTest {
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);

View File

@ -53,13 +53,17 @@ public class UserPayloadModelVOTest {
false,
null,
null,
"string",
new byte[]{10, 0, 0},
Lists.newArrayList(),
Lists.newArrayList(),
Lists.newArrayList(),
Lists.newArrayList(),
null,
Lists.newArrayList(),
Lists.newArrayList(),
Lists.newArrayList(),
Lists.newArrayList()));
0,
null,
null,
null,
null));
vo.setRegisteredArbitrator(ArbitratorTest.getArbitratorMock());
vo.setRegisteredMediator(MediatorTest.getMediatorMock());
vo.setAcceptedArbitrators(Lists.newArrayList(ArbitratorTest.getArbitratorMock()));

View File

@ -22,6 +22,7 @@ import bisq.core.dao.governance.param.Param;
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 class FeeReceiverSelectorTest {
}
private static Filter filterWithReceivers(List<String> 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);
}
}

View File

@ -79,7 +79,7 @@ public class FilterWindow extends Overlay<FilterWindow> {
if (headLine == null)
headLine = Res.get("filterWindow.headline");
width = 968;
width = 1000;
createGridPane();
@ -87,7 +87,7 @@ public class FilterWindow extends Overlay<FilterWindow> {
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 @@ public class FilterWindow extends Overlay<FilterWindow> {
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 keyTF = addInputTextField(gridPane, ++rowIndex,
Res.get("shared.unlock"), 10);
if (useDevPrivilegeKeys) {
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 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"));
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;
final Filter filter = filterManager.getDevelopersFilter();
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 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())
)
hide();
else
new Popup().warning(Res.get("shared.invalidKey")).width(300).onClose(this::blurAgain).show();
});
Button removeFilterMessageButton = new AutoTooltipButton(Res.get("filterWindow.remove"));
removeFilterMessageButton.setDisable(filterManager.getDevFilter() == null);
Button sendButton = new AutoTooltipButton(Res.get("filterWindow.add"));
sendButton.setOnAction(e -> {
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")).onClose(this::blurAgain).show();
}
});
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 @@ public class FilterWindow extends Overlay<FilterWindow> {
private void setupFieldFromList(InputTextField field, List<String> values) {
if (values != null)
field.setText(values.stream().collect(Collectors.joining(", ")));
field.setText(String.join(", ", values));
}
private void setupFieldFromPaymentAccountFiltersList(InputTextField field, List<PaymentAccountFilter> 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())

View File

@ -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