Skip to content

Commit ad4bee6

Browse files
committed
Added Coin Tests
1 parent 0901588 commit ad4bee6

9 files changed

+615
-3
lines changed

src/main/java/org/twostack/bitcoin/Coin.java

+72
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import java.math.BigDecimal;
2525

2626
import static com.google.common.base.Preconditions.checkArgument;
27+
import static org.twostack.bitcoin.transaction.Transaction.MAX_COINS;
2728

2829
/**
2930
* Represents a monetary Bitcoin value. This class is immutable.
@@ -36,6 +37,7 @@ public final class Coin implements Monetary, Comparable<Coin>, Serializable {
3637
*/
3738
public static final int SMALLEST_UNIT_EXPONENT = 8;
3839

40+
3941
/**
4042
* The number of satoshis equal to one bitcoin.
4143
*/
@@ -132,6 +134,76 @@ public static Coin parseCoin(final String str) {
132134
}
133135
}
134136

137+
/**
138+
* Convert a decimal amount of BTC into satoshis.
139+
*
140+
* @param coins number of coins
141+
* @return number of satoshis
142+
*/
143+
public static long btcToSatoshi(BigDecimal coins) {
144+
return coins.movePointRight(SMALLEST_UNIT_EXPONENT).longValueExact();
145+
}
146+
147+
/**
148+
* Convert an amount in satoshis to an amount in BTC.
149+
*
150+
* @param satoshis number of satoshis
151+
* @return number of bitcoins (in BTC)
152+
*/
153+
public static BigDecimal satoshiToBtc(long satoshis) {
154+
return new BigDecimal(satoshis).movePointLeft(SMALLEST_UNIT_EXPONENT);
155+
}
156+
157+
/**
158+
* Create a {@code Coin} from a decimal amount of BTC.
159+
*
160+
* @param coins number of coins (in BTC)
161+
* @return {@code Coin} object containing value in satoshis
162+
*/
163+
public static Coin ofBtc(BigDecimal coins) {
164+
return Coin.valueOf(btcToSatoshi(coins));
165+
}
166+
167+
/**
168+
* Create a {@code Coin} from a long integer number of satoshis.
169+
*
170+
* @param satoshis number of satoshis
171+
* @return {@code Coin} object containing value in satoshis
172+
*/
173+
public static Coin ofSat(long satoshis) {
174+
return Coin.valueOf(satoshis);
175+
}
176+
177+
178+
179+
/**
180+
* Convert to number of bitcoin (in BTC)
181+
*
182+
* @return decimal number of bitcoin (in BTC)
183+
*/
184+
public BigDecimal toBtc() {
185+
return satoshiToBtc(this.value);
186+
}
187+
188+
/**
189+
* Create a {@code Coin} by parsing a {@code String} amount expressed in "the way humans are used to".
190+
* The amount is cut to satoshi precision.
191+
*
192+
* @param str string in a format understood by {@link BigDecimal#BigDecimal(String)}, for example "0", "1", "0.10",
193+
* * "1.23E3", "1234.5E-5".
194+
* @return {@code Coin} object containing value in satoshis
195+
* @throws IllegalArgumentException
196+
* if you try to specify a value out of range.
197+
*/
198+
public static Coin parseCoinInexact(final String str) {
199+
try {
200+
long satoshis = new BigDecimal(str).movePointRight(SMALLEST_UNIT_EXPONENT).longValue();
201+
return Coin.valueOf(satoshis);
202+
} catch (ArithmeticException e) {
203+
throw new IllegalArgumentException(e); // Repackage exception to honor method contract
204+
}
205+
}
206+
135207
public Coin add(final Coin value) {
136208
return new Coin(LongMath.checkedAdd(this.value, value.value));
137209
}

