diff --git a/build.gradle b/build.gradle index 1a5e2ef66c..a2a1c3164b 100644 --- a/build.gradle +++ b/build.gradle @@ -469,7 +469,6 @@ configure(project(':pricenode')) { exclude(module: 'commons-codec') } compile("org.knowm.xchange:xchange-bitbay:$knowmXchangeVersion") - compile("org.knowm.xchange:xchange-bitcoinaverage:$knowmXchangeVersion") compile("org.knowm.xchange:xchange-btcmarkets:$knowmXchangeVersion") compile("org.knowm.xchange:xchange-binance:$knowmXchangeVersion") compile("org.knowm.xchange:xchange-bitfinex:$knowmXchangeVersion") diff --git a/pricenode/README-HEROKU.md b/pricenode/README-HEROKU.md index 19365dc9ef..ab6081cd96 100644 --- a/pricenode/README-HEROKU.md +++ b/pricenode/README-HEROKU.md @@ -5,7 +5,6 @@ Run the following commands: heroku create heroku buildpacks:add heroku/gradle - heroku config:set BITCOIN_AVG_PUBKEY=[your pubkey] BITCOIN_AVG_PRIVKEY=[your privkey] git push heroku master curl https://your-app-123456.herokuapp.com/getAllMarketPrices diff --git a/pricenode/README.md b/pricenode/README.md index 55165678d3..d71dcda6e6 100644 --- a/pricenode/README.md +++ b/pricenode/README.md @@ -24,7 +24,6 @@ Operating a production pricenode is a valuable service to the Bisq network, and To run a pricenode, you will need: - - [BitcoinAverage API keys](https://bitcoinaverage.com/en/plans). Free plans are fine for local development or personal nodes; paid plans should be used for well-known production nodes. - JDK 8 if you want to build and run a node locally. - The `tor` binary (e.g. `brew install tor`) if you want to run a hidden service locally. @@ -40,21 +39,6 @@ curl -s https://raw.githubusercontent.com/bisq-network/bisq/master/pricenode/ins At the end of the installer script, it should print your Tor onion hostname. -### Setting your BitcoinAverage API keys - -Open `/etc/default/bisq-pricenode.env` in a text editor and look for these lines: -```bash -BITCOIN_AVG_PUBKEY=foo -BITCOIN_AVG_PRIVKEY=bar -``` - -Add your pubkey and privkey and then reload/restart bisq-pricenode service: - -```bash -systemctl daemon-reload -systemctl restart bisq-pricenode -``` - ### Test To manually test endpoints, run each of the following: diff --git a/pricenode/bisq-pricenode.env b/pricenode/bisq-pricenode.env index 56a5fe3453..a70fa924c0 100644 --- a/pricenode/bisq-pricenode.env +++ b/pricenode/bisq-pricenode.env @@ -1,3 +1 @@ -BITCOIN_AVG_PUBKEY=foo -BITCOIN_AVG_PRIVKEY=bar JAVA_OPTS="" diff --git a/pricenode/docker/loop.sh b/pricenode/docker/loop.sh index 1720f6eed9..1382fbb13c 100644 --- a/pricenode/docker/loop.sh +++ b/pricenode/docker/loop.sh @@ -2,7 +2,7 @@ while true do echo `date` "(Re)-starting node" -BITCOIN_AVG_PUBKEY=$BTCAVERAGE_PUBKEY BITCOIN_AVG_PRIVKEY=$BTCAVERAGE_PRIVKEY java -jar ./build/libs/bisq-pricenode.jar 2 2 +java -jar ./build/libs/bisq-pricenode.jar 2 2 echo `date` "node terminated unexpectedly!!" sleep 3 done diff --git a/pricenode/src/main/java/bisq/price/spot/ExchangeRateService.java b/pricenode/src/main/java/bisq/price/spot/ExchangeRateService.java index f7bc35f891..81cefd266b 100644 --- a/pricenode/src/main/java/bisq/price/spot/ExchangeRateService.java +++ b/pricenode/src/main/java/bisq/price/spot/ExchangeRateService.java @@ -17,8 +17,6 @@ package bisq.price.spot; -import bisq.price.spot.providers.BitcoinAverage; - import org.springframework.stereotype.Service; import java.math.BigDecimal; @@ -167,10 +165,6 @@ class ExchangeRateService { t.printStackTrace(); } - if (provider instanceof BitcoinAverage.Local) { - metadata.put("btcAverageTs", timestamp); - } - String prefix = provider.getPrefix(); metadata.put(prefix + "Ts", timestamp); metadata.put(prefix + "Count", exchangeRates.size()); diff --git a/pricenode/src/main/java/bisq/price/spot/providers/BitcoinAverage.java b/pricenode/src/main/java/bisq/price/spot/providers/BitcoinAverage.java index d8150b5a10..b5b04ad09e 100644 --- a/pricenode/src/main/java/bisq/price/spot/providers/BitcoinAverage.java +++ b/pricenode/src/main/java/bisq/price/spot/providers/BitcoinAverage.java @@ -19,141 +19,38 @@ package bisq.price.spot.providers; import bisq.price.spot.ExchangeRate; import bisq.price.spot.ExchangeRateProvider; -import bisq.common.util.Hex; -import org.knowm.xchange.bitcoinaverage.dto.marketdata.BitcoinAverageTicker; -import org.knowm.xchange.bitcoinaverage.dto.marketdata.BitcoinAverageTickers; - -import org.springframework.core.annotation.Order; -import org.springframework.core.env.Environment; -import org.springframework.http.RequestEntity; import org.springframework.stereotype.Component; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.UriComponentsBuilder; - -import com.google.common.base.Charsets; - -import javax.crypto.Mac; -import javax.crypto.SecretKey; -import javax.crypto.spec.SecretKeySpec; - -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; import java.time.Duration; -import java.time.Instant; -import java.util.Map; +import java.util.HashSet; import java.util.Set; -import java.util.stream.Collectors; /** - * See the BitcoinAverage API documentation at https://apiv2.bitcoinaverage.com/#ticker-data-all + * Stub implementation (similar to #CoinMarketCap) for backward compatibility with legacy + * Bisq clients */ -public abstract class BitcoinAverage extends ExchangeRateProvider { +@Component +class BitcoinAverage extends ExchangeRateProvider { - /** - * Max number of requests allowed per month on the BitcoinAverage developer plan. - * Note the actual max value is 45,000; we use the more conservative value below to - * ensure we do not exceed it. See https://bitcoinaverage.com/en/plans. - */ - private static final double MAX_REQUESTS_PER_MONTH = 42_514; - - private final RestTemplate restTemplate = new RestTemplate(); - private final String symbolSet; - - private String pubKey; - private Mac mac; - - /** - * @param symbolSet "global" or "local"; see https://apiv2.bitcoinaverage.com/#supported-currencies - */ - public BitcoinAverage(String name, String prefix, double pctMaxRequests, String symbolSet, Environment env) { - super(name, prefix, refreshIntervalFor(pctMaxRequests)); - this.symbolSet = symbolSet; - this.pubKey = env.getRequiredProperty("BITCOIN_AVG_PUBKEY"); - this.mac = initMac(env.getRequiredProperty("BITCOIN_AVG_PRIVKEY")); + public BitcoinAverage() { + // Simulate a deactivated BitcoinAverage provider + // We still need the class to exist and be registered as a provider though, + // because the returned data structure must contain the "btcAverageTs" key + // for backward compatibility with Bisq clients which hardcode that key + super("BA", "btcAverage", Duration.ofMinutes(100)); } + /** + * @see CoinMarketCap#doGet() + * @return + */ @Override public Set doGet() { - return getTickersKeyedByCurrency().entrySet().stream() - .filter(e -> supportedCurrency(e.getKey())) - .map(e -> - new ExchangeRate( - e.getKey(), - e.getValue().getLast(), - e.getValue().getTimestamp(), - this.getName() - ) - ) - .collect(Collectors.toSet()); - } - - private boolean supportedCurrency(String currencyCode) { - // ignore Venezuelan bolivars as the "official" exchange rate is just wishful thinking - // we should use this API with a custom provider instead: http://api.bitcoinvenezuela.com/1 - return !"VEF".equals(currencyCode); - } - - private Map getTickersKeyedByCurrency() { - // go from a map with keys like "BTCUSD", "BTCVEF" - return getTickersKeyedByCurrencyPair().entrySet().stream() - // to a map with keys like "USD", "VEF" - .collect(Collectors.toMap(e -> e.getKey().substring(3), Map.Entry::getValue)); - } - - private Map getTickersKeyedByCurrencyPair() { - return restTemplate.exchange( - RequestEntity - .get(UriComponentsBuilder - .fromUriString("https://apiv2.bitcoinaverage.com/indices/{symbol-set}/ticker/all?crypto=BTC") - .buildAndExpand(symbolSet) - .toUri()) - .header("X-signature", getAuthSignature()) - .build(), - BitcoinAverageTickers.class - ).getBody().getTickers(); - } - - protected String getAuthSignature() { - String payload = String.format("%s.%s", Instant.now().getEpochSecond(), pubKey); - return String.format("%s.%s", payload, Hex.encode(mac.doFinal(payload.getBytes(Charsets.UTF_8)))); - } - - private static Mac initMac(String privKey) { - String algorithm = "HmacSHA256"; - SecretKey secretKey = new SecretKeySpec(privKey.getBytes(Charsets.UTF_8), algorithm); - try { - Mac mac = Mac.getInstance(algorithm); - mac.init(secretKey); - return mac; - } catch (NoSuchAlgorithmException | InvalidKeyException ex) { - throw new RuntimeException(ex); - } - } - - private static Duration refreshIntervalFor(double pctMaxRequests) { - long requestsPerMonth = (long) (MAX_REQUESTS_PER_MONTH * pctMaxRequests); - return Duration.ofDays(31).dividedBy(requestsPerMonth); - } - - - @Component - @Order(1) - public static class Global extends BitcoinAverage { - public Global(Environment env) { - super("BTCA_G", "btcAverageG", 0.3, "global", env); - } - } - - - @Component - @Order(2) - public static class Local extends BitcoinAverage { - public Local(Environment env) { - super("BTCA_L", "btcAverageL", 0.7, "local", env); - } + HashSet exchangeRates = new HashSet<>(); + exchangeRates.add(new ExchangeRate("NON_EXISTING_SYMBOL_BA", 0, 0L, getName())); + return exchangeRates; } }