Skip to content

Commit 63e94c8

Browse files
author
Andreas Schildbach
committed
Implement native segwit addresses in new SegwitAddress class.
This commit establishes a base class for legacy and segwit addresses. Uses Bech32 code from sipa/bech32#40.
1 parent 30ec71d commit 63e94c8

18 files changed

+857
-53
lines changed
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2018 Andreas Schildbach
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.bitcoinj.core;
18+
19+
import javax.annotation.Nullable;
20+
21+
import org.bitcoinj.script.Script.ScriptType;
22+
23+
/**
24+
* <p>
25+
* Base class for addresses, e.g. native segwit addresses ({@link SegwitAddress}) or legacy addresses ({@link Address}).
26+
* </p>
27+
*
28+
* <p>
29+
* Use {@link #fromString(NetworkParameters, String)} to conveniently construct any kind of address from its textual
30+
* form.
31+
* </p>
32+
*/
33+
public abstract class AbstractAddress extends VersionedChecksummedBytes {
34+
public AbstractAddress(NetworkParameters params, byte[] bytes) {
35+
super(params, bytes);
36+
}
37+
38+
/**
39+
* Construct an address from its textual form.
40+
*
41+
* @param params
42+
* the expected network this address is valid for, or null if the network should be derived from the
43+
* textual form
44+
* @param str
45+
* the textual form of the address, such as "17kzeh4N8g49GFvdDzSf8PjaPfyoD1MndL" or
46+
* "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
47+
* @return constructed address
48+
* @throws AddressFormatException
49+
* if the given string doesn't parse or the checksum is invalid
50+
* @throws WrongNetworkException
51+
* if the given string is valid but not for the expected network (eg testnet vs mainnet)
52+
*/
53+
public static AbstractAddress fromString(@Nullable NetworkParameters params, String str)
54+
throws AddressFormatException {
55+
try {
56+
return Address.fromBase58(params, str);
57+
} catch (WrongNetworkException x) {
58+
throw x;
59+
} catch (AddressFormatException x) {
60+
try {
61+
return SegwitAddress.fromBech32(params, str);
62+
} catch (WrongNetworkException x2) {
63+
throw x;
64+
} catch (AddressFormatException x2) {
65+
throw new AddressFormatException(str);
66+
}
67+
}
68+
}
69+
70+
/**
71+
* Get either the public key hash or script hash that is encoded in the address.
72+
*
73+
* @return hash that is encoded in the address
74+
*/
75+
public abstract byte[] getHash();
76+
77+
/**
78+
* Get the type of output script that will be used for sending to the address.
79+
*
80+
* @return type of output script
81+
*/
82+
public abstract ScriptType getOutputScriptType();
83+
}

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

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626

2727
import org.bitcoinj.params.Networks;
2828
import org.bitcoinj.script.Script;
29+
import org.bitcoinj.script.Script.ScriptType;
2930
import org.bitcoinj.script.ScriptPattern;
3031

