mirror of
https://github.com/bisq-network/bisq.git
synced 2025-03-03 18:56:59 +01:00
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:
parent
8d335441c3
commit
4dc24e5606
7 changed files with 19 additions and 148 deletions
|
@ -469,7 +469,6 @@ configure(project(':pricenode')) {
|
||||||
exclude(module: 'commons-codec')
|
exclude(module: 'commons-codec')
|
||||||
}
|
}
|
||||||
compile("org.knowm.xchange:xchange-bitbay:$knowmXchangeVersion")
|
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-btcmarkets:$knowmXchangeVersion")
|
||||||
compile("org.knowm.xchange:xchange-binance:$knowmXchangeVersion")
|
compile("org.knowm.xchange:xchange-binance:$knowmXchangeVersion")
|
||||||
compile("org.knowm.xchange:xchange-bitfinex:$knowmXchangeVersion")
|
compile("org.knowm.xchange:xchange-bitfinex:$knowmXchangeVersion")
|
||||||
|
|
|
@ -5,7 +5,6 @@ Run the following commands:
|
||||||
|
|
||||||
heroku create
|
heroku create
|
||||||
heroku buildpacks:add heroku/gradle
|
heroku buildpacks:add heroku/gradle
|
||||||
heroku config:set BITCOIN_AVG_PUBKEY=[your pubkey] BITCOIN_AVG_PRIVKEY=[your privkey]
|
|
||||||
git push heroku master
|
git push heroku master
|
||||||
curl https://your-app-123456.herokuapp.com/getAllMarketPrices
|
curl https://your-app-123456.herokuapp.com/getAllMarketPrices
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,6 @@ Operating a production pricenode is a valuable service to the Bisq network, and
|
||||||
|
|
||||||
To run a pricenode, you will need:
|
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.
|
- 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.
|
- 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.
|
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
|
### Test
|
||||||
|
|
||||||
To manually test endpoints, run each of the following:
|
To manually test endpoints, run each of the following:
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
BITCOIN_AVG_PUBKEY=foo
|
|
||||||
BITCOIN_AVG_PRIVKEY=bar
|
|
||||||
JAVA_OPTS=""
|
JAVA_OPTS=""
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
while true
|
while true
|
||||||
do
|
do
|
||||||
echo `date` "(Re)-starting node"
|
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!!"
|
echo `date` "node terminated unexpectedly!!"
|
||||||
sleep 3
|
sleep 3
|
||||||
done
|
done
|
||||||
|
|
|
@ -17,8 +17,6 @@
|
||||||
|
|
||||||
package bisq.price.spot;
|
package bisq.price.spot;
|
||||||
|
|
||||||
import bisq.price.spot.providers.BitcoinAverage;
|
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
@ -167,10 +165,6 @@ class ExchangeRateService {
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (provider instanceof BitcoinAverage.Local) {
|
|
||||||
metadata.put("btcAverageTs", timestamp);
|
|
||||||
}
|
|
||||||
|
|
||||||
String prefix = provider.getPrefix();
|
String prefix = provider.getPrefix();
|
||||||
metadata.put(prefix + "Ts", timestamp);
|
metadata.put(prefix + "Ts", timestamp);
|
||||||
metadata.put(prefix + "Count", exchangeRates.size());
|
metadata.put(prefix + "Count", exchangeRates.size());
|
||||||
|
|
|
@ -19,141 +19,38 @@ package bisq.price.spot.providers;
|
||||||
|
|
||||||
import bisq.price.spot.ExchangeRate;
|
import bisq.price.spot.ExchangeRate;
|
||||||
import bisq.price.spot.ExchangeRateProvider;
|
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.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.Duration;
|
||||||
import java.time.Instant;
|
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
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 {
|
||||||
|
|
||||||
/**
|
public BitcoinAverage() {
|
||||||
* Max number of requests allowed per month on the BitcoinAverage developer plan.
|
// Simulate a deactivated BitcoinAverage provider
|
||||||
* Note the actual max value is 45,000; we use the more conservative value below to
|
// We still need the class to exist and be registered as a provider though,
|
||||||
* ensure we do not exceed it. See https://bitcoinaverage.com/en/plans.
|
// because the returned data structure must contain the "btcAverageTs" key
|
||||||
*/
|
// for backward compatibility with Bisq clients which hardcode that key
|
||||||
private static final double MAX_REQUESTS_PER_MONTH = 42_514;
|
super("BA", "btcAverage", Duration.ofMinutes(100));
|
||||||
|
|
||||||
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"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see CoinMarketCap#doGet()
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
@Override
|
@Override
|
||||||
public Set<ExchangeRate> doGet() {
|
public Set<ExchangeRate> doGet() {
|
||||||
|
|
||||||
return getTickersKeyedByCurrency().entrySet().stream()
|
HashSet<ExchangeRate> exchangeRates = new HashSet<>();
|
||||||
.filter(e -> supportedCurrency(e.getKey()))
|
exchangeRates.add(new ExchangeRate("NON_EXISTING_SYMBOL_BA", 0, 0L, getName()));
|
||||||
.map(e ->
|
return exchangeRates;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue