mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-02-25 07:07:39 +01:00
Changes to the Coin
class: some convenience comparison operators;
`parseCoin()` now accepts negative values; the check for an excessive value is moved to the constructor from `parseCoin()` and uses `checkArgument()`; some `Coin`-type constants broken out into one `long` one `Coin` in order to be usable in the constructor. Corresponding tests included. The `BitcoinURI` class constructor throws exception on parsing a negative amount, which is needed now that `Coin` class accepts negative amounts.
This commit is contained in:
parent
e2ebe69aae
commit
e8048cb672
6 changed files with 108 additions and 17 deletions
|
@ -35,6 +35,11 @@ public final class Coin implements Comparable<Coin>, Serializable {
|
|||
*/
|
||||
public static final int NUM_COIN_DECIMALS = 8;
|
||||
|
||||
/**
|
||||
* The number of satoshis equal to one bitcoin.
|
||||
*/
|
||||
private static final long COIN_VALUE = LongMath.pow(10, NUM_COIN_DECIMALS);
|
||||
|
||||
/**
|
||||
* Zero Bitcoins.
|
||||
*/
|
||||
|
@ -43,7 +48,7 @@ public final class Coin implements Comparable<Coin>, Serializable {
|
|||
/**
|
||||
* One Bitcoin.
|
||||
*/
|
||||
public static final Coin COIN = Coin.valueOf(LongMath.pow(10, NUM_COIN_DECIMALS));
|
||||
public static final Coin COIN = Coin.valueOf(COIN_VALUE);
|
||||
|
||||
/**
|
||||
* 0.01 Bitcoins. This unit is not really used much.
|
||||
|
@ -66,7 +71,11 @@ public final class Coin implements Comparable<Coin>, Serializable {
|
|||
public static final Coin SATOSHI = Coin.valueOf(1);
|
||||
|
||||
public static final Coin FIFTY_COINS = COIN.multiply(50);
|
||||
public static final Coin NEGATIVE_ONE = Coin.valueOf(-1);
|
||||
|
||||
/**
|
||||
* Represents a monetary value of minus one satoshi.
|
||||
*/
|
||||
public static final Coin NEGATIVE_SATOSHI = Coin.valueOf(-1);
|
||||
|
||||
/**
|
||||
* The number of satoshis of this monetary value.
|
||||
|
@ -74,6 +83,8 @@ public final class Coin implements Comparable<Coin>, Serializable {
|
|||
public final long value;
|
||||
|
||||
private Coin(final long satoshis) {
|
||||
checkArgument(Math.abs(satoshis) <= COIN_VALUE * NetworkParameters.MAX_COINS,
|
||||
"%s satoshis exceeds maximum possible quantity of Bitcoin.", satoshis);
|
||||
this.value = satoshis;
|
||||
}
|
||||
|
||||
|
@ -99,15 +110,10 @@ public final class Coin implements Comparable<Coin>, Serializable {
|
|||
* 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 satoshis, or a value out of range.
|
||||
* @throws IllegalArgumentException if you try to specify fractional satoshis, or a value out of range.
|
||||
*/
|
||||
public static Coin parseCoin(final String str) {
|
||||
Coin coin = Coin.valueOf(new BigDecimal(str).movePointRight(8).toBigIntegerExact().longValue());
|
||||
if (coin.signum() < 0)
|
||||
throw new ArithmeticException("Negative coins specified");
|
||||
if (coin.compareTo(NetworkParameters.MAX_MONEY) > 0)
|
||||
throw new ArithmeticException("Amount larger than the total quantity of Bitcoins possible specified.");
|
||||
return coin;
|
||||
return Coin.valueOf(new BigDecimal(str).movePointRight(8).toBigIntegerExact().longValue());
|
||||
}
|
||||
|
||||
public Coin add(final Coin value) {
|
||||
|
@ -134,6 +140,46 @@ public final class Coin implements Comparable<Coin>, Serializable {
|
|||
return this.value / divisor.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if and only if this instance represents a monetary value greater than zero,
|
||||
* otherwise false.
|
||||
*/
|
||||
public boolean isPositive() {
|
||||
return signum() == 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if and only if this instance represents a monetary value less than zero,
|
||||
* otherwise false.
|
||||
*/
|
||||
public boolean isNegative() {
|
||||
return signum() == -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if and only if this instance represents zero monetary value,
|
||||
* otherwise false.
|
||||
*/
|
||||
public boolean isZero() {
|
||||
return signum() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the monetary value represented by this instance is greater than that
|
||||
* of the given other Coin, otherwise false.
|
||||
*/
|
||||
public boolean isGreaterThan(Coin other) {
|
||||
return compareTo(other) > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the monetary value represented by this instance is less than that
|
||||
* of the given other Coin, otherwise false.
|
||||
*/
|
||||
public boolean isLessThan(Coin other) {
|
||||
return compareTo(other) < 0;
|
||||
}
|
||||
|
||||
public Coin shiftLeft(final int n) {
|
||||
return new Coin(this.value << n);
|
||||
}
|
||||
|
|
|
@ -130,10 +130,15 @@ public abstract class NetworkParameters implements Serializable {
|
|||
*/
|
||||
public static final int BIP16_ENFORCE_TIME = 1333238400;
|
||||
|
||||
/**
|
||||
* The maximum number of coins to be generated
|
||||
*/
|
||||
public static final long MAX_COINS = 21000000;
|
||||
|
||||
/**
|
||||
* The maximum money to be generated
|
||||
*/
|
||||
public static final Coin MAX_MONEY = COIN.multiply(21000000);
|
||||
public static final Coin MAX_MONEY = COIN.multiply(MAX_COINS);
|
||||
|
||||
/** Alias for TestNet3Params.get(), use that instead. */
|
||||
@Deprecated
|
||||
|
|
|
@ -1093,7 +1093,7 @@ public class Transaction extends ChildMessage implements Serializable {
|
|||
// that position are "nulled out". Unintuitively, the value in a "null" transaction is set to -1.
|
||||
this.outputs = new ArrayList<TransactionOutput>(this.outputs.subList(0, inputIndex + 1));
|
||||
for (int i = 0; i < inputIndex; i++)
|
||||
this.outputs.set(i, new TransactionOutput(params, this, Coin.NEGATIVE_ONE, new byte[] {}));
|
||||
this.outputs.set(i, new TransactionOutput(params, this, Coin.NEGATIVE_SATOSHI, new byte[] {}));
|
||||
// The signature isn't broken by new versions of the transaction issued by other parties.
|
||||
for (int i = 0; i < inputs.size(); i++)
|
||||
if (i != inputIndex)
|
||||
|
|
|
@ -112,7 +112,7 @@ public class TransactionOutput extends ChildMessage implements Serializable {
|
|||
super(params);
|
||||
// Negative values obviously make no sense, except for -1 which is used as a sentinel value when calculating
|
||||
// SIGHASH_SINGLE signatures, so unfortunately we have to allow that here.
|
||||
checkArgument(value.signum() >= 0 || value.equals(Coin.NEGATIVE_ONE), "Negative values not allowed");
|
||||
checkArgument(value.signum() >= 0 || value.equals(Coin.NEGATIVE_SATOSHI), "Negative values not allowed");
|
||||
checkArgument(value.compareTo(NetworkParameters.MAX_MONEY) < 0, "Values larger than MAX_MONEY not allowed");
|
||||
this.value = value;
|
||||
this.scriptBytes = scriptBytes;
|
||||
|
|
|
@ -209,8 +209,10 @@ public class BitcoinURI {
|
|||
// Decode the amount (contains an optional decimal component to 8dp).
|
||||
try {
|
||||
Coin amount = Coin.parseCoin(valueToken);
|
||||
if (amount.signum() < 0)
|
||||
throw new ArithmeticException("Negative coins specified");
|
||||
putWithValidation(FIELD_AMOUNT, amount);
|
||||
} catch (NumberFormatException e) {
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new OptionalFieldValidationException(String.format("'%s' is not a valid amount", valueToken), e);
|
||||
} catch (ArithmeticException e) {
|
||||
throw new OptionalFieldValidationException(String.format("'%s' has too many decimal places", valueToken), e);
|
||||
|
|
|
@ -17,7 +17,11 @@
|
|||
package com.google.bitcoin.core;
|
||||
|
||||
import static com.google.bitcoin.core.Coin.*;
|
||||
import static com.google.bitcoin.core.NetworkParameters.MAX_MONEY;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import org.junit.Assert;
|
||||
|
@ -31,14 +35,32 @@ public class CoinTest {
|
|||
assertEquals(CENT, parseCoin("0.01"));
|
||||
assertEquals(CENT, parseCoin("1E-2"));
|
||||
assertEquals(COIN.add(CENT), parseCoin("1.01"));
|
||||
assertEquals(COIN.negate(), parseCoin("-1"));
|
||||
try {
|
||||
parseCoin("2E-20");
|
||||
org.junit.Assert.fail("should not have accepted fractional satoshis");
|
||||
} catch (ArithmeticException e) {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValueOf() {
|
||||
// int version
|
||||
assertEquals(CENT, valueOf(0, 1));
|
||||
assertEquals(SATOSHI, valueOf(1));
|
||||
assertEquals(NEGATIVE_SATOSHI, valueOf(-1));
|
||||
assertEquals(MAX_MONEY, valueOf(MAX_MONEY.value));
|
||||
assertEquals(MAX_MONEY.negate(), valueOf(MAX_MONEY.value * -1));
|
||||
try {
|
||||
valueOf(MAX_MONEY.value + 1);
|
||||
org.junit.Assert.fail("should not have accepted too-great a monetary value");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
try {
|
||||
valueOf( (MAX_MONEY.value * -1) - 1);
|
||||
org.junit.Assert.fail("should not have accepted too-little a monetary value");
|
||||
} catch (IllegalArgumentException e) {
|
||||
}
|
||||
|
||||
try {
|
||||
valueOf(1, -1);
|
||||
|
@ -48,10 +70,26 @@ public class CoinTest {
|
|||
valueOf(-1, 0);
|
||||
fail();
|
||||
} catch (IllegalArgumentException e) {}
|
||||
try {
|
||||
parseCoin("-1");
|
||||
fail();
|
||||
} catch (ArithmeticException e) {}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperators() {
|
||||
assertTrue(SATOSHI.isPositive());
|
||||
assertFalse(SATOSHI.isNegative());
|
||||
assertFalse(SATOSHI.isZero());
|
||||
assertFalse(NEGATIVE_SATOSHI.isPositive());
|
||||
assertTrue(NEGATIVE_SATOSHI.isNegative());
|
||||
assertFalse(NEGATIVE_SATOSHI.isZero());
|
||||
assertFalse(ZERO.isPositive());
|
||||
assertFalse(ZERO.isNegative());
|
||||
assertTrue(ZERO.isZero());
|
||||
|
||||
assertTrue(valueOf(2).isGreaterThan(valueOf(1)));
|
||||
assertFalse(valueOf(2).isGreaterThan(valueOf(2)));
|
||||
assertFalse(valueOf(1).isGreaterThan(valueOf(2)));
|
||||
assertTrue(valueOf(1).isLessThan(valueOf(2)));
|
||||
assertFalse(valueOf(2).isLessThan(valueOf(2)));
|
||||
assertFalse(valueOf(2).isLessThan(valueOf(1)));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
Loading…
Add table
Reference in a new issue