3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-02-07 23:03:04 +00:00

Eliminate support for parsing negative or too large amounts in Utils.toNanoCoins. Add tests for out of range URI amounts. Resolves issue 407.

This commit is contained in:
Mike Hearn 2013-07-11 17:33:31 +02:00
parent d3eab06dba
commit 719a786db1
3 changed files with 52 additions and 24 deletions

View File

@ -29,7 +29,6 @@ import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Date; import java.util.Date;
import java.util.concurrent.locks.ReentrantLock;
import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkArgument;
@ -75,6 +74,9 @@ public class Utils {
*/ */
public static BigInteger toNanoCoins(int coins, int cents) { public static BigInteger toNanoCoins(int coins, int cents) {
checkArgument(cents < 100); checkArgument(cents < 100);
checkArgument(cents >= 0);
checkArgument(coins >= 0);
checkArgument(coins < NetworkParameters.MAX_MONEY.divide(Utils.COIN).longValue());
BigInteger bi = BigInteger.valueOf(coins).multiply(COIN); BigInteger bi = BigInteger.valueOf(coins).multiply(COIN);
bi = bi.add(BigInteger.valueOf(cents).multiply(CENT)); bi = bi.add(BigInteger.valueOf(cents).multiply(CENT));
return bi; return bi;
@ -106,10 +108,15 @@ public class Utils {
* This takes string in a format understood by {@link BigDecimal#BigDecimal(String)}, * This takes string in a format understood by {@link BigDecimal#BigDecimal(String)},
* for example "0", "1", "0.10", "1.23E3", "1234.5E-5". * for example "0", "1", "0.10", "1.23E3", "1234.5E-5".
* *
* @throws ArithmeticException if you try to specify fractional nanocoins * @throws ArithmeticException if you try to specify fractional nanocoins, or nanocoins out of range.
*/ */
public static BigInteger toNanoCoins(String coins) { public static BigInteger toNanoCoins(String coins) {
return new BigDecimal(coins).movePointRight(8).toBigIntegerExact(); BigInteger bigint = new BigDecimal(coins).movePointRight(8).toBigIntegerExact();
if (bigint.compareTo(BigInteger.ZERO) < 0)
throw new ArithmeticException("Negative coins specified");
if (bigint.compareTo(NetworkParameters.MAX_MONEY) > 0)
throw new ArithmeticException("Amount larger than the total quantity of Bitcoins possible specified.");
return bigint;
} }
public static void uint32ToByteArrayBE(long val, byte[] out, int offset) { public static void uint32ToByteArrayBE(long val, byte[] out, int offset) {

View File

@ -23,6 +23,7 @@ import java.math.BigInteger;
import static com.google.bitcoin.core.Utils.*; import static com.google.bitcoin.core.Utils.*;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.fail;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class UtilsTest { public class UtilsTest {
@ -42,10 +43,18 @@ public class UtilsTest {
// int version // int version
assertEquals(CENT, toNanoCoins(0, 1)); assertEquals(CENT, toNanoCoins(0, 1));
// TODO: should this really pass? try {
assertEquals(COIN.subtract(CENT), toNanoCoins(1, -1)); toNanoCoins(1, -1);
assertEquals(COIN.negate(), toNanoCoins(-1, 0)); fail();
assertEquals(COIN.negate(), toNanoCoins("-1")); } catch (IllegalArgumentException e) {}
try {
toNanoCoins(-1, 0);
fail();
} catch (IllegalArgumentException e) {}
try {
toNanoCoins("-1");
fail();
} catch (ArithmeticException e) {}
} }
@Test @Test
@ -71,7 +80,6 @@ public class UtilsTest {
assertEquals("0.0015", bitcoinValueToPlainString(BigInteger.valueOf(150000))); assertEquals("0.0015", bitcoinValueToPlainString(BigInteger.valueOf(150000)));
assertEquals("1.23", bitcoinValueToPlainString(toNanoCoins("1.23"))); assertEquals("1.23", bitcoinValueToPlainString(toNanoCoins("1.23")));
assertEquals("-1.23", bitcoinValueToPlainString(toNanoCoins("-1.23")));
assertEquals("0.1", bitcoinValueToPlainString(toNanoCoins("0.1"))); assertEquals("0.1", bitcoinValueToPlainString(toNanoCoins("0.1")));
assertEquals("1.1", bitcoinValueToPlainString(toNanoCoins("1.1"))); assertEquals("1.1", bitcoinValueToPlainString(toNanoCoins("1.1")));
@ -81,7 +89,10 @@ public class UtilsTest {
assertEquals("54321.12345", bitcoinValueToPlainString(toNanoCoins("54321.12345"))); assertEquals("54321.12345", bitcoinValueToPlainString(toNanoCoins("54321.12345")));
assertEquals("654321.123456", bitcoinValueToPlainString(toNanoCoins("654321.123456"))); assertEquals("654321.123456", bitcoinValueToPlainString(toNanoCoins("654321.123456")));
assertEquals("7654321.1234567", bitcoinValueToPlainString(toNanoCoins("7654321.1234567"))); assertEquals("7654321.1234567", bitcoinValueToPlainString(toNanoCoins("7654321.1234567")));
try {
assertEquals("87654321.12345678", bitcoinValueToPlainString(toNanoCoins("87654321.12345678"))); assertEquals("87654321.12345678", bitcoinValueToPlainString(toNanoCoins("87654321.12345678")));
Assert.fail(); // More than MAX_MONEY
} catch (Exception e) {}
// check there are no trailing zeros // check there are no trailing zeros
assertEquals("1", bitcoinValueToPlainString(toNanoCoins("1.0"))); assertEquals("1", bitcoinValueToPlainString(toNanoCoins("1.0")));

View File

@ -44,14 +44,6 @@ public class BitcoinURITest {
// example with spaces, ampersand and plus // example with spaces, ampersand and plus
assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello%20World&message=Mess%20%26%20age%20%2B%20hope", BitcoinURI.convertToBitcoinURI(goodAddress, Utils.toNanoCoins("12.34"), "Hello World", "Mess & age + hope")); assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello%20World&message=Mess%20%26%20age%20%2B%20hope", BitcoinURI.convertToBitcoinURI(goodAddress, Utils.toNanoCoins("12.34"), "Hello World", "Mess & age + hope"));
// amount negative
try {
BitcoinURI.convertToBitcoinURI(goodAddress, Utils.toNanoCoins("-0.1"), "hope", "glory");
fail("Expecting IllegalArgumentException");
} catch (IllegalArgumentException e) {
assertTrue(e.getMessage().contains("Amount must be positive"));
}
// no amount, label present, message present // no amount, label present, message present
assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?label=Hello&message=glory", BitcoinURI.convertToBitcoinURI(goodAddress, null, "Hello", "glory")); assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?label=Hello&message=glory", BitcoinURI.convertToBitcoinURI(goodAddress, null, "Hello", "glory"));
@ -164,8 +156,8 @@ public class BitcoinURITest {
public void testGood_Amount() throws BitcoinURIParseException { public void testGood_Amount() throws BitcoinURIParseException {
// Test the decimal parsing // Test the decimal parsing
testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
+ "?amount=9876543210.12345678"); + "?amount=6543210.12345678");
assertEquals("987654321012345678", testObject.getAmount().toString()); assertEquals("654321012345678", testObject.getAmount().toString());
// Test the decimal parsing // Test the decimal parsing
testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
@ -174,8 +166,8 @@ public class BitcoinURITest {
// Test the integer parsing // Test the integer parsing
testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
+ "?amount=9876543210"); + "?amount=6543210");
assertEquals("987654321000000000", testObject.getAmount().toString()); assertEquals("654321000000000", testObject.getAmount().toString());
} }
/** /**
@ -246,9 +238,9 @@ public class BitcoinURITest {
@Test @Test
public void testGood_Combinations() throws BitcoinURIParseException { public void testGood_Combinations() throws BitcoinURIParseException {
testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
+ "?amount=9876543210&label=Hello%20World&message=Be%20well"); + "?amount=6543210&label=Hello%20World&message=Be%20well");
assertEquals( assertEquals(
"BitcoinURI['address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH','amount'='987654321000000000','label'='Hello World','message'='Be well']", "BitcoinURI['address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH','amount'='654321000000000','label'='Hello World','message'='Be well']",
testObject.toString()); testObject.toString());
} }
@ -406,4 +398,22 @@ public class BitcoinURITest {
assertEquals("1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH", uri.getAddress().toString()); assertEquals("1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH", uri.getAddress().toString());
assertEquals(Utils.toNanoCoins(0, 1), uri.getAmount()); assertEquals(Utils.toNanoCoins(0, 1), uri.getAmount());
} }
@Test(expected = BitcoinURIParseException.class)
public void testBad_AmountTooPrecise() throws BitcoinURIParseException {
new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
+ "?amount=0.123456789");
}
@Test(expected = BitcoinURIParseException.class)
public void testBad_NegativeAmount() throws BitcoinURIParseException {
new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
+ "?amount=-1");
}
@Test(expected = BitcoinURIParseException.class)
public void testBad_TooLargeAmount() throws BitcoinURIParseException {
new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS
+ "?amount=100000000");
}
} }