Refactor and fix asset checks

This commit is contained in:
Manfred Karrer 2018-09-26 18:37:41 -05:00
parent c231750900
commit fd87ff369c
No known key found for this signature in database
GPG Key ID: 401250966A6B2C46
4 changed files with 249 additions and 36 deletions

View File

@ -18,6 +18,7 @@
package bisq.core.locale;
import bisq.core.app.BisqEnvironment;
import bisq.core.btc.BaseCurrencyNetwork;
import bisq.common.app.DevEnv;
@ -34,6 +35,8 @@ import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import static com.google.common.base.Preconditions.checkArgument;
import bisq.asset.Asset;
@ -108,8 +111,8 @@ public class CurrencyUtil {
private static List<CryptoCurrency> createAllSortedCryptoCurrenciesList() {
List<CryptoCurrency> result = assetRegistry.stream()
.filter(CurrencyUtil::assetIsNotBaseCurrency)
.filter(CurrencyUtil::isNotBsqOrBsqTradingActivated)
.filter(CurrencyUtil::assetMatchesNetwork)
.filter(asset -> isNotBsqOrBsqTradingActivated(asset, BisqEnvironment.getBaseCurrencyNetwork(), DevEnv.isDaoTradingActivated()))
.filter(asset -> assetMatchesNetworkIfMainnet(asset, BisqEnvironment.getBaseCurrencyNetwork()))
.map(CurrencyUtil::assetToCryptoCurrency)
.sorted(TradeCurrency::compareTo)
.collect(Collectors.toList());
@ -367,21 +370,71 @@ public class CurrencyUtil {
}
private static boolean assetIsNotBaseCurrency(Asset asset) {
return !asset.getTickerSymbol().equals(baseCurrencyCode);
return !assetMatchesCurrencyCode(asset, baseCurrencyCode);
}
// TODO We handle assets of other types (Token, ERC20) as matching the network which is not correct.
// We should add support for network property in those tokens as well.
private static boolean assetMatchesNetwork(Asset asset) {
public static boolean assetMatchesNetwork(Asset asset, BaseCurrencyNetwork baseCurrencyNetwork) {
return !(asset instanceof Coin) ||
((Coin) asset).getNetwork().name().equals(BisqEnvironment.getBaseCurrencyNetwork().getNetwork());
((Coin) asset).getNetwork().name().equals(baseCurrencyNetwork.getNetwork());
}
// We only check for coins not other types of assets (TODO network check should be supported for all assets)
public static boolean assetMatchesNetworkIfMainnet(Asset asset, BaseCurrencyNetwork baseCurrencyNetwork) {
return !(asset instanceof Coin) ||
coinMatchesNetworkIfMainnet((Coin) asset, baseCurrencyNetwork);
}
// We want all coins available also in testnet or regtest for testing purpose
public static boolean coinMatchesNetworkIfMainnet(Coin coin, BaseCurrencyNetwork baseCurrencyNetwork) {
boolean matchesNetwork = assetMatchesNetwork(coin, baseCurrencyNetwork);
return !baseCurrencyNetwork.isMainnet() ||
matchesNetwork;
}
private static CryptoCurrency assetToCryptoCurrency(Asset asset) {
return new CryptoCurrency(asset.getTickerSymbol(), asset.getName(), asset instanceof Token);
}
private static boolean isNotBsqOrBsqTradingActivated(Asset asset) {
return !(asset instanceof BSQ) || DevEnv.isDaoTradingActivated() && assetMatchesNetwork(asset);
private static boolean isNotBsqOrBsqTradingActivated(Asset asset, BaseCurrencyNetwork baseCurrencyNetwork, boolean daoTradingActivated) {
return !(asset instanceof BSQ) ||
daoTradingActivated && assetMatchesNetwork(asset, baseCurrencyNetwork);
}
public static boolean assetMatchesCurrencyCode(Asset asset, String currencyCode) {
return currencyCode.equals(asset.getTickerSymbol());
}
public static Optional<Asset> findAsset(AssetRegistry assetRegistry, String currencyCode,
BaseCurrencyNetwork baseCurrencyNetwork, boolean daoTradingActivated) {
List<Asset> assets = assetRegistry.stream()
.filter(asset -> assetMatchesCurrencyCode(asset, currencyCode)).collect(Collectors.toList());
// If we don't have the ticker symbol we throw an exception
if (!assets.stream().findFirst().isPresent())
return Optional.empty();
if (currencyCode.equals("BSQ") && baseCurrencyNetwork.isMainnet() && !daoTradingActivated)
return Optional.empty();
// We check for exact match with network, e.g. BTC$TESTNET
Optional<Asset> optionalAssetMatchesNetwork = assets.stream()
.filter(asset -> assetMatchesNetwork(asset, baseCurrencyNetwork))
.findFirst();
if (optionalAssetMatchesNetwork.isPresent())
return optionalAssetMatchesNetwork;
// In testnet or regtest we want to show all coins as well. Most coins have only Mainnet defined so we deliver
// that if no exact match was found in previous step
if (!baseCurrencyNetwork.isMainnet()) {
Optional<Asset> optionalAsset = assets.stream().findFirst();
checkArgument(optionalAsset.isPresent(), "optionalAsset must be present as we checked for " +
"not matching ticker symbols already above");
return optionalAsset;
}
// If we are in mainnet we need have a mainet asset defined.
throw new IllegalArgumentException("We are on mainnet and we could not find an asset with network type mainnet");
}
}

View File

@ -18,22 +18,23 @@
package bisq.core.payment.validation;
import bisq.core.app.BisqEnvironment;
import bisq.core.btc.BaseCurrencyNetwork;
import bisq.core.locale.CurrencyUtil;
import bisq.core.locale.Res;
import bisq.core.util.validation.InputValidator;
import bisq.common.app.DevEnv;
import com.google.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import java.util.Optional;
import static java.lang.String.format;
import lombok.extern.slf4j.Slf4j;
import bisq.asset.AddressValidationResult;
import bisq.asset.Asset;
import bisq.asset.AssetRegistry;
import bisq.asset.Coin;
@Slf4j
public final class AltCoinAddressValidator extends InputValidator {
@ -56,30 +57,19 @@ public final class AltCoinAddressValidator extends InputValidator {
if (!validationResult.isValid || currencyCode == null)
return validationResult;
Asset asset = assetRegistry.stream()
.filter(this::assetMatchesSelectedCurrencyCode)
.filter(this::assetIsNotBaseCurrencyForDifferentNetwork)
.findFirst()
.orElseThrow(() ->
new IllegalArgumentException(format("'%s' is not a registered asset", currencyCode)));
Optional<Asset> optionalAsset = CurrencyUtil.findAsset(assetRegistry, currencyCode,
BisqEnvironment.getBaseCurrencyNetwork(), DevEnv.isDaoTradingActivated());
if (optionalAsset.isPresent()) {
Asset asset = optionalAsset.get();
AddressValidationResult result = asset.validateAddress(input);
if (!result.isValid()) {
return new ValidationResult(false, Res.get(result.getI18nKey(), asset.getTickerSymbol(),
result.getMessage()));
}
AddressValidationResult result = asset.validateAddress(input);
if (!result.isValid())
return new ValidationResult(false,
Res.get(result.getI18nKey(), asset.getTickerSymbol(), result.getMessage()));
return new ValidationResult(true);
}
private boolean assetMatchesSelectedCurrencyCode(Asset a) {
return currencyCode.equals(a.getTickerSymbol());
}
private boolean assetIsNotBaseCurrencyForDifferentNetwork(Asset asset) {
BaseCurrencyNetwork baseCurrencyNetwork = BisqEnvironment.getBaseCurrencyNetwork();
return !(asset instanceof Coin)
|| !asset.getTickerSymbol().equals(baseCurrencyNetwork.getCurrencyCode())
|| (((Coin) asset).getNetwork().name().equals(baseCurrencyNetwork.getNetwork()));
return new ValidationResult(true);
} else {
return new ValidationResult(false);
}
}
}

View File

@ -17,15 +17,30 @@
package bisq.core.locale;
import bisq.core.btc.BaseCurrencyNetwork;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.ServiceLoader;
import java.util.stream.Stream;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import bisq.asset.Asset;
import bisq.asset.AssetRegistry;
import bisq.asset.Coin;
import bisq.asset.coins.Ether;
public class CurrencyUtilTest {
@Before
@ -39,9 +54,95 @@ public class CurrencyUtilTest {
Optional<TradeCurrency> naira = CurrencyUtil.getTradeCurrency("NGN");
Optional<TradeCurrency> fake = CurrencyUtil.getTradeCurrency("FAK");
assertTrue(euro.isPresent());
assertTrue(naira.isPresent());
assertFalse("Fake currency shouldn't exist", fake.isPresent());
}
@Test
public void testFindAsset() {
MockAssetRegistry assetRegistry = new MockAssetRegistry();
// test if code is matching
boolean daoTradingActivated = false;
// Test if BSQ on mainnet is failing
Assert.assertFalse(CurrencyUtil.findAsset(assetRegistry, "BSQ",
BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).isPresent());
// on testnet/regtest it is allowed
assertEquals(CurrencyUtil.findAsset(assetRegistry, "BSQ",
BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "BSQ");
daoTradingActivated = true;
// With daoTradingActivated we can request BSQ
assertEquals(CurrencyUtil.findAsset(assetRegistry, "BSQ",
BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).get().getTickerSymbol(), "BSQ");
// Test if not matching ticker is failing
Assert.assertFalse(CurrencyUtil.findAsset(assetRegistry, "BSQ1",
BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).isPresent());
// Add a mock coin which has no mainnet version, needs to fail if we are on mainnet
MockTestnetCoin.Testnet mockTestnetCoin = new MockTestnetCoin.Testnet();
try {
assetRegistry.addAsset(mockTestnetCoin);
CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN",
BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated);
Assert.fail("Expected an IllegalArgumentException");
} catch (IllegalArgumentException e) {
String wantMessage = "We are on mainnet and we could not find an asset with network type mainnet";
Assert.assertTrue("Unexpected exception, want message starting with " +
"'" + wantMessage + "', got '" + e.getMessage() + "'", e.getMessage().startsWith(wantMessage));
}
// For testnet its ok
assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN",
BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "MOCK_COIN");
assertEquals(Coin.Network.TESTNET, mockTestnetCoin.getNetwork());
// For regtest its still found
assertEquals(CurrencyUtil.findAsset(assetRegistry, "MOCK_COIN",
BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get().getTickerSymbol(), "MOCK_COIN");
// We test if we are not on mainnet to get the mainnet coin
Coin ether = new Ether();
assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get().getTickerSymbol(), "ETH");
assertEquals(CurrencyUtil.findAsset(assetRegistry, "ETH",
BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get().getTickerSymbol(), "ETH");
assertEquals(Coin.Network.MAINNET, ether.getNetwork());
// We test if network matches exactly if there are distinct network types defined like with BSQ
Coin bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_MAINNET, daoTradingActivated).get();
assertEquals("BSQ", bsq.getTickerSymbol());
assertEquals(Coin.Network.MAINNET, bsq.getNetwork());
bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_TESTNET, daoTradingActivated).get();
assertEquals("BSQ", bsq.getTickerSymbol());
assertEquals(Coin.Network.TESTNET, bsq.getNetwork());
bsq = (Coin) CurrencyUtil.findAsset(assetRegistry, "BSQ", BaseCurrencyNetwork.BTC_REGTEST, daoTradingActivated).get();
assertEquals("BSQ", bsq.getTickerSymbol());
assertEquals(Coin.Network.REGTEST, bsq.getNetwork());
}
class MockAssetRegistry extends AssetRegistry {
private List<Asset> registeredAssets = new ArrayList<>();
MockAssetRegistry() {
for (Asset asset : ServiceLoader.load(Asset.class)) {
registeredAssets.add(asset);
}
}
void addAsset(Asset asset) {
registeredAssets.add(asset);
}
public Stream<Asset> stream() {
return registeredAssets.stream();
}
}
}

View File

@ -0,0 +1,69 @@
/*
* 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 bisq.core.locale;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.params.MainNetParams;
import org.bitcoinj.params.RegTestParams;
import org.bitcoinj.params.TestNet3Params;
import bisq.asset.AddressValidationResult;
import bisq.asset.Base58BitcoinAddressValidator;
import bisq.asset.Coin;
public class MockTestnetCoin extends Coin {
public MockTestnetCoin(Network network, NetworkParameters networkParameters) {
super("MockTestnetCoin", "MOCK_COIN", new BSQAddressValidator(networkParameters), network);
}
public static class Mainnet extends MockTestnetCoin {
public Mainnet() {
super(Network.MAINNET, MainNetParams.get());
}
}
public static class Testnet extends MockTestnetCoin {
public Testnet() {
super(Network.TESTNET, TestNet3Params.get());
}
}
public static class Regtest extends MockTestnetCoin {
public Regtest() {
super(Network.REGTEST, RegTestParams.get());
}
}
public static class BSQAddressValidator extends Base58BitcoinAddressValidator {
public BSQAddressValidator(NetworkParameters networkParameters) {
super(networkParameters);
}
@Override
public AddressValidationResult validate(String address) {
return super.validate(address);
}
}
}