Merge branch 'Development' into dao

# Conflicts:
#	core/src/main/java/io/bisq/core/dao/blockchain/BsqNode.java
This commit is contained in:
Manfred Karrer 2017-12-08 11:17:47 -05:00
commit 0e16522307
No known key found for this signature in database
GPG key ID: 401250966A6B2C46
18 changed files with 135 additions and 74 deletions

View file

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

View file

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

View file

@ -175,9 +175,7 @@ public class BisqEnvironment extends StandardEnvironment {
@Setter
private boolean isBitcoinLocalhostNodeRunning;
@Getter
private List<String> bannedPriceRelayNodes;
@Getter
private List<String> bannedSeedNodes;
private List<String> 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<String> bannedNodes) {
setProperty(FilterManager.BANNED_BTC_NODES, bannedNodes == null ? "" : String.join(",", bannedNodes));
}
public void saveBannedPriceRelayNodes(@Nullable List<String> bannedNodes) {
setProperty(FilterManager.BANNED_PRICE_RELAY_NODES, bannedNodes == null ? "" : String.join(",", bannedNodes));
}

View file

@ -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<String> 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<BtcNode> getProvidedBtcNodes() {
return useProvidedBtcNodes() ?
Arrays.asList(
public Set<BtcNode> getProvidedBtcNodes() {
Set<BtcNode> 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;
}
}
}

View file

@ -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<BitcoinNodes.BtcNode> btcNodeList = new ArrayList<>();
Set<BitcoinNodes.BtcNode> 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<PeerAddress> peerAddressList = new ArrayList<>();
btcNodeList.forEach(btcNode -> {
btcNodes.forEach(btcNode -> {
if (useTorForBitcoinJ) {
if (btcNode.isHiddenService()) {
// no DNS lookup for onion addresses

View file

@ -53,7 +53,7 @@ public final class Filter implements ProtectedStoragePayload {
@Nullable
private final List<String> bannedPaymentMethods;
// added in v0.6
// added in v0.6.0
@Nullable
private final List<String> arbitrators;
@Nullable
@ -62,6 +62,11 @@ public final class Filter implements ProtectedStoragePayload {
private final List<String> priceRelayNodes;
private final boolean preventPublicBtcNetwork;
// added in v0.6.2
@Nullable
private final List<String> 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<String> arbitrators,
@Nullable List<String> seedNodes,
@Nullable List<String> priceRelayNodes,
boolean preventPublicBtcNetwork) {
boolean preventPublicBtcNetwork,
@Nullable List<String> 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<String> seedNodes,
@Nullable List<String> priceRelayNodes,
boolean preventPublicBtcNetwork,
@Nullable List<String> btcNodes,
String signatureAsBase64,
byte[] ownerPubKeyBytes,
@Nullable Map<String, String> 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());

View file

@ -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<String> priceRelayNodes = filter.getPriceRelayNodes();

View file

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

View file

@ -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));
}
});

View file

@ -67,9 +67,4 @@ public class PriceProvider extends HttpClientProvider {
});
return new Tuple2<>(tsMap, marketPriceMap);
}
@Override
public String toString() {
return "PriceProvider";
}
}

View file

@ -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()));

View file

@ -127,6 +127,7 @@ public class FilterWindow extends Overlay<FilterWindow> {
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<FilterWindow> {
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<FilterWindow> {
List<String> arbitrators = new ArrayList<>();
List<String> seedNodes = new ArrayList<>();
List<String> priceRelayNodes = new ArrayList<>();
List<String> 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<FilterWindow> {
.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<FilterWindow> {
arbitrators,
seedNodes,
priceRelayNodes,
preventPublicBtcNetworkCheckBox.isSelected()),
preventPublicBtcNetworkCheckBox.isSelected(),
btcNodes),
keyInputTextField.getText()))
hide();
else

View file

@ -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();

View file

@ -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";
}

View file

@ -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();

View file

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

View file

@ -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();
}

View file

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