3132
import com.google.common.base.Objects;
@@ -41,7 +42,7 @@
4142
* should be interpreted. Whilst almost all addresses today are hashes of public keys, another (currently unsupported
4243
* type) can contain a hash of a script instead.</p>
4344
*/
44-
public class Address extends VersionedChecksummedBytes {
45+
public class Address extends AbstractAddress {
4546
/**
4647
* An address is a RIPEMD160 hash of a public key, therefore is always 160 bits or 20 bytes.
4748
*/
@@ -143,20 +144,44 @@ public Address(NetworkParameters params, byte[] hash160) {
143144
this(params, false, hash160);
144145
}
145146

146-
@Override
147-
protected int getVersion() {
148-
return p2sh ? params.getP2SHHeader() : params.getAddressHeader();
147+
/**
148+
* Returns the base58-encoded textual form, including version and checksum bytes.
149+
*
150+
* @return textual form
151+
*/
152+
public String toBase58() {
153+
return Base58.encodeChecked(p2sh ? params.getP2SHHeader() : params.getAddressHeader(), bytes);
149154
}
150155

151-
/** The (big endian) 20 byte hash that is the core of a Bitcoin address. */
156+
/** @deprecated use {@link #getHash()} */
157+
@Deprecated
152158
public byte[] getHash160() {
159+
return getHash();
160+
}
161+
162+
/** The (big endian) 20 byte hash that is the core of a Bitcoin address. */
163+
@Override
164+
public byte[] getHash() {
153165
return bytes;
154166
}
155167

168+
/**
169+
* Get the type of output script that will be used for sending to the address. This is either
170+
* {@link ScriptType#P2PKH} or {@link ScriptType#P2SH}.
171+
*
172+
* @return type of output script
173+
*/
174+
@Override
175+
public ScriptType getOutputScriptType() {
176+
return p2sh ? ScriptType.P2SH : ScriptType.P2PKH;
177+
}
178+
156179
/**
157180
* Returns true if this address is a Pay-To-Script-Hash (P2SH) address.
158181
* See also https://github.com/bitcoin/bips/blob/master/bip-0013.mediawiki: Address Format for pay-to-script-hash
182+
* @deprecated Use {@link #getOutputScriptType()}
159183
*/
184+
@Deprecated
160185
public boolean isP2SHAddress() {
161186
return p2sh;
162187
}
@@ -191,6 +216,11 @@ public int hashCode() {
191216
return Objects.hashCode(super.hashCode(), p2sh);
192217
}
193218

219+
@Override
220+
public String toString() {
221+
return toBase58();
222+
}
223+
194224
/**
195225
* This implementation narrows the return type to <code>Address</code>.
196226
*/
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/* Copyright (c) 2018 Coinomi Ltd
2+
*
3+
* Permission is hereby granted, free of charge, to any person obtaining a copy
4+
* of this software and associated documentation files (the "Software"), to deal
5+
* in the Software without restriction, including without limitation the rights
6+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7+
* copies of the Software, and to permit persons to whom the Software is
8+
* furnished to do so, subject to the following conditions:
9+
*
10+
* The above copyright notice and this permission notice shall be included in
11+
* all copies or substantial portions of the Software.
12+
*
13+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19+
* THE SOFTWARE.
20+
*/
21+
22+
package org.bitcoinj.core;
23+
24+
import java.util.Arrays;
25+
import java.util.Locale;
26+
27+
public class Bech32 {
28+
/** The Bech32 character set for encoding. */
29+
private static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
30+
31+
/** The Bech32 character set for decoding. */
32+
private static final byte[] CHARSET_REV = {
33+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
34+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
35+
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
36+
15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1,
37+
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
38+
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1,
39+
-1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1,
40+
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
41+
};
42+
43+
public static class Bech32Data {
44+
final String hrp;
45+
final byte[] data;
46+
47+
private Bech32Data(final String hrp, final byte[] data) {
48+
this.hrp = hrp;
49+
this.data = data;
50+
}
51+
}
52+
53+
/** Find the polynomial with value coefficients mod the generator as 30-bit. */
54+
private static int polymod(final byte[] values) {
55+
int c = 1;
56+
for (byte v_i: values) {
57+
int c0 = (c >>> 25) & 0xff;
58+
c = ((c & 0x1ffffff) << 5) ^ (v_i & 0xff);
59+
if ((c0 & 1) != 0) c ^= 0x3b6a57b2;
60+
if ((c0 & 2) != 0) c ^= 0x26508e6d;
61+
if ((c0 & 4) != 0) c ^= 0x1ea119fa;
62+
if ((c0 & 8) != 0) c ^= 0x3d4233dd;
63+
if ((c0 & 16) != 0) c ^= 0x2a1462b3;
64+
}
65+
return c;
66+
}
67+
68+
/** Expand a HRP for use in checksum computation. */
69+
private static byte[] expandHrp(final String hrp) {
70+
int hrpLength = hrp.length();
71+
byte ret[] = new byte[hrpLength * 2 + 1];
72+
for (int i = 0; i < hrpLength; ++i) {
73+
int c = hrp.charAt(i) & 0x7f; // Limit to standard 7-bit ASCII
74+
ret[i] = (byte) ((c >>> 5) & 0x07);
75+
ret[i + hrpLength + 1] = (byte) (c & 0x1f);
76+
}
77+
ret[hrpLength] = 0;
78+
return ret;
79+
}
80+
81+
/** Verify a checksum. */
82+
private static boolean verifyChecksum(final String hrp, final byte[] values) {
83+
byte[] hrpExpanded = expandHrp(hrp);
84+
byte[] combined = new byte[hrpExpanded.length + values.length];
85+
System.arraycopy(hrpExpanded, 0, combined, 0, hrpExpanded.length);
86+
System.arraycopy(values, 0, combined, hrpExpanded.length, values.length);
87+
return polymod(combined) == 1;
88+
}
89+
90+
/** Create a checksum. */
91+
private static byte[] createChecksum(final String hrp, final byte[] values) {
92+
byte[] hrpExpanded = expandHrp(hrp);
93+
byte[] enc = new byte[hrpExpanded.length + values.length + 6];
94+
System.arraycopy(hrpExpanded, 0, enc, 0, hrpExpanded.length);
95+
System.arraycopy(values, 0, enc, hrpExpanded.length, values.length);
96+
int mod = polymod(enc) ^ 1;
97+
byte[] ret = new byte[6];
98+
for (int i = 0; i < 6; ++i) {
99+
ret[i] = (byte) ((mod >>> (5 * (5 - i))) & 31);
100+
}
101+
return ret;
102+
}
103+
104+
/** Encode a Bech32 string. */
105+
public static String encode(final Bech32Data bech32) throws AddressFormatException {
106+
return encode(bech32.hrp, bech32.data);
107+
}
108+
109+
/** Encode a Bech32 string. */
110+
public static String encode(String hrp, final byte[] values) throws AddressFormatException {
111+
if (hrp.length() < 1) throw new AddressFormatException("Human-readable part is too short");
112+
if (hrp.length() > 83) throw new AddressFormatException("Human-readable part is too long");
113+
hrp = hrp.toLowerCase(Locale.ROOT);
114+
byte[] checksum = createChecksum(hrp, values);
115+
byte[] combined = new byte[values.length + checksum.length];
116+
System.arraycopy(values, 0, combined, 0, values.length);
117+
System.arraycopy(checksum, 0, combined, values.length, checksum.length);
118+
StringBuilder sb = new StringBuilder(hrp.length() + 1 + combined.length);
119+
sb.append(hrp);
120+
sb.append('1');
121+
for (byte b : combined) {
122+
sb.append(CHARSET.charAt(b));
123+
}
124+
return sb.toString();
125+
}
126+
127+
/** Decode a Bech32 string. */
128+
public static Bech32Data decode(final String str) throws AddressFormatException {
129+
boolean lower = false, upper = false;
130+
if (str.length() < 8) throw new AddressFormatException("Input too short");
131+
if (str.length() > 90) throw new AddressFormatException("Input too long");
132+
for (int i = 0; i < str.length(); ++i) {
133+
char c = str.charAt(i);
134+
if (c < 33 || c > 126) throw new AddressFormatException("Characters out of range");
135+
if (c >= 'a' && c <= 'z') lower = true;
136+
if (c >= 'A' && c <= 'Z') upper = true;
137+
}
138+
if (lower && upper) throw new AddressFormatException("Cannot mix upper and lower cases");
139+
int pos = str.lastIndexOf('1');
140+
if (pos < 1) throw new AddressFormatException("Missing human-readable part");
141+
if (pos + 7 > str.length()) throw new AddressFormatException("Data part too short");
142+
byte[] values = new byte[str.length() - 1 - pos];
143+
for (int i = 0; i < str.length() - 1 - pos; ++i) {
144+
char c = str.charAt(i + pos + 1);
145+
if (CHARSET_REV[c] == -1) throw new AddressFormatException("Characters out of range");
146+
values[i] = CHARSET_REV[c];
147+
}
148+
String hrp = str.substring(0, pos).toLowerCase(Locale.ROOT);
149+
if (!verifyChecksum(hrp, values)) throw new AddressFormatException("Invalid checksum");
150+
return new Bech32Data(hrp, Arrays.copyOfRange(values, 0, values.length - 6));
151+
}
152+
}

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

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,13 @@ private DumpedPrivateKey(NetworkParameters params, byte[] bytes) {
7171
this(params, encode(keyBytes, compressed));
7272
}
7373

74-
@Override
75-
protected int getVersion() {
76-
return params.getDumpedPrivateKeyHeader();
74+
/**
75+
* Returns the base58-encoded textual form, including version and checksum bytes.
76+
*
77+
* @return textual form
78+
*/
79+
public String toBase58() {
80+
return Base58.encodeChecked(params.getDumpedPrivateKeyHeader(), bytes);
7781
}
7882

7983
private static byte[] encode(byte[] keyBytes, boolean compressed) {
@@ -102,4 +106,9 @@ public ECKey getKey() {
102106
public boolean isPubKeyCompressed() {
103107
return bytes.length == 33 && bytes[32] == 1;
104108
}
109+
110+
@Override
111+
public String toString() {
112+
return toBase58();
113+
}
105114
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public abstract class NetworkParameters {
7777
protected int addressHeader;
7878
protected int p2shHeader;
7979
protected int dumpedPrivateKeyHeader;
80+
protected String segwitAddressHrp;
8081
protected int interval;
8182
protected int targetTimespan;
8283
protected byte[] alertSigningKey;
@@ -336,6 +337,11 @@ public int getDumpedPrivateKeyHeader() {
336337
return dumpedPrivateKeyHeader;
337338
}
338339

340+
/** Human readable part of bech32 encoded segwit address. */
341+
public String getSegwitAddressHrp() {
342+
return segwitAddressHrp;
343+
}
344+
339345
/**
340346
* How much time in seconds is supposed to pass between "interval" blocks. If the actual elapsed time is
341347
* significantly different from this value, the network difficulty formula will produce a different value. Both

0 commit comments

Comments
 (0)