Skip to content

Commit 77b1893

Browse files
author
Andreas Schildbach
committed
WIP VersionedChecksummedBytes: Store network params rather than version, in preparation for Segwit addresses.
1 parent ea2dc69 commit 77b1893

File tree

7 files changed

+166
-196
lines changed

7 files changed

+166
-196
lines changed

core/src/main/java/org/bitcoinj/core/Address.java

Lines changed: 52 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,17 @@
1818

1919
package org.bitcoinj.core;
2020

21-
import java.io.IOException;
22-
import java.io.ObjectInputStream;
23-
import java.io.ObjectOutputStream;
21+
import static com.google.common.base.Preconditions.checkArgument;
22+
23+
import java.util.Arrays;
24+
25+
import javax.annotation.Nullable;
2426

2527
import org.bitcoinj.params.Networks;
2628
import org.bitcoinj.script.Script;
2729
import org.bitcoinj.script.ScriptPattern;
2830

29-
import javax.annotation.Nullable;
30-
31-
import static com.google.common.base.Preconditions.checkArgument;
32-
import static com.google.common.base.Preconditions.checkNotNull;
31+
import com.google.common.base.Objects;
3332

3433
/**
3534
* <p>A Bitcoin address looks like 1MsScoe2fTJoq4ZPdQgqyhgWeoNamYPevy and is derived from an elliptic curve public key
@@ -48,20 +47,25 @@ public class Address extends VersionedChecksummedBytes {
4847
*/
4948
public static final int LENGTH = 20;
5049

51-
private transient NetworkParameters params;
50+
/** True if P2SH, false if P2PKH. */
51+
public final boolean p2sh;
5252

