Sitelet https://github.com/schildbach/bitcoinj/commit/30ec71dc5187da4366ac3cc74b7ef8154ceea3c4
Skip to content

Commit 30ec71d

Browse files
author
Andreas Schildbach
committed
VersionedChecksummedBytes: Store network params rather than version, in preparation for native segwit addresses.
1 parent 44474be commit 30ec71d

6 files changed

Lines changed: 157 additions & 184 deletions

File tree

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

Lines changed: 48 additions & 74 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,7 +47,8 @@ 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
* Private constructor. Use {@link #fromBase58(NetworkParameters, String)},
@@ -62,13 +62,10 @@ public class Address extends VersionedChecksummedBytes {
6262
* @param hash160
6363
* 20-byte hash of pubkey or script
6464
*/
65-
private Address(NetworkParameters params, int version, byte[] hash160) throws WrongNetworkException {
66-
super(version, hash160);
67-
checkNotNull(params);
65+
private Address(NetworkParameters params, boolean p2sh, byte[] hash160) throws WrongNetworkException {
66+
super(params, hash160);
6867
checkArgument(hash160.length == 20, "Addresses are 160-bit hashes, so you must provide 20 bytes");
69-
if (!isAcceptableVersion(params, version))
70-
throw new WrongNetworkException(version);
71-
this.params = params;
68+
this.p2sh = p2sh;
7269
}
7370

7471
/**
@@ -82,7 +79,7 @@ private Address(NetworkParameters params, int version, byte[] hash160) throws Wr
8279
* @return constructed address
8380
*/
8481
public static Address fromPubKeyHash(NetworkParameters params, byte[] hash160) {
85-
return new Address(params, params.getAddressHeader(), hash160);
82+
return new Address(params, false, hash160);
8683
}
8784

8885
/**
@@ -96,7 +93,7 @@ public static Address fromKey(NetworkParameters params, ECKey key) {
9693
/** Returns an Address that represents the given P2SH script hash. */
9794
public static Address fromP2SHHash(NetworkParameters params, byte[] hash160) {
9895
try {
99-
return new Address(params, params.getP2SHHeader(), hash160);
96+
return new Address(params, true, hash160);
10097
} catch (WrongNetworkException e) {
10198
throw new RuntimeException(e); // Cannot happen.
10299
}
@@ -120,37 +117,35 @@ public static Address fromP2SHScript(NetworkParameters params, Script scriptPubK
120117
* if the given address is valid but for a different chain (eg testnet vs mainnet)
121118
*/
122119
public static Address fromBase58(@Nullable NetworkParameters params, String base58) throws AddressFormatException {
123-
return new Address(params, base58);
120+
byte[] versionAndDataBytes = Base58.decodeChecked(base58);
121+
int version = versionAndDataBytes[0] & 0xFF;
122+
byte[] bytes = Arrays.copyOfRange(versionAndDataBytes, 1, versionAndDataBytes.length);
123+
if (params == null) {
124+
for (NetworkParameters p : Networks.get()) {
125+
if (version == p.getAddressHeader())
126+
return new Address(p, false, bytes);
127+
else if (version == p.getP2SHHeader())
128+
return new Address(p, true, bytes);
129+
}
130+
throw new AddressFormatException("No network found for " + base58);
131+
} else {
132+
if (version == params.getAddressHeader())
133+
return new Address(params, false, bytes);
134+
else if (version == params.getP2SHHeader())
135+
return new Address(params, true, bytes);
136+
throw new WrongNetworkException(version);
137+
}
124138
}
125139

126140
/** @deprecated use {@link #fromPubKeyHash(NetworkParameters, byte[])} */
127141
@Deprecated
128142
public Address(NetworkParameters params, byte[] hash160) {
129-
this(params, params.getAddressHeader(), hash160);
143+
this(params, false, hash160);
130144
}
131145

132-
/** @deprecated Use {@link #fromBase58(NetworkParameters, String)} */
133-
@Deprecated
134-
public Address(@Nullable NetworkParameters params, String address) throws AddressFormatException {
135-
super(address);
136-
if (params != null) {
137-
if (!isAcceptableVersion(params, version)) {
138-
throw new WrongNetworkException(version);
139-
}
140-
this.params = params;
141-
} else {
142-
NetworkParameters paramsFound = null;
143-
for (NetworkParameters p : Networks.get()) {
144-
if (isAcceptableVersion(p, version)) {
145-
paramsFound = p;
146-
break;
147-
}
148-
}
149-
if (paramsFound == null)
150-
throw new AddressFormatException("No network found for " + address);
151-
152-
this.params = paramsFound;
153-
}
146+
@Override
147+
protected int getVersion() {
148+
return p2sh ? params.getP2SHHeader() : params.getAddressHeader();
154149
}
155150

156151
/** The (big endian) 20 byte hash that is the core of a Bitcoin address. */
@@ -163,20 +158,7 @@ public byte[] getHash160() {
163158
* See also https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki: Address Format for pay-to-script-hash
164159
*/
165160
public boolean isP2SHAddress() {
166-
final NetworkParameters parameters = getParameters();
167-
return parameters != null && this.version == parameters.p2shHeader;
168-
}
169-
170-
/**
171-
* Examines the version byte of the address and attempts to find a matching NetworkParameters. If you aren't sure
172-
* which network the address is intended for (eg, it was provided by a user), you can use this to decide if it is
173-
* compatible with the current wallet. You should be able to handle a null response from this method. Note that the
174-
* parameters returned is not necessarily the same as the one the Address was created with.
175-
*
176-
* @return a NetworkParameters representing the network the address is intended for.
177-
*/
178-
public NetworkParameters getParameters() {
179-
return params;
161+
return p2sh;
180162
}
181163

182164
/**
@@ -194,15 +176,19 @@ public static NetworkParameters getParametersFromAddress(String address) throws
194176
}
195177
}
196178

197-
/**
198-
* Check if a given address version is valid given the NetworkParameters.
199-
*/
200-
private static boolean isAcceptableVersion(NetworkParameters params, int version) {
201-
if (version == params.getAddressHeader())
202-
return true;
203-
if (version == params.getP2SHHeader())
179+
@Override
180+
public boolean equals(Object o) {
181+
if (this == o)
204182
return true;
205-
return false;
183+
if (o == null || getClass() != o.getClass())
184+
return false;
185+
Address other = (Address) o;
186+
return super.equals(other) && this.p2sh == other.p2sh;
187+
}
188+
189+
@Override
190+
public int hashCode() {
191+
return Objects.hashCode(super.hashCode(), p2sh);
206192
}
207193

208194
/**
@@ -212,16 +198,4 @@ private static boolean isAcceptableVersion(NetworkParameters params, int version
212198
public Address clone() throws CloneNotSupportedException {
213199
return (Address) super.clone();
214200
}
215-
216-
// Java serialization
217-
218-
private void writeObject(ObjectOutputStream out) throws IOException {
219-
out.defaultWriteObject();
220-
out.writeUTF(params.id);
221-
}
222-
223-
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
224-
in.defaultReadObject();
225-
params = NetworkParameters.fromID(in.readUTF());
226-
}
227201
}

‎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)