mirror of
https://github.com/bisq-network/bisq.git
synced 2025-02-23 15:00:30 +01:00
Remove provider module after extracting to new repo
See bisq-network/pricenode#2
This commit is contained in:
parent
8075f9eef9
commit
c53695ae60
13 changed files with 0 additions and 1043 deletions
1
pom.xml
1
pom.xml
|
@ -45,7 +45,6 @@
|
|||
<module>gui</module>
|
||||
<module>seednode</module>
|
||||
<module>statistics</module>
|
||||
<module>provider</module>
|
||||
<module>consensus</module>
|
||||
<module>monitor</module>
|
||||
</modules>
|
||||
|
|
152
provider/pom.xml
152
provider/pom.xml
|
@ -1,152 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ This file is part of Bisq.
|
||||
~
|
||||
~ Bisq is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU Affero General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or (at
|
||||
~ your option) any later version.
|
||||
~
|
||||
~ Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
~ License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>parent</artifactId>
|
||||
<groupId>io.bisq</groupId>
|
||||
<version>0.6.4</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>provider</artifactId>
|
||||
|
||||
<build>
|
||||
|
||||
<resources>
|
||||
<resource>
|
||||
<filtering>false</filtering>
|
||||
<directory>${basedir}/src/main/java</directory>
|
||||
<includes>
|
||||
<include>**/*.fxml</include>
|
||||
<include>**/*.css</include>
|
||||
</includes>
|
||||
</resource>
|
||||
<resource>
|
||||
<filtering>false</filtering>
|
||||
<directory>${basedir}/src/main/resources</directory>
|
||||
<includes>
|
||||
<include>**/*.*</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
|
||||
<plugins>
|
||||
<!-- Bouncycastle jars are signed and cannot be placed inside shaded jar.
|
||||
we ship them beside our app in /lib -->
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<version>3.0.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-bouncycastle</id>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>copy</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<stripVersion>true</stripVersion>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk15on</artifactId>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
</artifactItem>
|
||||
<artifactItem>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpg-jdk15on</artifactId>
|
||||
<overWrite>true</overWrite>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.1.0</version>
|
||||
<configuration>
|
||||
<artifactSet>
|
||||
<excludes>
|
||||
<exclude>org.bouncycastle:*:*:*</exclude>
|
||||
</excludes>
|
||||
</artifactSet>
|
||||
<!-- broken with Java 8 (MSHADE-174), using ProGuard instead. -->
|
||||
<minimizeJar>false</minimizeJar>
|
||||
<transformers>
|
||||
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
|
||||
<manifestEntries>
|
||||
<Main-Class>io.bisq.provider.ProviderMain</Main-Class>
|
||||
<!-- the specified bouncy castle jar classes -->
|
||||
<Class-Path>lib/bcpg-jdk15on.jar lib/bcprov-jdk15on.jar</Class-Path>
|
||||
</manifestEntries>
|
||||
</transformer>
|
||||
</transformers>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
<filters>
|
||||
<filter>
|
||||
<!-- exclude signatures, the bundling process breaks them for some reason -->
|
||||
<artifact>*:*</artifact>
|
||||
<excludes>
|
||||
<exclude>META-INF/maven/**/pom.properties</exclude>
|
||||
<exclude>META-INF/*.SF</exclude>
|
||||
<exclude>META-INF/*.DSA</exclude>
|
||||
<exclude>META-INF/*.RSA</exclude>
|
||||
</excludes>
|
||||
</filter>
|
||||
</filters>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<phase>package</phase>
|
||||
<goals>
|
||||
<goal>shade</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<shadedArtifactAttached>true</shadedArtifactAttached>
|
||||
<shadedClassifierName>bundled</shadedClassifierName>
|
||||
<finalName>provider</finalName>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>io.bisq</groupId>
|
||||
<artifactId>core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.sparkjava</groupId>
|
||||
<artifactId>spark-core</artifactId>
|
||||
<version>2.5.2</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -1,130 +0,0 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.provider;
|
||||
|
||||
import ch.qos.logback.classic.Level;
|
||||
import io.bisq.common.app.Log;
|
||||
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;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
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;
|
||||
|
||||
public class ProviderMain {
|
||||
private static final Logger log = LoggerFactory.getLogger(ProviderMain.class);
|
||||
|
||||
static {
|
||||
// Need to set default locale initially otherwise we get problems at non-english OS
|
||||
Locale.setDefault(new Locale("en", Locale.getDefault().getCountry()));
|
||||
|
||||
Utilities.removeCryptographyRestrictions();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException, NoSuchAlgorithmException, NoSuchProviderException, InvalidKeySpecException, InvalidKeyException, HttpException {
|
||||
final String logPath = System.getProperty("user.home") + File.separator + "provider";
|
||||
Log.setup(logPath);
|
||||
Log.setLevel(Level.INFO);
|
||||
log.info("Log files under: " + logPath);
|
||||
log.info("ProviderVersion.VERSION: " + ProviderVersion.VERSION);
|
||||
log.info("Bisq exchange Version{" +
|
||||
"VERSION=" + Version.VERSION +
|
||||
", 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" +
|
||||
'}');
|
||||
Utilities.printSysInfo();
|
||||
|
||||
String bitcoinAveragePrivKey = null;
|
||||
String bitcoinAveragePubKey = null;
|
||||
int capacity = BtcFeesProvider.CAPACITY;
|
||||
int maxBlocks = BtcFeesProvider.MAX_BLOCKS;
|
||||
long requestIntervalInMs = TimeUnit.MINUTES.toMillis(FeeRequestService.REQUEST_INTERVAL_MIN);
|
||||
|
||||
// extract command line arguments
|
||||
if (args.length < 2) {
|
||||
log.error("You need to provide the BitcoinAverage API keys. Private key as first argument, public key as second argument.");
|
||||
System.exit(1);
|
||||
}
|
||||
if (args.length >= 2) {
|
||||
bitcoinAveragePrivKey = args[0];
|
||||
bitcoinAveragePubKey = args[1];
|
||||
}
|
||||
if (args.length >= 4) {
|
||||
capacity = Integer.valueOf(args[2]);
|
||||
maxBlocks = Integer.valueOf(args[3]);
|
||||
}
|
||||
if (args.length >= 5) {
|
||||
requestIntervalInMs = TimeUnit.MINUTES.toMillis(Long.valueOf(args[4]));
|
||||
}
|
||||
|
||||
port(8080);
|
||||
|
||||
handleGetAllMarketPrices(bitcoinAveragePrivKey, bitcoinAveragePubKey);
|
||||
handleGetFees(capacity, maxBlocks, requestIntervalInMs);
|
||||
handleGetVersion();
|
||||
handleGetParams(capacity, maxBlocks, requestIntervalInMs);
|
||||
}
|
||||
|
||||
private static void handleGetAllMarketPrices(String bitcoinAveragePrivKey, String bitcoinAveragePubKey)
|
||||
throws IOException, NoSuchAlgorithmException, InvalidKeyException {
|
||||
PriceRequestService priceRequestService = new PriceRequestService(bitcoinAveragePrivKey, bitcoinAveragePubKey);
|
||||
get("/getAllMarketPrices", (req, res) -> {
|
||||
log.info("Incoming getAllMarketPrices request from: " + req.userAgent());
|
||||
return priceRequestService.getJson();
|
||||
});
|
||||
}
|
||||
|
||||
private static void handleGetFees(int capacity, int maxBlocks, long requestIntervalInMs) throws IOException {
|
||||
FeeRequestService feeRequestService = new FeeRequestService(capacity, maxBlocks, requestIntervalInMs);
|
||||
get("/getFees", (req, res) -> {
|
||||
log.info("Incoming getFees request from: " + req.userAgent());
|
||||
return feeRequestService.getJson();
|
||||
});
|
||||
}
|
||||
|
||||
private static void handleGetVersion() throws IOException {
|
||||
get("/getVersion", (req, res) -> {
|
||||
log.info("Incoming getVersion request from: " + req.userAgent());
|
||||
return ProviderVersion.VERSION;
|
||||
});
|
||||
}
|
||||
|
||||
private static void handleGetParams(int capacity, int maxBlocks, long requestIntervalInMs) throws IOException {
|
||||
get("/getParams", (req, res) -> {
|
||||
log.info("Incoming getParams request from: " + req.userAgent());
|
||||
return capacity + ";" + maxBlocks + ";" + requestIntervalInMs;
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* This file is part of bisq.
|
||||
*
|
||||
* bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.provider;
|
||||
|
||||
public class ProviderVersion {
|
||||
public static final String VERSION = "0.6.4";
|
||||
}
|
|
@ -1,102 +0,0 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
@Slf4j
|
||||
public class FeeRequestService {
|
||||
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;
|
||||
|
||||
private final Timer timerBitcoinFeesLocal = new Timer();
|
||||
|
||||
private final BtcFeesProvider btcFeesProvider;
|
||||
private final Map<String, Long> dataMap = new ConcurrentHashMap<>();
|
||||
private long bitcoinFeesTs;
|
||||
private String json;
|
||||
|
||||
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
|
||||
dataMap.put("ltcTxFee", FeeService.LTC_DEFAULT_TX_FEE);
|
||||
dataMap.put("dogeTxFee", FeeService.DOGE_DEFAULT_TX_FEE);
|
||||
dataMap.put("dashTxFee", FeeService.DASH_DEFAULT_TX_FEE);
|
||||
|
||||
writeToJson();
|
||||
startRequests(requestIntervalInMs);
|
||||
}
|
||||
|
||||
private void startRequests(long requestIntervalInMs) throws IOException {
|
||||
timerBitcoinFeesLocal.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
requestBitcoinFees();
|
||||
} catch (IOException e) {
|
||||
log.warn(e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, requestIntervalInMs, requestIntervalInMs);
|
||||
|
||||
|
||||
requestBitcoinFees();
|
||||
}
|
||||
|
||||
private void requestBitcoinFees() throws IOException {
|
||||
long ts = System.currentTimeMillis();
|
||||
long btcFee = btcFeesProvider.getFee();
|
||||
log.info("requestBitcoinFees took {} ms.", (System.currentTimeMillis() - ts));
|
||||
if (btcFee < FeeRequestService.BTC_MIN_TX_FEE) {
|
||||
log.warn("Response for fee is lower as min fee. Fee=" + btcFee);
|
||||
} else if (btcFee > FeeRequestService.BTC_MAX_TX_FEE) {
|
||||
log.warn("Response for fee is larger as max fee. Fee=" + btcFee);
|
||||
} else {
|
||||
bitcoinFeesTs = Instant.now().getEpochSecond();
|
||||
dataMap.put("btcTxFee", btcFee);
|
||||
writeToJson();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeToJson() {
|
||||
Map<String, Object> map = new HashMap<>();
|
||||
map.put("bitcoinFeesTs", bitcoinFeesTs);
|
||||
map.put("dataMap", dataMap);
|
||||
json = Utilities.objectToJson(map);
|
||||
}
|
||||
|
||||
public String getJson() {
|
||||
return json;
|
||||
}
|
||||
}
|
|
@ -1,68 +0,0 @@
|
|||
package io.bisq.provider.fee.providers;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
import io.bisq.common.util.MathUtils;
|
||||
import io.bisq.network.http.HttpClient;
|
||||
import io.bisq.provider.fee.FeeRequestService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
|
||||
//TODO consider alternative https://www.bitgo.com/api/v1/tx/fee?numBlocks=3
|
||||
@Slf4j
|
||||
public class BtcFeesProvider {
|
||||
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(int capacity, int maxBlocks) {
|
||||
this.capacity = capacity;
|
||||
this.maxBlocks = maxBlocks;
|
||||
this.httpClient = new HttpClient("https://bitcoinfees.earn.com/api/v1/fees/");
|
||||
}
|
||||
|
||||
public Long getFee() throws IOException {
|
||||
// prev. used: https://bitcoinfees.earn.com/api/v1/fees/recommended
|
||||
// but was way too high
|
||||
|
||||
// https://bitcoinfees.earn.com/api/v1/fees/list
|
||||
String response = httpClient.requestWithGET("list", "User-Agent", "");
|
||||
log.info("Get recommended fee response: " + response);
|
||||
|
||||
LinkedTreeMap<String, ArrayList<LinkedTreeMap<String, Double>>> treeMap = new Gson().fromJson(response, LinkedTreeMap.class);
|
||||
final long[] fee = new long[1];
|
||||
// we want a fee which is at least in 20 blocks in (21.co estimation seem to be way too high, so we get
|
||||
// prob much faster in
|
||||
treeMap.entrySet().stream()
|
||||
.flatMap(e -> e.getValue().stream())
|
||||
.forEach(e -> {
|
||||
Double maxDelay = e.get("maxDelay");
|
||||
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);
|
||||
|
||||
return getAverage(fee[0]);
|
||||
}
|
||||
|
||||
// We take the average of the last 12 calls (every 5 minute) so we smooth extreme values.
|
||||
// We observed very radical jumps in the fee estimations, so that should help to avoid that.
|
||||
long getAverage(long newFee) {
|
||||
log.info("new fee " + newFee);
|
||||
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)
|
||||
fees.removeFirst();
|
||||
|
||||
return average;
|
||||
}
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.provider.price;
|
||||
|
||||
import lombok.Value;
|
||||
|
||||
@Value
|
||||
public class PriceData {
|
||||
private final String currencyCode;
|
||||
private final double price;
|
||||
private final long timestampSec;
|
||||
private final String provider;
|
||||
}
|
|
@ -1,240 +0,0 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.provider.price;
|
||||
|
||||
import io.bisq.common.util.Utilities;
|
||||
import io.bisq.provider.price.providers.BtcAverageProvider;
|
||||
import io.bisq.provider.price.providers.CoinmarketcapProvider;
|
||||
import io.bisq.provider.price.providers.PoloniexProvider;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Instant;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
public class PriceRequestService {
|
||||
public static final String POLO_PROVIDER = "POLO";
|
||||
public static final String COINMKTC_PROVIDER = "CMC";
|
||||
public static final String BTCAVERAGE_LOCAL_PROVIDER = "BTCA_L";
|
||||
public static final String BTCAVERAGE_GLOBAL_PROVIDER = "BTCA_G";
|
||||
|
||||
// We adjust request time to fit into BitcoinAverage developer plan (45k request per month).
|
||||
// We get 42514 (29760+12754) request with below numbers.
|
||||
private static final long INTERVAL_BTC_AV_LOCAL_MS = 90_000; // 90 sec; 29760 requests for 31 days
|
||||
private static final long INTERVAL_BTC_AV_GLOBAL_MS = 210_000; // 3.5 min; 12754 requests for 31 days
|
||||
|
||||
private static final long INTERVAL_POLONIEX_MS = 60_000; // 1 min
|
||||
private static final long INTERVAL_COIN_MARKET_CAP_MS = 300_000; // 5 min that data structure is quite heavy so we don't request too often.
|
||||
private static final long MARKET_PRICE_TTL_SEC = 1800; // 30 min
|
||||
|
||||
private final Timer timerBtcAverageLocal = new Timer();
|
||||
private final Timer timerBtcAverageGlobal = new Timer();
|
||||
private final Timer timerPoloniex = new Timer();
|
||||
private final Timer timerCoinmarketcap = new Timer();
|
||||
|
||||
private final BtcAverageProvider btcAverageProvider;
|
||||
private final PoloniexProvider poloniexProvider;
|
||||
private final CoinmarketcapProvider coinmarketcapProvider;
|
||||
|
||||
private final Map<String, PriceData> allPricesMap = new ConcurrentHashMap<>();
|
||||
private Map<String, PriceData> btcAverageLocalMap;
|
||||
private Map<String, PriceData> poloniexMap;
|
||||
|
||||
private long btcAverageTs;
|
||||
private long poloniexTs;
|
||||
private long coinmarketcapTs;
|
||||
private long btcAverageLCount;
|
||||
private long btcAverageGCount;
|
||||
private long poloniexCount;
|
||||
private long coinmarketcapCount;
|
||||
|
||||
private String json;
|
||||
|
||||
public PriceRequestService(String bitcoinAveragePrivKey, String bitcoinAveragePubKey) throws IOException, NoSuchAlgorithmException, InvalidKeyException {
|
||||
btcAverageProvider = new BtcAverageProvider(bitcoinAveragePrivKey, bitcoinAveragePubKey);
|
||||
poloniexProvider = new PoloniexProvider();
|
||||
coinmarketcapProvider = new CoinmarketcapProvider();
|
||||
|
||||
startRequests();
|
||||
}
|
||||
|
||||
public String getJson() {
|
||||
return json;
|
||||
}
|
||||
|
||||
private void startRequests() throws InvalidKeyException, NoSuchAlgorithmException, IOException {
|
||||
timerBtcAverageLocal.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
requestBtcAverageLocalPrices();
|
||||
} catch (Throwable e) {
|
||||
log.warn(e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, INTERVAL_BTC_AV_LOCAL_MS, INTERVAL_BTC_AV_LOCAL_MS);
|
||||
|
||||
timerBtcAverageGlobal.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
requestBtcAverageGlobalPrices();
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
log.error(e.toString());
|
||||
e.printStackTrace();
|
||||
} catch (IOException e) {
|
||||
log.warn(e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, INTERVAL_BTC_AV_GLOBAL_MS, INTERVAL_BTC_AV_GLOBAL_MS);
|
||||
|
||||
timerPoloniex.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
requestPoloniexPrices();
|
||||
} catch (IOException e) {
|
||||
log.warn(e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, INTERVAL_POLONIEX_MS, INTERVAL_POLONIEX_MS);
|
||||
|
||||
timerCoinmarketcap.scheduleAtFixedRate(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
requestCoinmarketcapPrices();
|
||||
} catch (IOException e) {
|
||||
log.warn(e.toString());
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}, INTERVAL_COIN_MARKET_CAP_MS, INTERVAL_COIN_MARKET_CAP_MS);
|
||||
|
||||
requestBtcAverageLocalPrices();
|
||||
requestBtcAverageGlobalPrices();
|
||||
requestPoloniexPrices();
|
||||
requestCoinmarketcapPrices();
|
||||
}
|
||||
|
||||
|
||||
private void requestCoinmarketcapPrices() throws IOException {
|
||||
long ts = System.currentTimeMillis();
|
||||
Map<String, PriceData> map = coinmarketcapProvider.request();
|
||||
log.info("requestCoinmarketcapPrices took {} ms.", (System.currentTimeMillis() - ts));
|
||||
removeOutdatedPrices(poloniexMap);
|
||||
removeOutdatedPrices(allPricesMap);
|
||||
// we don't replace prices which we got form the Poloniex request, just in case the Coinmarketcap data are
|
||||
// received earlier at startup we allow them but Poloniex will overwrite them.
|
||||
map.entrySet().stream()
|
||||
.filter(e -> poloniexMap == null || !poloniexMap.containsKey(e.getKey()))
|
||||
.forEach(e -> allPricesMap.put(e.getKey(), e.getValue()));
|
||||
coinmarketcapTs = Instant.now().getEpochSecond();
|
||||
coinmarketcapCount = map.size();
|
||||
|
||||
if (map.get("LTC") != null)
|
||||
log.info("Coinmarketcap LTC (last): " + map.get("LTC").getPrice());
|
||||
|
||||
writeToJson();
|
||||
}
|
||||
|
||||
|
||||
private void requestPoloniexPrices() throws IOException {
|
||||
long ts = System.currentTimeMillis();
|
||||
poloniexMap = poloniexProvider.request();
|
||||
log.info("requestPoloniexPrices took {} ms.", (System.currentTimeMillis() - ts));
|
||||
removeOutdatedPrices(allPricesMap);
|
||||
allPricesMap.putAll(poloniexMap);
|
||||
poloniexTs = Instant.now().getEpochSecond();
|
||||
poloniexCount = poloniexMap.size();
|
||||
|
||||
if (poloniexMap.get("LTC") != null)
|
||||
log.info("Poloniex LTC (last): " + poloniexMap.get("LTC").getPrice());
|
||||
|
||||
writeToJson();
|
||||
}
|
||||
|
||||
private void requestBtcAverageLocalPrices() throws NoSuchAlgorithmException, InvalidKeyException, IOException {
|
||||
long ts = System.currentTimeMillis();
|
||||
btcAverageLocalMap = btcAverageProvider.getLocal();
|
||||
|
||||
if (btcAverageLocalMap.get("USD") != null)
|
||||
log.info("BTCAverage local USD (last):" + btcAverageLocalMap.get("USD").getPrice());
|
||||
log.info("requestBtcAverageLocalPrices took {} ms.", (System.currentTimeMillis() - ts));
|
||||
|
||||
removeOutdatedPrices(allPricesMap);
|
||||
allPricesMap.putAll(btcAverageLocalMap);
|
||||
btcAverageTs = Instant.now().getEpochSecond();
|
||||
btcAverageLCount = btcAverageLocalMap.size();
|
||||
writeToJson();
|
||||
}
|
||||
|
||||
private void requestBtcAverageGlobalPrices() throws NoSuchAlgorithmException, InvalidKeyException, IOException {
|
||||
long ts = System.currentTimeMillis();
|
||||
Map<String, PriceData> map = btcAverageProvider.getGlobal();
|
||||
|
||||
if (map.get("USD") != null)
|
||||
log.info("BTCAverage global USD (last):" + map.get("USD").getPrice());
|
||||
log.info("requestBtcAverageGlobalPrices took {} ms.", (System.currentTimeMillis() - ts));
|
||||
|
||||
removeOutdatedPrices(btcAverageLocalMap);
|
||||
removeOutdatedPrices(allPricesMap);
|
||||
// we don't replace prices which we got form the local request, just in case the global data are received
|
||||
// earlier at startup we allow them but the local request will overwrite them.
|
||||
map.entrySet().stream()
|
||||
.filter(e -> btcAverageLocalMap == null || !btcAverageLocalMap.containsKey(e.getKey()))
|
||||
.forEach(e -> allPricesMap.put(e.getKey(), e.getValue()));
|
||||
btcAverageTs = Instant.now().getEpochSecond();
|
||||
btcAverageGCount = map.size();
|
||||
writeToJson();
|
||||
}
|
||||
|
||||
private void writeToJson() {
|
||||
Map<String, Object> map = new LinkedHashMap<>();
|
||||
map.put("btcAverageTs", btcAverageTs);
|
||||
map.put("poloniexTs", poloniexTs);
|
||||
map.put("coinmarketcapTs", coinmarketcapTs);
|
||||
map.put("btcAverageLCount", btcAverageLCount);
|
||||
map.put("btcAverageGCount", btcAverageGCount);
|
||||
map.put("poloniexCount", poloniexCount);
|
||||
map.put("coinmarketcapCount", coinmarketcapCount);
|
||||
map.put("data", allPricesMap.values().toArray());
|
||||
json = Utilities.objectToJson(map);
|
||||
}
|
||||
|
||||
private void removeOutdatedPrices(Map<String, PriceData> map) {
|
||||
long now = Instant.now().getEpochSecond();
|
||||
long limit = now - MARKET_PRICE_TTL_SEC;
|
||||
Map<String, PriceData> filtered = map.entrySet().stream()
|
||||
.filter(e -> e.getValue().getTimestampSec() > limit)
|
||||
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
map.clear();
|
||||
map.putAll(filtered);
|
||||
}
|
||||
}
|
|
@ -1,101 +0,0 @@
|
|||
/*
|
||||
* This file is part of Bisq.
|
||||
*
|
||||
* Bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.provider.price.providers;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
import io.bisq.network.http.HttpClient;
|
||||
import io.bisq.provider.price.PriceData;
|
||||
import io.bisq.provider.price.PriceRequestService;
|
||||
import org.bouncycastle.util.encoders.Hex;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.io.IOException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class BtcAverageProvider {
|
||||
private static final Logger log = LoggerFactory.getLogger(BtcAverageProvider.class);
|
||||
|
||||
private final HttpClient httpClient;
|
||||
private final String pubKey;
|
||||
private final SecretKey secretKey;
|
||||
|
||||
public BtcAverageProvider(String privKey, String pubKey) {
|
||||
this.httpClient = new HttpClient("https://apiv2.bitcoinaverage.com/");
|
||||
this.pubKey = pubKey;
|
||||
this.secretKey = new SecretKeySpec(privKey.getBytes(), "HmacSHA256");
|
||||
}
|
||||
|
||||
private String getHeader() throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
String payload = Instant.now().getEpochSecond() + "." + pubKey;
|
||||
Mac mac = Mac.getInstance("HmacSHA256");
|
||||
mac.init(secretKey);
|
||||
return payload + "." + Hex.toHexString(mac.doFinal(payload.getBytes()));
|
||||
}
|
||||
|
||||
public Map<String, PriceData> getLocal() throws NoSuchAlgorithmException, InvalidKeyException, IOException {
|
||||
return getMap(httpClient.requestWithGETNoProxy("indices/local/ticker/all?crypto=BTC", "X-signature", getHeader()), PriceRequestService.BTCAVERAGE_LOCAL_PROVIDER);
|
||||
}
|
||||
|
||||
public Map<String, PriceData> getGlobal() throws NoSuchAlgorithmException, InvalidKeyException, IOException {
|
||||
return getMap(httpClient.requestWithGETNoProxy("indices/global/ticker/all?crypto=BTC", "X-signature", getHeader()), PriceRequestService.BTCAVERAGE_GLOBAL_PROVIDER);
|
||||
}
|
||||
|
||||
private Map<String, PriceData> getMap(String json, String provider) {
|
||||
Map<String, PriceData> marketPriceMap = new HashMap<>();
|
||||
LinkedTreeMap<String, Object> treeMap = new Gson().<LinkedTreeMap<String, Object>>fromJson(json, LinkedTreeMap.class);
|
||||
long ts = Instant.now().getEpochSecond();
|
||||
treeMap.entrySet().stream().forEach(e -> {
|
||||
Object value = e.getValue();
|
||||
// We need to check the type as we get an unexpected "timestamp" object at the end:
|
||||
if (value instanceof LinkedTreeMap) {
|
||||
//noinspection unchecked
|
||||
LinkedTreeMap<String, Object> data = (LinkedTreeMap) value;
|
||||
String currencyCode = e.getKey().substring(3);
|
||||
// We ignore venezuelan currency as the official exchange rate is wishful thinking only....
|
||||
// We should use that api with a custom provider: http://api.bitcoinvenezuela.com/1
|
||||
if (!("VEF".equals(currencyCode))) {
|
||||
try {
|
||||
final Object lastAsObject = data.get("last");
|
||||
double last = 0;
|
||||
if (lastAsObject instanceof String)
|
||||
last = Double.valueOf((String) lastAsObject);
|
||||
else if (lastAsObject instanceof Double)
|
||||
last = (double) lastAsObject;
|
||||
else
|
||||
log.warn("Unexpected data type: lastAsObject=" + lastAsObject);
|
||||
|
||||
marketPriceMap.put(currencyCode,
|
||||
new PriceData(currencyCode, last, ts, provider));
|
||||
} catch (Throwable exception) {
|
||||
log.error("Error converting btcaverage data: " + currencyCode, exception);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return marketPriceMap;
|
||||
}
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
package io.bisq.provider.price.providers;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
import io.bisq.common.locale.CurrencyUtil;
|
||||
import io.bisq.common.locale.TradeCurrency;
|
||||
import io.bisq.network.http.HttpClient;
|
||||
import io.bisq.provider.price.PriceData;
|
||||
import io.bisq.provider.price.PriceRequestService;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.lang.Double.parseDouble;
|
||||
|
||||
public class CoinmarketcapProvider {
|
||||
private final Set<String> supportedAltcoins;
|
||||
|
||||
private final HttpClient httpClient;
|
||||
|
||||
public CoinmarketcapProvider() {
|
||||
this.httpClient = new HttpClient("https://api.coinmarketcap.com/");
|
||||
supportedAltcoins = CurrencyUtil.getAllSortedCryptoCurrencies().stream()
|
||||
.map(TradeCurrency::getCode)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Map<String, PriceData> request() throws IOException {
|
||||
Map<String, PriceData> marketPriceMap = new HashMap<>();
|
||||
String response = httpClient.requestWithGET("v1/ticker/?limit=200", "User-Agent", "");
|
||||
//noinspection unchecked
|
||||
List<LinkedTreeMap<String, Object>> list = new Gson().fromJson(response, ArrayList.class);
|
||||
long ts = Instant.now().getEpochSecond();
|
||||
list.stream().forEach(treeMap -> {
|
||||
String code = (String) treeMap.get("symbol");
|
||||
if (supportedAltcoins.contains(code)) {
|
||||
double price_btc = parseDouble((String) treeMap.get("price_btc"));
|
||||
marketPriceMap.put(code, new PriceData(code, price_btc, ts, PriceRequestService.COINMKTC_PROVIDER));
|
||||
}
|
||||
});
|
||||
return marketPriceMap;
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
package io.bisq.provider.price.providers;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.internal.LinkedTreeMap;
|
||||
import io.bisq.common.locale.CurrencyUtil;
|
||||
import io.bisq.common.locale.TradeCurrency;
|
||||
import io.bisq.network.http.HttpClient;
|
||||
import io.bisq.provider.price.PriceData;
|
||||
import io.bisq.provider.price.PriceRequestService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.Instant;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static java.lang.Double.parseDouble;
|
||||
|
||||
public class PoloniexProvider {
|
||||
private static final Logger log = LoggerFactory.getLogger(PoloniexProvider.class);
|
||||
|
||||
private final Set<String> supportedAltcoins;
|
||||
private final HttpClient httpClient;
|
||||
|
||||
public PoloniexProvider() {
|
||||
this.httpClient = new HttpClient("https://poloniex.com/public");
|
||||
|
||||
supportedAltcoins = CurrencyUtil.getAllSortedCryptoCurrencies().stream()
|
||||
.map(TradeCurrency::getCode)
|
||||
.collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public Map<String, PriceData> request() throws IOException {
|
||||
Map<String, PriceData> marketPriceMap = new HashMap<>();
|
||||
String response = httpClient.requestWithGET("?command=returnTicker", "User-Agent", "");
|
||||
//noinspection unchecked
|
||||
LinkedTreeMap<String, Object> treeMap = new Gson().fromJson(response, LinkedTreeMap.class);
|
||||
long ts = Instant.now().getEpochSecond();
|
||||
treeMap.entrySet().stream().forEach(e -> {
|
||||
Object value = e.getValue();
|
||||
String invertedCurrencyPair = e.getKey();
|
||||
String altcoinCurrency = null;
|
||||
if (invertedCurrencyPair.startsWith("BTC")) {
|
||||
String[] tokens = invertedCurrencyPair.split("_");
|
||||
if (tokens.length == 2) {
|
||||
altcoinCurrency = tokens[1];
|
||||
if (supportedAltcoins.contains(altcoinCurrency)) {
|
||||
if (value instanceof LinkedTreeMap) {
|
||||
//noinspection unchecked
|
||||
LinkedTreeMap<String, Object> data = (LinkedTreeMap) value;
|
||||
marketPriceMap.put(altcoinCurrency,
|
||||
new PriceData(altcoinCurrency,
|
||||
parseDouble((String) data.get("last")),
|
||||
ts,
|
||||
PriceRequestService.POLO_PROVIDER)
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.error("invertedCurrencyPair has invalid format: invertedCurrencyPair=" + invertedCurrencyPair);
|
||||
}
|
||||
}
|
||||
});
|
||||
return marketPriceMap;
|
||||
}
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
~ This file is part of Bisq.
|
||||
~
|
||||
~ Bisq is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU Affero General Public License as published by
|
||||
~ the Free Software Foundation, either version 3 of the License, or (at
|
||||
~ your option) any later version.
|
||||
~
|
||||
~ Bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
~ License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU Affero General Public License
|
||||
~ along with Bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
-->
|
||||
|
||||
<configuration>
|
||||
<appender name="CONSOLE_APPENDER" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>%highlight(%d{MMM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{15}: %msg %xEx%n)</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<root level="INFO">
|
||||
<appender-ref ref="CONSOLE_APPENDER"/>
|
||||
</root>
|
||||
|
||||
</configuration>
|
|
@ -1,55 +0,0 @@
|
|||
/*
|
||||
* This file is part of bisq.
|
||||
*
|
||||
* bisq is free software: you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or (at
|
||||
* your option) any later version.
|
||||
*
|
||||
* bisq is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||
* License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with bisq. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package io.bisq.provider.fee.providers;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class BtcFeesProviderTest {
|
||||
|
||||
@Test
|
||||
public void testGetAverage() {
|
||||
BtcFeesProvider btcFeesProvider = new BtcFeesProvider(BtcFeesProvider.CAPACITY, BtcFeesProvider.MAX_BLOCKS);
|
||||
|
||||
assertEquals(0, btcFeesProvider.getAverage(0));
|
||||
|
||||
btcFeesProvider = new BtcFeesProvider(BtcFeesProvider.CAPACITY, BtcFeesProvider.MAX_BLOCKS);
|
||||
assertEquals(1, btcFeesProvider.getAverage(1));
|
||||
|
||||
btcFeesProvider = new BtcFeesProvider(BtcFeesProvider.CAPACITY, BtcFeesProvider.MAX_BLOCKS);
|
||||
assertEquals(0, btcFeesProvider.getAverage(0));
|
||||
assertEquals(0, btcFeesProvider.getAverage(1));
|
||||
|
||||
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.CAPACITY, BtcFeesProvider.MAX_BLOCKS);
|
||||
assertEquals(10, btcFeesProvider.getAverage(10));
|
||||
assertEquals(15, btcFeesProvider.getAverage(20));
|
||||
assertEquals(20, btcFeesProvider.getAverage(30));
|
||||
assertEquals(20, btcFeesProvider.getAverage(20));
|
||||
assertEquals(20, btcFeesProvider.getAverage(20)); //5th
|
||||
|
||||
assertEquals(22, btcFeesProvider.getAverage(20)); // first removed
|
||||
assertEquals(28, btcFeesProvider.getAverage(50)); // second removed
|
||||
assertEquals(30, btcFeesProvider.getAverage(40)); // third removed
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue