diff --git a/core/src/main/java/com/google/bitcoin/uri/BitcoinURI.java b/core/src/main/java/com/google/bitcoin/uri/BitcoinURI.java index c18d6654..4dccbcd4 100644 --- a/core/src/main/java/com/google/bitcoin/uri/BitcoinURI.java +++ b/core/src/main/java/com/google/bitcoin/uri/BitcoinURI.java @@ -149,10 +149,9 @@ public class BitcoinURI { // Split off the address from the rest of the query parameters. String[] addressSplitTokens = schemeSpecificPart.split("\\?"); - if (addressSplitTokens.length == 0 || "".equals(addressSplitTokens[0])) { - throw new BitcoinURIParseException("Missing address"); - } - String addressToken = addressSplitTokens[0]; + if (addressSplitTokens.length == 0) + throw new BitcoinURIParseException("No data found after the bitcoin: prefix"); + String addressToken = addressSplitTokens[0]; // may be empty! String[] nameValuePairTokens; if (addressSplitTokens.length == 1) { @@ -169,6 +168,20 @@ public class BitcoinURI { // Attempt to parse the rest of the URI parameters. parseParameters(params, addressToken, nameValuePairTokens); + + if (!addressToken.isEmpty()) { + // Attempt to parse the addressToken as a Bitcoin address for this network + try { + Address address = new Address(params, addressToken); + putWithValidation(FIELD_ADDRESS, address); + } catch (final AddressFormatException e) { + throw new BitcoinURIParseException("Bad address", e); + } + } + + if (addressToken.isEmpty() && getPaymentRequestUrl() == null) { + throw new BitcoinURIParseException("No address and no r= parameter found"); + } } /** @@ -177,14 +190,6 @@ public class BitcoinURI { * separated by '=' e.g. 'amount=0.2') */ private void parseParameters(@Nullable NetworkParameters params, String addressToken, String[] nameValuePairTokens) throws BitcoinURIParseException { - // Attempt to parse the addressToken as a Bitcoin address for this network - try { - Address address = new Address(params, addressToken); - putWithValidation(FIELD_ADDRESS, address); - } catch (final AddressFormatException e) { - throw new BitcoinURIParseException("Bad address", e); - } - // Attempt to decode the rest of the tokens into a parameter map. for (String nameValuePairToken : nameValuePairTokens) { String[] tokens = nameValuePairToken.split("="); @@ -242,8 +247,11 @@ public class BitcoinURI { } /** - * @return The Bitcoin Address from the URI + * The Bitcoin Address from the URI, if one was present. It's possible to have Bitcoin URI's with no address if a + * r= payment protocol parameter is specified, though this form is not recommended as older wallets can't understand + * it. */ + @Nullable public Address getAddress() { return (Address) parameterMap.get(FIELD_ADDRESS); } 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 ea19f968..ba63e191 100644 --- a/core/src/test/java/com/google/bitcoin/uri/BitcoinURITest.java +++ b/core/src/test/java/com/google/bitcoin/uri/BitcoinURITest.java @@ -240,7 +240,7 @@ public class BitcoinURITest { testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=6543210&label=Hello%20World&message=Be%20well"); assertEquals( - "BitcoinURI['address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH','amount'='654321000000000','label'='Hello World','message'='Be well']", + "BitcoinURI['amount'='654321000000000','label'='Hello World','message'='Be well','address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH']", testObject.toString()); } @@ -367,7 +367,7 @@ public class BitcoinURITest { // Unknown not required field testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?aardvark=true"); - assertEquals("BitcoinURI['address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH','aardvark'='true']", testObject.toString()); + assertEquals("BitcoinURI['aardvark'='true','address'='1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH']", testObject.toString()); assertEquals("true", (String) testObject.getParameterByName("aardvark")); @@ -416,4 +416,12 @@ public class BitcoinURITest { new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?amount=100000000"); } + + @Test + public void testPaymentProtocolReq() throws Exception { + // Non-backwards compatible form ... + BitcoinURI uri = new BitcoinURI(TestNet3Params.get(), "bitcoin:?r=https%3A%2F%2Fbitcoincore.org%2F%7Egavin%2Ff.php%3Fh%3Db0f02e7cea67f168e25ec9b9f9d584f9"); + assertEquals("https://bitcoincore.org/~gavin/f.php?h=b0f02e7cea67f168e25ec9b9f9d584f9", uri.getPaymentRequestUrl()); + assertNull(uri.getAddress()); + } } diff --git a/tools/src/main/java/com/google/bitcoin/tools/WalletTool.java b/tools/src/main/java/com/google/bitcoin/tools/WalletTool.java index 59cc0c99..c8f2a2d3 100644 --- a/tools/src/main/java/com/google/bitcoin/tools/WalletTool.java +++ b/tools/src/main/java/com/google/bitcoin/tools/WalletTool.java @@ -506,7 +506,7 @@ public class WalletTool { try { paymentRequestURI = new BitcoinURI(location); } catch (BitcoinURIParseException e) { - System.err.println("Invalid bitcoin uri " + e.getMessage()); + System.err.println("Invalid bitcoin uri: " + e.getMessage()); System.exit(1); } try {