From 469a58995176f07ce5de738f64a188844399577d Mon Sep 17 00:00:00 2001 From: Amichai Rothman Date: Wed, 24 Jun 2015 22:19:32 +0300 Subject: [PATCH] Reduced unnecessary UnsupportedEncodingException handling --- .../main/java/org/bitcoinj/core/Base58.java | 7 +-- .../org/bitcoinj/core/BitcoinSerializer.java | 7 +-- .../main/java/org/bitcoinj/core/Message.java | 9 +--- .../main/java/org/bitcoinj/core/Utils.java | 49 ++++++++++++++++--- .../java/org/bitcoinj/uri/BitcoinURI.java | 6 +-- .../bitcoinj/wallet/DeterministicSeed.java | 23 ++------- .../bitcoinj/crypto/KeyCrypterScryptTest.java | 4 +- .../java/org/bitcoinj/uri/BitcoinURITest.java | 8 +-- 8 files changed, 54 insertions(+), 59 deletions(-) diff --git a/core/src/main/java/org/bitcoinj/core/Base58.java b/core/src/main/java/org/bitcoinj/core/Base58.java index e7d3d106..739bd4f3 100644 --- a/core/src/main/java/org/bitcoinj/core/Base58.java +++ b/core/src/main/java/org/bitcoinj/core/Base58.java @@ -16,7 +16,6 @@ package org.bitcoinj.core; -import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.util.Arrays; @@ -84,11 +83,7 @@ public class Base58 { } byte[] output = copyOfRange(temp, j, temp.length); - try { - return new String(output, "US-ASCII"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); // Cannot happen. - } + return Utils.toString(output, "US-ASCII"); } public static byte[] decode(String input) throws AddressFormatException { diff --git a/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java b/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java index 8b06a31c..bad970c9 100644 --- a/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java +++ b/core/src/main/java/org/bitcoinj/core/BitcoinSerializer.java @@ -22,7 +22,6 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.HashMap; @@ -305,11 +304,7 @@ public class BitcoinSerializer { for (; header[cursor] != 0 && cursor < COMMAND_LEN; cursor++) ; byte[] commandBytes = new byte[cursor]; System.arraycopy(header, 0, commandBytes, 0, cursor); - try { - command = new String(commandBytes, "US-ASCII"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); // Cannot happen. - } + command = Utils.toString(commandBytes, "US-ASCII"); cursor = COMMAND_LEN; size = (int) readUint32(header, cursor); diff --git a/core/src/main/java/org/bitcoinj/core/Message.java b/core/src/main/java/org/bitcoinj/core/Message.java index 3450ba5b..1d5604e6 100644 --- a/core/src/main/java/org/bitcoinj/core/Message.java +++ b/core/src/main/java/org/bitcoinj/core/Message.java @@ -457,14 +457,7 @@ public abstract class Message implements Serializable { String readStr() throws ProtocolException { long length = readVarInt(); - if (length == 0) { - return ""; // a little optimization - } - try { - return new String(readBytes((int) length), "UTF-8"); - } catch (UnsupportedEncodingException e) { - throw new RuntimeException(e); // Cannot happen, UTF-8 is always supported. - } + return length == 0 ? "" : Utils.toString(readBytes((int) length), "UTF-8"); // optimization for empty strings } Sha256Hash readHash() throws ProtocolException { diff --git a/core/src/main/java/org/bitcoinj/core/Utils.java b/core/src/main/java/org/bitcoinj/core/Utils.java index 418065bf..967f3cb9 100644 --- a/core/src/main/java/org/bitcoinj/core/Utils.java +++ b/core/src/main/java/org/bitcoinj/core/Utils.java @@ -31,6 +31,7 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; +import java.io.UnsupportedEncodingException; import java.math.BigInteger; import java.net.URL; import java.text.DateFormat; @@ -371,19 +372,13 @@ public class Utils { * Returns the current time, or a mocked out equivalent. */ public static Date now() { - if (mockTime != null) - return mockTime; - else - return new Date(); + return mockTime != null ? mockTime : new Date(); } // TODO: Replace usages of this where the result is / 1000 with currentTimeSeconds. /** Returns the current time in milliseconds since the epoch, or a mocked out equivalent. */ public static long currentTimeMillis() { - if (mockTime != null) - return mockTime.getTime(); - else - return System.currentTimeMillis(); + return mockTime != null ? mockTime.getTime() : System.currentTimeMillis(); } public static long currentTimeSeconds() { @@ -427,6 +422,44 @@ public class Utils { return result; } + /** + * Constructs a new String by decoding the given bytes using the specified charset. + *

+ * This is a convenience method which wraps the checked exception with a RuntimeException. + * The exception can never occur given the charsets + * US-ASCII, ISO-8859-1, UTF-8, UTF-16, UTF-16LE or UTF-16BE. + * + * @param bytes the bytes to be decoded into characters + * @param charsetName the name of a supported {@linkplain java.nio.charset.Charset charset} + * @return the decoded String + */ + public static String toString(byte[] bytes, String charsetName) { + try { + return new String(bytes, charsetName); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + /** + * Encodes the given string into a sequence of bytes using the named charset. + *

+ * This is a convenience method which wraps the checked exception with a RuntimeException. + * The exception can never occur given the charsets + * US-ASCII, ISO-8859-1, UTF-8, UTF-16, UTF-16LE or UTF-16BE. + * + * @param str the string to encode into bytes + * @param charsetName the name of a supported {@linkplain java.nio.charset.Charset charset} + * @return the encoded bytes + */ + public static byte[] toBytes(CharSequence str, String charsetName) { + try { + return str.toString().getBytes(charsetName); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + /** * Attempts to parse the given string as arbitrary-length hex or base58 and then return the results, or null if * neither parse was successful. diff --git a/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java b/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java index 2e3aba05..77b882a4 100644 --- a/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java +++ b/core/src/main/java/org/bitcoinj/uri/BitcoinURI.java @@ -237,8 +237,7 @@ public class BitcoinURI { if (valueToken.length() > 0) putWithValidation(nameToken, URLDecoder.decode(valueToken, "UTF-8")); } catch (UnsupportedEncodingException e) { - // Unreachable. - throw new RuntimeException(e); + throw new RuntimeException(e); // can't happen } } } @@ -406,8 +405,7 @@ public class BitcoinURI { try { return java.net.URLEncoder.encode(stringToEncode, "UTF-8").replace("+", ENCODED_SPACE_CHARACTER); } catch (UnsupportedEncodingException e) { - // should not happen - UTF-8 is a valid encoding - throw new RuntimeException(e); + throw new RuntimeException(e); // can't happen } } } diff --git a/core/src/main/java/org/bitcoinj/wallet/DeterministicSeed.java b/core/src/main/java/org/bitcoinj/wallet/DeterministicSeed.java index 4aeb8f7c..1ced8e74 100644 --- a/core/src/main/java/org/bitcoinj/wallet/DeterministicSeed.java +++ b/core/src/main/java/org/bitcoinj/wallet/DeterministicSeed.java @@ -17,6 +17,7 @@ package org.bitcoinj.wallet; +import org.bitcoinj.core.Utils; import org.bitcoinj.crypto.*; import org.bitcoinj.store.UnreadableWalletException; import com.google.common.base.Charsets; @@ -25,7 +26,6 @@ import com.google.common.base.Splitter; import org.spongycastle.crypto.params.KeyParameter; import javax.annotation.Nullable; -import java.io.UnsupportedEncodingException; import java.security.SecureRandom; import java.util.List; @@ -195,17 +195,8 @@ public class DeterministicSeed implements EncryptableItem { public DeterministicSeed decrypt(KeyCrypter crypter, String passphrase, KeyParameter aesKey) { checkState(isEncrypted()); checkNotNull(encryptedMnemonicCode); - List mnemonic = null; - byte[] seed = null; - try { - mnemonic = decodeMnemonicCode(crypter.decrypt(encryptedMnemonicCode, aesKey)); - if (encryptedSeed != null) { - seed = crypter.decrypt(encryptedSeed, aesKey); - } - } catch (UnreadableWalletException e) { - // TODO what is the best way to handle this exception? - throw new RuntimeException(e); - } + List mnemonic = decodeMnemonicCode(crypter.decrypt(encryptedMnemonicCode, aesKey)); + byte[] seed = encryptedSeed == null ? null : crypter.decrypt(encryptedSeed, aesKey); return new DeterministicSeed(mnemonic, seed, passphrase, creationTimeSeconds); } @@ -255,12 +246,8 @@ public class DeterministicSeed implements EncryptableItem { return mnemonicCode; } - private static List decodeMnemonicCode(byte[] mnemonicCode) throws UnreadableWalletException { - try { - return Splitter.on(" ").splitToList(new String(mnemonicCode, "UTF-8")); - } catch (UnsupportedEncodingException e) { - throw new UnreadableWalletException(e.toString()); - } + private static List decodeMnemonicCode(byte[] mnemonicCode) { + return decodeMnemonicCode(Utils.toString(mnemonicCode, "UTF-8")); } private static List decodeMnemonicCode(String mnemonicCode) { diff --git a/core/src/test/java/org/bitcoinj/crypto/KeyCrypterScryptTest.java b/core/src/test/java/org/bitcoinj/crypto/KeyCrypterScryptTest.java index 38f4f400..6a71b959 100644 --- a/core/src/test/java/org/bitcoinj/crypto/KeyCrypterScryptTest.java +++ b/core/src/test/java/org/bitcoinj/crypto/KeyCrypterScryptTest.java @@ -27,7 +27,6 @@ import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.UnsupportedEncodingException; import java.security.SecureRandom; import java.util.Random; import java.util.UUID; @@ -77,10 +76,9 @@ public class KeyCrypterScryptTest { * Test with random plain text strings and random passwords. * UUIDs are used and hence will only cover hex characters (and the separator hyphen). * @throws KeyCrypterException - * @throws UnsupportedEncodingException */ @Test - public void testKeyCrypterGood2() throws Exception { + public void testKeyCrypterGood2() { KeyCrypterScrypt keyCrypter = new KeyCrypterScrypt(scryptParameters); System.out.print("EncrypterDecrypterTest: Trying random UUIDs for plainText and passwords :"); diff --git a/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java b/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java index 8fe8539b..67b8e191 100644 --- a/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java +++ b/core/src/test/java/org/bitcoinj/uri/BitcoinURITest.java @@ -23,8 +23,6 @@ import org.bitcoinj.params.TestNet3Params; import com.google.common.collect.ImmutableList; import org.junit.Test; -import java.io.UnsupportedEncodingException; - import static org.bitcoinj.core.Coin.*; import static org.junit.Assert.*; @@ -187,10 +185,9 @@ public class BitcoinURITest { * * @throws BitcoinURIParseException * If something goes wrong - * @throws UnsupportedEncodingException */ @Test - public void testGood_LabelWithAmpersandAndPlus() throws Exception { + public void testGood_LabelWithAmpersandAndPlus() throws BitcoinURIParseException { String testString = "Hello Earth & Mars + Venus"; String encodedLabel = BitcoinURI.encodeURLString(testString); testObject = new BitcoinURI(MainNetParams.get(), BitcoinURI.BITCOIN_SCHEME + ":" + MAINNET_GOOD_ADDRESS + "?label=" @@ -203,10 +200,9 @@ public class BitcoinURITest { * * @throws BitcoinURIParseException * If something goes wrong - * @throws UnsupportedEncodingException */ @Test - public void testGood_LabelWithRussian() throws Exception { + public void testGood_LabelWithRussian() throws BitcoinURIParseException { // Moscow in Russian in Cyrillic String moscowString = "\u041c\u043e\u0441\u043a\u0432\u0430"; String encodedLabel = BitcoinURI.encodeURLString(moscowString);