Skip to content

Commit 0901588

Browse files
committed
HD Keys / Bip32 and Tests
- Ported over HD Keys + Mnemonics (Bip32/38) support along with tests
1 parent daf1dbd commit 0901588

33 files changed

+5789
-61
lines changed

src/main/java/org/twostack/bitcoin/address/Address.java renamed to src/main/java/org/twostack/bitcoin/Address.java

+14-11
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.twostack.bitcoin.address;
17+
package org.twostack.bitcoin;
1818

19-
import org.twostack.bitcoin.ECKey;
20-
import org.twostack.bitcoin.PublicKey;
19+
import org.twostack.bitcoin.address.LegacyAddress;
20+
import org.twostack.bitcoin.address.PrefixedChecksummedBytes;
2121
import org.twostack.bitcoin.exception.AddressFormatException;
2222
import org.twostack.bitcoin.params.NetworkAddressType;
23-
import org.twostack.bitcoin.script.Script;
23+
import org.twostack.bitcoin.params.NetworkParameters;
2424
import org.twostack.bitcoin.script.Script.ScriptType;
2525

2626
import javax.annotation.Nullable;
@@ -29,21 +29,25 @@
2929
* <p>
3030
* Base class for addresses, e.g. legacy addresses ({@link org.twostack.bitcoin.address.LegacyAddress}).
3131
* </p>
32-
*
32+
*
3333
* <p>
3434
* Use {@link #fromString(NetworkAddressType, String)} to conveniently construct any kind of address from its textual
3535
* form.
3636
* </p>
3737
*/
3838
public abstract class Address extends PrefixedChecksummedBytes implements Comparable<Address> {
39+
40+
protected final transient NetworkAddressType networkAddressType;
41+
3942
public Address(NetworkAddressType networkAddressType, byte[] bytes) {
40-
super(networkAddressType, bytes);
43+
super(NetworkParameters.getNetworkType(networkAddressType), bytes);
44+
this.networkAddressType = networkAddressType;
4145
}
4246

4347
/**
4448
* Construct an address from its textual form.
45-
*
46-
* @param params
49+
*
50+
* @param addressType
4751
* the expected network this address is valid for, or null if the network should be derived from the
4852
* textual form
4953
* @param str
@@ -55,9 +59,8 @@ public Address(NetworkAddressType networkAddressType, byte[] bytes) {
5559
* @throws AddressFormatException.WrongNetwork
5660
* if the given string is valid but not for the expected network (eg testnet vs mainnet)
5761
*/
58-
public static Address fromString(@Nullable NetworkAddressType params, String str)
59-
throws AddressFormatException {
60-
return LegacyAddress.fromBase58(params, str);
62+
public static Address fromString(@Nullable NetworkAddressType addressType, String str) throws AddressFormatException {
63+
return LegacyAddress.fromBase58(addressType, str);
6164
}
6265

6366
/**

src/main/java/org/twostack/bitcoin/ECKey.java

+23-3
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,14 @@
4141
import org.bouncycastle.util.encoders.Base64;
4242
import org.slf4j.Logger;
4343
import org.slf4j.LoggerFactory;
44-
import org.twostack.bitcoin.address.Address;
45-
import org.twostack.bitcoin.address.LegacyAddress;
4644
import org.twostack.bitcoin.ecc.NativeSecp256k1;
4745
import org.twostack.bitcoin.ecc.NativeSecp256k1Util;
4846
import org.twostack.bitcoin.ecc.Secp256k1Context;
4947
import org.twostack.bitcoin.crypto.LinuxSecureRandom;
5048
import org.twostack.bitcoin.exception.SignatureDecodeException;
5149
import org.twostack.bitcoin.crypto.*;
5250
import org.twostack.bitcoin.params.NetworkParameters;
53-
import org.twostack.bitcoin.script.Script;
51+
import org.twostack.bitcoin.params.NetworkType;
5452

5553
import javax.annotation.Nullable;
5654
import java.io.ByteArrayOutputStream;
@@ -1006,6 +1004,28 @@ public byte[] getPrivKeyBytes() {
10061004
return Utils.bigIntegerToBytes(getPrivKey(), 32);
10071005
}
10081006

1007+
/**
1008+
* Returns the creation time of this key or zero if the key was deserialized from a version that did not store
1009+
* that data.
1010+
*/
1011+
public long getCreationTimeSeconds() {
1012+
return creationTimeSeconds;
1013+
}
1014+
1015+
1016+
/**
1017+
* Exports the private key in the form used by Bitcoin Core's "dumpprivkey" and "importprivkey" commands. Use
1018+
* the {@link DumpedPrivateKey#toString()} method to get the string.
1019+
*
1020+
* @param networkType The network this key is intended for use on.
1021+
* @return Private key bytes as a {@link DumpedPrivateKey}.
1022+
* @throws IllegalStateException if the private key is not available.
1023+
*/
1024+
public DumpedPrivateKey getPrivateKeyEncoded(NetworkType networkType) {
1025+
return new DumpedPrivateKey(networkType, getPrivKeyBytes(), isCompressed());
1026+
}
1027+
1028+
10091029

10101030
/**
10111031
* Sets the creation time of this key. Zero is a convention to mean "unavailable". This method can be useful when

src/main/java/org/twostack/bitcoin/PublicKey.java

+4
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ public static PublicKey fromHex(String encoded) {
1515
return new PublicKey(ECKey.fromPublicOnly(pubkeyBytes));
1616
}
1717

18+
public static PublicKey fromBytes(byte[] pubkeyBytes){
19+
return new PublicKey(ECKey.fromPublicOnly(pubkeyBytes));
20+
}
21+
1822
public byte[] getPubKeyHash(){
1923
return key.getPubKeyHash();
2024
}

src/main/java/org/twostack/bitcoin/address/LegacyAddress.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@
1919
package org.twostack.bitcoin.address;
2020

2121
import com.google.common.primitives.UnsignedBytes;
22+
import org.twostack.bitcoin.Address;
2223
import org.twostack.bitcoin.ECKey;
2324
import org.twostack.bitcoin.PublicKey;
2425
import org.twostack.bitcoin.exception.AddressFormatException;
2526
import org.twostack.bitcoin.params.NetworkAddressType;
2627
import org.twostack.bitcoin.params.NetworkParameters;
27-
import org.twostack.bitcoin.params.NetworkType;
2828
import org.twostack.bitcoin.script.Script.ScriptType;
2929

3030
import javax.annotation.Nullable;
@@ -47,6 +47,7 @@ public class LegacyAddress extends Address {
4747
*/
4848
public static final int LENGTH = 20;
4949

50+
5051
/**
5152
* Private constructor. Use {@link #fromBase58(NetworkAddressType, String)},
5253
* {@link #fromPubKeyHash(NetworkAddressType, byte[])},

src/main/java/org/twostack/bitcoin/address/PrefixedChecksummedBytes.java

+29-23
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,13 @@
1818
package org.twostack.bitcoin.address;
1919

2020
import org.twostack.bitcoin.params.NetworkAddressType;
21+
import org.twostack.bitcoin.params.NetworkType;
2122

23+
import java.io.IOException;
24+
import java.io.ObjectInputStream;
25+
import java.io.ObjectOutputStream;
2226
import java.io.Serializable;
27+
import java.lang.reflect.Field;
2328
import java.util.Arrays;
2429
import java.util.Objects;
2530

@@ -39,32 +44,33 @@
3944
* </p>
4045
*/
4146
public abstract class PrefixedChecksummedBytes implements Serializable, Cloneable {
42-
protected final transient NetworkAddressType networkAddressType;
47+
protected final transient NetworkType networkType;
4348
protected final byte[] bytes;
4449

45-
protected PrefixedChecksummedBytes(NetworkAddressType networkAddressType, byte[] bytes) {
46-
this.networkAddressType = checkNotNull(networkAddressType);
50+
protected PrefixedChecksummedBytes(NetworkType networkType, byte[] bytes) {
51+
this.networkType = checkNotNull(networkType);
4752
this.bytes = checkNotNull(bytes);
4853
}
4954

5055
/**
5156
* @return network this data is valid for
5257
*/
53-
public final NetworkAddressType getNetworkAddressType() {
54-
return networkAddressType;
58+
public final NetworkType getNetworkType(){
59+
return networkType;
5560
}
5661

62+
5763
@Override
5864
public int hashCode() {
59-
return Objects.hash(networkAddressType, Arrays.hashCode(bytes));
65+
return Objects.hash(networkType, Arrays.hashCode(bytes));
6066
}
6167

6268
@Override
6369
public boolean equals(Object o) {
6470
if (this == o) return true;
6571
if (o == null || getClass() != o.getClass()) return false;
6672
PrefixedChecksummedBytes other = (PrefixedChecksummedBytes) o;
67-
return this.networkAddressType.equals(other.networkAddressType) && Arrays.equals(this.bytes, other.bytes);
73+
return this.networkType.equals(other.networkType) && Arrays.equals(this.bytes, other.bytes);
6874
}
6975

7076
/**
@@ -78,21 +84,21 @@ public PrefixedChecksummedBytes clone() throws CloneNotSupportedException {
7884
}
7985

8086
// Java serialization
81-
//
82-
// private void writeObject(ObjectOutputStream out) throws IOException {
83-
// out.defaultWriteObject();
84-
// out.writeUTF(params.getId());
85-
// }
8687

87-
// private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
88-
// in.defaultReadObject();
89-
// try {
90-
// Field paramsField = PrefixedChecksummedBytes.class.getDeclaredField("params");
91-
// paramsField.setAccessible(true);
92-
// paramsField.set(this, checkNotNull(NetworkParameters.fromID(in.readUTF())));
93-
// paramsField.setAccessible(false);
94-
// } catch (NoSuchFieldException | IllegalAccessException x) {
95-
// throw new RuntimeException(x);
96-
// }
97-
// }
88+
private void writeObject(ObjectOutputStream out) throws IOException {
89+
out.defaultWriteObject();
90+
out.writeUTF(networkType.name());
91+
}
92+
93+
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
94+
in.defaultReadObject();
95+
try {
96+
Field paramsField = PrefixedChecksummedBytes.class.getDeclaredField("networkType");
97+
paramsField.setAccessible(true);
98+
paramsField.set(this, checkNotNull(NetworkType.valueOf(in.readUTF())));
99+
paramsField.setAccessible(false);
100+
} catch (NoSuchFieldException | IllegalAccessException x) {
101+
throw new RuntimeException(x);
102+
}
103+
}
98104
}

0 commit comments

Comments
 (0)