From 747bfa915f1ecf743b5e8876b9a4c54062e57c94 Mon Sep 17 00:00:00 2001 From: Craig Raw Date: Mon, 28 Sep 2020 14:31:25 +0200 Subject: [PATCH] add support for alternative (non-mainnet) networks --- .../com/sparrowwallet/drongo/ExtendedKey.java | 45 +++++++--- .../com/sparrowwallet/drongo/Network.java | 87 +++++++++++++++++++ .../sparrowwallet/drongo/address/Address.java | 52 ++++++++--- .../drongo/address/P2PKAddress.java | 6 +- .../drongo/address/P2PKHAddress.java | 8 +- .../drongo/address/P2SHAddress.java | 6 +- .../drongo/address/P2WPKHAddress.java | 12 +-- .../drongo/address/P2WSHAddress.java | 12 +-- .../drongo/address/AddressTest.java | 52 +++++++++++ .../sparrowwallet/drongo/psbt/PSBTTest.java | 9 ++ 10 files changed, 252 insertions(+), 37 deletions(-) create mode 100644 src/main/java/com/sparrowwallet/drongo/Network.java diff --git a/src/main/java/com/sparrowwallet/drongo/ExtendedKey.java b/src/main/java/com/sparrowwallet/drongo/ExtendedKey.java index 4caae34..8eababa 100644 --- a/src/main/java/com/sparrowwallet/drongo/ExtendedKey.java +++ b/src/main/java/com/sparrowwallet/drongo/ExtendedKey.java @@ -6,6 +6,7 @@ import com.sparrowwallet.drongo.protocol.ScriptType; import java.nio.ByteBuffer; import java.util.*; +import java.util.stream.Collectors; public class ExtendedKey { private final byte[] parentFingerprint; @@ -53,7 +54,7 @@ public class ExtendedKey { } public byte[] getExtendedKeyBytes() { - return getExtendedKeyBytes(key.isPubKeyOnly() ? Header.xpub : Header.xprv); + return getExtendedKeyBytes(key.isPubKeyOnly() ? Network.get().getXpubHeader() : Network.get().getXprvHeader()); } public byte[] getExtendedKeyBytes(Header extendedKeyHeader) { @@ -79,7 +80,7 @@ public class ExtendedKey { int headerInt = buffer.getInt(); Header header = Header.getHeader(headerInt); if(header == null) { - throw new IllegalArgumentException("Unknown header bytes: " + DeterministicKey.toBase58(serializedKey).substring(0, 4)); + throw new IllegalArgumentException("Unknown header bytes for extended key on " + Network.get().getName() + ": " + DeterministicKey.toBase58(serializedKey).substring(0, 4)); } int depth = buffer.get() & 0xFF; // convert signed byte to positive int since depth cannot be negative @@ -196,38 +197,62 @@ public class ExtendedKey { return privateKey; } - public boolean isMainnet() { - return mainnet; + public Network getNetwork() { + return mainnet ? Network.MAINNET : Network.TESTNET; + } + + public static List
getHeaders(Network network) { + return Arrays.stream(Header.values()).filter(header -> header.getNetwork() == network || (header.getNetwork() == Network.TESTNET && network == Network.REGTEST)).collect(Collectors.toList()); } public static Header fromExtendedKey(String xkey) { - for(Header extendedKeyHeader : Header.values()) { + for(Header extendedKeyHeader : getHeaders(Network.get())) { if(xkey.startsWith(extendedKeyHeader.name)) { return extendedKeyHeader; } } - throw new IllegalArgumentException("Unrecognised extended key header for extended key: " + xkey); + for(Network network : getOtherNetworks(Network.get())) { + for(Header otherNetworkKeyHeader : getHeaders(network)) { + if(xkey.startsWith(otherNetworkKeyHeader.name)) { + throw new IllegalArgumentException("Provided " + otherNetworkKeyHeader.name + " extended key invalid on configured " + Network.get().getName() + " network. Use a " + network.getName() + " configuration to use this extended key."); + } + } + } + + throw new IllegalArgumentException("Unrecognised extended key header for " + Network.get().getName() + ": " + xkey); } public static Header fromScriptType(ScriptType scriptType, boolean privateKey) { - for(Header header : Header.values()) { + for(Header header : getHeaders(Network.get())) { if(header.defaultScriptType != null && header.defaultScriptType.equals(scriptType) && header.isPrivateKey() == privateKey) { return header; } } - return Header.xpub; + return Network.get().getXpubHeader(); } - public static Header getHeader(int header) { - for(Header extendedKeyHeader : Header.values()) { + private static Header getHeader(int header) { + for(Header extendedKeyHeader : getHeaders(Network.get())) { if(header == extendedKeyHeader.header) { return extendedKeyHeader; } } + for(Network otherNetwork : getOtherNetworks(Network.get())) { + for(Header otherNetworkKeyHeader : getHeaders(otherNetwork)) { + if(header == otherNetworkKeyHeader.header) { + throw new IllegalArgumentException("Provided " + otherNetworkKeyHeader.name + " extended key invalid on configured " + Network.get().getName() + " network. Use a " + otherNetwork.getName() + " configuration to use this extended key."); + } + } + } + return null; } + + private static List getOtherNetworks(Network providedNetwork) { + return Arrays.stream(Network.values()).filter(network -> network != providedNetwork).collect(Collectors.toList()); + } } } \ No newline at end of file diff --git a/src/main/java/com/sparrowwallet/drongo/Network.java b/src/main/java/com/sparrowwallet/drongo/Network.java new file mode 100644 index 0000000..7db2433 --- /dev/null +++ b/src/main/java/com/sparrowwallet/drongo/Network.java @@ -0,0 +1,87 @@ +package com.sparrowwallet.drongo; + +public enum Network { + MAINNET("mainnet", 0, "1", 5, "3", "bc", ExtendedKey.Header.xprv, ExtendedKey.Header.xpub), + TESTNET("testnet", 111, "mn", 196, "2", "tb", ExtendedKey.Header.tprv, ExtendedKey.Header.tpub), + REGTEST("regtest", 111, "mn", 196, "2", "bcrt", ExtendedKey.Header.tprv, ExtendedKey.Header.tpub); + + Network(String name, int p2pkhAddressHeader, String p2pkhAddressPrefix, int p2shAddressHeader, String p2shAddressPrefix, String bech32AddressHrp, ExtendedKey.Header xprvHeader, ExtendedKey.Header xpubHeader) { + this.name = name; + this.p2pkhAddressHeader = p2pkhAddressHeader; + this.p2pkhAddressPrefix = p2pkhAddressPrefix; + this.p2shAddressHeader = p2shAddressHeader; + this.p2shAddressPrefix = p2shAddressPrefix; + this.bech32AddressHrp = bech32AddressHrp; + this.xprvHeader = xprvHeader; + this.xpubHeader = xpubHeader; + } + + private final String name; + private final int p2pkhAddressHeader; + private final String p2pkhAddressPrefix; + private final int p2shAddressHeader; + private final String p2shAddressPrefix; + private final String bech32AddressHrp; + private final ExtendedKey.Header xprvHeader; + private final ExtendedKey.Header xpubHeader; + + private static Network currentNetwork; + + public String getName() { + return name; + } + + public int getP2PKHAddressHeader() { + return p2pkhAddressHeader; + } + + public int getP2SHAddressHeader() { + return p2shAddressHeader; + } + + public String getBech32AddressHRP() { + return bech32AddressHrp; + } + + public ExtendedKey.Header getXprvHeader() { + return xprvHeader; + } + + public ExtendedKey.Header getXpubHeader() { + return xpubHeader; + } + + public boolean hasP2PKHAddressPrefix(String address) { + for(String prefix : p2pkhAddressPrefix.split("")) { + if(address.startsWith(prefix)) { + return true; + } + } + + return false; + } + + public boolean hasP2SHAddressPrefix(String address) { + return address.startsWith(p2shAddressPrefix); + } + + public static Network get() { + if(currentNetwork == null) { + currentNetwork = MAINNET; + } + + return currentNetwork; + } + + public static void set(Network network) { + if(currentNetwork != null && network != currentNetwork && !isTest()) { + throw new IllegalStateException("Network already set to " + currentNetwork.getName()); + } + + currentNetwork = network; + } + + private static boolean isTest() { + return System.getProperty("org.gradle.test.worker") != null; + } +} diff --git a/src/main/java/com/sparrowwallet/drongo/address/Address.java b/src/main/java/com/sparrowwallet/drongo/address/Address.java index 44802d3..9b367cd 100644 --- a/src/main/java/com/sparrowwallet/drongo/address/Address.java +++ b/src/main/java/com/sparrowwallet/drongo/address/Address.java @@ -1,5 +1,6 @@ package com.sparrowwallet.drongo.address; +import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.protocol.Base58; import com.sparrowwallet.drongo.protocol.Bech32; import com.sparrowwallet.drongo.protocol.Script; @@ -7,8 +8,6 @@ import com.sparrowwallet.drongo.protocol.ScriptType; import java.util.Arrays; -import static com.sparrowwallet.drongo.address.P2WPKHAddress.HRP; - public abstract class Address { protected final byte[] hash; @@ -21,14 +20,26 @@ public abstract class Address { } public String getAddress() { - return Base58.encodeChecked(getVersion(), hash); + return getAddress(Network.get()); + } + + public String getAddress(Network network) { + return Base58.encodeChecked(getVersion(network), hash); } public String toString() { - return getAddress(); + return getAddress(Network.get()); } - public abstract int getVersion(); + public String toString(Network network) { + return getAddress(network); + } + + public int getVersion() { + return getVersion(Network.get()); + } + + public abstract int getVersion(Network network); public abstract ScriptType getScriptType(); @@ -52,18 +63,37 @@ public abstract class Address { } public static Address fromString(String address) throws InvalidAddressException { + try { + return fromString(Network.get(), address); + } catch(InvalidAddressException e) { + for(Network network : Network.values()) { + try { + fromString(network, address); + if(network != Network.get()) { + throw new InvalidAddressException("Provided " + network.getName() + " address invalid on configured " + Network.get().getName() + " network: " + address + ". Use a " + network.getName() + " configuration to use this address."); + } + } catch(InvalidAddressException i) { + //ignore + } + } + + throw e; + } + } + + public static Address fromString(Network network, String address) throws InvalidAddressException { Exception nested = null; - if(address != null && (address.startsWith("1") || address.startsWith("3"))) { + if(address != null && (network.hasP2PKHAddressPrefix(address) || network.hasP2SHAddressPrefix(address))) { try { byte[] decodedBytes = Base58.decodeChecked(address); if(decodedBytes.length == 21) { - int version = decodedBytes[0]; + int version = Byte.toUnsignedInt(decodedBytes[0]); byte[] hash = Arrays.copyOfRange(decodedBytes, 1, 21); - if(version == 0) { + if(version == network.getP2PKHAddressHeader()) { return new P2PKHAddress(hash); } - if(version == 5) { + if(version == network.getP2SHAddressHeader()) { return new P2SHAddress(hash); } } @@ -72,10 +102,10 @@ public abstract class Address { } } - if(address != null && address.startsWith(HRP)) { + if(address != null && address.startsWith(network.getBech32AddressHRP())) { try { Bech32.Bech32Data data = Bech32.decode(address); - if (data.hrp.equals(HRP)) { + if(data.hrp.equals(network.getBech32AddressHRP())) { int witnessVersion = data.data[0]; if (witnessVersion == 0) { byte[] convertedProgram = Arrays.copyOfRange(data.data, 1, data.data.length); diff --git a/src/main/java/com/sparrowwallet/drongo/address/P2PKAddress.java b/src/main/java/com/sparrowwallet/drongo/address/P2PKAddress.java index f168bdf..35df236 100644 --- a/src/main/java/com/sparrowwallet/drongo/address/P2PKAddress.java +++ b/src/main/java/com/sparrowwallet/drongo/address/P2PKAddress.java @@ -1,5 +1,6 @@ package com.sparrowwallet.drongo.address; +import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.protocol.Script; import com.sparrowwallet.drongo.protocol.ScriptType; @@ -12,8 +13,9 @@ public class P2PKAddress extends Address { this.pubKey = pubKey; } - public int getVersion() { - return 0; + @Override + public int getVersion(Network network) { + return network.getP2PKHAddressHeader(); } public ScriptType getScriptType() { diff --git a/src/main/java/com/sparrowwallet/drongo/address/P2PKHAddress.java b/src/main/java/com/sparrowwallet/drongo/address/P2PKHAddress.java index 51b331a..dcf9488 100644 --- a/src/main/java/com/sparrowwallet/drongo/address/P2PKHAddress.java +++ b/src/main/java/com/sparrowwallet/drongo/address/P2PKHAddress.java @@ -1,5 +1,6 @@ package com.sparrowwallet.drongo.address; +import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.protocol.Script; import com.sparrowwallet.drongo.protocol.ScriptType; @@ -8,14 +9,17 @@ public class P2PKHAddress extends Address { super(pubKeyHash); } - public int getVersion() { - return 0; + @Override + public int getVersion(Network network) { + return network.getP2PKHAddressHeader(); } + @Override public ScriptType getScriptType() { return ScriptType.P2PKH; } + @Override public Script getOutputScript() { return getScriptType().getOutputScript(hash); } diff --git a/src/main/java/com/sparrowwallet/drongo/address/P2SHAddress.java b/src/main/java/com/sparrowwallet/drongo/address/P2SHAddress.java index d7465f8..8692a07 100644 --- a/src/main/java/com/sparrowwallet/drongo/address/P2SHAddress.java +++ b/src/main/java/com/sparrowwallet/drongo/address/P2SHAddress.java @@ -1,5 +1,6 @@ package com.sparrowwallet.drongo.address; +import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.protocol.Script; import com.sparrowwallet.drongo.protocol.ScriptType; @@ -9,8 +10,9 @@ public class P2SHAddress extends Address { super(scriptHash); } - public int getVersion() { - return 5; + @Override + public int getVersion(Network network) { + return network.getP2SHAddressHeader(); } @Override diff --git a/src/main/java/com/sparrowwallet/drongo/address/P2WPKHAddress.java b/src/main/java/com/sparrowwallet/drongo/address/P2WPKHAddress.java index d7a02f6..b4ed379 100644 --- a/src/main/java/com/sparrowwallet/drongo/address/P2WPKHAddress.java +++ b/src/main/java/com/sparrowwallet/drongo/address/P2WPKHAddress.java @@ -1,24 +1,26 @@ package com.sparrowwallet.drongo.address; +import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.protocol.Bech32; import com.sparrowwallet.drongo.protocol.Script; import com.sparrowwallet.drongo.protocol.ScriptType; public class P2WPKHAddress extends Address { - public static final String HRP = "bc"; - public P2WPKHAddress(byte[] pubKeyHash) { super(pubKeyHash); } - public int getVersion() { + @Override + public int getVersion(Network network) { return 0; } - public String getAddress() { - return Bech32.encode(HRP, getVersion(), hash); + @Override + public String getAddress(Network network) { + return Bech32.encode(network.getBech32AddressHRP(), getVersion(), hash); } + @Override public ScriptType getScriptType() { return ScriptType.P2WPKH; } diff --git a/src/main/java/com/sparrowwallet/drongo/address/P2WSHAddress.java b/src/main/java/com/sparrowwallet/drongo/address/P2WSHAddress.java index 19d2e9c..0886dc4 100644 --- a/src/main/java/com/sparrowwallet/drongo/address/P2WSHAddress.java +++ b/src/main/java/com/sparrowwallet/drongo/address/P2WSHAddress.java @@ -1,22 +1,24 @@ package com.sparrowwallet.drongo.address; +import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.protocol.*; -import static com.sparrowwallet.drongo.address.P2WPKHAddress.HRP; - public class P2WSHAddress extends Address { public P2WSHAddress(byte[] scriptHash) { super(scriptHash); } - public int getVersion() { + @Override + public int getVersion(Network network) { return 0; } - public String getAddress() { - return Bech32.encode(HRP, getVersion(), hash); + @Override + public String getAddress(Network network) { + return Bech32.encode(network.getBech32AddressHRP(), getVersion(), hash); } + @Override public ScriptType getScriptType() { return ScriptType.P2WSH; } diff --git a/src/test/java/com/sparrowwallet/drongo/address/AddressTest.java b/src/test/java/com/sparrowwallet/drongo/address/AddressTest.java index 7b7959c..ea93994 100644 --- a/src/test/java/com/sparrowwallet/drongo/address/AddressTest.java +++ b/src/test/java/com/sparrowwallet/drongo/address/AddressTest.java @@ -1,5 +1,7 @@ package com.sparrowwallet.drongo.address; +import com.sparrowwallet.drongo.Network; +import org.junit.After; import org.junit.Assert; import org.junit.Test; @@ -23,6 +25,51 @@ public class AddressTest { Address address4 = Address.fromString("34jnjFM4SbaB7Q8aMtNDG849RQ1gUYgpgo"); Assert.assertTrue(address4 instanceof P2SHAddress); Assert.assertEquals("34jnjFM4SbaB7Q8aMtNDG849RQ1gUYgpgo", address4.toString()); + + Address address5 = Address.fromString(Network.TESTNET, "tb1qawkzyj2l5yck5jq4wyhkc4837x088580y9uyk8"); + Assert.assertTrue(address5 instanceof P2WPKHAddress); + Assert.assertEquals("tb1qawkzyj2l5yck5jq4wyhkc4837x088580y9uyk8", address5.toString(Network.TESTNET)); + + Address address6 = Address.fromString(Network.TESTNET, "tb1q8kdkthp5a6vfrdas84efkpv25ul3s9wpzc755cra8av48xq4a7wsjcsdma"); + Assert.assertTrue(address6 instanceof P2WSHAddress); + Assert.assertEquals("tb1q8kdkthp5a6vfrdas84efkpv25ul3s9wpzc755cra8av48xq4a7wsjcsdma", address6.toString(Network.TESTNET)); + + Address address7 = Address.fromString(Network.TESTNET, "mng6R5oLWBBo8iFWU9Mx4zFy5pWhrWMeW2"); + Assert.assertTrue(address7 instanceof P2PKHAddress); + Assert.assertEquals("mng6R5oLWBBo8iFWU9Mx4zFy5pWhrWMeW2", address7.toString(Network.TESTNET)); + + Address address8 = Address.fromString(Network.TESTNET, "n1S1rnnZm3RdW9iuAF6Hjk3gLZWGc59zDi"); + Assert.assertTrue(address8 instanceof P2PKHAddress); + Assert.assertEquals("n1S1rnnZm3RdW9iuAF6Hjk3gLZWGc59zDi", address8.toString(Network.TESTNET)); + + Address address9 = Address.fromString(Network.TESTNET, "2NCZUtUt6gzXyBiPEQi5yQyrgR6f6F6Ki6A"); + Assert.assertTrue(address9 instanceof P2SHAddress); + Assert.assertEquals("2NCZUtUt6gzXyBiPEQi5yQyrgR6f6F6Ki6A", address9.toString(Network.TESTNET)); + } + + @Test + public void testnetValidAddressTest() throws InvalidAddressException { + Network.set(Network.TESTNET); + + Address address5 = Address.fromString("tb1qawkzyj2l5yck5jq4wyhkc4837x088580y9uyk8"); + Assert.assertTrue(address5 instanceof P2WPKHAddress); + Assert.assertEquals("tb1qawkzyj2l5yck5jq4wyhkc4837x088580y9uyk8", address5.toString()); + + Address address6 = Address.fromString("tb1q8kdkthp5a6vfrdas84efkpv25ul3s9wpzc755cra8av48xq4a7wsjcsdma"); + Assert.assertTrue(address6 instanceof P2WSHAddress); + Assert.assertEquals("tb1q8kdkthp5a6vfrdas84efkpv25ul3s9wpzc755cra8av48xq4a7wsjcsdma", address6.toString()); + + Address address7 = Address.fromString("mng6R5oLWBBo8iFWU9Mx4zFy5pWhrWMeW2"); + Assert.assertTrue(address7 instanceof P2PKHAddress); + Assert.assertEquals("mng6R5oLWBBo8iFWU9Mx4zFy5pWhrWMeW2", address7.toString()); + + Address address8 = Address.fromString("n1S1rnnZm3RdW9iuAF6Hjk3gLZWGc59zDi"); + Assert.assertTrue(address8 instanceof P2PKHAddress); + Assert.assertEquals("n1S1rnnZm3RdW9iuAF6Hjk3gLZWGc59zDi", address8.toString()); + + Address address9 = Address.fromString("2NCZUtUt6gzXyBiPEQi5yQyrgR6f6F6Ki6A"); + Assert.assertTrue(address9 instanceof P2SHAddress); + Assert.assertEquals("2NCZUtUt6gzXyBiPEQi5yQyrgR6f6F6Ki6A", address9.toString()); } @Test @@ -67,4 +114,9 @@ public class AddressTest { public void invalidChecksumAddressTest2() throws InvalidAddressException { Address address1 = Address.fromString("bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmb3"); } + + @After + public void tearDown() throws Exception { + Network.set(null); + } } \ No newline at end of file diff --git a/src/test/java/com/sparrowwallet/drongo/psbt/PSBTTest.java b/src/test/java/com/sparrowwallet/drongo/psbt/PSBTTest.java index 8ecc3a0..efa0780 100644 --- a/src/test/java/com/sparrowwallet/drongo/psbt/PSBTTest.java +++ b/src/test/java/com/sparrowwallet/drongo/psbt/PSBTTest.java @@ -2,10 +2,12 @@ package com.sparrowwallet.drongo.psbt; import com.sparrowwallet.drongo.ExtendedKey; import com.sparrowwallet.drongo.KeyDerivation; +import com.sparrowwallet.drongo.Network; import com.sparrowwallet.drongo.Utils; import com.sparrowwallet.drongo.crypto.ECKey; import com.sparrowwallet.drongo.protocol.*; import org.bouncycastle.util.encoders.Hex; +import org.junit.After; import org.junit.Assert; import org.junit.Test; @@ -197,6 +199,8 @@ public class PSBTTest { @Test public void validP2wshMultisigWithXpubs() throws PSBTParseException { + Network.set(Network.TESTNET); + String psbt = "cHNidP8BAFICAAAAAZ38ZijCbFiZ/hvT3DOGZb/VXXraEPYiCXPfLTht7BJ2AQAAAAD/////AfA9zR0AAAAAFgAUezoAv9wU0neVwrdJAdCdpu8TNXkAAAAATwEENYfPAto/0AiAAAAAlwSLGtBEWx7IJ1UXcnyHtOTrwYogP/oPlMAVZr046QADUbdDiH7h1A3DKmBDck8tZFmztaTXPa7I+64EcvO8Q+IM2QxqT64AAIAAAACATwEENYfPAto/0AiAAAABuQRSQnE5zXjCz/JES+NTzVhgXj5RMoXlKLQH+uP2FzUD0wpel8itvFV9rCrZp+OcFyLrrGnmaLbyZnzB1nHIPKsM2QxqT64AAIABAACAAAEBKwBlzR0AAAAAIgAgLFSGEmxJeAeagU4TcV1l82RZ5NbMre0mbQUIZFuvpjIBBUdSIQKdoSzbWyNWkrkVNq/v5ckcOrlHPY5DtTODarRWKZyIcSEDNys0I07Xz5wf6l0F1EFVeSe+lUKxYusC4ass6AIkwAtSriIGAp2hLNtbI1aSuRU2r+/lyRw6uUc9jkO1M4NqtFYpnIhxENkMak+uAACAAAAAgAAAAAAiBgM3KzQjTtfPnB/qXQXUQVV5J76VQrFi6wLhqyzoAiTACxDZDGpPrgAAgAEAAIAAAAAAACICA57/H1R6HV+S36K6evaslxpL0DukpzSwMVaiVritOh75EO3kXMUAAACAAAAAgAEAAIAA"; PSBT psbt1 = PSBT.fromString(psbt); @@ -371,4 +375,9 @@ public class PSBTTest { Assert.assertEquals("0200000000010258e87a21b56daf0c23be8e7070456c336f7cbaa5c8757924f545887bb2abdd7500000000da00473044022074018ad4180097b873323c0015720b3684cc8123891048e7dbcd9b55ad679c99022073d369b740e3eb53dcefa33823c8070514ca55a7dd9544f157c167913261118c01483045022100f61038b308dc1da865a34852746f015772934208c6d24454393cd99bdf2217770220056e675a675a6d0a02b85b14e5e29074d8a25a9b5760bea2816f661910a006ea01475221029583bf39ae0a609747ad199addd634fa6108559d6c5cd39b4c2183f1ab96e07f2102dab61ff49a14db6a7d02b0cd1fbb78fc4b18312b5b4e54dae4dba2fbfef536d752aeffffffff838d0427d0ec650a68aa46bb0b098aea4422c071b2ca78352a077959d07cea1d01000000232200208c2353173743b595dfb4a07b72ba8e42e3797da74e87fe7d9d7497e3b2028903ffffffff0270aaf00800000000160014d85c2b71d0060b09c9886aeb815e50991dda124d00e1f5050000000016001400aea9a2e5f0f876a588df5546e8742d1d87008f000400473044022062eb7a556107a7c73f45ac4ab5a1dddf6f7075fb1275969a7f383efff784bcb202200c05dbb7470dbf2f08557dd356c7325c1ed30913e996cd3840945db12228da5f01473044022065f45ba5998b59a27ffe1a7bed016af1f1f90d54b3aa8f7450aa5f56a25103bd02207f724703ad1edb96680b284b56d4ffcb88f7fb759eabbe08aa30f29b851383d20147522103089dc10c7ac6db54f91329af617333db388cead0c231f723379d1b99030b02dc21023add904f3d6dcf59ddb906b0dee23529b7ffb9ed50e5e86151926860221f0e7352ae00000000", Utils.bytesToHex(transaction.bitcoinSerialize())); } + + @After + public void tearDown() throws Exception { + Network.set(null); + } }