diff --git a/common/src/main/proto/pb.proto b/common/src/main/proto/pb.proto index d4cee48b25..2a6a53c265 100644 --- a/common/src/main/proto/pb.proto +++ b/common/src/main/proto/pb.proto @@ -566,6 +566,7 @@ message Filter { repeated string seed_nodes = 10; repeated string price_relay_nodes = 11; bool prevent_public_btc_network = 12; + repeated string btc_nodes = 13; } // not used anymore from v0.6 on. But leave it for receiving TradeStatistics objects from older diff --git a/common/src/main/resources/i18n/displayStrings.properties b/common/src/main/resources/i18n/displayStrings.properties index f32245a307..4e423f5d4a 100644 --- a/common/src/main/resources/i18n/displayStrings.properties +++ b/common/src/main/resources/i18n/displayStrings.properties @@ -1187,6 +1187,7 @@ filterWindow.bannedPaymentMethods=Filtered payment method IDs (comma sep.): filterWindow.arbitrators=Filtered arbitrators (comma sep. onion addresses): filterWindow.seedNode=Filtered seed nodes (comma sep. onion addresses): filterWindow.priceRelayNode=Filtered price relay nodes (comma sep. onion addresses): +filterWindow.btcNode=Filtered Bitcoin nodes (comma sep. addresses + port): filterWindow.preventPublicBtcNetwork=Prevent usage of public Bitcoin network: filterWindow.add=Add filter filterWindow.remove=Remove filter diff --git a/core/src/main/java/io/bisq/core/app/BisqEnvironment.java b/core/src/main/java/io/bisq/core/app/BisqEnvironment.java index bb7a7569cd..d59caf003e 100644 --- a/core/src/main/java/io/bisq/core/app/BisqEnvironment.java +++ b/core/src/main/java/io/bisq/core/app/BisqEnvironment.java @@ -175,9 +175,7 @@ public class BisqEnvironment extends StandardEnvironment { @Setter private boolean isBitcoinLocalhostNodeRunning; @Getter - private List bannedPriceRelayNodes; - @Getter - private List bannedSeedNodes; + private List bannedSeedNodes, bannedBtcNodes, bannedPriceRelayNodes; private final String btcNodes, seedNodes, ignoreDevMsg, useTorForBtc, rpcUser, rpcPassword, rpcPort, rpcBlockNotificationPort, dumpBlockchainData, fullDaoNode, @@ -281,6 +279,9 @@ public class BisqEnvironment extends StandardEnvironment { final String bannedSeedNodesAsString = getProperty(FilterManager.BANNED_SEED_NODES, ""); bannedSeedNodes = !bannedSeedNodesAsString.isEmpty() ? Arrays.asList(StringUtils.deleteWhitespace(bannedSeedNodesAsString).split(",")) : null; + final String bannedBtcNodesAsString = getProperty(FilterManager.BANNED_BTC_NODES, ""); + bannedBtcNodes = !bannedBtcNodesAsString.isEmpty() ? Arrays.asList(StringUtils.deleteWhitespace(bannedBtcNodesAsString).split(",")) : null; + baseCurrencyNetwork = BaseCurrencyNetwork.valueOf(getProperty(BtcOptionKeys.BASE_CURRENCY_NETWORK, getDefaultBaseCurrencyNetwork().name()).toUpperCase()); @@ -306,6 +307,10 @@ public class BisqEnvironment extends StandardEnvironment { setProperty(FilterManager.BANNED_SEED_NODES, bannedNodes == null ? "" : String.join(",", bannedNodes)); } + public void saveBannedBtcNodes(@Nullable List bannedNodes) { + setProperty(FilterManager.BANNED_BTC_NODES, bannedNodes == null ? "" : String.join(",", bannedNodes)); + } + public void saveBannedPriceRelayNodes(@Nullable List bannedNodes) { setProperty(FilterManager.BANNED_PRICE_RELAY_NODES, bannedNodes == null ? "" : String.join(",", bannedNodes)); } diff --git a/core/src/main/java/io/bisq/core/btc/BitcoinNodes.java b/core/src/main/java/io/bisq/core/btc/BitcoinNodes.java index 0dc6b95aee..1bbadade18 100644 --- a/core/src/main/java/io/bisq/core/btc/BitcoinNodes.java +++ b/core/src/main/java/io/bisq/core/btc/BitcoinNodes.java @@ -20,44 +20,67 @@ package io.bisq.core.btc; import io.bisq.core.app.BisqEnvironment; import lombok.Getter; import lombok.ToString; +import lombok.extern.slf4j.Slf4j; import javax.annotation.Nullable; -import java.util.ArrayList; +import javax.inject.Inject; import java.util.Arrays; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; // Managed here: https://github.com/bisq-network/roles/issues/39 +@Slf4j public class BitcoinNodes { + private final List bannedNodes; + public enum BitcoinNodesOption { PROVIDED, CUSTOM, PUBLIC } + @Inject + public BitcoinNodes(BisqEnvironment bisqEnvironment) { + bannedNodes = bisqEnvironment.getBannedBtcNodes(); + } + // For other base currencies or testnet we ignore provided nodes - public List getProvidedBtcNodes() { - return useProvidedBtcNodes() ? - Arrays.asList( + public Set getProvidedBtcNodes() { + Set btcNodes = useProvidedBtcNodes() ? + new HashSet<>(Arrays.asList( BtcNode.fromHostNameAddressAndPort("kirsche.emzy.de", "78.47.61.83", BtcNode.DEFAULT_PORT, "https://github.com/emzy"), - BtcNode.fromOnion("poyvpdt762gllauu.onion", BtcNode.DEFAULT_PORT, "https://github.com/emzy"), + BtcNode.fromHostName("poyvpdt762gllauu.onion", BtcNode.DEFAULT_PORT, "https://github.com/emzy"), BtcNode.fromHostNameAndAddress("bitcoin.christophatteneder.com", "174.138.35.229", "https://github.com/ripcurlx"), - BtcNode.fromOnion("r3dsojfhwcm7x7p6.onion", BtcNode.DEFAULT_PORT, "https://github.com/emzy"), - BtcNode.fromOnion("vlf5i3grro3wux24.onion", BtcNode.DEFAULT_PORT, "https://github.com/alexej996"), - BtcNode.fromOnion("3r44ddzjitznyahw.onion", BtcNode.DEFAULT_PORT, "https://github.com/sqrrm"), - BtcNode.fromOnion("i3a5xtzfm4xwtybd.onion", BtcNode.DEFAULT_PORT, "https://github.com/sqrrm"), + BtcNode.fromHostName("r3dsojfhwcm7x7p6.onion", BtcNode.DEFAULT_PORT, "https://github.com/emzy"), + BtcNode.fromHostName("vlf5i3grro3wux24.onion", BtcNode.DEFAULT_PORT, "https://github.com/alexej996"), + BtcNode.fromHostName("3r44ddzjitznyahw.onion", BtcNode.DEFAULT_PORT, "https://github.com/sqrrm"), + BtcNode.fromHostName("i3a5xtzfm4xwtybd.onion", BtcNode.DEFAULT_PORT, "https://github.com/sqrrm"), BtcNode.fromHostNameAddressAndPort("bitcoin4-fullnode.csg.uzh.ch", "192.41.136.217", BtcNode.DEFAULT_PORT, "https://github.com/tbocek"), BtcNode.fromHostNameAddressAndPort("bcwat.ch", "5.189.166.193", BtcNode.DEFAULT_PORT, "https://github.com/sgeisler"), BtcNode.fromHostNameAddressAndPort("btc.jochen-hoenicke.de", "37.221.198.57", BtcNode.DEFAULT_PORT, "https://github.com/jhoenicke"), // BtcNode.fromHostNameAddressAndPort("btc.vante.me", "138.68.117.247", BtcNode.DEFAULT_PORT, "https://github.com/mrosseel"), - BtcNode.fromOnion("mxdtrjhe2yfsx3pg.onion", BtcNode.DEFAULT_PORT, "https://github.com/mrosseel"), + BtcNode.fromHostName("mxdtrjhe2yfsx3pg.onion", BtcNode.DEFAULT_PORT, "https://github.com/mrosseel"), BtcNode.fromAddressAndPort("62.75.210.81", BtcNode.DEFAULT_PORT, "https://github.com/emzy"), BtcNode.fromAddressAndPort("163.172.171.119", BtcNode.DEFAULT_PORT, "https://github.com/emzy") - ) : - new ArrayList<>(); + )) : + new HashSet<>(); + + btcNodes = btcNodes.stream() + .filter(e -> bannedNodes == null || !bannedNodes.contains(e.getAddressOrHostWithPort())) + .collect(Collectors.toSet()); + + if (bannedNodes == null) + log.info("btcNodes={}", btcNodes); + else + log.warn("We received banned btc nodes={}, btcNodes={}", bannedNodes, btcNodes); + + return btcNodes; } public boolean useProvidedBtcNodes() { @@ -85,14 +108,14 @@ public class BitcoinNodes { String[] parts = fullAddress.split(":"); checkArgument(parts.length > 0); if (parts.length == 1) { - return BtcNode.fromOnion(parts[0], DEFAULT_PORT, null); + return BtcNode.fromHostName(parts[0], DEFAULT_PORT, null); } else { checkArgument(parts.length == 2); return BtcNode.fromHostNameAndPort(parts[0], Integer.valueOf(parts[1]), null); } } - public static BtcNode fromOnion(String hostName, int port, @Nullable String operator) { + public static BtcNode fromHostName(String hostName, int port, @Nullable String operator) { return new BtcNode(hostName, null, port, operator); } @@ -149,5 +172,10 @@ public class BitcoinNodes { else return hostName; } + + public String getAddressOrHostWithPort() { + log.error(getHostAddressOrHostName() + ":" + port); + return getHostAddressOrHostName() + ":" + port; + } } } diff --git a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java index 5d1fcaf8fd..497810aba7 100644 --- a/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java +++ b/core/src/main/java/io/bisq/core/btc/wallet/WalletsSetup.java @@ -57,10 +57,7 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.UnknownHostException; import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Set; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.stream.Collectors; @@ -269,17 +266,17 @@ public class WalletsSetup { walletConfig.connectToLocalHost(); } } else { - List btcNodeList = new ArrayList<>(); + Set btcNodes = new HashSet<>(); switch (BitcoinNodes.BitcoinNodesOption.values()[preferences.getBitcoinNodesOptionOrdinal()]) { case CUSTOM: String bitcoinNodesString = preferences.getBitcoinNodes(); if (bitcoinNodesString != null) { - btcNodeList = Splitter.on(",") + btcNodes = Splitter.on(",") .splitToList(StringUtils.deleteWhitespace(bitcoinNodesString)) .stream() .filter(e -> !e.isEmpty()) .map(BitcoinNodes.BtcNode::fromFullAddress) - .collect(Collectors.toList()); + .collect(Collectors.toSet()); } break; case PUBLIC: @@ -287,13 +284,13 @@ public class WalletsSetup { break; default: case PROVIDED: - btcNodeList = bitcoinNodes.getProvidedBtcNodes(); + btcNodes = bitcoinNodes.getProvidedBtcNodes(); break; } final boolean useTorForBitcoinJ = socks5Proxy != null; List peerAddressList = new ArrayList<>(); - btcNodeList.forEach(btcNode -> { + btcNodes.forEach(btcNode -> { if (useTorForBitcoinJ) { if (btcNode.isHiddenService()) { // no DNS lookup for onion addresses diff --git a/core/src/main/java/io/bisq/core/filter/Filter.java b/core/src/main/java/io/bisq/core/filter/Filter.java index 853cfa8a44..61de2761be 100644 --- a/core/src/main/java/io/bisq/core/filter/Filter.java +++ b/core/src/main/java/io/bisq/core/filter/Filter.java @@ -53,7 +53,7 @@ public final class Filter implements ProtectedStoragePayload { @Nullable private final List bannedPaymentMethods; - // added in v0.6 + // added in v0.6.0 @Nullable private final List arbitrators; @Nullable @@ -62,6 +62,11 @@ public final class Filter implements ProtectedStoragePayload { 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 @@ -79,7 +84,8 @@ public final class Filter implements ProtectedStoragePayload { @Nullable List arbitrators, @Nullable List seedNodes, @Nullable List priceRelayNodes, - boolean preventPublicBtcNetwork) { + boolean preventPublicBtcNetwork, + @Nullable List btcNodes) { this.bannedOfferIds = bannedOfferIds; this.bannedNodeAddress = bannedNodeAddress; this.bannedPaymentAccounts = bannedPaymentAccounts; @@ -89,6 +95,7 @@ public final class Filter implements ProtectedStoragePayload { this.seedNodes = seedNodes; this.priceRelayNodes = priceRelayNodes; this.preventPublicBtcNetwork = preventPublicBtcNetwork; + this.btcNodes = btcNodes; } @@ -106,6 +113,7 @@ public final class Filter implements ProtectedStoragePayload { @Nullable List seedNodes, @Nullable List priceRelayNodes, boolean preventPublicBtcNetwork, + @Nullable List btcNodes, String signatureAsBase64, byte[] ownerPubKeyBytes, @Nullable Map extraDataMap) { @@ -117,7 +125,8 @@ public final class Filter implements ProtectedStoragePayload { arbitrators, seedNodes, priceRelayNodes, - preventPublicBtcNetwork); + preventPublicBtcNetwork, + btcNodes); this.signatureAsBase64 = signatureAsBase64; this.ownerPubKeyBytes = ownerPubKeyBytes; this.extraDataMap = extraDataMap; @@ -145,6 +154,7 @@ public final class Filter implements ProtectedStoragePayload { 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(extraDataMap).ifPresent(builder::putAllExtraData); return PB.StoragePayload.newBuilder().setFilter(builder).build(); @@ -162,6 +172,7 @@ public final class Filter implements ProtectedStoragePayload { CollectionUtils.isEmpty(proto.getSeedNodesList()) ? null : proto.getSeedNodesList().stream().collect(Collectors.toList()), CollectionUtils.isEmpty(proto.getPriceRelayNodesList()) ? null : proto.getPriceRelayNodesList().stream().collect(Collectors.toList()), proto.getPreventPublicBtcNetwork(), + CollectionUtils.isEmpty(proto.getBtcNodesList()) ? null : proto.getBtcNodesList().stream().collect(Collectors.toList()), proto.getSignatureAsBase64(), proto.getOwnerPubKeyBytes().toByteArray(), CollectionUtils.isEmpty(proto.getExtraDataMap()) ? null : proto.getExtraDataMap()); diff --git a/core/src/main/java/io/bisq/core/filter/FilterManager.java b/core/src/main/java/io/bisq/core/filter/FilterManager.java index d9c4c7b9ab..707ebc5fe6 100644 --- a/core/src/main/java/io/bisq/core/filter/FilterManager.java +++ b/core/src/main/java/io/bisq/core/filter/FilterManager.java @@ -58,6 +58,7 @@ public class FilterManager { 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"; /////////////////////////////////////////////////////////////////////////////////////////// @@ -131,7 +132,7 @@ public class FilterManager { Filter filter = (Filter) data.getProtectedStoragePayload(); if (verifySignature(filter)) { bisqEnvironment.saveBannedSeedNodes(null); - + bisqEnvironment.saveBannedBtcNodes(null); bisqEnvironment.saveBannedPriceRelayNodes(null); providersRepository.applyBannedNodes(null); providersRepository.selectNewRandomBaseUrl(); @@ -150,6 +151,7 @@ public class FilterManager { // 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. bisqEnvironment.saveBannedSeedNodes(filter.getSeedNodes()); + bisqEnvironment.saveBannedBtcNodes(filter.getBtcNodes()); // Banned price relay nodes we can apply at runtime final List priceRelayNodes = filter.getPriceRelayNodes(); diff --git a/core/src/main/java/io/bisq/core/provider/ProvidersRepository.java b/core/src/main/java/io/bisq/core/provider/ProvidersRepository.java index a3c0931231..01975a287a 100644 --- a/core/src/main/java/io/bisq/core/provider/ProvidersRepository.java +++ b/core/src/main/java/io/bisq/core/provider/ProvidersRepository.java @@ -37,6 +37,8 @@ public class ProvidersRepository { "http://ceaanhbvluug4we6.onion/, " +// @mrosseel "http://rb2l2qale2pqzjyo.onion/"; // @sqrrm + // Old nodes before v 0.6.0: "http://44mgyoe2b6oqiytt.onion/, http://5bmpx76qllutpcyp.onion/"; @ManfredKarrer + private final String providersFromProgramArgs; private final boolean useLocalhostForP2P; diff --git a/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java b/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java index da9b9e5fa4..01da002925 100644 --- a/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java +++ b/core/src/main/java/io/bisq/core/provider/price/PriceFeedService.java @@ -239,7 +239,7 @@ public class PriceFeedService { } } else { - String errorMessage = "We don't have a price for " + currencyCode; + String errorMessage = "We don't have a price for " + currencyCode + ". priceProvider=" + priceProvider; log.debug(errorMessage); faultHandler.handleFault(errorMessage, new PriceRequestException(errorMessage)); } @@ -307,6 +307,7 @@ public class PriceFeedService { @Override public void onFailure(@NotNull Throwable throwable) { + log.error("Could not load marketPrices. Error: " + throwable.toString()); UserThread.execute(() -> faultHandler.handleFault("Could not load marketPrices", throwable)); } }); diff --git a/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java b/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java index fe2260d689..a7d6690a63 100644 --- a/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java +++ b/core/src/main/java/io/bisq/core/provider/price/PriceProvider.java @@ -67,9 +67,4 @@ public class PriceProvider extends HttpClientProvider { }); return new Tuple2<>(tsMap, marketPriceMap); } - - @Override - public String toString() { - return "PriceProvider"; - } } diff --git a/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java b/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java index 0afb7c4ca6..01e65bff70 100644 --- a/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java +++ b/core/src/test/java/io/bisq/core/user/UserPayloadModelVOTest.java @@ -26,13 +26,10 @@ import org.junit.Ignore; */ @SuppressWarnings("UnusedAssignment") public class UserPayloadModelVOTest { - - @Ignore("TODO InvalidKeySpecException at io.bisq.common.crypto.Sig.getPublicKeyFromBytes(Sig.java:135)") public void testRoundtrip() { UserPayload vo = new UserPayload(); vo.setAccountId("accountId"); - UserPayload newVo = UserPayload.fromProto(vo.toProtoMessage().getUserPayload(), new CoreProtoResolver()); } @@ -43,7 +40,7 @@ public class UserPayloadModelVOTest { vo.setDisplayedAlert(new Alert("message", true, "version", new byte[]{12, -64, 12}, "string", null)); vo.setDevelopersFilter(new Filter(Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), Lists.newArrayList(), - false, "string", new byte[]{10, 0, 0}, null)); + false, Lists.newArrayList(), "string", new byte[]{10, 0, 0}, null)); vo.setRegisteredArbitrator(ArbitratorTest.getArbitratorMock()); vo.setRegisteredMediator(MediatorTest.getMediatorMock()); vo.setAcceptedArbitrators(Lists.newArrayList(ArbitratorTest.getArbitratorMock())); diff --git a/gui/src/main/java/io/bisq/gui/main/overlays/windows/FilterWindow.java b/gui/src/main/java/io/bisq/gui/main/overlays/windows/FilterWindow.java index 1532c6654e..736f24f6d9 100644 --- a/gui/src/main/java/io/bisq/gui/main/overlays/windows/FilterWindow.java +++ b/gui/src/main/java/io/bisq/gui/main/overlays/windows/FilterWindow.java @@ -127,6 +127,7 @@ public class FilterWindow extends Overlay { InputTextField arbitratorsInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.arbitrators")).second; InputTextField seedNodesInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.seedNode")).second; InputTextField priceRelayNodesInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.priceRelayNode")).second; + InputTextField btcNodesInputTextField = addLabelInputTextField(gridPane, ++rowIndex, Res.get("filterWindow.btcNode")).second; CheckBox preventPublicBtcNetworkCheckBox = addLabelCheckBox(gridPane, ++rowIndex, Res.get("filterWindow.preventPublicBtcNetwork")).second; // TODO add BTC full nodes @@ -164,6 +165,9 @@ public class FilterWindow extends Overlay { if (filter.getPriceRelayNodes() != null) priceRelayNodesInputTextField.setText(filter.getPriceRelayNodes().stream().collect(Collectors.joining(", "))); + if (filter.getBtcNodes() != null) + btcNodesInputTextField.setText(filter.getBtcNodes().stream().collect(Collectors.joining(", "))); + preventPublicBtcNetworkCheckBox.setSelected(filter.isPreventPublicBtcNetwork()); } @@ -177,6 +181,7 @@ public class FilterWindow extends Overlay { List arbitrators = new ArrayList<>(); List seedNodes = new ArrayList<>(); List priceRelayNodes = new ArrayList<>(); + List btcNodes = new ArrayList<>(); if (!offerIdsInputTextField.getText().isEmpty()) { offerIds = new ArrayList<>(Arrays.asList(StringUtils.deleteWhitespace(offerIdsInputTextField.getText()) @@ -235,6 +240,11 @@ public class FilterWindow extends Overlay { .split(","))); } + if (!btcNodesInputTextField.getText().isEmpty()) { + btcNodes = new ArrayList<>(Arrays.asList(StringUtils.deleteWhitespace(btcNodesInputTextField.getText()) + .split(","))); + } + if (sendFilterMessageHandler.handle(new Filter(offerIds, nodes, paymentAccountFilters, @@ -243,7 +253,8 @@ public class FilterWindow extends Overlay { arbitrators, seedNodes, priceRelayNodes, - preventPublicBtcNetworkCheckBox.isSelected()), + preventPublicBtcNetworkCheckBox.isSelected(), + btcNodes), keyInputTextField.getText())) hide(); else diff --git a/provider/src/main/java/io/bisq/provider/ProviderMain.java b/provider/src/main/java/io/bisq/provider/ProviderMain.java index 00fb13da6e..c1c3d66b8b 100644 --- a/provider/src/main/java/io/bisq/provider/ProviderMain.java +++ b/provider/src/main/java/io/bisq/provider/ProviderMain.java @@ -23,6 +23,7 @@ import io.bisq.common.app.Version; import io.bisq.common.util.Utilities; import io.bisq.network.http.HttpException; import io.bisq.provider.fee.FeeRequestService; +import io.bisq.provider.fee.providers.BtcFeesProvider; import io.bisq.provider.price.PriceRequestService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -34,6 +35,7 @@ import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.spec.InvalidKeySpecException; import java.util.Locale; +import java.util.concurrent.TimeUnit; import static spark.Spark.get; import static spark.Spark.port; @@ -59,20 +61,20 @@ public class ProviderMain { ", P2P_NETWORK_VERSION=" + Version.P2P_NETWORK_VERSION + ", LOCAL_DB_VERSION=" + Version.LOCAL_DB_VERSION + ", TRADE_PROTOCOL_VERSION=" + Version.TRADE_PROTOCOL_VERSION + - ", BASE_CURRENCY_NETWORK=NOT SET" + - ", getP2PNetworkId()=NOT SET" + + ", BASE_CURRENCY_NETWORK=NOT SET" + + ", getP2PNetworkId()=NOT SET" + '}'); Utilities.printSysInfo(); port(8080); handleGetAllMarketPrices(args); - handleGetFees(); + handleGetFees(args); handleGetVersion(); } private static void handleGetAllMarketPrices(String[] args) throws IOException, NoSuchAlgorithmException, InvalidKeyException { - if (args.length == 2) { + if (args.length >= 2) { String bitcoinAveragePrivKey = args[0]; String bitcoinAveragePubKey = args[1]; @@ -86,8 +88,19 @@ public class ProviderMain { } } - private static void handleGetFees() throws IOException { - FeeRequestService feeRequestService = new FeeRequestService(); + private static void handleGetFees(String[] args) throws IOException { + int capacity = BtcFeesProvider.CAPACITY; + int maxBlocks = BtcFeesProvider.MAX_BLOCKS; + if (args.length >= 4) { + capacity = Integer.valueOf(args[2]); + maxBlocks = Integer.valueOf(args[3]); + } + + long requestIntervalInMs = TimeUnit.MINUTES.toMillis(FeeRequestService.REQUEST_INTERVAL_MIN); + if (args.length >= 5) + requestIntervalInMs = TimeUnit.MINUTES.toMillis(Long.valueOf(args[4])); + + FeeRequestService feeRequestService = new FeeRequestService(capacity, maxBlocks, requestIntervalInMs); get("/getFees", (req, res) -> { log.info("Incoming getFees request from: " + req.userAgent()); return feeRequestService.getJson(); diff --git a/provider/src/main/java/io/bisq/provider/ProviderVersion.java b/provider/src/main/java/io/bisq/provider/ProviderVersion.java index d98901329f..f8c678ac8b 100644 --- a/provider/src/main/java/io/bisq/provider/ProviderVersion.java +++ b/provider/src/main/java/io/bisq/provider/ProviderVersion.java @@ -18,5 +18,5 @@ package io.bisq.provider; public class ProviderVersion { - public static final String VERSION = "0.6.1"; + public static final String VERSION = "0.6.2"; } diff --git a/provider/src/main/java/io/bisq/provider/fee/FeeRequestService.java b/provider/src/main/java/io/bisq/provider/fee/FeeRequestService.java index e2fa3c02cf..05c198a0e3 100644 --- a/provider/src/main/java/io/bisq/provider/fee/FeeRequestService.java +++ b/provider/src/main/java/io/bisq/provider/fee/FeeRequestService.java @@ -20,8 +20,7 @@ package io.bisq.provider.fee; import io.bisq.common.util.Utilities; import io.bisq.core.provider.fee.FeeService; import io.bisq.provider.fee.providers.BtcFeesProvider; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import lombok.extern.slf4j.Slf4j; import java.io.IOException; import java.time.Instant; @@ -30,13 +29,10 @@ import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -//TODO use protobuffer instead of json +@Slf4j public class FeeRequestService { - private static final Logger log = LoggerFactory.getLogger(FeeRequestService.class); - - private static final long INTERVAL_BTC_FEES_MS = TimeUnit.MINUTES.toMillis(5); + public static int REQUEST_INTERVAL_MIN = 5; public static final long BTC_MIN_TX_FEE = 10; // satoshi/byte public static final long BTC_MAX_TX_FEE = 1000; @@ -48,8 +44,8 @@ public class FeeRequestService { private long bitcoinFeesTs; private String json; - public FeeRequestService() throws IOException { - btcFeesProvider = new BtcFeesProvider(); + public FeeRequestService(int capacity, int maxBlocks, long requestIntervalInMs) throws IOException { + btcFeesProvider = new BtcFeesProvider(capacity, maxBlocks); // For now we don't need a fee estimation for LTC so we set it fixed, but we keep it in the provider to // be flexible if fee pressure grows on LTC @@ -58,10 +54,10 @@ public class FeeRequestService { dataMap.put("dashTxFee", FeeService.DASH_DEFAULT_TX_FEE); writeToJson(); - startRequests(); + startRequests(requestIntervalInMs); } - private void startRequests() throws IOException { + private void startRequests(long requestIntervalInMs) throws IOException { timerBitcoinFeesLocal.scheduleAtFixedRate(new TimerTask() { @Override public void run() { @@ -72,7 +68,7 @@ public class FeeRequestService { e.printStackTrace(); } } - }, INTERVAL_BTC_FEES_MS, INTERVAL_BTC_FEES_MS); + }, requestIntervalInMs, requestIntervalInMs); requestBitcoinFees(); diff --git a/provider/src/main/java/io/bisq/provider/fee/providers/BtcFeesProvider.java b/provider/src/main/java/io/bisq/provider/fee/providers/BtcFeesProvider.java index 82580a25f9..5c620f379b 100644 --- a/provider/src/main/java/io/bisq/provider/fee/providers/BtcFeesProvider.java +++ b/provider/src/main/java/io/bisq/provider/fee/providers/BtcFeesProvider.java @@ -14,14 +14,18 @@ import java.util.LinkedList; //TODO consider alternative https://www.bitgo.com/api/v1/tx/fee?numBlocks=3 @Slf4j public class BtcFeesProvider { - static int CAPACITY = 6; // we request each 5 min. so we take average of last 30 min. - static int MAX_BLOCKS = 15; + public static int CAPACITY = 4; // if we request each 5 min. we take average of last 20 min. + public static int MAX_BLOCKS = 10; private final HttpClient httpClient; LinkedList fees = new LinkedList<>(); + private final int capacity; + private final int maxBlocks; // other: https://estimatefee.com/n/2 - public BtcFeesProvider() { + public BtcFeesProvider(int capacity, int maxBlocks) { + this.capacity = capacity; + this.maxBlocks = maxBlocks; this.httpClient = new HttpClient("https://bitcoinfees.earn.com/api/v1/fees/"); } @@ -41,7 +45,7 @@ public class BtcFeesProvider { .flatMap(e -> e.getValue().stream()) .forEach(e -> { Double maxDelay = e.get("maxDelay"); - if (maxDelay <= MAX_BLOCKS && fee[0] == 0) + if (maxDelay <= maxBlocks && fee[0] == 0) fee[0] = MathUtils.roundDoubleToLong(e.get("maxFee")); }); fee[0] = Math.min(Math.max(fee[0], FeeRequestService.BTC_MIN_TX_FEE), FeeRequestService.BTC_MAX_TX_FEE); @@ -56,7 +60,7 @@ public class BtcFeesProvider { fees.add(newFee); long average = ((Double) fees.stream().mapToDouble(e -> e).average().getAsDouble()).longValue(); log.info("average of last {} calls: {}", fees.size(), average); - if (fees.size() == CAPACITY) + if (fees.size() == capacity) fees.removeFirst(); return average; diff --git a/provider/src/main/java/io/bisq/provider/price/PriceRequestService.java b/provider/src/main/java/io/bisq/provider/price/PriceRequestService.java index 77f8a054fb..27e3cdc540 100644 --- a/provider/src/main/java/io/bisq/provider/price/PriceRequestService.java +++ b/provider/src/main/java/io/bisq/provider/price/PriceRequestService.java @@ -84,10 +84,7 @@ public class PriceRequestService { public void run() { try { requestBtcAverageLocalPrices(); - } catch (NoSuchAlgorithmException | InvalidKeyException e) { - log.error(e.toString()); - e.printStackTrace(); - } catch (IOException e) { + } catch (Throwable e) { log.warn(e.toString()); e.printStackTrace(); } diff --git a/provider/src/test/java/io/bisq/provider/fee/providers/BtcFeesProviderTest.java b/provider/src/test/java/io/bisq/provider/fee/providers/BtcFeesProviderTest.java index a5d3cd4c3b..f289698969 100644 --- a/provider/src/test/java/io/bisq/provider/fee/providers/BtcFeesProviderTest.java +++ b/provider/src/test/java/io/bisq/provider/fee/providers/BtcFeesProviderTest.java @@ -25,23 +25,23 @@ public class BtcFeesProviderTest { @Test public void testGetAverage() { - BtcFeesProvider btcFeesProvider = new BtcFeesProvider(); + BtcFeesProvider btcFeesProvider = new BtcFeesProvider(BtcFeesProvider.CAPACITY, BtcFeesProvider.MAX_BLOCKS); assertEquals(0, btcFeesProvider.getAverage(0)); - btcFeesProvider = new BtcFeesProvider(); + btcFeesProvider = new BtcFeesProvider(BtcFeesProvider.CAPACITY, BtcFeesProvider.MAX_BLOCKS); assertEquals(1, btcFeesProvider.getAverage(1)); - btcFeesProvider = new BtcFeesProvider(); + btcFeesProvider = new BtcFeesProvider(BtcFeesProvider.CAPACITY, BtcFeesProvider.MAX_BLOCKS); assertEquals(0, btcFeesProvider.getAverage(0)); assertEquals(0, btcFeesProvider.getAverage(1)); - btcFeesProvider = new BtcFeesProvider(); + btcFeesProvider = new BtcFeesProvider(BtcFeesProvider.CAPACITY, BtcFeesProvider.MAX_BLOCKS); assertEquals(0, btcFeesProvider.getAverage(0)); assertEquals(1, btcFeesProvider.getAverage(2)); BtcFeesProvider.CAPACITY = 5; - btcFeesProvider = new BtcFeesProvider(); + btcFeesProvider = new BtcFeesProvider(BtcFeesProvider.CAPACITY, BtcFeesProvider.MAX_BLOCKS); assertEquals(10, btcFeesProvider.getAverage(10)); assertEquals(15, btcFeesProvider.getAverage(20)); assertEquals(20, btcFeesProvider.getAverage(30));