Disable BitcoinAverage

Disable BitcoinAverage provider. Keep it registered as a provider to
ensure that the data structure returned by the pricenode to the Bisq
clients contain the hardcoded "btcAverageTs" key.
This commit is contained in:
cd2357 2020-07-12 16:31:43 +02:00
parent 8d335441c3
commit 4dc24e5606
No known key found for this signature in database
GPG key ID: F26C56748514D0D3
7 changed files with 19 additions and 148 deletions

View file

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

View file

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

View file

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

View file

@ -1,3 +1 @@
BITCOIN_AVG_PUBKEY=foo
BITCOIN_AVG_PRIVKEY=bar
JAVA_OPTS=""

View file

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

View file

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

View file

@ -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<ExchangeRate> 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<String, BitcoinAverageTicker> 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<String, BitcoinAverageTicker> 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<ExchangeRate> exchangeRates = new HashSet<>();
exchangeRates.add(new ExchangeRate("NON_EXISTING_SYMBOL_BA", 0, 0L, getName()));
return exchangeRates;
}
}