mirror of
https://github.com/bitcoinj/bitcoinj.git
synced 2025-02-23 14:40:40 +01:00
Bech32, SegwitAddress: Implement Bech32m format for v1+ witness addresses.
This commit is contained in:
parent
4dc4cf743d
commit
183986c980
5 changed files with 155 additions and 42 deletions
|
@ -73,6 +73,20 @@ public class AddressFormatException extends IllegalArgumentException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This exception is thrown by {@link SegwitAddress} when you try to decode data and the witness version doesn't
|
||||||
|
* match the Bech32 encoding as per BIP350. You shouldn't allow the user to proceed in this case.
|
||||||
|
*/
|
||||||
|
public static class UnexpectedWitnessVersion extends AddressFormatException {
|
||||||
|
public UnexpectedWitnessVersion() {
|
||||||
|
super("Unexpected witness version");
|
||||||
|
}
|
||||||
|
|
||||||
|
public UnexpectedWitnessVersion(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This exception is thrown by the {@link PrefixedChecksummedBytes} hierarchy of classes when you try and decode an
|
* This exception is thrown by the {@link PrefixedChecksummedBytes} hierarchy of classes when you try and decode an
|
||||||
* address or private key with an invalid prefix (version header or human-readable part). You shouldn't allow the
|
* address or private key with an invalid prefix (version header or human-readable part). You shouldn't allow the
|
||||||
|
|
|
@ -16,11 +16,19 @@
|
||||||
|
|
||||||
package org.bitcoinj.core;
|
package org.bitcoinj.core;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkArgument;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <p>Implementation of the Bech32 encoding.</p>
|
||||||
|
*
|
||||||
|
* <p>See <a href="https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki">BIP350</a> and
|
||||||
|
* <a href="https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki">BIP173</a> for details.</p>
|
||||||
|
*/
|
||||||
public class Bech32 {
|
public class Bech32 {
|
||||||
/** The Bech32 character set for encoding. */
|
/** The Bech32 character set for encoding. */
|
||||||
private static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
private static final String CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||||
|
@ -37,11 +45,18 @@ public class Bech32 {
|
||||||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
|
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final int BECH32_CONST = 1;
|
||||||
|
private static final int BECH32M_CONST = 0x2bc830a3;
|
||||||
|
|
||||||
|
public enum Encoding { BECH32, BECH32M }
|
||||||
|
|
||||||
public static class Bech32Data {
|
public static class Bech32Data {
|
||||||
|
public final Encoding encoding;
|
||||||
public final String hrp;
|
public final String hrp;
|
||||||
public final byte[] data;
|
public final byte[] data;
|
||||||
|
|
||||||
private Bech32Data(final String hrp, final byte[] data) {
|
private Bech32Data(final Encoding encoding, final String hrp, final byte[] data) {
|
||||||
|
this.encoding = encoding;
|
||||||
this.hrp = hrp;
|
this.hrp = hrp;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
@ -76,21 +91,28 @@ public class Bech32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Verify a checksum. */
|
/** Verify a checksum. */
|
||||||
private static boolean verifyChecksum(final String hrp, final byte[] values) {
|
private static @Nullable
|
||||||
|
Encoding verifyChecksum(final String hrp, final byte[] values) {
|
||||||
byte[] hrpExpanded = expandHrp(hrp);
|
byte[] hrpExpanded = expandHrp(hrp);
|
||||||
byte[] combined = new byte[hrpExpanded.length + values.length];
|
byte[] combined = new byte[hrpExpanded.length + values.length];
|
||||||
System.arraycopy(hrpExpanded, 0, combined, 0, hrpExpanded.length);
|
System.arraycopy(hrpExpanded, 0, combined, 0, hrpExpanded.length);
|
||||||
System.arraycopy(values, 0, combined, hrpExpanded.length, values.length);
|
System.arraycopy(values, 0, combined, hrpExpanded.length, values.length);
|
||||||
return polymod(combined) == 1;
|
final int check = polymod(combined);
|
||||||
|
if (check == BECH32_CONST)
|
||||||
|
return Encoding.BECH32;
|
||||||
|
else if (check == BECH32M_CONST)
|
||||||
|
return Encoding.BECH32M;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Create a checksum. */
|
/** Create a checksum. */
|
||||||
private static byte[] createChecksum(final String hrp, final byte[] values) {
|
private static byte[] createChecksum(final Encoding encoding, final String hrp, final byte[] values) {
|
||||||
byte[] hrpExpanded = expandHrp(hrp);
|
byte[] hrpExpanded = expandHrp(hrp);
|
||||||
byte[] enc = new byte[hrpExpanded.length + values.length + 6];
|
byte[] enc = new byte[hrpExpanded.length + values.length + 6];
|
||||||
System.arraycopy(hrpExpanded, 0, enc, 0, hrpExpanded.length);
|
System.arraycopy(hrpExpanded, 0, enc, 0, hrpExpanded.length);
|
||||||
System.arraycopy(values, 0, enc, hrpExpanded.length, values.length);
|
System.arraycopy(values, 0, enc, hrpExpanded.length, values.length);
|
||||||
int mod = polymod(enc) ^ 1;
|
int mod = polymod(enc) ^ (encoding == Encoding.BECH32 ? BECH32_CONST : BECH32M_CONST);
|
||||||
byte[] ret = new byte[6];
|
byte[] ret = new byte[6];
|
||||||
for (int i = 0; i < 6; ++i) {
|
for (int i = 0; i < 6; ++i) {
|
||||||
ret[i] = (byte) ((mod >>> (5 * (5 - i))) & 31);
|
ret[i] = (byte) ((mod >>> (5 * (5 - i))) & 31);
|
||||||
|
@ -100,15 +122,15 @@ public class Bech32 {
|
||||||
|
|
||||||
/** Encode a Bech32 string. */
|
/** Encode a Bech32 string. */
|
||||||
public static String encode(final Bech32Data bech32) {
|
public static String encode(final Bech32Data bech32) {
|
||||||
return encode(bech32.hrp, bech32.data);
|
return encode(bech32.encoding, bech32.hrp, bech32.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Encode a Bech32 string. */
|
/** Encode a Bech32 string. */
|
||||||
public static String encode(String hrp, final byte[] values) {
|
public static String encode(Encoding encoding, String hrp, final byte[] values) {
|
||||||
checkArgument(hrp.length() >= 1, "Human-readable part is too short");
|
checkArgument(hrp.length() >= 1, "Human-readable part is too short");
|
||||||
checkArgument(hrp.length() <= 83, "Human-readable part is too long");
|
checkArgument(hrp.length() <= 83, "Human-readable part is too long");
|
||||||
hrp = hrp.toLowerCase(Locale.ROOT);
|
hrp = hrp.toLowerCase(Locale.ROOT);
|
||||||
byte[] checksum = createChecksum(hrp, values);
|
byte[] checksum = createChecksum(encoding, hrp, values);
|
||||||
byte[] combined = new byte[values.length + checksum.length];
|
byte[] combined = new byte[values.length + checksum.length];
|
||||||
System.arraycopy(values, 0, combined, 0, values.length);
|
System.arraycopy(values, 0, combined, 0, values.length);
|
||||||
System.arraycopy(checksum, 0, combined, values.length, checksum.length);
|
System.arraycopy(checksum, 0, combined, values.length, checksum.length);
|
||||||
|
@ -153,7 +175,8 @@ public class Bech32 {
|
||||||
values[i] = CHARSET_REV[c];
|
values[i] = CHARSET_REV[c];
|
||||||
}
|
}
|
||||||
String hrp = str.substring(0, pos).toLowerCase(Locale.ROOT);
|
String hrp = str.substring(0, pos).toLowerCase(Locale.ROOT);
|
||||||
if (!verifyChecksum(hrp, values)) throw new AddressFormatException.InvalidChecksum();
|
Encoding encoding = verifyChecksum(hrp, values);
|
||||||
return new Bech32Data(hrp, Arrays.copyOfRange(values, 0, values.length - 6));
|
if (encoding == null) throw new AddressFormatException.InvalidChecksum();
|
||||||
|
return new Bech32Data(encoding, hrp, Arrays.copyOfRange(values, 0, values.length - 6));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,8 @@ import org.bitcoinj.script.Script;
|
||||||
* bits into groups of 5).</li>
|
* bits into groups of 5).</li>
|
||||||
* </ul>
|
* </ul>
|
||||||
*
|
*
|
||||||
* <p>See <a href="https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki">BIP173</a> for details.</p>
|
* <p>See <a href="https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki">BIP350</a> and
|
||||||
|
* <a href="https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki">BIP173</a> for details.</p>
|
||||||
*
|
*
|
||||||
* <p>However, you don't need to care about the internals. Use {@link #fromBech32(NetworkParameters, String)},
|
* <p>However, you don't need to care about the internals. Use {@link #fromBech32(NetworkParameters, String)},
|
||||||
* {@link #fromHash(NetworkParameters, byte[])} or {@link #fromKey(NetworkParameters, ECKey)} to construct a native
|
* {@link #fromHash(NetworkParameters, byte[])} or {@link #fromKey(NetworkParameters, ECKey)} to construct a native
|
||||||
|
@ -105,7 +106,7 @@ public class SegwitAddress extends Address {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the witness version in decoded form. Only version 0 is in use right now.
|
* Returns the witness version in decoded form. Only versions 0 and 1 are in use right now.
|
||||||
*
|
*
|
||||||
* @return witness version, between 0 and 16
|
* @return witness version, between 0 and 16
|
||||||
*/
|
*/
|
||||||
|
@ -168,16 +169,25 @@ public class SegwitAddress extends Address {
|
||||||
if (params == null) {
|
if (params == null) {
|
||||||
for (NetworkParameters p : Networks.get()) {
|
for (NetworkParameters p : Networks.get()) {
|
||||||
if (bechData.hrp.equals(p.getSegwitAddressHrp()))
|
if (bechData.hrp.equals(p.getSegwitAddressHrp()))
|
||||||
return new SegwitAddress(p, bechData.data);
|
return fromBechData(p, bechData);
|
||||||
}
|
}
|
||||||
throw new AddressFormatException.InvalidPrefix("No network found for " + bech32);
|
throw new AddressFormatException.InvalidPrefix("No network found for " + bech32);
|
||||||
} else {
|
} else {
|
||||||
if (bechData.hrp.equals(params.getSegwitAddressHrp()))
|
if (bechData.hrp.equals(params.getSegwitAddressHrp()))
|
||||||
return new SegwitAddress(params, bechData.data);
|
return fromBechData(params, bechData);
|
||||||
throw new AddressFormatException.WrongNetwork(bechData.hrp);
|
throw new AddressFormatException.WrongNetwork(bechData.hrp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static SegwitAddress fromBechData(NetworkParameters params, Bech32.Bech32Data bechData) {
|
||||||
|
final SegwitAddress address = new SegwitAddress(params, bechData.data);
|
||||||
|
final int witnessVersion = address.getWitnessVersion();
|
||||||
|
if ((witnessVersion == 0 && bechData.encoding != Bech32.Encoding.BECH32) ||
|
||||||
|
(witnessVersion != 0 && bechData.encoding != Bech32.Encoding.BECH32M))
|
||||||
|
throw new AddressFormatException.UnexpectedWitnessVersion("Unexpected witness version: " + witnessVersion);
|
||||||
|
return address;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Construct a {@link SegwitAddress} that represents the given hash, which is either a pubkey hash or a script hash.
|
* Construct a {@link SegwitAddress} that represents the given hash, which is either a pubkey hash or a script hash.
|
||||||
* The resulting address will be either a P2WPKH or a P2WSH type of address.
|
* The resulting address will be either a P2WPKH or a P2WSH type of address.
|
||||||
|
@ -213,7 +223,10 @@ public class SegwitAddress extends Address {
|
||||||
* @return textual form encoded in bech32
|
* @return textual form encoded in bech32
|
||||||
*/
|
*/
|
||||||
public String toBech32() {
|
public String toBech32() {
|
||||||
return Bech32.encode(params.getSegwitAddressHrp(), bytes);
|
if (getWitnessVersion() == 0)
|
||||||
|
return Bech32.encode(Bech32.Encoding.BECH32, params.getSegwitAddressHrp(), bytes);
|
||||||
|
else
|
||||||
|
return Bech32.encode(Bech32.Encoding.BECH32M, params.getSegwitAddressHrp(), bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,20 +25,29 @@ import org.junit.Test;
|
||||||
|
|
||||||
public class Bech32Test {
|
public class Bech32Test {
|
||||||
@Test
|
@Test
|
||||||
public void valid() {
|
public void valid_bech32() {
|
||||||
for (String valid : VALID) {
|
for (String valid : VALID_BECH32)
|
||||||
Bech32.Bech32Data bechData = Bech32.decode(valid);
|
valid(valid);
|
||||||
String recode = Bech32.encode(bechData);
|
|
||||||
assertEquals(String.format("Failed to roundtrip '%s' -> '%s'", valid, recode),
|
|
||||||
valid.toLowerCase(Locale.ROOT), recode.toLowerCase(Locale.ROOT));
|
|
||||||
// Test encoding with an uppercase HRP
|
|
||||||
recode = Bech32.encode(bechData.hrp.toUpperCase(Locale.ROOT), bechData.data);
|
|
||||||
assertEquals(String.format("Failed to roundtrip '%s' -> '%s'", valid, recode),
|
|
||||||
valid.toLowerCase(Locale.ROOT), recode.toLowerCase(Locale.ROOT));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String[] VALID = {
|
@Test
|
||||||
|
public void valid_bech32m() {
|
||||||
|
for (String valid : VALID_BECH32M)
|
||||||
|
valid(valid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void valid(String valid) {
|
||||||
|
Bech32.Bech32Data bechData = Bech32.decode(valid);
|
||||||
|
String recode = Bech32.encode(bechData);
|
||||||
|
assertEquals(String.format("Failed to roundtrip '%s' -> '%s'", valid, recode),
|
||||||
|
valid.toLowerCase(Locale.ROOT), recode.toLowerCase(Locale.ROOT));
|
||||||
|
// Test encoding with an uppercase HRP
|
||||||
|
recode = Bech32.encode(bechData.encoding, bechData.hrp.toUpperCase(Locale.ROOT), bechData.data);
|
||||||
|
assertEquals(String.format("Failed to roundtrip '%s' -> '%s'", valid, recode),
|
||||||
|
valid.toLowerCase(Locale.ROOT), recode.toLowerCase(Locale.ROOT));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String[] VALID_BECH32 = {
|
||||||
"A12UEL5L",
|
"A12UEL5L",
|
||||||
"a12uel5l",
|
"a12uel5l",
|
||||||
"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
|
"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1tt5tgs",
|
||||||
|
@ -47,20 +56,38 @@ public class Bech32Test {
|
||||||
"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
|
"split1checkupstagehandshakeupstreamerranterredcaperred2y9e3w",
|
||||||
"?1ezyfcl",
|
"?1ezyfcl",
|
||||||
};
|
};
|
||||||
|
private static final String[] VALID_BECH32M = {
|
||||||
|
"A1LQFN3A",
|
||||||
|
"a1lqfn3a",
|
||||||
|
"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6",
|
||||||
|
"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx",
|
||||||
|
"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8",
|
||||||
|
"split1checkupstagehandshakeupstreamerranterredcaperredlc445v",
|
||||||
|
"?1v759aa"
|
||||||
|
};
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void invalid() {
|
public void invalid_bech32() {
|
||||||
for (String invalid : INVALID) {
|
for (String invalid : INVALID_BECH32)
|
||||||
try {
|
invalid(invalid);
|
||||||
Bech32.decode(invalid);
|
}
|
||||||
fail(String.format("Parsed an invalid code: '%s'", invalid));
|
|
||||||
} catch (AddressFormatException x) {
|
@Test
|
||||||
/* expected */
|
public void invalid_bech32m() {
|
||||||
}
|
for (String invalid : INVALID_BECH32M)
|
||||||
|
invalid(invalid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invalid(String invalid) {
|
||||||
|
try {
|
||||||
|
Bech32.decode(invalid);
|
||||||
|
fail(String.format("Parsed an invalid code: '%s'", invalid));
|
||||||
|
} catch (AddressFormatException x) {
|
||||||
|
/* expected */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String[] INVALID = {
|
private static final String[] INVALID_BECH32 = {
|
||||||
" 1nwldj5", // HRP character out of range
|
" 1nwldj5", // HRP character out of range
|
||||||
new String(new char[] { 0x7f }) + "1axkwrx", // HRP character out of range
|
new String(new char[] { 0x7f }) + "1axkwrx", // HRP character out of range
|
||||||
new String(new char[] { 0x80 }) + "1eym55h", // HRP character out of range
|
new String(new char[] { 0x80 }) + "1eym55h", // HRP character out of range
|
||||||
|
@ -75,6 +102,23 @@ public class Bech32Test {
|
||||||
"1qzzfhee", // empty HRP
|
"1qzzfhee", // empty HRP
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final String[] INVALID_BECH32M = {
|
||||||
|
" 1xj0phk", // HRP character out of range
|
||||||
|
new String(new char[] { 0x7f }) + "1g6xzxy", // HRP character out of range
|
||||||
|
new String(new char[] { 0x80 }) + "1vctc34", // HRP character out of range
|
||||||
|
"an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4", // overall max length exceeded
|
||||||
|
"qyrz8wqd2c9m", // No separator character
|
||||||
|
"1qyrz8wqd2c9m", // Empty HRP
|
||||||
|
"y1b0jsk6g", // Invalid data character
|
||||||
|
"lt1igcx5c0", // Invalid data character
|
||||||
|
"in1muywd", // Too short checksum
|
||||||
|
"mm1crxm3i", // Invalid character in checksum
|
||||||
|
"au1s5cgom", // Invalid character in checksum
|
||||||
|
"M1VUXWEZ", // checksum calculated with uppercase form of HRP
|
||||||
|
"16plkw9", // empty HRP
|
||||||
|
"1p2gdwpf", // empty HRP
|
||||||
|
};
|
||||||
|
|
||||||
@Test(expected = AddressFormatException.InvalidCharacter.class)
|
@Test(expected = AddressFormatException.InvalidCharacter.class)
|
||||||
public void decode_invalidCharacter_notInAlphabet() {
|
public void decode_invalidCharacter_notInAlphabet() {
|
||||||
Bech32.decode("A12OUEL5X");
|
Bech32.decode("A12OUEL5X");
|
||||||
|
|
|
@ -148,16 +148,22 @@ public class SegwitAddressTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static AddressData[] VALID_ADDRESSES = {
|
private static AddressData[] VALID_ADDRESSES = {
|
||||||
|
// from BIP350 (includes the corrected BIP173 vectors):
|
||||||
new AddressData("BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", MAINNET,
|
new AddressData("BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4", MAINNET,
|
||||||
"0014751e76e8199196d454941c45d1b3a323f1433bd6", 0),
|
"0014751e76e8199196d454941c45d1b3a323f1433bd6", 0),
|
||||||
new AddressData("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", TESTNET,
|
new AddressData("tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7", TESTNET,
|
||||||
"00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", 0),
|
"00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262", 0),
|
||||||
new AddressData("bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx", MAINNET,
|
new AddressData("bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kt5nd6y", MAINNET,
|
||||||
"5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", 1),
|
"5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6", 1),
|
||||||
new AddressData("BC1SW50QA3JX3S", MAINNET, "6002751e", 16),
|
new AddressData("BC1SW50QGDZ25J", MAINNET, "6002751e", 16),
|
||||||
new AddressData("bc1zw508d6qejxtdg4y5r3zarvaryvg6kdaj", MAINNET, "5210751e76e8199196d454941c45d1b3a323", 2),
|
new AddressData("bc1zw508d6qejxtdg4y5r3zarvaryvaxxpcs", MAINNET, "5210751e76e8199196d454941c45d1b3a323", 2),
|
||||||
new AddressData("tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", TESTNET,
|
new AddressData("tb1qqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesrxh6hy", TESTNET,
|
||||||
"0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", 0) };
|
"0020000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", 0),
|
||||||
|
new AddressData("tb1pqqqqp399et2xygdj5xreqhjjvcmzhxw4aywxecjdzew6hylgvsesf3hn0c", TESTNET,
|
||||||
|
"5120000000c4a5cad46221b2a187905e5266362b99d5e91c6ce24d165dab93e86433", 1),
|
||||||
|
new AddressData("bc1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqzk5jj0", MAINNET,
|
||||||
|
"512079be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798", 1),
|
||||||
|
};
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void invalidAddresses() {
|
public void invalidAddresses() {
|
||||||
|
@ -171,7 +177,8 @@ public class SegwitAddressTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String[] INVALID_ADDRESSES = { //
|
private static String[] INVALID_ADDRESSES = {
|
||||||
|
// from BIP173:
|
||||||
"tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", // Invalid human-readable part
|
"tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", // Invalid human-readable part
|
||||||
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", // Invalid checksum
|
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", // Invalid checksum
|
||||||
"BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", // Invalid witness version
|
"BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", // Invalid witness version
|
||||||
|
@ -182,6 +189,18 @@ public class SegwitAddressTest {
|
||||||
"bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", // Zero padding of more than 4 bits
|
"bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", // Zero padding of more than 4 bits
|
||||||
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", // Non-zero padding in 8-to-5 conversion
|
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", // Non-zero padding in 8-to-5 conversion
|
||||||
"bc1gmk9yu", // Empty data section
|
"bc1gmk9yu", // Empty data section
|
||||||
|
|
||||||
|
// from BIP350:
|
||||||
|
"tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty", // Invalid human-readable part
|
||||||
|
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5", // Invalid checksum
|
||||||
|
"BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2", // Invalid witness version
|
||||||
|
"bc1rw5uspcuh", // Invalid program length
|
||||||
|
"bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90", // Invalid program length
|
||||||
|
"BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P", // Invalid program length for witness version 0 (per BIP141)
|
||||||
|
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7", // Mixed case
|
||||||
|
"bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du", // zero padding of more than 4 bits
|
||||||
|
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv", // Non-zero padding in 8-to-5 conversion
|
||||||
|
"bc1gmk9yu", // Empty data section
|
||||||
};
|
};
|
||||||
|
|
||||||
@Test(expected = AddressFormatException.InvalidDataLength.class)
|
@Test(expected = AddressFormatException.InvalidDataLength.class)
|
||||||
|
@ -211,7 +230,7 @@ public class SegwitAddressTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testJavaSerialization() throws Exception {
|
public void testJavaSerialization() throws Exception {
|
||||||
SegwitAddress address = SegwitAddress.fromBech32(null, "BC1SW50QA3JX3S");
|
SegwitAddress address = SegwitAddress.fromBech32(null, "BC1SW50QGDZ25J");
|
||||||
|
|
||||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||||
new ObjectOutputStream(os).writeObject(address);
|
new ObjectOutputStream(os).writeObject(address);
|
||||||
|
|
Loading…
Add table
Reference in a new issue