Remove provider module after extracting to new repo

See bisq-network/pricenode#2
This commit is contained in:
Chris Beams 2018-01-24 02:50:09 +01:00
parent 8075f9eef9
commit c53695ae60
No known key found for this signature in database
GPG key ID: 3D214F8F5BC5ED73
13 changed files with 0 additions and 1043 deletions

View file

@ -45,7 +45,6 @@
<module>gui</module>
<module>seednode</module>
<module>statistics</module>
<module>provider</module>
<module>consensus</module>
<module>monitor</module>
</modules>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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