update price node from upstream, fix XMR price, default port 8078

Co-authored-by: Christoph Atteneder <christoph.atteneder@gmail.com>
Co-authored-by: wiz <j@wiz.biz>
Co-authored-by: jmacxx <47253594+jmacxx@users.noreply.github.com>
Co-authored-by: Stephan Oeste <emzy@emzy.de>
Co-authored-by: Chris Beams <chris@beams.io>
This commit is contained in:
woodser 2022-02-14 08:18:46 -05:00
parent 45a61ae835
commit e864210f4b
58 changed files with 343 additions and 442 deletions

View File

@ -491,51 +491,57 @@ configure(project(':monitor')) {
configure(project(':pricenode')) {
apply plugin: "org.springframework.boot"
apply plugin: 'io.spring.dependency-management'
mainClassName = 'bisq.price.Main'
version = file("src/main/resources/version.txt").text.trim()
jar.manifest.attributes(
"Implementation-Title": project.name,
"Implementation-Version": version)
ext['log4j2.version'] = '2.17.0'
dependencies {
compile project(":core")
compileOnly "org.projectlombok:lombok:$lombokVersion"
implementation project(":common")
implementation project(":core")
annotationProcessor "org.projectlombok:lombok:$lombokVersion"
compileOnly "org.projectlombok:lombok:$lombokVersion"
implementation "com.google.code.gson:gson:$gsonVersion"
implementation "com.google.guava:guava:$guavaVersion"
implementation "commons-codec:commons-codec:$codecVersion"
implementation "org.apache.httpcomponents:httpcore:$httpcoreVersion"
implementation("org.apache.httpcomponents:httpclient:$httpclientVersion") {
exclude(module: 'commons-codec')
}
compile("org.knowm.xchange:xchange-bitbay:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-btcmarkets:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-binance:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-bitfinex:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-bitflyer:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-bitstamp:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-cexio:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-coinmate:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-coinmarketcap:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-coinone:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-exmo:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-hitbtc:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-huobi:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-independentreserve:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-kraken:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-luno:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-mercadobitcoin:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-paribu:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-poloniex:$knowmXchangeVersion")
compile("org.knowm.xchange:xchange-quoine:$knowmXchangeVersion")
compile("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
compile("org.springframework.boot:spring-boot-starter-actuator")
testCompile "org.junit.jupiter:junit-jupiter-api:$jupiterVersion"
testCompile "org.junit.jupiter:junit-jupiter-params:$jupiterVersion"
testRuntime("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion")
testCompileOnly "org.projectlombok:lombok:$lombokVersion"
implementation("org.knowm.xchange:xchange-binance:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-bitbay:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-bitfinex:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-bitflyer:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-bitstamp:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-btcmarkets:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-cexio:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-coinmarketcap:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-coinmate:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-coinone:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-exmo:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-hitbtc:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-huobi:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-independentreserve:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-kraken:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-luno:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-mercadobitcoin:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-paribu:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-poloniex:$knowmXchangeVersion")
implementation("org.knowm.xchange:xchange-quoine:$knowmXchangeVersion")
implementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
testAnnotationProcessor "org.projectlombok:lombok:$lombokVersion"
testCompileOnly "org.projectlombok:lombok:$lombokVersion"
testImplementation "org.junit.jupiter:junit-jupiter-api:$jupiterVersion"
testImplementation "org.junit.jupiter:junit-jupiter-params:$jupiterVersion"
testImplementation "org.mockito:mockito-core:$mockitoVersion"
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion")
}
test {

View File

@ -110,9 +110,11 @@ public class Config {
public static final String API_PORT = "apiPort";
public static final String PREVENT_PERIODIC_SHUTDOWN_AT_SEED_NODE = "preventPeriodicShutdownAtSeedNode";
public static final String REPUBLISH_MAILBOX_ENTRIES = "republishMailboxEntries";
public static final String LEGACY_FEE_DATAMAP = "dataMap";
public static final String BTC_TX_FEE = "btcTxFee";
public static final String BTC_MIN_TX_FEE = "btcMinTxFee";
public static final String BTC_FEES_TS = "bitcoinFeesTs";
public static final String BTC_FEE_INFO = "bitcoinFeeInfo";
public static final String BYPASS_MEMPOOL_VALIDATION = "bypassMempoolValidation";
public static final String PASSWORD_REQUIRED = "passwordRequired";

View File

@ -47,7 +47,6 @@ To manually test endpoints, run each of the following:
curl http://localhost:8080/getAllMarketPrices
curl http://localhost:8080/getFees
curl http://localhost:8080/getParams
curl http://localhost:8080/getVersion
curl http://localhost:8080/info
```

View File

@ -1,16 +1,16 @@
[Unit]
Description=Bisq Price Node
Description=Haveno Price Node
After=network.target
[Service]
SyslogIdentifier=bisq-pricenode
EnvironmentFile=/etc/default/bisq-pricenode.env
ExecStart=/bisq/bisq/bisq-pricenode 2
SyslogIdentifier=Haveno-pricenode
EnvironmentFile=/etc/default/Haveno-pricenode.env
ExecStart=/Haveno/Haveno/Haveno-pricenode 2
ExecStop=/bin/kill -TERM ${MAINPID}
Restart=on-failure
User=bisq
Group=bisq
User=Haveno
Group=Haveno
PrivateTmp=true
ProtectSystem=full

View File

@ -16,7 +16,7 @@ HAVENO_USER=haveno
HAVENO_GROUP=haveno
HAVENO_HOME=/haveno
HAVENO_REPO_URL=https://github.com/haveno-dex/haveno
HAVENO_REPO_URL=https://github.com/haveno-network/haveno
HAVENO_REPO_NAME=haveno
HAVENO_REPO_TAG=master
HAVENO_LATEST_RELEASE=master

View File

@ -78,13 +78,14 @@ public abstract class PriceProvider<T> implements SmartLifecycle, Supplier<T> {
}
private void refresh() {
long ts = System.currentTimeMillis();
cachedResult = doGet();
log.info("refresh took {} ms.", (System.currentTimeMillis() - ts));
onRefresh();
try {
long ts = System.currentTimeMillis();
cachedResult = doGet();
log.info("refresh took {} ms.", (System.currentTimeMillis() - ts));
onRefresh();
} catch (Exception e) {
log.warn("Error refreshing price provider {}", getClass());
}
}
protected abstract T doGet();

View File

@ -26,7 +26,8 @@ import java.time.Duration;
*/
public abstract class FeeRateProvider extends PriceProvider<FeeRate> {
public static final long MIN_FEE_RATE = 10; // satoshi/vbyte
public static final long MIN_FEE_RATE_FOR_WITHDRAWAL = 2; // satoshi/vbyte
public static final long MIN_FEE_RATE_FOR_TRADING = 10; // satoshi/vbyte
public static final long MAX_FEE_RATE = 1000;
public FeeRateProvider(Duration refreshInterval) {

View File

@ -36,7 +36,7 @@ import org.slf4j.LoggerFactory;
* High-level mining {@link FeeRate} operations.
*/
@Service
class FeeRateService {
public class FeeRateService {
private final List<FeeRateProvider> providers;
protected final Logger log = LoggerFactory.getLogger(this.getClass());
@ -78,15 +78,15 @@ class FeeRateService {
// Calculate the average
long averageFeeRate = (amountOfFeeRates.intValue() > 0)
? sumOfAllFeeRates.longValue() / amountOfFeeRates.intValue()
: FeeRateProvider.MIN_FEE_RATE;
: FeeRateProvider.MIN_FEE_RATE_FOR_TRADING;
long averageMinFeeRate = (amountOfFeeRates.intValue() > 0)
? sumOfAllMinFeeRates.longValue() / amountOfFeeRates.intValue()
: FeeRateProvider.MIN_FEE_RATE;
: FeeRateProvider.MIN_FEE_RATE_FOR_WITHDRAWAL;
// Make sure the returned value is within the min-max range
averageFeeRate = Math.max(averageFeeRate, FeeRateProvider.MIN_FEE_RATE);
averageFeeRate = Math.max(averageFeeRate, FeeRateProvider.MIN_FEE_RATE_FOR_TRADING);
averageFeeRate = Math.min(averageFeeRate, FeeRateProvider.MAX_FEE_RATE);
averageMinFeeRate = Math.max(averageMinFeeRate, FeeRateProvider.MIN_FEE_RATE);
averageMinFeeRate = Math.max(averageMinFeeRate, FeeRateProvider.MIN_FEE_RATE_FOR_WITHDRAWAL);
averageMinFeeRate = Math.min(averageMinFeeRate, FeeRateProvider.MAX_FEE_RATE);
// Prepare response: Add timestamp of now
@ -101,7 +101,7 @@ class FeeRateService {
// Build response
return new HashMap<>() {{
putAll(metadata);
put("dataMap", allFeeRates);
put(Config.LEGACY_FEE_DATAMAP, allFeeRates);
}};
}
}

View File

@ -85,24 +85,24 @@ abstract class MempoolFeeRateProvider extends FeeRateProvider {
log.error("Error retrieving bitcoin mining fee estimation: " + e.getMessage());
}
return new FeeRate("BTC", MIN_FEE_RATE, MIN_FEE_RATE, Instant.now().getEpochSecond());
return new FeeRate("BTC", MIN_FEE_RATE_FOR_TRADING, MIN_FEE_RATE_FOR_WITHDRAWAL, Instant.now().getEpochSecond());
}
private FeeRate getEstimatedFeeRate() {
Set<Map.Entry<String, Long>> feeRatePredictions = getFeeRatePredictions();
long estimatedFeeRate = feeRatePredictions.stream()
.filter(p -> p.getKey().equalsIgnoreCase("fastestFee"))
.filter(p -> p.getKey().equalsIgnoreCase("halfHourFee"))
.map(Map.Entry::getValue)
.findFirst()
.map(r -> Math.max(r, MIN_FEE_RATE))
.map(r -> Math.max(r, MIN_FEE_RATE_FOR_TRADING))
.map(r -> Math.min(r, MAX_FEE_RATE))
.orElse(MIN_FEE_RATE);
.orElse(MIN_FEE_RATE_FOR_TRADING);
long minimumFee = feeRatePredictions.stream()
.filter(p -> p.getKey().equalsIgnoreCase("minimumFee"))
.map(Map.Entry::getValue)
.findFirst()
.map(r -> Math.multiplyExact(r, 2)) // multiply the minimumFee by 2 (per wiz)
.orElse(MIN_FEE_RATE);
.orElse(MIN_FEE_RATE_FOR_WITHDRAWAL);
log.info("Retrieved estimated mining fee of {} sat/vB and minimumFee of {} sat/vB from {}", estimatedFeeRate, minimumFee, getMempoolApiHostname());
return new FeeRate("BTC", estimatedFeeRate, minimumFee, Instant.now().getEpochSecond());
}

View File

@ -18,6 +18,9 @@
package bisq.price.spot;
import bisq.price.PriceController;
import bisq.price.mining.FeeRateService;
import bisq.common.config.Config;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@ -28,13 +31,28 @@ import java.util.Map;
class ExchangeRateController extends PriceController {
private final ExchangeRateService exchangeRateService;
private final FeeRateService feeRateService;
public ExchangeRateController(ExchangeRateService exchangeRateService) {
public ExchangeRateController(ExchangeRateService exchangeRateService, FeeRateService feeRateService) {
this.exchangeRateService = exchangeRateService;
this.feeRateService = feeRateService;
}
@GetMapping(path = "/getAllMarketPrices")
public Map<String, Object> getAllMarketPrices() {
return exchangeRateService.getAllMarketPrices();
Map<String, Object> retVal = exchangeRateService.getAllMarketPrices();
// add the fee info to results
feeRateService.getFees().forEach((key, value) -> {
retVal.put(translateFieldName(key), value);
});
return retVal;
}
static String translateFieldName(String name) {
if (name.equals(Config.LEGACY_FEE_DATAMAP))
name = Config.BTC_FEE_INFO; // name changed for clarity
return name;
}
}

View File

@ -33,11 +33,14 @@ import org.knowm.xchange.service.marketdata.MarketDataService;
import org.knowm.xchange.service.marketdata.params.CurrencyPairsParam;
import org.knowm.xchange.service.marketdata.params.Params;
import org.springframework.core.env.Environment;
import java.time.Duration;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
@ -59,21 +62,56 @@ import java.util.stream.Stream;
*/
public abstract class ExchangeRateProvider extends PriceProvider<Set<ExchangeRate>> {
public static final Set<String> SUPPORTED_CRYPTO_CURRENCIES = CurrencyUtil.getAllSortedCryptoCurrencies().stream()
.map(TradeCurrency::getCode)
.collect(Collectors.toSet());
public static final Set<String> SUPPORTED_FIAT_CURRENCIES = CurrencyUtil.getAllSortedFiatCurrencies().stream()
.map(TradeCurrency::getCode)
.collect(Collectors.toSet());
private static Set<String> SUPPORTED_CRYPTO_CURRENCIES = new HashSet<>();
private static Set<String> SUPPORTED_FIAT_CURRENCIES = new HashSet<>();
private final String name;
private final String prefix;
private final Environment env;
public ExchangeRateProvider(String name, String prefix, Duration refreshInterval) {
public ExchangeRateProvider(Environment env, String name, String prefix, Duration refreshInterval) {
super(refreshInterval);
this.name = name;
this.prefix = prefix;
this.env = env;
}
public Set<String> getSupportedFiatCurrencies() {
if (SUPPORTED_FIAT_CURRENCIES.isEmpty()) { // one-time initialization
List<String> excludedFiatCurrencies =
Arrays.asList(env.getProperty("bisq.price.fiatcurrency.excluded", "")
.toUpperCase().trim().split("\\s*,\\s*"));
String validatedExclusionList = excludedFiatCurrencies.stream()
.filter(ccy -> !ccy.isEmpty())
.filter(CurrencyUtil::isFiatCurrency)
.collect(Collectors.toList()).toString();
SUPPORTED_FIAT_CURRENCIES = CurrencyUtil.getAllSortedFiatCurrencies().stream()
.map(TradeCurrency::getCode)
.filter(ccy -> !validatedExclusionList.contains(ccy.toUpperCase()))
.collect(Collectors.toSet());
log.info("fiat currencies excluded: {}", validatedExclusionList);
log.info("fiat currencies supported: {}", SUPPORTED_FIAT_CURRENCIES.size());
}
return SUPPORTED_FIAT_CURRENCIES;
}
public Set<String> getSupportedCryptoCurrencies() {
if (SUPPORTED_CRYPTO_CURRENCIES.isEmpty()) { // one-time initialization
List<String> excludedCryptoCurrencies =
Arrays.asList(env.getProperty("bisq.price.cryptocurrency.excluded", "")
.toUpperCase().trim().split("\\s*,\\s*"));
String validatedExclusionList = excludedCryptoCurrencies.stream()
.filter(ccy -> !ccy.isEmpty())
.filter(CurrencyUtil::isCryptoCurrency)
.collect(Collectors.toList()).toString();
SUPPORTED_CRYPTO_CURRENCIES = CurrencyUtil.getAllSortedCryptoCurrencies().stream()
.map(TradeCurrency::getCode)
.filter(ccy -> !validatedExclusionList.contains(ccy.toUpperCase()))
.collect(Collectors.toSet());
SUPPORTED_CRYPTO_CURRENCIES.add("XMR"); // XMR is skipped because it's a base currency
log.info("crypto currencies excluded: {}", validatedExclusionList);
log.info("crypto currencies supported: {}", SUPPORTED_CRYPTO_CURRENCIES.size());
}
return SUPPORTED_CRYPTO_CURRENCIES;
}
public String getName() {
@ -87,7 +125,7 @@ public abstract class ExchangeRateProvider extends PriceProvider<Set<ExchangeRat
@Override
protected void onRefresh() {
get().stream()
.filter(e -> "USD".equals(e.getCurrency()) || "LTC".equals(e.getCurrency()))
.filter(e -> "USD".equals(e.getCurrency()) || "XMR".equals(e.getCurrency()) || "ETH".equals(e.getCurrency()) || "BCH".equals(e.getCurrency()))
.forEach(e -> log.info("BTC/{}: {}", e.getCurrency(), e.getPrice()));
}
@ -119,13 +157,13 @@ public abstract class ExchangeRateProvider extends PriceProvider<Set<ExchangeRat
// Find the desired fiat pairs (pair format is BTC-FIAT)
List<CurrencyPair> desiredFiatPairs = allCurrencyPairsOnExchange.stream()
.filter(cp -> cp.base.equals(Currency.BTC))
.filter(cp -> SUPPORTED_FIAT_CURRENCIES.contains(cp.counter.getCurrencyCode()))
.filter(cp -> getSupportedFiatCurrencies().contains(cp.counter.getCurrencyCode()))
.collect(Collectors.toList());
// Find the desired altcoin pairs (pair format is ALT-BTC)
List<CurrencyPair> desiredCryptoPairs = allCurrencyPairsOnExchange.stream()
.filter(cp -> cp.counter.equals(Currency.BTC))
.filter(cp -> SUPPORTED_CRYPTO_CURRENCIES.contains(cp.base.getCurrencyCode()))
.filter(cp -> getSupportedCryptoCurrencies().contains(cp.base.getCurrencyCode()))
.collect(Collectors.toList());
// Retrieve in bulk all tickers offered by the exchange

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.btcmarkets.BTCMarketsExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class BTCMarkets extends ExchangeRateProvider {
public BTCMarkets() {
super("BTCMARKETS", "btcmarkets", Duration.ofMinutes(1));
public BTCMarkets(Environment env) {
super(env, "BTCMARKETS", "btcmarkets", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.binance.BinanceExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Binance extends ExchangeRateProvider {
public Binance() {
super("BINANCE", "binance", Duration.ofMinutes(1));
public Binance(Environment env) {
super(env, "BINANCE", "binance", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.bitbay.BitbayExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Bitbay extends ExchangeRateProvider {
public Bitbay() {
super("BITBAY", "bitbay", Duration.ofMinutes(1));
public Bitbay(Environment env) {
super(env, "BITBAY", "bitbay", Duration.ofMinutes(1));
}
@Override

View File

@ -20,6 +20,7 @@ package bisq.price.spot.providers;
import bisq.price.spot.ExchangeRate;
import bisq.price.spot.ExchangeRateProvider;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -34,12 +35,12 @@ import java.util.Set;
@Component
class BitcoinAverage extends ExchangeRateProvider {
public BitcoinAverage() {
public BitcoinAverage(Environment env) {
// 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));
super(env, "BA", "btcAverage", Duration.ofMinutes(100));
}
/**

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.bitfinex.BitfinexExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Bitfinex extends ExchangeRateProvider {
public Bitfinex() {
super("BITFINEX", "bitfinex", Duration.ofMinutes(1));
public Bitfinex(Environment env) {
super(env, "BITFINEX", "bitfinex", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.bitflyer.BitflyerExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Bitflyer extends ExchangeRateProvider {
public Bitflyer() {
super("BITFLYER", "bitflyer", Duration.ofMinutes(1));
public Bitflyer(Environment env) {
super(env, "BITFLYER", "bitflyer", Duration.ofMinutes(1));
}
@Override

View File

@ -1,102 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno 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.
*
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.price.spot.providers;
import bisq.price.spot.ExchangeRate;
import bisq.price.spot.ExchangeRateProvider;
import bisq.price.util.bitpay.BitpayMarketData;
import bisq.price.util.bitpay.BitpayTicker;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.RequestEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;
import java.time.Duration;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
@Component
class Bitpay extends ExchangeRateProvider {
private final RestTemplate restTemplate = new RestTemplate();
public Bitpay() {
super("BITPAY", "bitpay", Duration.ofMinutes(1));
}
@Override
public Set<ExchangeRate> doGet() {
Set<ExchangeRate> result = new HashSet<ExchangeRate>();
Predicate<BitpayTicker> isDesiredFiatPair = t -> SUPPORTED_FIAT_CURRENCIES.contains(t.getCode());
Predicate<BitpayTicker> isDesiredCryptoPair = t -> SUPPORTED_CRYPTO_CURRENCIES.contains(t.getCode());
getTickers()
.filter(isDesiredFiatPair.or(isDesiredCryptoPair))
.forEach(ticker -> {
boolean useInverseRate = false;
if (SUPPORTED_CRYPTO_CURRENCIES.contains(ticker.getCode())) {
// Use inverse rate for alts, because the API returns the
// conversion rate in the opposite direction than what we need
// API returns the BTC/Alt rate, we need the Alt/BTC rate
useInverseRate = true;
}
BigDecimal rate = ticker.getRate();
// Find the inverse rate, while using enough decimals to reflect very
// small exchange rates
BigDecimal inverseRate = (rate.compareTo(BigDecimal.ZERO) > 0) ?
BigDecimal.ONE.divide(rate, 8, RoundingMode.HALF_UP) :
BigDecimal.ZERO;
result.add(new ExchangeRate(
ticker.getCode(),
(useInverseRate ? inverseRate : rate),
new Date(),
this.getName()
));
});
return result;
}
private Stream<BitpayTicker> getTickers() {
BitpayMarketData marketData = restTemplate.exchange(
RequestEntity
.get(UriComponentsBuilder
.fromUriString("https://bitpay.com/rates").build()
.toUri())
.build(),
new ParameterizedTypeReference<BitpayMarketData>() {
}
).getBody();
return Arrays.stream(marketData.getData());
}
}

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.bitstamp.BitstampExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Bitstamp extends ExchangeRateProvider {
public Bitstamp() {
super("BITSTAMP", "bitstamp", Duration.ofMinutes(1));
public Bitstamp(Environment env) {
super(env, "BITSTAMP", "bitstamp", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import bisq.price.util.coingecko.CoinGeckoMarketData;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.env.Environment;
import org.springframework.http.RequestEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@ -44,8 +45,8 @@ class CoinGecko extends ExchangeRateProvider {
private final RestTemplate restTemplate = new RestTemplate();
public CoinGecko() {
super("COINGECKO", "coingecko", Duration.ofMinutes(1));
public CoinGecko(Environment env) {
super(env, "COINGECKO", "coingecko", Duration.ofMinutes(1));
}
@Override
@ -56,8 +57,8 @@ class CoinGecko extends ExchangeRateProvider {
Set<ExchangeRate> result = new HashSet<ExchangeRate>();
Predicate<Map.Entry> isDesiredFiatPair = t -> SUPPORTED_FIAT_CURRENCIES.contains(t.getKey());
Predicate<Map.Entry> isDesiredCryptoPair = t -> SUPPORTED_CRYPTO_CURRENCIES.contains(t.getKey());
Predicate<Map.Entry> isDesiredFiatPair = t -> getSupportedFiatCurrencies().contains(t.getKey());
Predicate<Map.Entry> isDesiredCryptoPair = t -> getSupportedCryptoCurrencies().contains(t.getKey());
getMarketData().getRates().entrySet().stream()
.filter(isDesiredFiatPair.or(isDesiredCryptoPair))
@ -65,7 +66,7 @@ class CoinGecko extends ExchangeRateProvider {
.forEach((key, ticker) -> {
boolean useInverseRate = false;
if (SUPPORTED_CRYPTO_CURRENCIES.contains(key)) {
if (getSupportedCryptoCurrencies().contains(key)) {
// Use inverse rate for alts, because the API returns the
// conversion rate in the opposite direction than what we need
// API returns the BTC/Alt rate, we need the Alt/BTC rate

View File

@ -20,6 +20,7 @@ package bisq.price.spot.providers;
import bisq.price.spot.ExchangeRate;
import bisq.price.spot.ExchangeRateProvider;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -33,8 +34,8 @@ import java.util.Set;
@Component
class CoinMarketCap extends ExchangeRateProvider {
public CoinMarketCap() {
super("CMC", "coinmarketcap", Duration.ofMinutes(5)); // large data structure, so don't request it too often
public CoinMarketCap(Environment env) {
super(env, "CMC", "coinmarketcap", Duration.ofMinutes(5)); // large data structure, so don't request it too often
}
/**

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.coinone.CoinoneExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Coinone extends ExchangeRateProvider {
public Coinone() {
super("COINONE", "coinone", Duration.ofMinutes(1));
public Coinone(Environment env) {
super(env, "COINONE", "coinone", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import bisq.price.util.coinpaprika.CoinpaprikaMarketData;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.env.Environment;
import org.springframework.http.RequestEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@ -55,8 +56,8 @@ class Coinpaprika extends ExchangeRateProvider {
"INR, MYR, NOK, PKR, SEK, TWD, ZAR, VND, BOB, COP, PEN, ARS, ISK")
.replace(" ", ""); // Strip any spaces
public Coinpaprika() {
super("COINPAPRIKA", "coinpaprika", Duration.ofMinutes(1));
public Coinpaprika(Environment env) {
super(env, "COINPAPRIKA", "coinpaprika", Duration.ofMinutes(1));
}
@Override
@ -67,7 +68,7 @@ class Coinpaprika extends ExchangeRateProvider {
Set<ExchangeRate> result = new HashSet<ExchangeRate>();
Predicate<Map.Entry> isDesiredFiatPair = t -> SUPPORTED_FIAT_CURRENCIES.contains(t.getKey());
Predicate<Map.Entry> isDesiredFiatPair = t -> getSupportedFiatCurrencies().contains(t.getKey());
getMarketData().getQuotes().entrySet().stream()
.filter(isDesiredFiatPair)

View File

@ -1,46 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno 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.
*
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.price.spot.providers;
import bisq.price.spot.ExchangeRate;
import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.exmo.ExmoExchange;
import org.springframework.stereotype.Component;
import java.time.Duration;
import java.util.Set;
@Component
class Exmo extends ExchangeRateProvider {
public Exmo() {
// API rate limit = 10 calls / second from the same IP ( see https://exmo.com/en/api )
super("EXMO", "exmo", Duration.ofMinutes(1));
}
@Override
public Set<ExchangeRate> doGet() {
// Supported fiat: EUR, PLN, RUB, UAH (Ukrainian hryvnia), USD
// Supported alts: DASH, DOGE, ETC, ETH, LTC, XMR, ZEC
return doGet(ExmoExchange.class);
}
}

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.huobi.HuobiExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Huobi extends ExchangeRateProvider {
public Huobi() {
super("HUOBI", "huobi", Duration.ofMinutes(1));
public Huobi(Environment env) {
super(env, "HUOBI", "huobi", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.independentreserve.IndependentReserveExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class IndependentReserve extends ExchangeRateProvider {
public IndependentReserve() {
super("IndependentReserve", "independentreserve", Duration.ofMinutes(1));
public IndependentReserve(Environment env) {
super(env, "IndependentReserve", "independentreserve", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.kraken.KrakenExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Kraken extends ExchangeRateProvider {
public Kraken() {
super("KRAKEN", "kraken", Duration.ofMinutes(1));
public Kraken(Environment env) {
super(env, "KRAKEN", "kraken", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.luno.LunoExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Luno extends ExchangeRateProvider {
public Luno() {
super("LUNO", "luno", Duration.ofMinutes(1));
public Luno(Environment env) {
super(env, "LUNO", "luno", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.mercadobitcoin.MercadoBitcoinExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class MercadoBitcoin extends ExchangeRateProvider {
public MercadoBitcoin() {
super("MercadoBitcoin", "mercadobitcoin", Duration.ofMinutes(1));
public MercadoBitcoin(Environment env) {
super(env, "MercadoBitcoin", "mercadobitcoin", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.paribu.ParibuExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Paribu extends ExchangeRateProvider {
public Paribu() {
super("PARIBU", "paribu", Duration.ofMinutes(1));
public Paribu(Environment env) {
super(env, "PARIBU", "paribu", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.poloniex.PoloniexExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Poloniex extends ExchangeRateProvider {
public Poloniex() {
super("POLO", "poloniex", Duration.ofMinutes(1));
public Poloniex(Environment env) {
super(env, "POLO", "poloniex", Duration.ofMinutes(1));
}
@Override

View File

@ -22,6 +22,7 @@ import bisq.price.spot.ExchangeRateProvider;
import org.knowm.xchange.quoine.QuoineExchange;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
import java.time.Duration;
@ -31,8 +32,8 @@ import java.util.Set;
@Component
class Quoine extends ExchangeRateProvider {
public Quoine() {
super("QUOINE", "quoine", Duration.ofMinutes(1));
public Quoine(Environment env) {
super(env, "QUOINE", "quoine", Duration.ofMinutes(1));
}
@Override

View File

@ -1,55 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno 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.
*
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.price.util;
import bisq.price.PriceController;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.core.io.Resource;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.IOException;
import java.io.InputStreamReader;
@RestController
class VersionController extends PriceController implements InfoContributor {
private final String version;
public VersionController(@Value("classpath:version.txt") Resource versionTxt) throws IOException {
this.version = FileCopyUtils.copyToString(
new InputStreamReader(
versionTxt.getInputStream()
)
).trim();
}
@GetMapping(path = "/getVersion")
public String getVersion() {
return version;
}
@Override
public void contribute(Info.Builder builder) {
builder.withDetail("version", version);
}
}

View File

@ -1,3 +1,4 @@
server.port=8078
spring.jackson.serialization.indent_output=true
# To enable another fee estimation endpoint, simply uncomment one of the following lines
@ -7,3 +8,5 @@ bisq.price.mining.providers.mempoolHostname.2=mempool.emzy.de
bisq.price.mining.providers.mempoolHostname.3=mempool.ninja
bisq.price.mining.providers.mempoolHostname.4=mempool.bisq.services
# bisq.price.mining.providers.mempoolHostname.5=someHostOrIP
bisq.price.fiatcurrency.excluded=LBP,ARS
bisq.price.cryptocurrency.excluded=

View File

@ -30,7 +30,7 @@ public abstract class AbstractExchangeRateProviderTest {
// Sanity checks
assertTrue(retrievedExchangeRates.size() > 0);
checkProviderCurrencyPairs(retrievedExchangeRates);
checkProviderCurrencyPairs(exchangeProvider, retrievedExchangeRates);
}
/**
@ -40,24 +40,24 @@ public abstract class AbstractExchangeRateProviderTest {
*
* @param retrievedExchangeRates Exchange rates retrieved from the provider
*/
private void checkProviderCurrencyPairs(Set<ExchangeRate> retrievedExchangeRates) {
private void checkProviderCurrencyPairs(ExchangeRateProvider exchangeProvider, Set<ExchangeRate> retrievedExchangeRates) {
Set<String> retrievedRatesCurrencies = retrievedExchangeRates.stream()
.map(ExchangeRate::getCurrency)
.collect(Collectors.toSet());
Set<String> supportedFiatCurrenciesRetrieved = ExchangeRateProvider.SUPPORTED_FIAT_CURRENCIES.stream()
Set<String> supportedFiatCurrenciesRetrieved = exchangeProvider.getSupportedFiatCurrencies().stream()
.filter(f -> retrievedRatesCurrencies.contains(f))
.collect(Collectors.toCollection(TreeSet::new));
log.info("Retrieved rates for supported fiat currencies: " + supportedFiatCurrenciesRetrieved);
Set<String> supportedCryptoCurrenciesRetrieved = ExchangeRateProvider.SUPPORTED_CRYPTO_CURRENCIES.stream()
Set<String> supportedCryptoCurrenciesRetrieved = exchangeProvider.getSupportedCryptoCurrencies().stream()
.filter(c -> retrievedRatesCurrencies.contains(c))
.collect(Collectors.toCollection(TreeSet::new));
log.info("Retrieved rates for supported altcoins: " + supportedCryptoCurrenciesRetrieved);
Set<String> supportedCurrencies = Sets.union(
ExchangeRateProvider.SUPPORTED_CRYPTO_CURRENCIES,
ExchangeRateProvider.SUPPORTED_FIAT_CURRENCIES);
exchangeProvider.getSupportedCryptoCurrencies(),
exchangeProvider.getSupportedFiatCurrencies());
Set unsupportedCurrencies = Sets.difference(retrievedRatesCurrencies, supportedCurrencies);
assertTrue("Retrieved exchange rates contain unsupported currencies: " + unsupportedCurrencies,

View File

@ -47,13 +47,13 @@ public class FeeRateServiceTest {
// Even with no working providers, we expect the service to return pre-configured
// minimum fee rate
doSanityChecksForRetrievedData(retrievedData, FeeRateProvider.MIN_FEE_RATE);
doSanityChecksForRetrievedData(retrievedData, FeeRateProvider.MIN_FEE_RATE_FOR_TRADING);
}
@Test
public void getFees_singleProvider_feeBelowMin() {
// One working provider, which returns a fee lower than the minimum
long providerFee = FeeRateProvider.MIN_FEE_RATE - 3;
long providerFee = FeeRateProvider.MIN_FEE_RATE_FOR_TRADING - 3;
FeeRateService service = new FeeRateService(
Collections.singletonList(
buildDummyReachableMempoolFeeRateProvider(providerFee)));
@ -62,7 +62,7 @@ public class FeeRateServiceTest {
// When the provider returns a value below the expected min, the service should
// return the min
doSanityChecksForRetrievedData(retrievedData, FeeRateProvider.MIN_FEE_RATE);
doSanityChecksForRetrievedData(retrievedData, FeeRateProvider.MIN_FEE_RATE_FOR_TRADING);
}
@Test
@ -84,14 +84,14 @@ public class FeeRateServiceTest {
public void getFees_multipleProviders() {
// 3 providers, returning 1xMIN, 2xMIN, 3xMIN
FeeRateService service = new FeeRateService(asList(
buildDummyReachableMempoolFeeRateProvider(FeeRateProvider.MIN_FEE_RATE * 1),
buildDummyReachableMempoolFeeRateProvider(FeeRateProvider.MIN_FEE_RATE * 2),
buildDummyReachableMempoolFeeRateProvider(FeeRateProvider.MIN_FEE_RATE * 3)));
buildDummyReachableMempoolFeeRateProvider(FeeRateProvider.MIN_FEE_RATE_FOR_TRADING * 1),
buildDummyReachableMempoolFeeRateProvider(FeeRateProvider.MIN_FEE_RATE_FOR_TRADING * 2),
buildDummyReachableMempoolFeeRateProvider(FeeRateProvider.MIN_FEE_RATE_FOR_TRADING * 3)));
Map<String, Object> retrievedData = service.getFees();
// The service should then return the average, which is 2xMIN
doSanityChecksForRetrievedData(retrievedData, FeeRateProvider.MIN_FEE_RATE * 2);
doSanityChecksForRetrievedData(retrievedData, FeeRateProvider.MIN_FEE_RATE_FOR_TRADING * 2);
}
/**
@ -103,7 +103,7 @@ public class FeeRateServiceTest {
// providers), we always expect a non-zero timestamp
assertNotEquals(0L, retrievedData.get(Config.BTC_FEES_TS));
Map<String, String> retrievedDataMap = (Map<String, String>) retrievedData.get("dataMap");
Map<String, String> retrievedDataMap = (Map<String, String>) retrievedData.get(Config.LEGACY_FEE_DATAMAP);
assertEquals(2, retrievedDataMap.size());
assertEquals(expectedFeeRate, retrievedDataMap.get(Config.BTC_TX_FEE));
}

View File

@ -50,7 +50,7 @@ public class MempoolFeeRateProviderTest {
FeeRate retrievedFeeRate = feeRateProvider.doGet();
// Check that the FeeRateProvider returns a fee within the defined parameters
assertTrue(retrievedFeeRate.getPrice() >= FeeRateProvider.MIN_FEE_RATE);
assertTrue(retrievedFeeRate.getPrice() >= FeeRateProvider.MIN_FEE_RATE_FOR_TRADING);
assertTrue(retrievedFeeRate.getPrice() <= FeeRateProvider.MAX_FEE_RATE);
}
@ -61,7 +61,7 @@ public class MempoolFeeRateProviderTest {
MempoolFeeRateProvider dummyProvider = new MempoolFeeRateProvider.First(env) {
@Override
protected FeeRate doGet() {
return new FeeRate("BTC", feeRate, MIN_FEE_RATE, Instant.now().getEpochSecond());
return new FeeRate("BTC", feeRate, MIN_FEE_RATE_FOR_WITHDRAWAL, Instant.now().getEpochSecond());
}
};

View File

@ -17,6 +17,11 @@
package bisq.price.spot;
import bisq.core.locale.CurrencyUtil;
import org.springframework.core.env.Environment;
import org.springframework.core.env.StandardEnvironment;
import com.google.common.collect.Sets;
import org.apache.commons.lang3.RandomStringUtils;
@ -46,10 +51,9 @@ import org.junit.jupiter.api.Test;
import static java.lang.Thread.sleep;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;
public class ExchangeRateServiceTest {
@ -156,6 +160,72 @@ public class ExchangeRateServiceTest {
assertNotEquals(0L, retrievedData.get(dummyProvider2.getPrefix() + "Ts"));
}
/**
* Tests the scenario when currencies are excluded from the PriceNode feed via configuration settings
*/
@Test
public void getAllMarketPrices_withMultipleProviders_excludedCurrencyCodes() {
String excludedCcyString = "LBP,USD,EUR";
Environment mockedEnvironment = mock(Environment.class);
when(mockedEnvironment.getProperty(eq("bisq.price.fiatcurrency.excluded"), anyString())).thenReturn(excludedCcyString);
class MockedExchangeRateProvider extends ExchangeRateProvider {
MockedExchangeRateProvider() {
super(mockedEnvironment, "ExchangeName", "EXCH", Duration.ofDays(1));
}
@Override
public boolean isRunning() {
return true;
}
@Override
public Set<ExchangeRate> doGet() {
HashSet<ExchangeRate> exchangeRates = new HashSet<>();
// Simulate rates for all the supported ccys
for (String rateCurrencyCode : getSupportedFiatCurrencies()) {
exchangeRates.add(new ExchangeRate(
rateCurrencyCode,
RandomUtils.nextDouble(1, 1000), // random price
System.currentTimeMillis(),
getName())); // ExchangeRateProvider name
}
return exchangeRates;
}
}
Logger exchangeRateProviderLogger;
String LIST_APPENDER_NAME2 = "testListAppender2";
exchangeRateProviderLogger = (Logger) LoggerFactory.getLogger(MockedExchangeRateProvider.class);
ListAppender<ILoggingEvent> listAppender2 = new ListAppender<>();
listAppender2.setName(LIST_APPENDER_NAME2);
listAppender2.start();
exchangeRateProviderLogger.addAppender(listAppender2);
// we request rates for all currencies, and check that:
// (a) the provider supplies more currency rates than the number of currencies we are trying to exclude (sanity test),
// (b) the number of missing currency rates equals the number of currencies we told PriceNode to exclude,
// (c) none of the rates supplied are for an excluded currency.
Set<String> excludedFiatCurrencies = new HashSet<>(asList(excludedCcyString.split(",")));
MockedExchangeRateProvider mockedExchangeRateProvider = new MockedExchangeRateProvider();
Set<ExchangeRate> exchangeRates = mockedExchangeRateProvider.doGet();
assertTrue(exchangeRates.size() > excludedFiatCurrencies.size());
int numSortedFiatCurrencies = CurrencyUtil.getAllSortedFiatCurrencies().size();
int numCurrenciesFromProvider = mockedExchangeRateProvider.getSupportedFiatCurrencies().size();
int missingCurrencyCount = numSortedFiatCurrencies - numCurrenciesFromProvider;
assertEquals(missingCurrencyCount, excludedFiatCurrencies.size());
for (ExchangeRate exchangeRate : exchangeRates) {
assertFalse(excludedCcyString.contains(exchangeRate.getCurrency()));
}
List<ILoggingEvent> logsList = ((ListAppender) exchangeRateProviderLogger.getAppender(LIST_APPENDER_NAME2)).list;
assertEquals(3, logsList.size());
assertEquals(Level.INFO, logsList.get(1).getLevel());
assertTrue(logsList.get(0).getFormattedMessage().endsWith("will refresh every PT24H"));
assertTrue(logsList.get(1).getFormattedMessage().endsWith("fiat currencies excluded: [LBP, USD, EUR]"));
assertTrue(logsList.get(2).getFormattedMessage().endsWith("fiat currencies supported: " + numCurrenciesFromProvider));
}
/**
* Performs generic sanity checks on the response format and contents.
*
@ -259,6 +329,7 @@ public class ExchangeRateServiceTest {
*/
private ExchangeRateProvider buildDummyExchangeRateProvider(int numberOfRatesAvailable) {
ExchangeRateProvider dummyProvider = new ExchangeRateProvider(
new StandardEnvironment(),
"ExchangeName-" + getRandomAlphaNumericString(5),
"EXCH-" + getRandomAlphaNumericString(3),
Duration.ofDays(1)) {
@ -298,6 +369,7 @@ public class ExchangeRateServiceTest {
private ExchangeRateProvider buildDummyExchangeRateProvider(Set<String> rateCurrencyCodes) {
ExchangeRateProvider dummyProvider = new ExchangeRateProvider(
new StandardEnvironment(),
"ExchangeName-" + getRandomAlphaNumericString(5),
"EXCH-" + getRandomAlphaNumericString(3),
Duration.ofDays(1)) {

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class BTCMarketsTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new BTCMarkets());
doGet_successfulCall(new BTCMarkets(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class BinanceTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Binance());
doGet_successfulCall(new Binance(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class BitbayTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Bitbay());
doGet_successfulCall(new Bitbay(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class BitfinexTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Bitfinex());
doGet_successfulCall(new Bitfinex(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class BitflyerTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Bitflyer());
doGet_successfulCall(new Bitflyer(new StandardEnvironment()));
}
}

View File

@ -1,34 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno 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.
*
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@Slf4j
public class BitpayTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Bitpay());
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class BitstampTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Bitstamp());
doGet_successfulCall(new Bitstamp(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class CoinGeckoTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new CoinGecko());
doGet_successfulCall(new CoinGecko(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class CoinoneTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Coinone());
doGet_successfulCall(new Coinone(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class CoinpaprikaTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Coinpaprika());
doGet_successfulCall(new Coinpaprika(new StandardEnvironment()));
}
}

View File

@ -1,34 +0,0 @@
/*
* This file is part of Haveno.
*
* Haveno 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.
*
* Haveno 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 Haveno. If not, see <http://www.gnu.org/licenses/>.
*/
package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@Slf4j
public class ExmoTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Exmo());
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class HuobiTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Huobi());
doGet_successfulCall(new Huobi(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class IndependentReserveTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new IndependentReserve());
doGet_successfulCall(new IndependentReserve(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class KrakenTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Kraken());
doGet_successfulCall(new Kraken(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class LunoTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Luno());
doGet_successfulCall(new Luno(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class MercadoBitcoinTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new MercadoBitcoin());
doGet_successfulCall(new MercadoBitcoin(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class ParibuTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Paribu());
doGet_successfulCall(new Paribu(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class PoloniexTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Poloniex());
doGet_successfulCall(new Poloniex(new StandardEnvironment()));
}
}

View File

@ -19,6 +19,8 @@ package bisq.price.spot.providers;
import bisq.price.AbstractExchangeRateProviderTest;
import org.springframework.core.env.StandardEnvironment;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
@ -28,7 +30,7 @@ public class QuoineTest extends AbstractExchangeRateProviderTest {
@Test
public void doGet_successfulCall() {
doGet_successfulCall(new Quoine());
doGet_successfulCall(new Quoine(new StandardEnvironment()));
}
}

View File

@ -1,21 +0,0 @@
package bisq.price.util;
import org.springframework.core.io.FileSystemResource;
import java.io.IOException;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertTrue;
public class VersionControllerTest {
@Test
public void getVersion() throws IOException {
VersionController controller = new VersionController(
new FileSystemResource("src/main/resources/version.txt"));
String version = controller.getVersion();
assertTrue(version.length() > 0);
}
}