src/main/java/org/twostack/bitcoin/utils/MonetaryFormat.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -52,11 +52,11 @@ public final class MonetaryFormat {
5252
/** Standard format for fiat amounts. */
5353
public static final MonetaryFormat FIAT = new MonetaryFormat().shift(0).minDecimals(2).repeatOptionalDecimals(2, 1);
5454
/** Currency code for base 1 Bitcoin. */
55-
public static final String CODE_BTC = "BCH";
55+
public static final String CODE_BTC = "BSV";
5656
/** Currency code for base 1/1000 Bitcoin. */
57-
public static final String CODE_MBTC = "mBCH";
57+
public static final String CODE_MBTC = "mBSV";
5858
/** Currency code for base 1/1000000 Bitcoin. */
59-
public static final String CODE_UBTC = "µBCH";
59+
public static final String CODE_UBTC = "µBSV";
6060

6161
public static final int MAX_DECIMALS = 8;
6262

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
/*
2+
* Copyright 2014 Andreas Schildbach
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.twostack.bitcoin;
18+
19+
import org.hamcrest.Matchers;
20+
import org.junit.Test;
21+
22+
import java.math.BigDecimal;
23+
24+
import static org.twostack.bitcoin.Coin.*;
25+
import static org.twostack.bitcoin.transaction.Transaction.MAX_MONEY;
26+
import static org.junit.Assert.*;
27+
28+
public class CoinTest {
29+
30+
@Test
31+
public void testParseCoin() {
32+
// String version
33+
assertEquals(CENT, parseCoin("0.01"));
34+
assertEquals(CENT, parseCoin("1E-2"));
35+
assertEquals(COIN.add(CENT), parseCoin("1.01"));
36+
assertEquals(COIN.negate(), parseCoin("-1"));
37+
try {
38+
parseCoin("2E-20");
39+
org.junit.Assert.fail("should not have accepted fractional satoshis");
40+
} catch (IllegalArgumentException expected) {
41+
} catch (Exception e) {
42+
org.junit.Assert.fail("should throw IllegalArgumentException");
43+
}
44+
assertEquals(1, parseCoin("0.00000001").value);
45+
assertEquals(1, parseCoin("0.000000010").value);
46+
}
47+
48+
@Test(expected = IllegalArgumentException.class)
49+
public void testParseCoinOverprecise() {
50+
parseCoin("0.000000011");
51+
}
52+
53+
@Test
54+
public void testParseCoinInexact() {
55+
assertEquals(1, parseCoinInexact("0.00000001").value);
56+
assertEquals(1, parseCoinInexact("0.000000011").value);
57+
}
58+
59+
@Test
60+
public void testValueOf() {
61+
// int version
62+
assertEquals(CENT, valueOf(0, 1));
63+
assertEquals(SATOSHI, valueOf(1));
64+
assertEquals(NEGATIVE_SATOSHI, valueOf(-1));
65+
assertEquals(Coin.valueOf(MAX_MONEY), valueOf(MAX_MONEY));
66+
assertEquals(valueOf(MAX_MONEY).negate().value, valueOf(MAX_MONEY).value * -1);
67+
valueOf(MAX_MONEY + 1);
68+
valueOf((MAX_MONEY * -1) - 1);
69+
valueOf(Long.MAX_VALUE);
70+
valueOf(Long.MIN_VALUE);
71+
72+
try {
73+
valueOf(1, -1);
74+
fail();
75+
} catch (IllegalArgumentException e) {}
76+
try {
77+
valueOf(-1, 0);
78+
fail();
79+
} catch (IllegalArgumentException e) {}
80+
}
81+
82+
83+
@Test
84+
public void testBtcToSatoshi() {
85+
assertEquals(Long.MIN_VALUE, btcToSatoshi(new BigDecimal("-92233720368.54775808")));
86+
assertEquals(0L, btcToSatoshi(BigDecimal.ZERO));
87+
assertEquals(COIN.value, btcToSatoshi(BigDecimal.ONE));
88+
assertEquals(Long.MAX_VALUE, btcToSatoshi(new BigDecimal("92233720368.54775807")));
89+
}
90+
91+
@Test(expected = ArithmeticException.class)
92+
public void testBtcToSatoshi_tooSmall() {
93+
btcToSatoshi(new BigDecimal("-92233720368.54775809")); // .00000001 less than minimum value
94+
}
95+
96+
@Test(expected = ArithmeticException.class)
97+
public void testBtcToSatoshi_tooBig() {
98+
btcToSatoshi(new BigDecimal("92233720368.54775808")); // .00000001 more than maximum value
99+
}
100+
101+
@Test(expected = ArithmeticException.class)
102+
public void testBtcToSatoshi_tooPrecise1() {
103+
btcToSatoshi(new BigDecimal("0.000000001")); // More than SMALLEST_UNIT_EXPONENT precision
104+
}
105+
106+
@Test(expected = ArithmeticException.class)
107+
public void testBtcToSatoshi_tooPrecise2() {
108+
btcToSatoshi(new BigDecimal("92233720368.547758079")); // More than SMALLEST_UNIT_EXPONENT precision
109+
}
110+
111+
@Test
112+
public void testSatoshiToBtc() {
113+
assertThat(new BigDecimal("-92233720368.54775808"), Matchers.comparesEqualTo(satoshiToBtc(Long.MIN_VALUE)));
114+
assertThat(new BigDecimal("-0.00000001"), Matchers.comparesEqualTo(satoshiToBtc(NEGATIVE_SATOSHI.value)));
115+
assertThat(BigDecimal.ZERO, Matchers.comparesEqualTo(satoshiToBtc(0L)));
116+
assertThat(new BigDecimal("0.00000001"), Matchers.comparesEqualTo(satoshiToBtc(SATOSHI.value)));
117+
assertThat(BigDecimal.ONE, Matchers.comparesEqualTo(satoshiToBtc(COIN.value)));
118+
assertThat(new BigDecimal(50), Matchers.comparesEqualTo(satoshiToBtc(FIFTY_COINS.value)));
119+
assertThat(new BigDecimal("92233720368.54775807"), Matchers.comparesEqualTo(satoshiToBtc(Long.MAX_VALUE)));
120+
}
121+
122+
@Test
123+
public void testOfBtc() {
124+
assertEquals(Coin.valueOf(Long.MIN_VALUE), Coin.ofBtc(new BigDecimal("-92233720368.54775808")));
125+
assertEquals(ZERO, Coin.ofBtc(BigDecimal.ZERO));
126+
assertEquals(COIN, Coin.ofBtc(BigDecimal.ONE));
127+
assertEquals(Coin.valueOf(Long.MAX_VALUE), Coin.ofBtc(new BigDecimal("92233720368.54775807")));
128+
}
129+
130+
@Test
131+
public void testOperators() {
132+
assertTrue(SATOSHI.isPositive());
133+
assertFalse(SATOSHI.isNegative());
134+
assertFalse(SATOSHI.isZero());
135+
assertFalse(NEGATIVE_SATOSHI.isPositive());
136+
assertTrue(NEGATIVE_SATOSHI.isNegative());
137+
assertFalse(NEGATIVE_SATOSHI.isZero());
138+
assertFalse(ZERO.isPositive());
139+
assertFalse(ZERO.isNegative());
140+
assertTrue(ZERO.isZero());
141+
142+
assertTrue(valueOf(2).isGreaterThan(valueOf(1)));
143+
assertFalse(valueOf(2).isGreaterThan(valueOf(2)));
144+
assertFalse(valueOf(1).isGreaterThan(valueOf(2)));
145+
assertTrue(valueOf(1).isLessThan(valueOf(2)));
146+
assertFalse(valueOf(2).isLessThan(valueOf(2)));
147+
assertFalse(valueOf(2).isLessThan(valueOf(1)));
148+
}
149+
150+
@Test(expected = ArithmeticException.class)
151+
public void testMultiplicationOverflow() {
152+
Coin.valueOf(Long.MAX_VALUE).multiply(2);
153+
}
154+
155+
@Test(expected = ArithmeticException.class)
156+
public void testMultiplicationUnderflow() {
157+
Coin.valueOf(Long.MIN_VALUE).multiply(2);
158+
}
159+
160+
@Test(expected = ArithmeticException.class)
161+
public void testAdditionOverflow() {
162+
Coin.valueOf(Long.MAX_VALUE).add(Coin.SATOSHI);
163+
}
164+
165+
@Test(expected = ArithmeticException.class)
166+
public void testSubtractionUnderflow() {
167+
Coin.valueOf(Long.MIN_VALUE).subtract(Coin.SATOSHI);
168+
}
169+
170+
@Test
171+
public void testToBtc() {
172+
assertThat(new BigDecimal("-92233720368.54775808"), Matchers.comparesEqualTo(Coin.valueOf(Long.MIN_VALUE).toBtc()));
173+
assertThat(new BigDecimal("-0.00000001"), Matchers.comparesEqualTo(NEGATIVE_SATOSHI.toBtc()));
174+
assertThat(BigDecimal.ZERO, Matchers.comparesEqualTo(ZERO.toBtc()));
175+
assertThat(new BigDecimal("0.00000001"), Matchers.comparesEqualTo(SATOSHI.toBtc()));
176+
assertThat(BigDecimal.ONE, Matchers.comparesEqualTo(COIN.toBtc()));
177+
assertThat(new BigDecimal(50), Matchers.comparesEqualTo(FIFTY_COINS.toBtc()));
178+
assertThat(new BigDecimal("92233720368.54775807"), Matchers.comparesEqualTo(Coin.valueOf(Long.MAX_VALUE).toBtc()));
179+
}
180+
181+
@Test
182+
public void testToFriendlyString() {
183+
assertEquals("1.00 BSV", COIN.toFriendlyString());
184+
assertEquals("1.23 BSV", valueOf(1, 23).toFriendlyString());
185+
assertEquals("0.001 BSV", COIN.divide(1000).toFriendlyString());
186+
assertEquals("-1.23 BSV", valueOf(1, 23).negate().toFriendlyString());
187+
}
188+
189+
/**
190+
* Test the bitcoinValueToPlainString amount formatter
191+
*/
192+
@Test
193+
public void testToPlainString() {
194+
assertEquals("0.0015", Coin.valueOf(150000).toPlainString());
195+
assertEquals("1.23", parseCoin("1.23").toPlainString());
196+
197+
assertEquals("0.1", parseCoin("0.1").toPlainString());
198+
assertEquals("1.1", parseCoin("1.1").toPlainString());
199+
assertEquals("21.12", parseCoin("21.12").toPlainString());
200+
assertEquals("321.123", parseCoin("321.123").toPlainString());
201+
assertEquals("4321.1234", parseCoin("4321.1234").toPlainString());
202+
assertEquals("54321.12345", parseCoin("54321.12345").toPlainString());
203+
assertEquals("654321.123456", parseCoin("654321.123456").toPlainString());
204+
assertEquals("7654321.1234567", parseCoin("7654321.1234567").toPlainString());
205+
assertEquals("87654321.12345678", parseCoin("87654321.12345678").toPlainString());
206+
207+
// check there are no trailing zeros
208+
assertEquals("1", parseCoin("1.0").toPlainString());
209+
assertEquals("2", parseCoin("2.00").toPlainString());
210+
assertEquals("3", parseCoin("3.000").toPlainString());
211+
assertEquals("4", parseCoin("4.0000").toPlainString());
212+
assertEquals("5", parseCoin("5.00000").toPlainString());
213+
assertEquals("6", parseCoin("6.000000").toPlainString());
214+
assertEquals("7", parseCoin("7.0000000").toPlainString());
215+
assertEquals("8", parseCoin("8.00000000").toPlainString());
216+
}
217+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright 2011 Google Inc.
3+
* Copyright 2014 Andreas Schildbach
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.twostack.bitcoin.base58;
19+
20+
import org.junit.Test;
21+
import org.twostack.bitcoin.address.Base58;
22+
import org.twostack.bitcoin.exception.AddressFormatException;
23+
24+
public class Base58DecodeCheckedInvalidChecksumTest {
25+
26+
@Test(expected = AddressFormatException.InvalidChecksum.class)
27+
public void testDecodeChecked_invalidChecksum() {
28+
Base58.decodeChecked("4stwEBjT6FYyVW");
29+
}
30+
}

0 commit comments

Comments
 (0)