diff --git a/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java b/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java index f9801c70a..6e50bd89c 100644 --- a/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java +++ b/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java @@ -106,9 +106,20 @@ public class BitcoinURI { * @param uri The raw URI data to be parsed (see class comments for accepted formats) * @throws BitcoinURIParseException if the URI is not syntactically or semantically valid. */ - public BitcoinURI(String uri) throws BitcoinURIParseException { + public static BitcoinURI of(String uri) throws BitcoinURIParseException { // TODO: Discover (via Service Loader mechanism) the correct Network from the URI string - this(uri, BitcoinNetwork.MAINNET); + return new BitcoinURI(uri, BitcoinNetwork.MAINNET); + } + + /** + * Constructs a new object by trying to parse the input as a valid Bitcoin URI. + * + * @param uri The raw URI data to be parsed (see class comments for accepted formats) + * @param network The network the URI is from + * @throws BitcoinURIParseException If the input fails Bitcoin URI syntax and semantic checks. + */ + public static BitcoinURI of(String uri, @Nonnull Network network) throws BitcoinURIParseException { + return new BitcoinURI(uri, network); } /** @@ -119,21 +130,14 @@ public class BitcoinURI { * @param input The raw URI data to be parsed (see class comments for accepted formats) * * @throws BitcoinURIParseException If the input fails Bitcoin URI syntax and semantic checks. - * @deprecated Use {@link BitcoinURI#BitcoinURI(String, Network)} or {@link BitcoinURI#BitcoinURI(String)} + * @deprecated Use {@link BitcoinURI#of(String, Network)} or {@link BitcoinURI#of(String)} */ @Deprecated public BitcoinURI(@Nullable NetworkParameters params, String input) throws BitcoinURIParseException { this(input, params != null ? params.network() : BitcoinNetwork.MAINNET); } - /** - * Constructs a new object by trying to parse the input as a valid Bitcoin URI. - * - * @param input The raw URI data to be parsed (see class comments for accepted formats) - * @param network The network the URI is from - * @throws BitcoinURIParseException If the input fails Bitcoin URI syntax and semantic checks. - */ - public BitcoinURI(String input, @Nonnull Network network) throws BitcoinURIParseException { + private BitcoinURI(String input, @Nonnull Network network) throws BitcoinURIParseException { Objects.requireNonNull(network); Objects.requireNonNull(input); diff --git a/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java b/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java index 4964b8c25..d93e757a9 100644 --- a/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java +++ b/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java @@ -105,7 +105,7 @@ public class BitcoinURITest { @Test public void testGood_legacy() throws BitcoinURIParseException { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS, MAINNET); + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS, MAINNET); assertEquals(MAINNET_GOOD_ADDRESS, testObject.getAddress().toString()); assertNull("Unexpected amount", testObject.getAmount()); assertNull("Unexpected label", testObject.getLabel()); @@ -114,7 +114,7 @@ public class BitcoinURITest { @Test public void testGood_uppercaseScheme() throws BitcoinURIParseException { - testObject = new BitcoinURI(BITCOIN_SCHEME.toUpperCase(Locale.US) + ":" + MAINNET_GOOD_ADDRESS, MAINNET); + testObject = BitcoinURI.of(BITCOIN_SCHEME.toUpperCase(Locale.US) + ":" + MAINNET_GOOD_ADDRESS, MAINNET); assertEquals(MAINNET_GOOD_ADDRESS, testObject.getAddress().toString()); assertNull("Unexpected amount", testObject.getAmount()); assertNull("Unexpected label", testObject.getLabel()); @@ -123,7 +123,7 @@ public class BitcoinURITest { @Test public void testGood_segwit() throws BitcoinURIParseException { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_SEGWIT_ADDRESS, MAINNET); + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_SEGWIT_ADDRESS, MAINNET); assertEquals(MAINNET_GOOD_SEGWIT_ADDRESS, testObject.getAddress().toString()); assertNull("Unexpected amount", testObject.getAmount()); assertNull("Unexpected label", testObject.getLabel()); @@ -135,7 +135,7 @@ public class BitcoinURITest { @Test public void testBad_Scheme() { try { - testObject = new BitcoinURI("blimpcoin:" + MAINNET_GOOD_ADDRESS, MAINNET); + testObject = BitcoinURI.of("blimpcoin:" + MAINNET_GOOD_ADDRESS, MAINNET); fail("Expecting BitcoinURIParseException"); } catch (BitcoinURIParseException e) { } @@ -148,14 +148,14 @@ public class BitcoinURITest { public void testBad_BadSyntax() { // Various illegal characters try { - testObject = new BitcoinURI(BITCOIN_SCHEME + "|" + MAINNET_GOOD_ADDRESS, MAINNET); + testObject = BitcoinURI.of(BITCOIN_SCHEME + "|" + MAINNET_GOOD_ADDRESS, MAINNET); fail("Expecting BitcoinURIParseException"); } catch (BitcoinURIParseException e) { assertTrue(e.getMessage().contains("Bad URI syntax")); } try { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "\\", MAINNET); + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "\\", MAINNET); fail("Expecting BitcoinURIParseException"); } catch (BitcoinURIParseException e) { assertTrue(e.getMessage().contains("Bad URI syntax")); @@ -163,7 +163,7 @@ public class BitcoinURITest { // Separator without field try { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":", MAINNET); + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":", MAINNET); fail("Expecting BitcoinURIParseException"); } catch (BitcoinURIParseException e) { assertTrue(e.getMessage().contains("Bad URI syntax")); @@ -176,7 +176,7 @@ public class BitcoinURITest { @Test public void testBad_Address() { try { - testObject = new BitcoinURI(BITCOIN_SCHEME, MAINNET); + testObject = BitcoinURI.of(BITCOIN_SCHEME, MAINNET); fail("Expecting BitcoinURIParseException"); } catch (BitcoinURIParseException e) { } @@ -188,7 +188,7 @@ public class BitcoinURITest { @Test public void testBad_IncorrectAddressType() { try { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS, TESTNET); + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS, TESTNET); fail("Expecting BitcoinURIParseException"); } catch (BitcoinURIParseException e) { assertTrue(e.getMessage().contains("Bad address")); @@ -204,22 +204,22 @@ public class BitcoinURITest { @Test public void testGood_Amount() throws BitcoinURIParseException { // Test the decimal parsing - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=6543210.12345678", MAINNET); assertEquals("654321012345678", testObject.getAmount().toString()); // Test the decimal parsing - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=.12345678", MAINNET); assertEquals("12345678", testObject.getAmount().toString()); // Test the integer parsing - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=6543210", MAINNET); assertEquals("654321000000000", testObject.getAmount().toString()); // the maximum amount - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=" + new BigDecimal(Long.MAX_VALUE).movePointLeft(8), MAINNET); assertEquals(Long.MAX_VALUE, testObject.getAmount().longValue()); } @@ -232,7 +232,7 @@ public class BitcoinURITest { */ @Test public void testGood_Label() throws BitcoinURIParseException { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=Hello%20World", MAINNET); assertEquals("Hello World", testObject.getLabel()); } @@ -247,7 +247,7 @@ public class BitcoinURITest { public void testGood_LabelWithAmpersandAndPlus() throws BitcoinURIParseException { String testString = "Hello Earth & Mars + Venus"; String encodedLabel = BitcoinURI.encodeURLString(testString); - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=" + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=" + encodedLabel, MAINNET); assertEquals(testString, testObject.getLabel()); } @@ -263,7 +263,7 @@ public class BitcoinURITest { // Moscow in Russian in Cyrillic String moscowString = "\u041c\u043e\u0441\u043a\u0432\u0430"; String encodedLabel = BitcoinURI.encodeURLString(moscowString); - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=" + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=" + encodedLabel, MAINNET); assertEquals(moscowString, testObject.getLabel()); } @@ -276,7 +276,7 @@ public class BitcoinURITest { */ @Test public void testGood_Message() throws BitcoinURIParseException { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?message=Hello%20World", MAINNET); assertEquals("Hello World", testObject.getMessage()); } @@ -289,7 +289,7 @@ public class BitcoinURITest { */ @Test public void testGood_Combinations() throws BitcoinURIParseException { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=6543210&label=Hello%20World&message=Be%20well", MAINNET); assertEquals( "BitcoinURI['amount'='654321000000000','label'='Hello World','message'='Be well','address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH']", @@ -303,7 +303,7 @@ public class BitcoinURITest { public void testBad_Amount() { // Missing try { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=", MAINNET); fail("Expecting BitcoinURIParseException"); } catch (BitcoinURIParseException e) { @@ -312,7 +312,7 @@ public class BitcoinURITest { // Non-decimal (BIP 21) try { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=12X4", MAINNET); fail("Expecting BitcoinURIParseException"); } catch (BitcoinURIParseException e) { @@ -322,13 +322,13 @@ public class BitcoinURITest { @Test public void testEmpty_Label() throws BitcoinURIParseException { - assertNull(new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + assertNull(BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=", MAINNET).getLabel()); } @Test public void testEmpty_Message() throws BitcoinURIParseException { - assertNull(new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + assertNull(BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?message=", MAINNET).getMessage()); } @@ -338,7 +338,7 @@ public class BitcoinURITest { @Test public void testBad_Duplicated() { try { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?address=aardvark", MAINNET); fail("Expecting BitcoinURIParseException"); } catch (BitcoinURIParseException e) { @@ -348,7 +348,7 @@ public class BitcoinURITest { @Test public void testGood_ManyEquals() throws BitcoinURIParseException { - assertEquals("aardvark=zebra", new BitcoinURI(BITCOIN_SCHEME + ":" + assertEquals("aardvark=zebra", BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=aardvark=zebra", MAINNET).getLabel()); } @@ -361,7 +361,7 @@ public class BitcoinURITest { @Test public void testUnknown() throws BitcoinURIParseException { // Unknown not required field - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?aardvark=true", MAINNET); assertEquals("BitcoinURI['aardvark'='true','address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH']", testObject.toString()); @@ -369,7 +369,7 @@ public class BitcoinURITest { // Unknown not required field (isolated) try { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?aardvark", MAINNET); fail("Expecting BitcoinURIParseException"); } catch (BitcoinURIParseException e) { @@ -378,7 +378,7 @@ public class BitcoinURITest { // Unknown and required field try { - testObject = new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + testObject = BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?req-aardvark=true", MAINNET); fail("Expecting BitcoinURIParseException"); } catch (BitcoinURIParseException e) { @@ -390,34 +390,34 @@ public class BitcoinURITest { public void brokenURIs() throws BitcoinURIParseException { // Check we can parse the incorrectly formatted URIs produced by blockchain.info and its iPhone app. String str = "bitcoin://1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH?amount=0.01000000"; - BitcoinURI uri = new BitcoinURI(str); + BitcoinURI uri = BitcoinURI.of(str); assertEquals("1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH", uri.getAddress().toString()); assertEquals(CENT, uri.getAmount()); } @Test(expected = BitcoinURIParseException.class) public void testBad_AmountTooPrecise() throws BitcoinURIParseException { - new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=0.123456789", MAINNET); } @Test(expected = BitcoinURIParseException.class) public void testBad_NegativeAmount() throws BitcoinURIParseException { - new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=-1", MAINNET); } @Test(expected = BitcoinURIParseException.class) public void testBad_TooLargeAmount() throws BitcoinURIParseException { BigDecimal tooLargeByOne = new BigDecimal(Long.MAX_VALUE).add(BigDecimal.ONE); - new BitcoinURI(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + BitcoinURI.of(BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=" + tooLargeByOne.movePointLeft(8), MAINNET); } @Test public void testPaymentProtocolReq() throws Exception { // Non-backwards compatible form ... - BitcoinURI uri = new BitcoinURI("bitcoin:?r=https%3A%2F%2Fbitcoincore.org%2F%7Egavin%2Ff.php%3Fh%3Db0f02e7cea67f168e25ec9b9f9d584f9", TESTNET); + BitcoinURI uri = BitcoinURI.of("bitcoin:?r=https%3A%2F%2Fbitcoincore.org%2F%7Egavin%2Ff.php%3Fh%3Db0f02e7cea67f168e25ec9b9f9d584f9", TESTNET); assertEquals("https://bitcoincore.org/~gavin/f.php?h=b0f02e7cea67f168e25ec9b9f9d584f9", uri.getPaymentRequestUrl()); assertEquals(Collections.singletonList("https://bitcoincore.org/~gavin/f.php?h=b0f02e7cea67f168e25ec9b9f9d584f9"), uri.getPaymentRequestUrls()); @@ -426,14 +426,14 @@ public class BitcoinURITest { @Test public void testMultiplePaymentProtocolReq() throws Exception { - BitcoinURI uri = new BitcoinURI("bitcoin:?r=https%3A%2F%2Fbitcoincore.org%2F%7Egavin&r1=bt:112233445566", MAINNET); + BitcoinURI uri = BitcoinURI.of("bitcoin:?r=https%3A%2F%2Fbitcoincore.org%2F%7Egavin&r1=bt:112233445566", MAINNET); assertEquals(Arrays.asList("bt:112233445566", "https://bitcoincore.org/~gavin"), uri.getPaymentRequestUrls()); assertEquals("https://bitcoincore.org/~gavin", uri.getPaymentRequestUrl()); } @Test public void testNoPaymentProtocolReq() throws Exception { - BitcoinURI uri = new BitcoinURI("bitcoin:" + MAINNET_GOOD_ADDRESS, MAINNET); + BitcoinURI uri = BitcoinURI.of("bitcoin:" + MAINNET_GOOD_ADDRESS, MAINNET); assertNull(uri.getPaymentRequestUrl()); assertEquals(Collections.emptyList(), uri.getPaymentRequestUrls()); assertNotNull(uri.getAddress()); @@ -441,7 +441,7 @@ public class BitcoinURITest { @Test public void testUnescapedPaymentProtocolReq() throws Exception { - BitcoinURI uri = new BitcoinURI("bitcoin:?r=https://merchant.com/pay.php?h%3D2a8628fc2fbe", TESTNET); + BitcoinURI uri = BitcoinURI.of("bitcoin:?r=https://merchant.com/pay.php?h%3D2a8628fc2fbe", TESTNET); assertEquals("https://merchant.com/pay.php?h=2a8628fc2fbe", uri.getPaymentRequestUrl()); assertEquals(Collections.singletonList("https://merchant.com/pay.php?h=2a8628fc2fbe"), uri.getPaymentRequestUrls()); assertNull(uri.getAddress()); diff --git a/tools/src/main/java/org/bitcoinj/tools/PaymentProtocolTool.java b/tools/src/main/java/org/bitcoinj/tools/PaymentProtocolTool.java index 673cc4106..38e41ef86 100644 --- a/tools/src/main/java/org/bitcoinj/tools/PaymentProtocolTool.java +++ b/tools/src/main/java/org/bitcoinj/tools/PaymentProtocolTool.java @@ -60,7 +60,7 @@ public class PaymentProtocolTool { } else if ("http".equals(uri.getScheme())) { session = PaymentSession.createFromUrl(arg).get(); } else if ("bitcoin".equals(uri.getScheme())) { - BitcoinURI bcuri = new BitcoinURI(arg); + BitcoinURI bcuri = BitcoinURI.of(arg); final String paymentRequestUrl = bcuri.getPaymentRequestUrl(); if (paymentRequestUrl == null) { System.err.println("No r= param in bitcoin URI"); diff --git a/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java b/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java index 1a146094a..8051f37c4 100644 --- a/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java +++ b/wallettool/src/main/java/org/bitcoinj/wallettool/WalletTool.java @@ -796,7 +796,7 @@ public class WalletTool implements Callable { if (location.startsWith("http")) { future = PaymentSession.createFromUrl(location, verifyPki); } else { - BitcoinURI paymentRequestURI = new BitcoinURI(location); + BitcoinURI paymentRequestURI = BitcoinURI.of(location); future = PaymentSession.createFromBitcoinUri(paymentRequestURI, verifyPki); } PaymentSession session = future.get();