5353
/**
5454
* Construct an address from parameters, the address version, and the hash160 form. Example:<p>
5555
*
5656
* <pre>new Address(MainNetParams.get(), NetworkParameters.getAddressHeader(), Hex.decode("4a22c3c4cbb31e4d03b15550636762bda0baf85a"));</pre>
5757
*/
58-
public Address(NetworkParameters params, int version, byte[] hash160) throws WrongNetworkException {
59-
super(version, hash160);
60-
checkNotNull(params);
58+
public Address(NetworkParameters params, boolean p2sh, byte[] hash160) throws WrongNetworkException {
59+
super(params, hash160);
6160
checkArgument(hash160.length == 20, "Addresses are 160-bit hashes, so you must provide 20 bytes");
62-
if (!isAcceptableVersion(params, version))
63-
throw new WrongNetworkException(version);
64-
this.params = params;
61+
this.p2sh = p2sh;
62+
}
63+
64+
/**
65+
* Constructs a P2PKH address.
66+
*/
67+
public Address(NetworkParameters params, byte[] hash160) throws WrongNetworkException {
68+
this(params, false, hash160);
6569
}
6670

6771
/**
@@ -75,7 +79,7 @@ public static Address fromKey(NetworkParameters params, ECKey key) {
7579
/** Returns an Address that represents the given P2SH script hash. */
7680
public static Address fromP2SHHash(NetworkParameters params, byte[] hash160) {
7781
try {
78-
return new Address(params, params.getP2SHHeader(), hash160);
82+
return new Address(params, true, hash160);
7983
} catch (WrongNetworkException e) {
8084
throw new RuntimeException(e); // Cannot happen.
8185
}
@@ -99,44 +103,31 @@ public static Address fromP2SHScript(NetworkParameters params, Script scriptPubK
99103
* if the given address is valid but for a different chain (eg testnet vs mainnet)
100104
*/
101105
public static Address fromBase58(@Nullable NetworkParameters params, String base58) throws AddressFormatException {
102-
return new Address(params, base58);
103-
}
104-
105-
/**
106-
* Construct an address from parameters and the hash160 form. Example:<p>
107-
*
108-
* <pre>new Address(MainNetParams.get(), Hex.decode("4a22c3c4cbb31e4d03b15550636762bda0baf85a"));</pre>
109-
*/
110-
public Address(NetworkParameters params, byte[] hash160) {
111-
super(params.getAddressHeader(), hash160);
112-
checkArgument(hash160.length == 20, "Addresses are 160-bit hashes, so you must provide 20 bytes");
113-
this.params = params;
114-
}
115-
116-
/** @deprecated Use {@link #fromBase58(NetworkParameters, String)} */
117-
@Deprecated
118-
public Address(@Nullable NetworkParameters params, String address) throws AddressFormatException {
119-
super(address);
120-
if (params != null) {
121-
if (!isAcceptableVersion(params, version)) {
122-
throw new WrongNetworkException(version);
123-
}
124-
this.params = params;
125-
} else {
126-
NetworkParameters paramsFound = null;
106+
byte[] versionAndDataBytes = Base58.decodeChecked(base58);
107+
int version = versionAndDataBytes[0] & 0xFF;
108+
byte[] bytes = Arrays.copyOfRange(versionAndDataBytes, 1, versionAndDataBytes.length);
109+
if (params == null) {
127110
for (NetworkParameters p : Networks.get()) {
128-
if (isAcceptableVersion(p, version)) {
129-
paramsFound = p;
130-
break;
131-
}
111+
if (version == p.getAddressHeader())
112+
return new Address(p, false, bytes);
113+
else if (version == p.getP2SHHeader())
114+
return new Address(p, true, bytes);
132115
}
133-
if (paramsFound == null)
134-
throw new AddressFormatException("No network found for " + address);
135-
136-
this.params = paramsFound;
116+
throw new AddressFormatException("No network found for " + base58);
117+
} else {
118+
if (version == params.getAddressHeader())
119+
return new Address(params, false, bytes);
120+
else if (version == params.getP2SHHeader())
121+
return new Address(params, true, bytes);
122+
throw new WrongNetworkException(version);
137123
}
138124
}
139125

126+
@Override
127+
protected int getVersion() {
128+
return p2sh ? params.getP2SHHeader() : params.getAddressHeader();
129+
}
130+
140131
/** The (big endian) 20 byte hash that is the core of a Bitcoin address. */
141132
public byte[] getHash160() {
142133
return bytes;
@@ -147,20 +138,7 @@ public byte[] getHash160() {
147138
* See also https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki: Address Format for pay-to-script-hash
148139
*/
149140
public boolean isP2SHAddress() {
150-
final NetworkParameters parameters = getParameters();
151-
return parameters != null && this.version == parameters.p2shHeader;
152-
}
153-
154-
/**
155-
* Examines the version byte of the address and attempts to find a matching NetworkParameters. If you aren't sure
156-
* which network the address is intended for (eg, it was provided by a user), you can use this to decide if it is
157-
* compatible with the current wallet. You should be able to handle a null response from this method. Note that the
158-
* parameters returned is not necessarily the same as the one the Address was created with.
159-
*
160-
* @return a NetworkParameters representing the network the address is intended for.
161-
*/
162-
public NetworkParameters getParameters() {
163-
return params;
141+
return p2sh;
164142
}
165143

166144
/**
@@ -178,15 +156,19 @@ public static NetworkParameters getParametersFromAddress(String address) throws
178156
}
179157
}
180158

181-
/**
182-
* Check if a given address version is valid given the NetworkParameters.
183-
*/
184-
private static boolean isAcceptableVersion(NetworkParameters params, int version) {
185-
if (version == params.getAddressHeader())
186-
return true;
187-
if (version == params.getP2SHHeader())
159+
@Override
160+
public boolean equals(Object o) {
161+
if (this == o)
188162
return true;
189-
return false;
163+
if (o == null || getClass() != o.getClass())
164+
return false;
165+
Address other = (Address) o;
166+
return super.equals(other) && this.p2sh == other.p2sh;
167+
}
168+
169+
@Override
170+
public int hashCode() {
171+
return Objects.hashCode(super.hashCode(), p2sh);
190172
}
191173

192174
/**
@@ -196,16 +178,4 @@ private static boolean isAcceptableVersion(NetworkParameters params, int version
196178
public Address clone() throws CloneNotSupportedException {
197179
return (Address) super.clone();
198180
}
199-
200-
// Java serialization
201-
202-
private void writeObject(ObjectOutputStream out) throws IOException {
203-
out.defaultWriteObject();
204-
out.writeUTF(params.id);
205-
}
206-
207-
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
208-
in.defaultReadObject();
209-
params = NetworkParameters.fromID(in.readUTF());
210-
}
211181
}

core/src/main/java/org/bitcoinj/core/DumpedPrivateKey.java

Lines changed: 29 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,14 @@
1717

1818
package org.bitcoinj.core;
1919

20-
import com.google.common.base.Objects;
2120
import com.google.common.base.Preconditions;
2221

2322
import java.util.Arrays;
2423

2524
import javax.annotation.Nullable;
2625

26+
import org.bitcoinj.params.Networks;
27+
2728
/**
2829
* Parses and generates private keys in the form used by the Bitcoin "dumpprivkey" command. This is the private key
2930
* bytes with a header byte and 4 checksum bytes at the end. If there are 33 private key bytes instead of 32, then
@@ -42,13 +43,37 @@ public class DumpedPrivateKey extends VersionedChecksummedBytes {
4243
* @throws WrongNetworkException
4344
* if the given private key is valid but for a different chain (eg testnet vs mainnet)
4445
*/
45-
public static DumpedPrivateKey fromBase58(@Nullable NetworkParameters params,String base58) throws AddressFormatException {
46-
return new DumpedPrivateKey(params, base58);
46+
public static DumpedPrivateKey fromBase58(@Nullable NetworkParameters params, String base58)
47+
throws AddressFormatException {
48+
byte[] versionAndDataBytes = Base58.decodeChecked(base58);
49+
int version = versionAndDataBytes[0] & 0xFF;
50+
byte[] bytes = Arrays.copyOfRange(versionAndDataBytes, 1, versionAndDataBytes.length);
51+
if (params == null) {
52+
for (NetworkParameters p : Networks.get())
53+
if (version == p.getDumpedPrivateKeyHeader())
54+
return new DumpedPrivateKey(p, bytes);
55+
throw new AddressFormatException("No network found for " + base58);
56+
} else {
57+
if (version == params.getDumpedPrivateKeyHeader())
58+
return new DumpedPrivateKey(params, bytes);
59+
throw new WrongNetworkException(version);
60+
}
61+
}
62+
63+
private DumpedPrivateKey(NetworkParameters params, byte[] bytes) {
64+
super(params, bytes);
65+
if (bytes.length != 32 && bytes.length != 33)
66+
throw new AddressFormatException("Wrong number of bytes for a private key, not 32 or 33");
4767
}
4868

4969
// Used by ECKey.getPrivateKeyEncoded()
5070
DumpedPrivateKey(NetworkParameters params, byte[] keyBytes, boolean compressed) {
51-
super(params.getDumpedPrivateKeyHeader(), encode(keyBytes, compressed));
71+
this(params, encode(keyBytes, compressed));
72+
}
73+
74+
@Override
75+
protected int getVersion() {
76+
return params.getDumpedPrivateKeyHeader();
5277
}
5378

5479
private static byte[] encode(byte[] keyBytes, boolean compressed) {
@@ -64,17 +89,6 @@ private static byte[] encode(byte[] keyBytes, boolean compressed) {
6489
}
6590
}
6691

67-
/** @deprecated Use {@link #fromBase58(NetworkParameters, String)} */
68-
@Deprecated
69-
public DumpedPrivateKey(@Nullable NetworkParameters params, String encoded) throws AddressFormatException {
70-
super(encoded);
71-
if (params != null && version != params.getDumpedPrivateKeyHeader())
72-
throw new WrongNetworkException(version);
73-
if (bytes.length != 32 && bytes.length != 33) {
74-
throw new AddressFormatException("Wrong number of bytes for a private key, not 32 or 33");
75-
}
76-
}
77-
7892
/**
7993
* Returns an ECKey created from this encoded private key.
8094
*/
@@ -88,17 +102,4 @@ public ECKey getKey() {
88102
public boolean isPubKeyCompressed() {
89103
return bytes.length == 33 && bytes[32] == 1;
90104
}
91-
92-
@Override
93-
public boolean equals(Object o) {
94-
if (this == o) return true;
95-
if (o == null || getClass() != o.getClass()) return false;
96-
DumpedPrivateKey other = (DumpedPrivateKey) o;
97-
return version == other.version && Arrays.equals(bytes, other.bytes);
98-
}
99-
100-
@Override
101-
public int hashCode() {
102-
return Objects.hashCode(version, Arrays.hashCode(bytes));
103-
}
104105
}

0 commit comments

Comments
 (0)