Skip to content

Commit 8c317d4

Browse files
author
Andreas Schildbach
committed
WIP VersionedChecksummedBytes: Store network params rather than version, in preparation for Segwit addresses.
This also does away with the concept of "acceptable version codes" (for a network), because this list of two versions is not likely to expand.
1 parent 2241253 commit 8c317d4

File tree

8 files changed

+160
-185
lines changed

8 files changed

+160
-185
lines changed

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

Lines changed: 52 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -18,17 +18,16 @@
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;
2422

25-
import org.bitcoinj.params.Networks;
26-
import org.bitcoinj.script.Script;
23+
import java.util.Arrays;
2724

2825
import javax.annotation.Nullable;
2926

30-
import static com.google.common.base.Preconditions.checkArgument;
31-
import static com.google.common.base.Preconditions.checkNotNull;
27+
import org.bitcoinj.params.Networks;
28+
import org.bitcoinj.script.Script;
29+
30+
import com.google.common.base.Objects;
3231

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

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

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

6670
/** Returns an Address that represents the given P2SH script hash. */
6771
public static Address fromP2SHHash(NetworkParameters params, byte[] hash160) {
6872
try {
69-
return new Address(params, params.getP2SHHeader(), hash160);
73+
return new Address(params, true, hash160);
7074
} catch (WrongNetworkException e) {
7175
throw new RuntimeException(e); // Cannot happen.
7276
}
@@ -90,44 +94,31 @@ public static Address fromP2SHScript(NetworkParameters params, Script scriptPubK
9094
* if the given address is valid but for a different chain (eg testnet vs mainnet)
9195
*/
9296
public static Address fromBase58(@Nullable NetworkParameters params, String base58) throws AddressFormatException {
93-
return new Address(params, base58);
94-
}
95-
96-
/**
97-
* Construct an address from parameters and the hash160 form. Example:<p>
98-
*
99-
* <pre>new Address(MainNetParams.get(), Hex.decode("4a22c3c4cbb31e4d03b15550636762bda0baf85a"));</pre>
100-
*/
101-
public Address(NetworkParameters params, byte[] hash160) {
102-
super(params.getAddressHeader(), hash160);
103-
checkArgument(hash160.length == 20, "Addresses are 160-bit hashes, so you must provide 20 bytes");
104-
this.params = params;
105-
}
106-
107-
/** @deprecated Use {@link #fromBase58(NetworkParameters, String)} */
108-
@Deprecated
109-
public Address(@Nullable NetworkParameters params, String address) throws AddressFormatException {
110-
super(address);
111-
if (params != null) {
112-
if (!isAcceptableVersion(params, version)) {
113-
throw new WrongNetworkException(version);
114-
}
115-
this.params = params;
116-
} else {
117-
NetworkParameters paramsFound = null;
97+
byte[] versionAndDataBytes = Base58.decodeChecked(base58);
98+
int version = versionAndDataBytes[0] & 0xFF;
99+
byte[] bytes = Arrays.copyOfRange(versionAndDataBytes, 1, versionAndDataBytes.length);
100+
if (params == null) {
118101
for (NetworkParameters p : Networks.get()) {
119-
if (isAcceptableVersion(p, version)) {
120-
paramsFound = p;
121-
break;
122-
}
102+
if (version == p.getAddressHeader())
103+
return new Address(p, false, bytes);
104+
else if (version == p.getP2SHHeader())
105+
return new Address(p, true, bytes);
123106
}
124-
if (paramsFound == null)
125-
throw new AddressFormatException("No network found for " + address);
126-
127-
this.params = paramsFound;
107+
throw new AddressFormatException("No network found for " + base58);
108+
} else {
109+
if (version == params.getAddressHeader())
110+
return new Address(params, false, bytes);
111+
else if (version == params.getP2SHHeader())
112+
return new Address(params, true, bytes);
113+
throw new WrongNetworkException(version);
128114
}
129115
}
130116

117+
@Override
118+
protected int getVersion() {
119+
return p2sh ? params.getP2SHHeader() : params.getAddressHeader();
120+
}
121+
131122
/** The (big endian) 20 byte hash that is the core of a Bitcoin address. */
132123
public byte[] getHash160() {
133124
return bytes;
@@ -138,8 +129,7 @@ public byte[] getHash160() {
138129
* See also https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki: Address Format for pay-to-script-hash
139130
*/
140131
public boolean isP2SHAddress() {
141-
final NetworkParameters parameters = getParameters();
142-
return parameters != null && this.version == parameters.p2shHeader;
132+
return p2sh;
143133
}
144134

145135
/**
@@ -169,15 +159,19 @@ public static NetworkParameters getParametersFromAddress(String address) throws
169159
}
170160
}
171161

172-
/**
173-
* Check if a given address version is valid given the NetworkParameters.
174-
*/
175-
private static boolean isAcceptableVersion(NetworkParameters params, int version) {
176-
if (version == params.getAddressHeader())
177-
return true;
178-
if (version == params.getP2SHHeader())
162+
@Override
163+
public boolean equals(Object o) {
164+
if (this == o)
179165
return true;
180-
return false;
166+
if (o == null || getClass() != o.getClass())
167+
return false;
168+
Address other = (Address) o;
169+
return super.equals(other) && this.p2sh == other.p2sh;
170+
}
171+
172+
@Override
173+
public int hashCode() {
174+
return Objects.hashCode(super.hashCode(), p2sh);
181175
}
182176

183177
/**
@@ -187,16 +181,4 @@ private static boolean isAcceptableVersion(NetworkParameters params, int version
187181
public Address clone() throws CloneNotSupportedException {
188182
return (Address) super.clone();
189183
}
190-
191-
// Java serialization
192-
193-
private void writeObject(ObjectOutputStream out) throws IOException {
194-
out.defaultWriteObject();
195-
out.writeUTF(params.id);
196-
}
197-
198-
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
199-
in.defaultReadObject();
200-
params = NetworkParameters.fromID(in.readUTF());
201-
}
202184
}

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
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ public boolean isCompressed() {
513513
* the RIPEMD-160 hash of the public key and is not the public key itself (which is too large to be convenient).
514514
*/
515515
public Address toAddress(NetworkParameters params) {
516-
return new Address(params, getPubKeyHash());
516+
return new Address(params, false, getPubKeyHash());
517517
}
518518

519519
/**

0 commit comments

Comments
 (0)