3
0
mirror of https://github.com/Qortal/altcoinj.git synced 2025-01-31 23:32:16 +00:00

Make BitcoinURIParseException checked so you can't forget it, and catch/rethrow ArithmeticErrors when parsing the value field.

Add more nullity annotations.
This commit is contained in:
Mike Hearn 2013-07-11 16:52:50 +02:00
parent bf585d8627
commit d3eab06dba
4 changed files with 47 additions and 42 deletions

View File

@ -19,6 +19,8 @@ package com.google.bitcoin.core;
import com.google.bitcoin.params.MainNetParams; import com.google.bitcoin.params.MainNetParams;
import com.google.bitcoin.params.TestNet3Params; import com.google.bitcoin.params.TestNet3Params;
import javax.annotation.Nullable;
/** /**
* <p>A Bitcoin address looks like 1MsScoe2fTJoq4ZPdQgqyhgWeoNamYPevy and is derived from an elliptic curve public key * <p>A Bitcoin address looks like 1MsScoe2fTJoq4ZPdQgqyhgWeoNamYPevy and is derived from an elliptic curve public key
* plus a set of network parameters. Not to be confused with a {@link PeerAddress} or {@link AddressMessage} * plus a set of network parameters. Not to be confused with a {@link PeerAddress} or {@link AddressMessage}
@ -57,7 +59,7 @@ public class Address extends VersionedChecksummedBytes {
* @throws AddressFormatException if the given address doesn't parse or the checksum is invalid * @throws AddressFormatException if the given address doesn't parse or the checksum is invalid
* @throws WrongNetworkException if the given address is valid but for a different chain (eg testnet vs prodnet) * @throws WrongNetworkException if the given address is valid but for a different chain (eg testnet vs prodnet)
*/ */
public Address(NetworkParameters params, String address) throws AddressFormatException, WrongNetworkException { public Address(@Nullable NetworkParameters params, String address) throws AddressFormatException, WrongNetworkException {
super(address); super(address);
if (params != null) { if (params != null) {
boolean found = false; boolean found = false;

View File

@ -22,10 +22,10 @@ import com.google.bitcoin.core.Address;
import com.google.bitcoin.core.AddressFormatException; import com.google.bitcoin.core.AddressFormatException;
import com.google.bitcoin.core.NetworkParameters; import com.google.bitcoin.core.NetworkParameters;
import com.google.bitcoin.core.Utils; import com.google.bitcoin.core.Utils;
import com.google.common.base.Preconditions;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import javax.annotation.Nullable;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import java.math.BigInteger; import java.math.BigInteger;
import java.net.URI; import java.net.URI;
@ -34,9 +34,10 @@ import java.net.URLDecoder;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import static com.google.common.base.Preconditions.checkNotNull;
/** /**
* <p>Provides a standard implementation of a Bitcoin URI with support for the * <p>Provides a standard implementation of a Bitcoin URI with support for the following:</p>
* following:</p>
* *
* <ul> * <ul>
* <li>URLEncoded URIs (as passed in by IE on the command line)</li> * <li>URLEncoded URIs (as passed in by IE on the command line)</li>
@ -56,15 +57,12 @@ import java.util.Map;
* <p>The name/value pairs are processed as follows.</p> * <p>The name/value pairs are processed as follows.</p>
* <ol> * <ol>
* <li>URL encoding is stripped and treated as UTF-8</li> * <li>URL encoding is stripped and treated as UTF-8</li>
* <li>names prefixed with {@code req-} are treated as required and if unknown * <li>names prefixed with {@code req-} are treated as required and if unknown or conflicting cause a parse exception</li>
* or conflicting cause a parse exception</li> * <li>Unknown names not prefixed with {@code req-} are added to a Map, accessible by parameter name</li>
* <li>Unknown names not prefixed with {@code req-} are added to a Map, accessible * <li>Known names not prefixed with {@code req-} are processed unless they are malformed</li>
* by parameter name</li>
* <li>Known names not prefixed with {@code req-} are processed unless they are
* malformed</li>
* </ol> * </ol>
* *
* <p>The following names are known and have the following formats</p> * <p>The following names are known and have the following formats:</p>
* <ul> * <ul>
* <li>{@code amount} decimal value to 8 dp (e.g. 0.12345678) <b>Note that the * <li>{@code amount} decimal value to 8 dp (e.g. 0.12345678) <b>Note that the
* exponent notation is not supported any more</b></li> * exponent notation is not supported any more</b></li>
@ -105,19 +103,21 @@ public class BitcoinURI {
* @param uri The raw URI data to be parsed (see class comments for accepted formats) * @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. * @throws BitcoinURIParseException if the URI is not syntactically or semantically valid.
*/ */
public BitcoinURI(String uri) { public BitcoinURI(String uri) throws BitcoinURIParseException {
this(null, uri); this(null, uri);
} }
/** /**
* Constructs a new object by trying to parse the input as a valid Bitcoin URI.
*
* @param params The network parameters that determine which network the URI is from, or null if you don't have * @param params The network parameters that determine which network the URI is from, or null if you don't have
* any expectation about what network the URI is for and wish to check yourself. * any expectation about what network the URI is for and wish to check yourself.
* @param input The raw URI data to be parsed (see class comments for accepted formats) * @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. * @throws BitcoinURIParseException If the input fails Bitcoin URI syntax and semantic checks.
*/ */
public BitcoinURI(NetworkParameters params, String input) { public BitcoinURI(@Nullable NetworkParameters params, String input) throws BitcoinURIParseException {
// Basic validation checkNotNull(input);
Preconditions.checkNotNull(input);
log.debug("Attempting to parse '{}' for {}", input, params == null ? "any" : params.getId()); log.debug("Attempting to parse '{}' for {}", input, params == null ? "any" : params.getId());
// Attempt to form the URI (fail fast syntax checking to official standards). // Attempt to form the URI (fail fast syntax checking to official standards).
@ -175,7 +175,7 @@ public class BitcoinURI {
* @param nameValuePairTokens The tokens representing the name value pairs (assumed to be * @param nameValuePairTokens The tokens representing the name value pairs (assumed to be
* separated by '=' e.g. 'amount=0.2') * separated by '=' e.g. 'amount=0.2')
*/ */
private void parseParameters(NetworkParameters params, String addressToken, String[] nameValuePairTokens) { private void parseParameters(@Nullable NetworkParameters params, String addressToken, String[] nameValuePairTokens) throws BitcoinURIParseException {
// Attempt to parse the addressToken as a Bitcoin address for this network // Attempt to parse the addressToken as a Bitcoin address for this network
try { try {
Address address = new Address(params, addressToken); Address address = new Address(params, addressToken);
@ -202,7 +202,9 @@ public class BitcoinURI {
BigInteger amount = Utils.toNanoCoins(valueToken); BigInteger amount = Utils.toNanoCoins(valueToken);
putWithValidation(FIELD_AMOUNT, amount); putWithValidation(FIELD_AMOUNT, amount);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
throw new OptionalFieldValidationException("'" + valueToken + "' value is not a valid amount", 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);
} }
} else { } else {
if (nameToken.startsWith("req-")) { if (nameToken.startsWith("req-")) {
@ -230,9 +232,9 @@ public class BitcoinURI {
* @param key The key for the map * @param key The key for the map
* @param value The value to store * @param value The value to store
*/ */
private void putWithValidation(String key, Object value) { private void putWithValidation(String key, Object value) throws BitcoinURIParseException {
if (parameterMap.containsKey(key)) { if (parameterMap.containsKey(key)) {
throw new BitcoinURIParseException("'" + key + "' is duplicated, URI is invalid"); throw new BitcoinURIParseException(String.format("'%s' is duplicated, URI is invalid", key));
} else { } else {
parameterMap.put(key, value); parameterMap.put(key, value);
} }
@ -305,7 +307,7 @@ public class BitcoinURI {
* @return A String containing the Bitcoin URI * @return A String containing the Bitcoin URI
*/ */
public static String convertToBitcoinURI(String address, BigInteger amount, String label, String message) { public static String convertToBitcoinURI(String address, BigInteger amount, String label, String message) {
Preconditions.checkNotNull(address); checkNotNull(address);
if (amount != null && amount.compareTo(BigInteger.ZERO) < 0) { if (amount != null && amount.compareTo(BigInteger.ZERO) < 0) {
throw new IllegalArgumentException("Amount must be positive"); throw new IllegalArgumentException("Amount must be positive");
} }

View File

@ -1,3 +1,21 @@
/*
* Copyright 2012 the original author or authors.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package com.google.bitcoin.uri; package com.google.bitcoin.uri;
/** /**
@ -8,10 +26,8 @@ package com.google.bitcoin.uri;
* <p>This base exception acts as a general failure mode not attributable to a specific cause (other than * <p>This base exception acts as a general failure mode not attributable to a specific cause (other than
* that reported in the exception message). Since this is in English, it may not be worth reporting directly * that reported in the exception message). Since this is in English, it may not be worth reporting directly
* to the user other than as part of a "general failure to parse" response.</p> * to the user other than as part of a "general failure to parse" response.</p>
*
* @since 0.4.0
*/ */
public class BitcoinURIParseException extends RuntimeException { public class BitcoinURIParseException extends Exception {
public BitcoinURIParseException(String s) { public BitcoinURIParseException(String s) {
super(s); super(s);
} }

View File

@ -19,7 +19,6 @@
package com.google.bitcoin.uri; package com.google.bitcoin.uri;
import com.google.bitcoin.core.Address; import com.google.bitcoin.core.Address;
import com.google.bitcoin.core.AddressFormatException;
import com.google.bitcoin.core.Utils; import com.google.bitcoin.core.Utils;
import com.google.bitcoin.params.MainNetParams; import com.google.bitcoin.params.MainNetParams;
import com.google.bitcoin.params.TestNet3Params; import com.google.bitcoin.params.TestNet3Params;
@ -27,22 +26,14 @@ import org.junit.Test;
import java.io.UnsupportedEncodingException; import java.io.UnsupportedEncodingException;
import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.*; import static org.junit.Assert.*;
public class BitcoinURITest {
public class BitcoinURITest {
private BitcoinURI testObject = null; private BitcoinURI testObject = null;
private static final String MAINNET_GOOD_ADDRESS = "1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH"; private static final String MAINNET_GOOD_ADDRESS = "1KzTSfqjF2iKCduwz59nv2uqh1W2JsTxZH";
/**
* Tests conversion to Bitcoin URI
*
* @throws BitcoinURIParseException
* If something goes wrong
* @throws AddressFormatException
*/
@Test @Test
public void testConvertToBitcoinURI() throws Exception { public void testConvertToBitcoinURI() throws Exception {
Address goodAddress = new Address(MainNetParams.get(), MAINNET_GOOD_ADDRESS); Address goodAddress = new Address(MainNetParams.get(), MAINNET_GOOD_ADDRESS);
@ -69,7 +60,7 @@ public class BitcoinURITest {
assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=0.1&message=glory", BitcoinURI.convertToBitcoinURI(goodAddress, Utils.toNanoCoins("0.1"), "", "glory")); assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=0.1&message=glory", BitcoinURI.convertToBitcoinURI(goodAddress, Utils.toNanoCoins("0.1"), "", "glory"));
// amount present, label present, no message // amount present, label present, no message
assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello", BitcoinURI.convertToBitcoinURI(goodAddress,Utils.toNanoCoins("12.34"), "Hello", null)); assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello", BitcoinURI.convertToBitcoinURI(goodAddress, Utils.toNanoCoins("12.34"), "Hello", null));
assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello", BitcoinURI.convertToBitcoinURI(goodAddress, Utils.toNanoCoins("12.34"), "Hello", "")); assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS + "?amount=12.34&label=Hello", BitcoinURI.convertToBitcoinURI(goodAddress, Utils.toNanoCoins("12.34"), "Hello", ""));
// amount present, no label, no message // amount present, no label, no message
@ -88,12 +79,6 @@ public class BitcoinURITest {
assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS, BitcoinURI.convertToBitcoinURI(goodAddress, null, "", "")); assertEquals("bitcoin:" + MAINNET_GOOD_ADDRESS, BitcoinURI.convertToBitcoinURI(goodAddress, null, "", ""));
} }
/**
* Test the simplest well-formed URI
*
* @throws BitcoinURIParseException
* If something goes wrong
*/
@Test @Test
public void testGood_Simple() throws BitcoinURIParseException { public void testGood_Simple() throws BitcoinURIParseException {
testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS); testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS);
@ -214,7 +199,7 @@ public class BitcoinURITest {
* @throws UnsupportedEncodingException * @throws UnsupportedEncodingException
*/ */
@Test @Test
public void testGood_LabelWithAmpersandAndPlus() throws BitcoinURIParseException, UnsupportedEncodingException { public void testGood_LabelWithAmpersandAndPlus() throws Exception {
String testString = "Hello Earth & Mars + Venus"; String testString = "Hello Earth & Mars + Venus";
String encodedLabel = BitcoinURI.encodeURLString(testString); String encodedLabel = BitcoinURI.encodeURLString(testString);
testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=" testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label="
@ -230,7 +215,7 @@ public class BitcoinURITest {
* @throws UnsupportedEncodingException * @throws UnsupportedEncodingException
*/ */
@Test @Test
public void testGood_LabelWithRussian() throws BitcoinURIParseException, UnsupportedEncodingException { public void testGood_LabelWithRussian() throws Exception {
// Moscow in Russian in Cyrillic // Moscow in Russian in Cyrillic
String moscowString = "\u041c\u043e\u0441\u043a\u0432\u0430"; String moscowString = "\u041c\u043e\u0441\u043a\u0432\u0430";
String encodedLabel = BitcoinURI.encodeURLString(moscowString); String encodedLabel = BitcoinURI.encodeURLString(moscowString);