diff --git a/core/src/main/java/com/google/bitcoin/core/Utils.java b/core/src/main/java/com/google/bitcoin/core/Utils.java index ae927fc2..0f2b9191 100644 --- a/core/src/main/java/com/google/bitcoin/core/Utils.java +++ b/core/src/main/java/com/google/bitcoin/core/Utils.java @@ -29,7 +29,6 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import java.util.Date; -import java.util.concurrent.locks.ReentrantLock; import static com.google.common.base.Preconditions.checkArgument; @@ -75,6 +74,9 @@ public class Utils { */ public static BigInteger toNanoCoins(int coins, int cents) { 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); bi = bi.add(BigInteger.valueOf(cents).multiply(CENT)); return bi; @@ -106,10 +108,15 @@ public class Utils { * This takes string in a format understood by {@link BigDecimal#BigDecimal(String)}, * 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) { - 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) { diff --git a/core/src/test/java/com/google/bitcoin/core/UtilsTest.java b/core/src/test/java/com/google/bitcoin/core/UtilsTest.java index 21cf26c8..8eddeff3 100644 --- a/core/src/test/java/com/google/bitcoin/core/UtilsTest.java +++ b/core/src/test/java/com/google/bitcoin/core/UtilsTest.java @@ -23,6 +23,7 @@ import java.math.BigInteger; import static com.google.bitcoin.core.Utils.*; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.fail; import static org.junit.Assert.assertTrue; public class UtilsTest { @@ -42,10 +43,18 @@ public class UtilsTest { // int version assertEquals(CENT, toNanoCoins(0, 1)); - // TODO: should this really pass? - assertEquals(COIN.subtract(CENT), toNanoCoins(1, -1)); - assertEquals(COIN.negate(), toNanoCoins(-1, 0)); - assertEquals(COIN.negate(), toNanoCoins("-1")); + try { + toNanoCoins(1, -1); + fail(); + } catch (IllegalArgumentException e) {} + try { + toNanoCoins(-1, 0); + fail(); + } catch (IllegalArgumentException e) {} + try { + toNanoCoins("-1"); + fail(); + } catch (ArithmeticException e) {} } @Test @@ -71,8 +80,7 @@ public class UtilsTest { assertEquals("0.0015", bitcoinValueToPlainString(BigInteger.valueOf(150000))); assertEquals("1.23", bitcoinValueToPlainString(toNanoCoins("1.23"))); - assertEquals("-1.23", bitcoinValueToPlainString(toNanoCoins("-1.23"))); - + assertEquals("0.1", bitcoinValueToPlainString(toNanoCoins("0.1"))); assertEquals("1.1", bitcoinValueToPlainString(toNanoCoins("1.1"))); assertEquals("21.12", bitcoinValueToPlainString(toNanoCoins("21.12"))); @@ -81,7 +89,10 @@ public class UtilsTest { assertEquals("54321.12345", bitcoinValueToPlainString(toNanoCoins("54321.12345"))); assertEquals("654321.123456", bitcoinValueToPlainString(toNanoCoins("654321.123456"))); assertEquals("7654321.1234567", bitcoinValueToPlainString(toNanoCoins("7654321.1234567"))); - assertEquals("87654321.12345678", bitcoinValueToPlainString(toNanoCoins("87654321.12345678"))); + try { + assertEquals("87654321.12345678", bitcoinValueToPlainString(toNanoCoins("87654321.12345678"))); + Assert.fail(); // More than MAX_MONEY + } catch (Exception e) {} // check there are no trailing zeros assertEquals("1", bitcoinValueToPlainString(toNanoCoins("1.0"))); diff --git a/core/src/test/java/com/google/bitcoin/uri/BitcoinURITest.java b/core/src/test/java/com/google/bitcoin/uri/BitcoinURITest.java index 00ff558c..ea19f968 100644 --- a/core/src/test/java/com/google/bitcoin/uri/BitcoinURITest.java +++ b/core/src/test/java/com/google/bitcoin/uri/BitcoinURITest.java @@ -44,14 +44,6 @@ public class BitcoinURITest { // 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")); - // 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 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 { // Test the decimal parsing testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?amount=9876543210.12345678"); - assertEquals("987654321012345678", testObject.getAmount().toString()); + + "?amount=6543210.12345678"); + assertEquals("654321012345678", testObject.getAmount().toString()); // Test the decimal parsing testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS @@ -174,8 +166,8 @@ public class BitcoinURITest { // Test the integer parsing testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS - + "?amount=9876543210"); - assertEquals("987654321000000000", testObject.getAmount().toString()); + + "?amount=6543210"); + assertEquals("654321000000000", testObject.getAmount().toString()); } /** @@ -246,9 +238,9 @@ public class BitcoinURITest { @Test public void testGood_Combinations() throws BitcoinURIParseException { 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( - "BitcoinURI['address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH','amount'='987654321000000000','label'='Hello World','message'='Be well']", + "BitcoinURI['address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH','amount'='654321000000000','label'='Hello World','message'='Be well']", testObject.toString()); } @@ -406,4 +398,22 @@ public class BitcoinURITest { assertEquals("1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH", uri.getAddress().toString()); 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"); + } }