mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-03-05 14:06:27 -05:00
Merge #20861: BIP 350: Implement Bech32m and use it for v1+ segwit addresses
03346022d6
naming nits (Fabian Jahr)2e7c80fb5b
Add signet support to gen_key_io_test_vectors.py (Pieter Wuille)fe5e495c31
Use Bech32m encoding for v1+ segwit addresses (Pieter Wuille)25b1c6e13d
Add Bech32m test vectors (Pieter Wuille)da2bb6976d
Implement Bech32m encoding/decoding (Pieter Wuille) Pull request description: This implements [BIP 350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki): * For segwit v1+ addresses, a new checksum algorithm called Bech32m is used. * Segwit v0 address keep using Bech32 as specified in [BIP 173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki). ACKs for top commit: Sjors: utACK0334602
jnewbery: utACK03346022d6
achow101: ACK0334602
fjahr: re-ACK0334602
benthecarman: ACK03346022d6
Tree-SHA512: 4424cfd44869d813d6152fb3ed867b204036736bc2344a039b93700b6f36a43e9110478f138eb81c97c77ab27ecb776dada5ba632cb5a3a9d244924d2540a557
This commit is contained in:
commit
8ec881d3b6
15 changed files with 945 additions and 689 deletions
|
@ -4,5 +4,5 @@ Utilities to generate test vectors for the data-driven Bitcoin tests.
|
|||
|
||||
Usage:
|
||||
|
||||
PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_valid.json
|
||||
PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_invalid.json
|
||||
PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 70 > ../../src/test/data/key_io_valid.json
|
||||
PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 70 > ../../src/test/data/key_io_invalid.json
|
||||
|
|
|
@ -3,11 +3,11 @@
|
|||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
'''
|
||||
Generate valid and invalid base58 address and private key test vectors.
|
||||
Generate valid and invalid base58/bech32(m) address and private key test vectors.
|
||||
|
||||
Usage:
|
||||
PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 50 > ../../src/test/data/key_io_valid.json
|
||||
PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 50 > ../../src/test/data/key_io_invalid.json
|
||||
PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py valid 70 > ../../src/test/data/key_io_valid.json
|
||||
PYTHONPATH=../../test/functional/test_framework ./gen_key_io_test_vectors.py invalid 70 > ../../src/test/data/key_io_invalid.json
|
||||
'''
|
||||
# 2012 Wladimir J. van der Laan
|
||||
# Released under MIT License
|
||||
|
@ -15,7 +15,7 @@ import os
|
|||
from itertools import islice
|
||||
from base58 import b58encode_chk, b58decode_chk, b58chars
|
||||
import random
|
||||
from segwit_addr import bech32_encode, decode_segwit_address, convertbits, CHARSET
|
||||
from segwit_addr import bech32_encode, decode_segwit_address, convertbits, CHARSET, Encoding
|
||||
|
||||
# key types
|
||||
PUBKEY_ADDRESS = 0
|
||||
|
@ -32,6 +32,7 @@ PRIVKEY_REGTEST = 239
|
|||
OP_0 = 0x00
|
||||
OP_1 = 0x51
|
||||
OP_2 = 0x52
|
||||
OP_3 = 0x53
|
||||
OP_16 = 0x60
|
||||
OP_DUP = 0x76
|
||||
OP_EQUAL = 0x87
|
||||
|
@ -44,6 +45,7 @@ script_prefix = (OP_HASH160, 20)
|
|||
script_suffix = (OP_EQUAL,)
|
||||
p2wpkh_prefix = (OP_0, 20)
|
||||
p2wsh_prefix = (OP_0, 32)
|
||||
p2tr_prefix = (OP_1, 32)
|
||||
|
||||
metadata_keys = ['isPrivkey', 'chain', 'isCompressed', 'tryCaseFlip']
|
||||
# templates for valid sequences
|
||||
|
@ -54,40 +56,58 @@ templates = [
|
|||
((SCRIPT_ADDRESS,), 20, (), (False, 'main', None, None), script_prefix, script_suffix),
|
||||
((PUBKEY_ADDRESS_TEST,), 20, (), (False, 'test', None, None), pubkey_prefix, pubkey_suffix),
|
||||
((SCRIPT_ADDRESS_TEST,), 20, (), (False, 'test', None, None), script_prefix, script_suffix),
|
||||
((PUBKEY_ADDRESS_TEST,), 20, (), (False, 'signet', None, None), pubkey_prefix, pubkey_suffix),
|
||||
((SCRIPT_ADDRESS_TEST,), 20, (), (False, 'signet', None, None), script_prefix, script_suffix),
|
||||
((PUBKEY_ADDRESS_REGTEST,), 20, (), (False, 'regtest', None, None), pubkey_prefix, pubkey_suffix),
|
||||
((SCRIPT_ADDRESS_REGTEST,), 20, (), (False, 'regtest', None, None), script_prefix, script_suffix),
|
||||
((PRIVKEY,), 32, (), (True, 'main', False, None), (), ()),
|
||||
((PRIVKEY,), 32, (1,), (True, 'main', True, None), (), ()),
|
||||
((PRIVKEY_TEST,), 32, (), (True, 'test', False, None), (), ()),
|
||||
((PRIVKEY_TEST,), 32, (1,), (True, 'test', True, None), (), ()),
|
||||
((PRIVKEY_TEST,), 32, (), (True, 'signet', False, None), (), ()),
|
||||
((PRIVKEY_TEST,), 32, (1,), (True, 'signet', True, None), (), ()),
|
||||
((PRIVKEY_REGTEST,), 32, (), (True, 'regtest', False, None), (), ()),
|
||||
((PRIVKEY_REGTEST,), 32, (1,), (True, 'regtest', True, None), (), ())
|
||||
]
|
||||
# templates for valid bech32 sequences
|
||||
bech32_templates = [
|
||||
# hrp, version, witprog_size, metadata, output_prefix
|
||||
('bc', 0, 20, (False, 'main', None, True), p2wpkh_prefix),
|
||||
('bc', 0, 32, (False, 'main', None, True), p2wsh_prefix),
|
||||
('bc', 1, 2, (False, 'main', None, True), (OP_1, 2)),
|
||||
('tb', 0, 20, (False, 'test', None, True), p2wpkh_prefix),
|
||||
('tb', 0, 32, (False, 'test', None, True), p2wsh_prefix),
|
||||
('tb', 2, 16, (False, 'test', None, True), (OP_2, 16)),
|
||||
('bcrt', 0, 20, (False, 'regtest', None, True), p2wpkh_prefix),
|
||||
('bcrt', 0, 32, (False, 'regtest', None, True), p2wsh_prefix),
|
||||
('bcrt', 16, 40, (False, 'regtest', None, True), (OP_16, 40))
|
||||
# hrp, version, witprog_size, metadata, encoding, output_prefix
|
||||
('bc', 0, 20, (False, 'main', None, True), Encoding.BECH32, p2wpkh_prefix),
|
||||
('bc', 0, 32, (False, 'main', None, True), Encoding.BECH32, p2wsh_prefix),
|
||||
('bc', 1, 32, (False, 'main', None, True), Encoding.BECH32M, p2tr_prefix),
|
||||
('bc', 2, 2, (False, 'main', None, True), Encoding.BECH32M, (OP_2, 2)),
|
||||
('tb', 0, 20, (False, 'test', None, True), Encoding.BECH32, p2wpkh_prefix),
|
||||
('tb', 0, 32, (False, 'test', None, True), Encoding.BECH32, p2wsh_prefix),
|
||||
('tb', 1, 32, (False, 'test', None, True), Encoding.BECH32M, p2tr_prefix),
|
||||
('tb', 3, 16, (False, 'test', None, True), Encoding.BECH32M, (OP_3, 16)),
|
||||
('tb', 0, 20, (False, 'signet', None, True), Encoding.BECH32, p2wpkh_prefix),
|
||||
('tb', 0, 32, (False, 'signet', None, True), Encoding.BECH32, p2wsh_prefix),
|
||||
('tb', 1, 32, (False, 'signet', None, True), Encoding.BECH32M, p2tr_prefix),
|
||||
('tb', 3, 32, (False, 'signet', None, True), Encoding.BECH32M, (OP_3, 32)),
|
||||
('bcrt', 0, 20, (False, 'regtest', None, True), Encoding.BECH32, p2wpkh_prefix),
|
||||
('bcrt', 0, 32, (False, 'regtest', None, True), Encoding.BECH32, p2wsh_prefix),
|
||||
('bcrt', 1, 32, (False, 'regtest', None, True), Encoding.BECH32M, p2tr_prefix),
|
||||
('bcrt', 16, 40, (False, 'regtest', None, True), Encoding.BECH32M, (OP_16, 40))
|
||||
]
|
||||
# templates for invalid bech32 sequences
|
||||
bech32_ng_templates = [
|
||||
# hrp, version, witprog_size, invalid_bech32, invalid_checksum, invalid_char
|
||||
('tc', 0, 20, False, False, False),
|
||||
('tb', 17, 32, False, False, False),
|
||||
('bcrt', 3, 1, False, False, False),
|
||||
('bc', 15, 41, False, False, False),
|
||||
('tb', 0, 16, False, False, False),
|
||||
('bcrt', 0, 32, True, False, False),
|
||||
('bc', 0, 16, True, False, False),
|
||||
('tb', 0, 32, False, True, False),
|
||||
('bcrt', 0, 20, False, False, True)
|
||||
# hrp, version, witprog_size, encoding, invalid_bech32, invalid_checksum, invalid_char
|
||||
('tc', 0, 20, Encoding.BECH32, False, False, False),
|
||||
('bt', 1, 32, Encoding.BECH32M, False, False, False),
|
||||
('tb', 17, 32, Encoding.BECH32M, False, False, False),
|
||||
('bcrt', 3, 1, Encoding.BECH32M, False, False, False),
|
||||
('bc', 15, 41, Encoding.BECH32M, False, False, False),
|
||||
('tb', 0, 16, Encoding.BECH32, False, False, False),
|
||||
('bcrt', 0, 32, Encoding.BECH32, True, False, False),
|
||||
('bc', 0, 16, Encoding.BECH32, True, False, False),
|
||||
('tb', 0, 32, Encoding.BECH32, False, True, False),
|
||||
('bcrt', 0, 20, Encoding.BECH32, False, False, True),
|
||||
('bc', 0, 20, Encoding.BECH32M, False, False, False),
|
||||
('tb', 0, 32, Encoding.BECH32M, False, False, False),
|
||||
('bcrt', 0, 20, Encoding.BECH32M, False, False, False),
|
||||
('bc', 1, 32, Encoding.BECH32, False, False, False),
|
||||
('tb', 2, 16, Encoding.BECH32, False, False, False),
|
||||
('bcrt', 16, 20, Encoding.BECH32, False, False, False),
|
||||
]
|
||||
|
||||
def is_valid(v):
|
||||
|
@ -127,8 +147,9 @@ def gen_valid_bech32_vector(template):
|
|||
hrp = template[0]
|
||||
witver = template[1]
|
||||
witprog = bytearray(os.urandom(template[2]))
|
||||
dst_prefix = bytearray(template[4])
|
||||
rv = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
|
||||
encoding = template[4]
|
||||
dst_prefix = bytearray(template[5])
|
||||
rv = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5), encoding)
|
||||
return rv, dst_prefix + witprog
|
||||
|
||||
def gen_valid_vectors():
|
||||
|
@ -186,22 +207,23 @@ def gen_invalid_bech32_vector(template):
|
|||
hrp = template[0]
|
||||
witver = template[1]
|
||||
witprog = bytearray(os.urandom(template[2]))
|
||||
encoding = template[3]
|
||||
|
||||
if no_data:
|
||||
rv = bech32_encode(hrp, [])
|
||||
rv = bech32_encode(hrp, [], encoding)
|
||||
else:
|
||||
data = [witver] + convertbits(witprog, 8, 5)
|
||||
if template[3] and not no_data:
|
||||
if template[4] and not no_data:
|
||||
if template[2] % 5 in {2, 4}:
|
||||
data[-1] |= 1
|
||||
else:
|
||||
data.append(0)
|
||||
rv = bech32_encode(hrp, data)
|
||||
rv = bech32_encode(hrp, data, encoding)
|
||||
|
||||
if template[4]:
|
||||
if template[5]:
|
||||
i = len(rv) - random.randrange(1, 7)
|
||||
rv = rv[:i] + random.choice(CHARSET.replace(rv[i], '')) + rv[i + 1:]
|
||||
if template[5]:
|
||||
if template[6]:
|
||||
i = len(hrp) + 1 + random.randrange(0, len(rv) - len(hrp) - 4)
|
||||
rv = rv[:i] + rv[i:i + 4].upper() + rv[i + 4:]
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.21.0**):
|
||||
BIPs that are implemented by Bitcoin Core (up-to-date up to **v22.0**):
|
||||
|
||||
* [`BIP 9`](https://github.com/bitcoin/bips/blob/master/bip-0009.mediawiki): The changes allowing multiple soft-forks to be deployed in parallel have been implemented since **v0.12.1** ([PR #7575](https://github.com/bitcoin/bitcoin/pull/7575))
|
||||
* [`BIP 11`](https://github.com/bitcoin/bips/blob/master/bip-0011.mediawiki): Multisig outputs are standard since **v0.6.0** ([PR #669](https://github.com/bitcoin/bitcoin/pull/669)).
|
||||
|
@ -50,3 +50,4 @@ BIPs that are implemented by Bitcoin Core (up-to-date up to **v0.21.0**):
|
|||
* [`BIP 325`](https://github.com/bitcoin/bips/blob/master/bip-0325.mediawiki): Signet test network is supported as of **v0.21.0** ([PR 18267](https://github.com/bitcoin/bitcoin/pull/18267)).
|
||||
* [`BIP 339`](https://github.com/bitcoin/bips/blob/master/bip-0339.mediawiki): Relay of transactions by wtxid is supported as of **v0.21.0** ([PR 18044](https://github.com/bitcoin/bitcoin/pull/18044)).
|
||||
* [`BIP 340`](https://github.com/bitcoin/bips/blob/master/bip-0340.mediawiki) [`341`](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki) [`342`](https://github.com/bitcoin/bips/blob/master/bip-0342.mediawiki): Validation rules for Taproot (including Schnorr signatures and Tapscript leaves) are implemented as of **v0.21.0** ([PR 19953](https://github.com/bitcoin/bitcoin/pull/19953)), without mainnet activation.
|
||||
* [`BIP 350`](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki): Addresses for native v1+ segregated Witness outputs use Bech32m instead of Bech32 as of **v22.0** ([PR 20861](https://github.com/bitcoin/bitcoin/pull/20861)).
|
||||
|
|
13
doc/release-notes-20861.md
Normal file
13
doc/release-notes-20861.md
Normal file
|
@ -0,0 +1,13 @@
|
|||
Updated RPCs
|
||||
------------
|
||||
|
||||
- Due to [BIP 350](https://github.com/bitcoin/bips/blob/master/bip-0350.mediawiki)
|
||||
being implemented, behavior for all RPCs that accept addresses is changed when
|
||||
a native witness version 1 (or higher) is passed. These now require a Bech32m
|
||||
encoding instead of a Bech32 one, and Bech32m encoding will be used for such
|
||||
addresses in RPC output as well. No version 1 addresses should be created
|
||||
for mainnet until consensus rules are adopted that give them meaning
|
||||
(e.g. through [BIP 341](https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki)).
|
||||
Once that happens, Bech32m is expected to be used for them, so this shouldn't
|
||||
affect any production systems, but may be observed on other networks where such
|
||||
addresses already have meaning (like signet).
|
|
@ -1,4 +1,4 @@
|
|||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Copyright (c) 2017, 2021 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
|
@ -7,15 +7,18 @@
|
|||
|
||||
#include <assert.h>
|
||||
|
||||
namespace bech32
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
typedef std::vector<uint8_t> data;
|
||||
|
||||
/** The Bech32 character set for encoding. */
|
||||
/** The Bech32 and Bech32m character set for encoding. */
|
||||
const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
/** The Bech32 character set for decoding. */
|
||||
/** The Bech32 and Bech32m character set for decoding. */
|
||||
const int8_t CHARSET_REV[128] = {
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
|
@ -27,6 +30,12 @@ const int8_t CHARSET_REV[128] = {
|
|||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
/* Determine the final constant to use for the specified encoding. */
|
||||
uint32_t EncodingConstant(Encoding encoding) {
|
||||
assert(encoding == Encoding::BECH32 || encoding == Encoding::BECH32M);
|
||||
return encoding == Encoding::BECH32 ? 1 : 0x2bc830a3;
|
||||
}
|
||||
|
||||
/** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to
|
||||
* make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
|
||||
* bits correspond to earlier values. */
|
||||
|
@ -111,21 +120,24 @@ data ExpandHRP(const std::string& hrp)
|
|||
}
|
||||
|
||||
/** Verify a checksum. */
|
||||
bool VerifyChecksum(const std::string& hrp, const data& values)
|
||||
Encoding VerifyChecksum(const std::string& hrp, const data& values)
|
||||
{
|
||||
// PolyMod computes what value to xor into the final values to make the checksum 0. However,
|
||||
// if we required that the checksum was 0, it would be the case that appending a 0 to a valid
|
||||
// list of values would result in a new valid list. For that reason, Bech32 requires the
|
||||
// resulting checksum to be 1 instead.
|
||||
return PolyMod(Cat(ExpandHRP(hrp), values)) == 1;
|
||||
// resulting checksum to be 1 instead. In Bech32m, this constant was amended.
|
||||
const uint32_t check = PolyMod(Cat(ExpandHRP(hrp), values));
|
||||
if (check == EncodingConstant(Encoding::BECH32)) return Encoding::BECH32;
|
||||
if (check == EncodingConstant(Encoding::BECH32M)) return Encoding::BECH32M;
|
||||
return Encoding::INVALID;
|
||||
}
|
||||
|
||||
/** Create a checksum. */
|
||||
data CreateChecksum(const std::string& hrp, const data& values)
|
||||
data CreateChecksum(Encoding encoding, const std::string& hrp, const data& values)
|
||||
{
|
||||
data enc = Cat(ExpandHRP(hrp), values);
|
||||
enc.resize(enc.size() + 6); // Append 6 zeroes
|
||||
uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes.
|
||||
uint32_t mod = PolyMod(enc) ^ EncodingConstant(encoding); // Determine what to XOR into those 6 zeroes.
|
||||
data ret(6);
|
||||
for (size_t i = 0; i < 6; ++i) {
|
||||
// Convert the 5-bit groups in mod to checksum values.
|
||||
|
@ -136,16 +148,13 @@ data CreateChecksum(const std::string& hrp, const data& values)
|
|||
|
||||
} // namespace
|
||||
|
||||
namespace bech32
|
||||
{
|
||||
|
||||
/** Encode a Bech32 string. */
|
||||
std::string Encode(const std::string& hrp, const data& values) {
|
||||
// First ensure that the HRP is all lowercase. BIP-173 requires an encoder
|
||||
// to return a lowercase Bech32 string, but if given an uppercase HRP, the
|
||||
/** Encode a Bech32 or Bech32m string. */
|
||||
std::string Encode(Encoding encoding, const std::string& hrp, const data& values) {
|
||||
// First ensure that the HRP is all lowercase. BIP-173 and BIP350 require an encoder
|
||||
// to return a lowercase Bech32/Bech32m string, but if given an uppercase HRP, the
|
||||
// result will always be invalid.
|
||||
for (const char& c : hrp) assert(c < 'A' || c > 'Z');
|
||||
data checksum = CreateChecksum(hrp, values);
|
||||
data checksum = CreateChecksum(encoding, hrp, values);
|
||||
data combined = Cat(values, checksum);
|
||||
std::string ret = hrp + '1';
|
||||
ret.reserve(ret.size() + combined.size());
|
||||
|
@ -155,8 +164,8 @@ std::string Encode(const std::string& hrp, const data& values) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
/** Decode a Bech32 string. */
|
||||
std::pair<std::string, data> Decode(const std::string& str) {
|
||||
/** Decode a Bech32 or Bech32m string. */
|
||||
DecodeResult Decode(const std::string& str) {
|
||||
bool lower = false, upper = false;
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
unsigned char c = str[i];
|
||||
|
@ -183,10 +192,9 @@ std::pair<std::string, data> Decode(const std::string& str) {
|
|||
for (size_t i = 0; i < pos; ++i) {
|
||||
hrp += LowerCase(str[i]);
|
||||
}
|
||||
if (!VerifyChecksum(hrp, values)) {
|
||||
return {};
|
||||
}
|
||||
return {hrp, data(values.begin(), values.end() - 6)};
|
||||
Encoding result = VerifyChecksum(hrp, values);
|
||||
if (result == Encoding::INVALID) return {};
|
||||
return {result, std::move(hrp), data(values.begin(), values.end() - 6)};
|
||||
}
|
||||
|
||||
} // namespace bech32
|
||||
|
|
39
src/bech32.h
39
src/bech32.h
|
@ -1,13 +1,14 @@
|
|||
// Copyright (c) 2017 Pieter Wuille
|
||||
// Copyright (c) 2017, 2021 Pieter Wuille
|
||||
// Distributed under the MIT software license, see the accompanying
|
||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
|
||||
// Bech32 is a string encoding format used in newer address types.
|
||||
// The output consists of a human-readable part (alphanumeric), a
|
||||
// separator character (1), and a base32 data section, the last
|
||||
// 6 characters of which are a checksum.
|
||||
// Bech32 and Bech32m are string encoding formats used in newer
|
||||
// address types. The outputs consist of a human-readable part
|
||||
// (alphanumeric), a separator character (1), and a base32 data
|
||||
// section, the last 6 characters of which are a checksum. The
|
||||
// module is namespaced under bech32 for historical reasons.
|
||||
//
|
||||
// For more information, see BIP 173.
|
||||
// For more information, see BIP 173 and BIP 350.
|
||||
|
||||
#ifndef BITCOIN_BECH32_H
|
||||
#define BITCOIN_BECH32_H
|
||||
|
@ -19,11 +20,29 @@
|
|||
namespace bech32
|
||||
{
|
||||
|
||||
/** Encode a Bech32 string. If hrp contains uppercase characters, this will cause an assertion error. */
|
||||
std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values);
|
||||
enum class Encoding {
|
||||
INVALID, //!< Failed decoding
|
||||
|
||||
/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */
|
||||
std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str);
|
||||
BECH32, //!< Bech32 encoding as defined in BIP173
|
||||
BECH32M, //!< Bech32m encoding as defined in BIP350
|
||||
};
|
||||
|
||||
/** Encode a Bech32 or Bech32m string. If hrp contains uppercase characters, this will cause an
|
||||
* assertion error. Encoding must be one of BECH32 or BECH32M. */
|
||||
std::string Encode(Encoding encoding, const std::string& hrp, const std::vector<uint8_t>& values);
|
||||
|
||||
struct DecodeResult
|
||||
{
|
||||
Encoding encoding; //!< What encoding was detected in the result; Encoding::INVALID if failed.
|
||||
std::string hrp; //!< The human readable part
|
||||
std::vector<uint8_t> data; //!< The payload (excluding checksum)
|
||||
|
||||
DecodeResult() : encoding(Encoding::INVALID) {}
|
||||
DecodeResult(Encoding enc, std::string&& h, std::vector<uint8_t>&& d) : encoding(enc), hrp(std::move(h)), data(std::move(d)) {}
|
||||
};
|
||||
|
||||
/** Decode a Bech32 or Bech32m string. */
|
||||
DecodeResult Decode(const std::string& str);
|
||||
|
||||
} // namespace bech32
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ static void Bech32Encode(benchmark::Bench& bench)
|
|||
tmp.reserve(1 + 32 * 8 / 5);
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { tmp.push_back(c); }, v.begin(), v.end());
|
||||
bench.batch(v.size()).unit("byte").run([&] {
|
||||
bech32::Encode("bc", tmp);
|
||||
bech32::Encode(bech32::Encoding::BECH32, "bc", tmp);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ public:
|
|||
std::vector<unsigned char> data = {0};
|
||||
data.reserve(33);
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
|
||||
return bech32::Encode(m_params.Bech32HRP(), data);
|
||||
return bech32::Encode(bech32::Encoding::BECH32, m_params.Bech32HRP(), data);
|
||||
}
|
||||
|
||||
std::string operator()(const WitnessV0ScriptHash& id) const
|
||||
|
@ -51,7 +51,7 @@ public:
|
|||
std::vector<unsigned char> data = {0};
|
||||
data.reserve(53);
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
|
||||
return bech32::Encode(m_params.Bech32HRP(), data);
|
||||
return bech32::Encode(bech32::Encoding::BECH32, m_params.Bech32HRP(), data);
|
||||
}
|
||||
|
||||
std::string operator()(const WitnessUnknown& id) const
|
||||
|
@ -62,7 +62,7 @@ public:
|
|||
std::vector<unsigned char> data = {(unsigned char)id.version};
|
||||
data.reserve(1 + (id.length * 8 + 4) / 5);
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.program, id.program + id.length);
|
||||
return bech32::Encode(m_params.Bech32HRP(), data);
|
||||
return bech32::Encode(bech32::Encoding::BECH32M, m_params.Bech32HRP(), data);
|
||||
}
|
||||
|
||||
std::string operator()(const CNoDestination& no) const { return {}; }
|
||||
|
@ -95,20 +95,26 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
|
|||
error_str = "Invalid prefix for Base58-encoded address";
|
||||
}
|
||||
data.clear();
|
||||
auto bech = bech32::Decode(str);
|
||||
if (bech.second.size() > 0) {
|
||||
const auto dec = bech32::Decode(str);
|
||||
if ((dec.encoding == bech32::Encoding::BECH32 || dec.encoding == bech32::Encoding::BECH32M) && dec.data.size() > 0) {
|
||||
// Bech32 decoding
|
||||
error_str = "";
|
||||
|
||||
if (bech.first != params.Bech32HRP()) {
|
||||
if (dec.hrp != params.Bech32HRP()) {
|
||||
error_str = "Invalid prefix for Bech32 address";
|
||||
return CNoDestination();
|
||||
}
|
||||
|
||||
// Bech32 decoding
|
||||
int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16)
|
||||
int version = dec.data[0]; // The first 5 bit symbol is the witness version (0-16)
|
||||
if (version == 0 && dec.encoding != bech32::Encoding::BECH32) {
|
||||
error_str = "Version 0 witness address must use Bech32 checksum";
|
||||
return CNoDestination();
|
||||
}
|
||||
if (version != 0 && dec.encoding != bech32::Encoding::BECH32M) {
|
||||
error_str = "Version 1+ witness address must use Bech32m checksum";
|
||||
return CNoDestination();
|
||||
}
|
||||
// The rest of the symbols are converted witness program bytes.
|
||||
data.reserve(((bech.second.size() - 1) * 5) / 8);
|
||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin() + 1, bech.second.end())) {
|
||||
data.reserve(((dec.data.size() - 1) * 5) / 8);
|
||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, dec.data.begin() + 1, dec.data.end())) {
|
||||
if (version == 0) {
|
||||
{
|
||||
WitnessV0KeyHash keyid;
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bip173_testvectors_valid)
|
||||
BOOST_AUTO_TEST_CASE(bech32_testvectors_valid)
|
||||
{
|
||||
static const std::string CASES[] = {
|
||||
"A12UEL5L",
|
||||
|
@ -22,15 +22,35 @@ BOOST_AUTO_TEST_CASE(bip173_testvectors_valid)
|
|||
"?1ezyfcl",
|
||||
};
|
||||
for (const std::string& str : CASES) {
|
||||
auto ret = bech32::Decode(str);
|
||||
BOOST_CHECK(!ret.first.empty());
|
||||
std::string recode = bech32::Encode(ret.first, ret.second);
|
||||
const auto dec = bech32::Decode(str);
|
||||
BOOST_CHECK(dec.encoding == bech32::Encoding::BECH32);
|
||||
std::string recode = bech32::Encode(bech32::Encoding::BECH32, dec.hrp, dec.data);
|
||||
BOOST_CHECK(!recode.empty());
|
||||
BOOST_CHECK(CaseInsensitiveEqual(str, recode));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid)
|
||||
BOOST_AUTO_TEST_CASE(bech32m_testvectors_valid)
|
||||
{
|
||||
static const std::string CASES[] = {
|
||||
"A1LQFN3A",
|
||||
"a1lqfn3a",
|
||||
"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11sg7hg6",
|
||||
"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqzd3ryx",
|
||||
"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllludsr8",
|
||||
"split1checkupstagehandshakeupstreamerranterredcaperredlc445v",
|
||||
"?1v759aa"
|
||||
};
|
||||
for (const std::string& str : CASES) {
|
||||
const auto dec = bech32::Decode(str);
|
||||
BOOST_CHECK(dec.encoding == bech32::Encoding::BECH32M);
|
||||
std::string recode = bech32::Encode(bech32::Encoding::BECH32M, dec.hrp, dec.data);
|
||||
BOOST_CHECK(!recode.empty());
|
||||
BOOST_CHECK(CaseInsensitiveEqual(str, recode));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bech32_testvectors_invalid)
|
||||
{
|
||||
static const std::string CASES[] = {
|
||||
" 1nwldj5",
|
||||
|
@ -49,8 +69,32 @@ BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid)
|
|||
"A12uEL5L",
|
||||
};
|
||||
for (const std::string& str : CASES) {
|
||||
auto ret = bech32::Decode(str);
|
||||
BOOST_CHECK(ret.first.empty());
|
||||
const auto dec = bech32::Decode(str);
|
||||
BOOST_CHECK(dec.encoding == bech32::Encoding::INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(bech32m_testvectors_invalid)
|
||||
{
|
||||
static const std::string CASES[] = {
|
||||
" 1xj0phk",
|
||||
"\x7f""1g6xzxy",
|
||||
"\x80""1vctc34",
|
||||
"an84characterslonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11d6pts4",
|
||||
"qyrz8wqd2c9m",
|
||||
"1qyrz8wqd2c9m",
|
||||
"y1b0jsk6g",
|
||||
"lt1igcx5c0",
|
||||
"in1muywd",
|
||||
"mm1crxm3i",
|
||||
"au1s5cgom",
|
||||
"M1VUXWEZ",
|
||||
"16plkw9",
|
||||
"1p2gdwpf"
|
||||
};
|
||||
for (const std::string& str : CASES) {
|
||||
const auto dec = bech32::Decode(str);
|
||||
BOOST_CHECK(dec.encoding == bech32::Encoding::INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,177 +6,207 @@
|
|||
"x"
|
||||
],
|
||||
[
|
||||
"37qgekLpCCHrQuSjvX3fs496FWTGsHFHizjJAs6NPcR47aefnnCWECAhHV6E3g4YN7u7Yuwod5Y"
|
||||
"2v7k5Bb8Lr1MMgTgW6HAf5YHXi6BzpPjHpQ4srD4RSwHYpzXKiXmLAgiLhkXvp3JF5v7nq45EWr"
|
||||
],
|
||||
[
|
||||
"dzb7VV1Ui55BARxv7ATxAtCUeJsANKovDGWFVgpTbhq9gvPqP3yv"
|
||||
"RAZzCGtMbiUgMiiyrZySrSpdfnQReFXA3r"
|
||||
],
|
||||
[
|
||||
"MuNu7ZAEDFiHthiunm7dPjwKqrVNCM3mAz6rP9zFveQu14YA8CxExSJTHcVP9DErn6u84E6Ej7S"
|
||||
"NYamy7tcPQTzoU5iyQojD3sqhiz7zxkvn8"
|
||||
],
|
||||
[
|
||||
"rPpQpYknyNQ5AEHuY6H8ijJJrYc2nDKKk9jjmKEXsWzyAQcFGpDLU2Zvsmoi8JLR7hAwoy3RQWf"
|
||||
"geaFG555Ex5nyRf7JjW6Pj2GwZA8KYxtJJLbr1eZhVW75STbYBZeRszy3wg4pkKdF4ez9J4wQiz"
|
||||
],
|
||||
[
|
||||
"4Uc3FmN6NQ6zLBK5QQBXRBUREaaHwCZYsGCueHauuDmJpZKn6jkEskMB2Zi2CNgtb5r6epWEFfUJq"
|
||||
"2Cxmid3c2XQ2zvQ8SA1ha2TKqvqbJS9XFmXRsCneBS3Po7Qqb65z5zNdsoF9AfieXFcpoVPmkmfa"
|
||||
],
|
||||
[
|
||||
"7aQgR5DFQ25vyXmqZAWmnVCjL3PkBcdVkBUpjrjMTcghHx3E8wb"
|
||||
"gaJ7UVge2njVg9tFTetJrtHgruMm7aQDiSAxfHrVEgzK8N2ooagDVmDkdph434xzc4K96Gjyxcs"
|
||||
],
|
||||
[
|
||||
"17QpPprjeg69fW1DV8DcYYCKvWjYhXvWkov6MJ1iTTvMFj6weAqW7wybZeH57WTNxXVCRH4veVs"
|
||||
"5JN5BEVQPZ3tAiatz1RGXkrJuE3EC6bervMaPb38wTNgEuZCeqp"
|
||||
],
|
||||
[
|
||||
"KxuACDviz8Xvpn1xAh9MfopySZNuyajYMZWz16Dv2mHHryznWUp3"
|
||||
"3TnFbyUtBRS5rE1KTW81qLVspjJNaB3uu6uuvLjxhZo2DB6PCGh"
|
||||
],
|
||||
[
|
||||
"7nK3GSmqdXJQtdohvGfJ7KsSmn3TmGqExug49583bDAL91pVSGq5xS9SHoAYL3Wv3ijKTit65th"
|
||||
"7UgSZGaMaTc4d2mdEgcGBFiMeS6eMsithGUqvBsKTQdGzD7XQDbMEYo3gojdbXEPbUdFF3CQoK72f"
|
||||
],
|
||||
[
|
||||
"cTivdBmq7bay3RFGEBBuNfMh2P1pDCgRYN2Wbxmgwr4ki3jNUL2va"
|
||||
"9261wfqQqruNDnBDhbbb4tN9oKA1KpRFHeoYeufyJApVGixyAG4V"
|
||||
],
|
||||
[
|
||||
"gjMV4vjNjyMrna4fsAr8bWxAbwtmMUBXJS3zL4NJt5qjozpbQLmAfK1uA3CquSqsZQMpoD1g2nk"
|
||||
"cS824CTUh18scFmYuqt6BgxuRhdR4dEEnCHs3fzBbcyQgbfasHbw"
|
||||
],
|
||||
[
|
||||
"emXm1naBMoVzPjbk7xpeTVMFy4oDEe25UmoyGgKEB1gGWsK8kRGs"
|
||||
"tc1q0ywf7wkz6t580n3yemd3ucfw8jxn93tpc6wskt"
|
||||
],
|
||||
[
|
||||
"7VThQnNRj1o3Zyvc7XHPRrjDf8j2oivPTeDXnRPYWeYGE4pXeRJDZgf28ppti5hsHWXS2GSobdqyo"
|
||||
"bt1pxeeuh96wpm5c6u3kavts2qgwlv6y8um7u7ga6ltlwrhrv7w9vers8lgt3k"
|
||||
],
|
||||
[
|
||||
"1G9u6oCVCPh2o8m3t55ACiYvG1y5BHewUkDSdiQarDcYXXhFHYdzMdYfUAhfxn5vNZBwpgUNpso"
|
||||
"tb130lvl2lyugsk2tf3zhwcjjv39dmwt2tt7ytqaexy8edwcuwks6p5scll5kz"
|
||||
],
|
||||
[
|
||||
"31QQ7ZMLkScDiB4VyZjuptr7AEc9j1SjstF7pRoLhHTGkW4Q2y9XELobQmhhWxeRvqcukGd1XCq"
|
||||
"bcrt1rhsveeudk"
|
||||
],
|
||||
[
|
||||
"DHqKSnpxa8ZdQyH8keAhvLTrfkyBMQxqngcQA5N8LQ9KVt25kmGN"
|
||||
"bc10rmfwl8nxdweeyc4sf89t0tn9fv9w6qpyzsnl2r4k48vjqh03qas9asdje0rlr0phru0wqw0p"
|
||||
],
|
||||
[
|
||||
"2LUHcJPbwLCy9GLH1qXmfmAwvadWw4bp4PCpDfduLqV17s6iDcy1imUwhQJhAoNoN1XNmweiJP4i"
|
||||
"tb1qjqnfsuatr54e957xzg9sqk7yqcry9lns"
|
||||
],
|
||||
[
|
||||
"7USRzBXAnmck8fX9HmW7RAb4qt92VFX6soCnts9s74wxm4gguVhtG5of8fZGbNPJA83irHVY6bCos"
|
||||
"bcrt1q8p08mv8echkf3es027u4cdswxlylm3th76ls8v6y4zy4vwsavngpr4e4td"
|
||||
],
|
||||
[
|
||||
"1DGezo7BfVebZxAbNT3XGujdeHyNNBF3vnficYoTSp4PfK2QaML9bHzAMxke3wdKdHYWmsMTJVu"
|
||||
"BC1QNC2H66VLWTWTW52DP0FYUSNU3QQG5VT4V"
|
||||
],
|
||||
[
|
||||
"2D12DqDZKwCxxkzs1ZATJWvgJGhQ4cFi3WrizQ5zLAyhN5HxuAJ1yMYaJp8GuYsTLLxTAz6otCfb"
|
||||
"tb1qgk665m2auw09rc7pqyf7aulcuhmatz9xqtr5mxew7zuysacaascqs9v0vn"
|
||||
],
|
||||
[
|
||||
"8AFJzuTujXjw1Z6M3fWhQ1ujDW7zsV4ePeVjVo7D1egERqSW9nZ"
|
||||
"bcrt17CAPP7"
|
||||
],
|
||||
[
|
||||
"163Q17qLbTCue8YY3AvjpUhotuaodLm2uqMhpYirsKjVqnxJRWTEoywMVY3NbBAHuhAJ2cF9GAZ"
|
||||
"bc1qxmf2d6aerjzam3rur0zufqxqnyqfts5u302s7x"
|
||||
],
|
||||
[
|
||||
"2MnmgiRH4eGLyLc9eAqStzk7dFgBjFtUCtu"
|
||||
"tb1qn8x5dnzpexq7nnvrvnhwr9c3wkakpcyu9wwsjzq9pstkwg0t6qhs4l3rv6"
|
||||
],
|
||||
[
|
||||
"461QQ2sYWxU7H2PV4oBwJGNch8XVTYYbZxU"
|
||||
"BCRT1Q397G2RNVYRL5LK07CE8NCKHVKP8Z4SC9U0MVH9"
|
||||
],
|
||||
[
|
||||
"2UCtv53VttmQYkVU4VMtXB31REvQg4ABzs41AEKZ8UcB7DAfVzdkV9JDErwGwyj5AUHLkmgZeobs"
|
||||
"bc1pgxwyajq0gdn389f69uwn2fw9q0z5c9s063j5dgkdd23ajaud4hpsercr9h"
|
||||
],
|
||||
[
|
||||
"cSNjAsnhgtiFMi6MtfvgscMB2Cbhn2v1FUYfviJ1CdjfidvmeW6mn"
|
||||
"tb1z6mnmp5k542l6yk4ul0mp4rq3yvz44lfm"
|
||||
],
|
||||
[
|
||||
"gmsow2Y6EWAFDFE1CE4Hd3Tpu2BvfmBfG1SXsuRARbnt1WjkZnFh1qGTiptWWbjsq2Q6qvpgJVj"
|
||||
"bcrt17capp7"
|
||||
],
|
||||
[
|
||||
"nksUKSkzS76v8EsSgozXGMoQFiCoCHzCVajFKAXqzK5on9ZJYVHMD5CKwgmX3S3c7M1U3xabUny"
|
||||
"2D2bqvKseKHdoKjCNvjVULUgmxHu9hjKGwDbPRjTRH59tsHNLeyKwq3vyVBbo9LByY9wiapqjwFY"
|
||||
],
|
||||
[
|
||||
"L3favK1UzFGgdzYBF2oBT5tbayCo4vtVBLJhg2iYuMeePxWG8SQc"
|
||||
"2SSjAim4wZpeQRe5zTj1qqS6Li9ttJDaZ3ze"
|
||||
],
|
||||
[
|
||||
"7VxLxGGtYT6N99GdEfi6xz56xdQ8nP2dG1CavuXx7Rf2PrvNMTBNevjkfgs9JmkcGm6EXpj8ipyPZ"
|
||||
"mi9H6MjLwXxy9kxe1x4ToxyLRBsmcZxgVi"
|
||||
],
|
||||
[
|
||||
"2mbZwFXF6cxShaCo2czTRB62WTx9LxhTtpP"
|
||||
"VciXoxEitcn88jy197J9n9cpJ1pZahzU3SyWUiHqLgcfjttLEEJz"
|
||||
],
|
||||
[
|
||||
"dB7cwYdcPSgiyAwKWL3JwCVwSk6epU2txw"
|
||||
"KppmwADGoExPT9Eq5hjRWpWFDbzJyfzHFgsfxBiDHNpVBgWPRNuy"
|
||||
],
|
||||
[
|
||||
"HPhFUhUAh8ZQQisH8QQWafAxtQYju3SFTX"
|
||||
"TN7EQXMxKffzvHo54yHHu9R4ks9f5gWBW3MMVf5k72zAqrgVK9ys"
|
||||
],
|
||||
[
|
||||
"4ctAH6AkHzq5ioiM1m9T3E2hiYEev5mTsB"
|
||||
"92dbrMEYzP5dD5UhQ6maNkCQ4GLG42BM4Gc6XKZzSSMSfosfkkcB"
|
||||
],
|
||||
[
|
||||
"Hn1uFi4dNexWrqARpjMqgT6cX1UsNPuV3cHdGg9ExyXw8HTKadbktRDtdeVmY3M1BxJStiL4vjJ"
|
||||
"J7VQxPxyzuWEkRstQWpCz2AgysEz1APgnWCEQrFvkN3umAnCrhQF"
|
||||
],
|
||||
[
|
||||
"Sq3fDbvutABmnAHHExJDgPLQn44KnNC7UsXuT7KZecpaYDMU9Txs"
|
||||
"tc1qymllj6c96v5qj2504y27ldtner6eh8ldx38t83"
|
||||
],
|
||||
[
|
||||
"6TqWyrqdgUEYDQU1aChMuFMMEimHX44qHFzCUgGfqxGgZNMUVWJ"
|
||||
"bt1flep4g"
|
||||
],
|
||||
[
|
||||
"giqJo7oWqFxNKWyrgcBxAVHXnjJ1t6cGoEffce5Y1y7u649Noj5wJ4mmiUAKEVVrYAGg2KPB3Y4"
|
||||
"tb13c553hwygcgj48qwmr9f8q0hgdcfklyaye5sxzcpcjnmxv4z506xs90tchn"
|
||||
],
|
||||
[
|
||||
"cNzHY5e8vcmM3QVJUcjCyiKMYfeYvyueq5qCMV3kqcySoLyGLYUK"
|
||||
"bcrt1tyddyu"
|
||||
],
|
||||
[
|
||||
"37uTe568EYc9WLoHEd9jXEvUiWbq5LFLscNyqvAzLU5vBArUJA6eydkLmnMwJDjkL5kXc2VK7ig"
|
||||
"bc10qssq2mknjqf0glwe2f3587wc4jysvs3f8s6chysae6hcl6fxzdm4wxyyscrl5k9f5qmnf05a"
|
||||
],
|
||||
[
|
||||
"EsYbG4tWWWY45G31nox838qNdzksbPySWc"
|
||||
"tb1q425lmgvxdgtyl2m6xuu2pc354y4fvgg8"
|
||||
],
|
||||
[
|
||||
"nbuzhfwMoNzA3PaFnyLcRxE9bTJPDkjZ6Rf6Y6o2ckXZfzZzXBT"
|
||||
"bcrt1q9wp8e5d2u3u4g0pll0cy7smeeuqezdun9xl439n3p2gg4fvgfvk3hu52hj"
|
||||
],
|
||||
[
|
||||
"cQN9PoxZeCWK1x56xnz6QYAsvR11XAce3Ehp3gMUdfSQ53Y2mPzx"
|
||||
"bc1qrz5acazpue8vl4zsaxn8fxtmeuqmyjkq3"
|
||||
],
|
||||
[
|
||||
"1Gm3N3rkef6iMbx4voBzaxtXcmmiMTqZPhcuAepRzYUJQW4qRpEnHvMojzof42hjFRf8PE2jPde"
|
||||
"tb1qkeuglpgmnex9tv3fr7htzfrh3rwrk23r52rx9halxzmv9fr85lwq0fwhmp"
|
||||
],
|
||||
[
|
||||
"2TAq2tuN6x6m233bpT7yqdYQPELdTDJn1eU"
|
||||
"bcrt1qd0t2wrhl7s57z99rsyaekpq0dyjcQRSSmz80r4"
|
||||
],
|
||||
[
|
||||
"ntEtnnGhqPii4joABvBtSEJG6BxjT2tUZqE8PcVYgk3RHpgxgHDCQxNbLJf7ardf1dDk2oCQ7Cf"
|
||||
"BC1QXLFDUCGX90T3E53PQCNKJ2PK25MSF3VLPMVY6T"
|
||||
],
|
||||
[
|
||||
"Ky1YjoZNgQ196HJV3HpdkecfhRBmRZdMJk89Hi5KGfpfPwS2bUbfd"
|
||||
"tb1qmycg4zszgnk34vaurx3cu8wpvteg9h40yq6cp52gt26gjel03t3su3x3xu"
|
||||
],
|
||||
[
|
||||
"2A1q1YsMZowabbvta7kTy2Fd6qN4r5ZCeG3qLpvZBMzCixMUdkN2Y4dHB1wPsZAeVXUGD83MfRED"
|
||||
"bcrt1q9hy58r4fnuxqzdqndpmq9pptc9nt2dw3rczf5e"
|
||||
],
|
||||
[
|
||||
"tc1qw508d6qejxtdg4y5r3zarvary0c5xw7kg3g4ty"
|
||||
"BC1PA7682NAY6JQSLUWAJYTC0ERWTMW7A4RPWLNTUS32LCXWLHVKKKTQ2UL8CG"
|
||||
],
|
||||
[
|
||||
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t5"
|
||||
"tb1z850dpxnwz2fzae5h2myatj4yvu6rq5xq"
|
||||
],
|
||||
[
|
||||
"BC13W508D6QEJXTDG4Y5R3ZARVARY0C5XW7KN40WF2"
|
||||
"bcrt1sp525pzjsmpqvcrawjreww36e9keg876skjvpwt"
|
||||
],
|
||||
[
|
||||
"bc1rw5uspcuh"
|
||||
"xcAvW5jurCpzSpLxBKEhCewCgwwuGhqJnC"
|
||||
],
|
||||
[
|
||||
"bc10w508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7kw5rljs90"
|
||||
"2Cvv8yp9kXbQt8EKh6Yma95yJ1uwYF9YKXuVhGJyu3dHGVsb2AVpTC62TFACZZ3KDNrALxR2CVNs"
|
||||
],
|
||||
[
|
||||
"BC1QR508D6QEJXTDG4Y5R3ZARVARYV98GJ9P"
|
||||
"niUuL46hCuEVvkAzZKHvD746qbmLmzip9Pv3F6UZV14JxzEXBnTkVxCT4URapChJG6qAEgsZs6G"
|
||||
],
|
||||
[
|
||||
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sL5k7"
|
||||
"2UHHgGfiipzvB8Eumnmvq6SowvrMJimjT3NwwG1839XEiUfwtpSdkUrseNsQuagXv21ce7aZu6yo"
|
||||
],
|
||||
[
|
||||
"bc1zw508d6qejxtdg4y5r3zarvaryvqyzf3du"
|
||||
"8u9djKu4u6o3bsgeR4BKNnLK3akpo64FYzDAmA9239wKeshgF97"
|
||||
],
|
||||
[
|
||||
"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3pjxtptv"
|
||||
"TC1QPAARXSLVMXHVRR0474LZXQYZWLGFZYPSFVL9E4"
|
||||
],
|
||||
[
|
||||
"bc1gmk9yu"
|
||||
"bt1pakek0n2267t9yaksxaczgr2syhv9y3xkx0wnsdwchfa6xkmjtvuqg3kgyr"
|
||||
],
|
||||
[
|
||||
"tb13h83rtwq62udrhwpn87uely7cyxcjrj0azz6a4r3n9s87x5uj98ys6ufp83"
|
||||
],
|
||||
[
|
||||
"bcrt1rk5vw5qf2"
|
||||
],
|
||||
[
|
||||
"bc10d3rmtg62h747en5j6fju5g5qyvsransrkty6ghh96pu647wumctejlsngh9pf26cysrys2x2"
|
||||
],
|
||||
[
|
||||
"tb1qajuy2cdwqgmrzc7la85al5cwcq374tsp"
|
||||
],
|
||||
[
|
||||
"bcrt1q3udxvj6x20chqh723mn064mzz65yr56ef00xk8czvu3jnx04ydapzk02s5"
|
||||
],
|
||||
[
|
||||
"bc1qule2szwzyaq4qy0s3aa4mauucyqt6fewe"
|
||||
],
|
||||
[
|
||||
"tb1ql0qny5vg9gh5tyzke6dw36px5ulkrp24x53x0pl2t5lpwrtejw3s2seej2"
|
||||
],
|
||||
[
|
||||
"bcrt17CAPP7"
|
||||
],
|
||||
[
|
||||
"bc1qtvm6davyf725wfedc2d5mrgfewqgcrce8gjrpl"
|
||||
],
|
||||
[
|
||||
"tb1q5acjgtqrrw3an0dzavxxxzlex8k7aukjzjk9v2u4rmfdqxjphcyq7ge97e"
|
||||
]
|
||||
]
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -16,28 +16,28 @@
|
|||
FUZZ_TARGET(bech32)
|
||||
{
|
||||
const std::string random_string(buffer.begin(), buffer.end());
|
||||
const std::pair<std::string, std::vector<uint8_t>> r1 = bech32::Decode(random_string);
|
||||
if (r1.first.empty()) {
|
||||
assert(r1.second.empty());
|
||||
const auto r1 = bech32::Decode(random_string);
|
||||
if (r1.hrp.empty()) {
|
||||
assert(r1.encoding == bech32::Encoding::INVALID);
|
||||
assert(r1.data.empty());
|
||||
} else {
|
||||
const std::string& hrp = r1.first;
|
||||
const std::vector<uint8_t>& data = r1.second;
|
||||
const std::string reencoded = bech32::Encode(hrp, data);
|
||||
assert(r1.encoding != bech32::Encoding::INVALID);
|
||||
const std::string reencoded = bech32::Encode(r1.encoding, r1.hrp, r1.data);
|
||||
assert(CaseInsensitiveEqual(random_string, reencoded));
|
||||
}
|
||||
|
||||
std::vector<unsigned char> input;
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { input.push_back(c); }, buffer.begin(), buffer.end());
|
||||
const std::string encoded = bech32::Encode("bc", input);
|
||||
assert(!encoded.empty());
|
||||
|
||||
const std::pair<std::string, std::vector<uint8_t>> r2 = bech32::Decode(encoded);
|
||||
if (r2.first.empty()) {
|
||||
assert(r2.second.empty());
|
||||
} else {
|
||||
const std::string& hrp = r2.first;
|
||||
const std::vector<uint8_t>& data = r2.second;
|
||||
assert(hrp == "bc");
|
||||
assert(data == input);
|
||||
if (input.size() + 3 + 6 <= 90) {
|
||||
// If it's possible to encode input in Bech32(m) without exceeding the 90-character limit:
|
||||
for (auto encoding : {bech32::Encoding::BECH32, bech32::Encoding::BECH32M}) {
|
||||
const std::string encoded = bech32::Encode(encoding, "bc", input);
|
||||
assert(!encoded.empty());
|
||||
const auto r2 = bech32::Decode(encoded);
|
||||
assert(r2.encoding == encoding);
|
||||
assert(r2.hrp == "bc");
|
||||
assert(r2.data == input);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,8 +12,12 @@ from test_framework.util import (
|
|||
)
|
||||
|
||||
BECH32_VALID = 'bcrt1qtmp74ayg7p24uslctssvjm06q5phz4yrxucgnv'
|
||||
BECH32_INVALID_SIZE = 'bcrt1sqqpl9r5c'
|
||||
BECH32_INVALID_PREFIX = 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'
|
||||
BECH32_INVALID_BECH32 = 'bcrt1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqdmchcc'
|
||||
BECH32_INVALID_BECH32M = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7k35mrzd'
|
||||
BECH32_INVALID_VERSION = 'bcrt130xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqynjegk'
|
||||
BECH32_INVALID_SIZE = 'bcrt1s0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav25430mtr'
|
||||
BECH32_INVALID_V0_SIZE = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kqqq5k3my'
|
||||
BECH32_INVALID_PREFIX = 'bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'
|
||||
|
||||
BASE58_VALID = 'mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn'
|
||||
BASE58_INVALID_PREFIX = '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem'
|
||||
|
@ -40,6 +44,18 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework):
|
|||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Invalid prefix for Bech32 address')
|
||||
|
||||
info = node.validateaddress(BECH32_INVALID_BECH32)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Version 1+ witness address must use Bech32m checksum')
|
||||
|
||||
info = node.validateaddress(BECH32_INVALID_BECH32M)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Version 0 witness address must use Bech32 checksum')
|
||||
|
||||
info = node.validateaddress(BECH32_INVALID_V0_SIZE)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Invalid Bech32 v0 address data size')
|
||||
|
||||
info = node.validateaddress(BECH32_VALID)
|
||||
assert info['isvalid']
|
||||
assert 'error' not in info
|
||||
|
|
|
@ -2,10 +2,18 @@
|
|||
# Copyright (c) 2017 Pieter Wuille
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Reference implementation for Bech32 and segwit addresses."""
|
||||
"""Reference implementation for Bech32/Bech32m and segwit addresses."""
|
||||
import unittest
|
||||
from enum import Enum
|
||||
|
||||
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
||||
BECH32_CONST = 1
|
||||
BECH32M_CONST = 0x2bc830a3
|
||||
|
||||
class Encoding(Enum):
|
||||
"""Enumeration type to list the various supported encodings."""
|
||||
BECH32 = 1
|
||||
BECH32M = 2
|
||||
|
||||
|
||||
def bech32_polymod(values):
|
||||
|
@ -27,38 +35,45 @@ def bech32_hrp_expand(hrp):
|
|||
|
||||
def bech32_verify_checksum(hrp, data):
|
||||
"""Verify a checksum given HRP and converted data characters."""
|
||||
return bech32_polymod(bech32_hrp_expand(hrp) + data) == 1
|
||||
check = bech32_polymod(bech32_hrp_expand(hrp) + data)
|
||||
if check == BECH32_CONST:
|
||||
return Encoding.BECH32
|
||||
elif check == BECH32M_CONST:
|
||||
return Encoding.BECH32M
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def bech32_create_checksum(hrp, data):
|
||||
def bech32_create_checksum(encoding, hrp, data):
|
||||
"""Compute the checksum values given HRP and data."""
|
||||
values = bech32_hrp_expand(hrp) + data
|
||||
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ 1
|
||||
const = BECH32M_CONST if encoding == Encoding.BECH32M else BECH32_CONST
|
||||
polymod = bech32_polymod(values + [0, 0, 0, 0, 0, 0]) ^ const
|
||||
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
|
||||
|
||||
|
||||
def bech32_encode(hrp, data):
|
||||
"""Compute a Bech32 string given HRP and data values."""
|
||||
combined = data + bech32_create_checksum(hrp, data)
|
||||
def bech32_encode(encoding, hrp, data):
|
||||
"""Compute a Bech32 or Bech32m string given HRP and data values."""
|
||||
combined = data + bech32_create_checksum(encoding, hrp, data)
|
||||
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
|
||||
|
||||
|
||||
def bech32_decode(bech):
|
||||
"""Validate a Bech32 string, and determine HRP and data."""
|
||||
"""Validate a Bech32/Bech32m string, and determine HRP and data."""
|
||||
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
|
||||
(bech.lower() != bech and bech.upper() != bech)):
|
||||
return (None, None)
|
||||
return (None, None, None)
|
||||
bech = bech.lower()
|
||||
pos = bech.rfind('1')
|
||||
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
|
||||
return (None, None)
|
||||
return (None, None, None)
|
||||
if not all(x in CHARSET for x in bech[pos+1:]):
|
||||
return (None, None)
|
||||
return (None, None, None)
|
||||
hrp = bech[:pos]
|
||||
data = [CHARSET.find(x) for x in bech[pos+1:]]
|
||||
if not bech32_verify_checksum(hrp, data):
|
||||
return (None, None)
|
||||
return (hrp, data[:-6])
|
||||
encoding = bech32_verify_checksum(hrp, data)
|
||||
if encoding is None:
|
||||
return (None, None, None)
|
||||
return (encoding, hrp, data[:-6])
|
||||
|
||||
|
||||
def convertbits(data, frombits, tobits, pad=True):
|
||||
|
@ -86,7 +101,7 @@ def convertbits(data, frombits, tobits, pad=True):
|
|||
|
||||
def decode_segwit_address(hrp, addr):
|
||||
"""Decode a segwit address."""
|
||||
hrpgot, data = bech32_decode(addr)
|
||||
encoding, hrpgot, data = bech32_decode(addr)
|
||||
if hrpgot != hrp:
|
||||
return (None, None)
|
||||
decoded = convertbits(data[1:], 5, 8, False)
|
||||
|
@ -96,12 +111,15 @@ def decode_segwit_address(hrp, addr):
|
|||
return (None, None)
|
||||
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
|
||||
return (None, None)
|
||||
if (data[0] == 0 and encoding != Encoding.BECH32) or (data[0] != 0 and encoding != Encoding.BECH32M):
|
||||
return (None, None)
|
||||
return (data[0], decoded)
|
||||
|
||||
|
||||
def encode_segwit_address(hrp, witver, witprog):
|
||||
"""Encode a segwit address."""
|
||||
ret = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
|
||||
encoding = Encoding.BECH32 if witver == 0 else Encoding.BECH32M
|
||||
ret = bech32_encode(encoding, hrp, [witver] + convertbits(witprog, 8, 5))
|
||||
if decode_segwit_address(hrp, ret) == (None, None):
|
||||
return None
|
||||
return ret
|
||||
|
@ -119,3 +137,5 @@ class TestFrameworkScript(unittest.TestCase):
|
|||
# P2WSH
|
||||
test_python_bech32('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj')
|
||||
test_python_bech32('bcrt1qft5p2uhsdcdc3l2ua4ap5qqfg4pjaqlp250x7us7a8qqhrxrxfsqseac85')
|
||||
# P2TR
|
||||
test_python_bech32('bcrt1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqc8gma6')
|
||||
|
|
|
@ -138,13 +138,13 @@ class WalletLabelsTest(BitcoinTestFramework):
|
|||
node.createwallet(wallet_name='watch_only', disable_private_keys=True)
|
||||
wallet_watch_only = node.get_wallet_rpc('watch_only')
|
||||
BECH32_VALID = {
|
||||
'✔️_VER15_PROG40': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqn2cjv3',
|
||||
'✔️_VER16_PROG03': 'bcrt1sqqqqqjq8pdp',
|
||||
'✔️_VER16_PROB02': 'bcrt1sqqqqqjq8pv',
|
||||
'✔️_VER15_PROG40': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxkg7fn',
|
||||
'✔️_VER16_PROG03': 'bcrt1sqqqqq8uhdgr',
|
||||
'✔️_VER16_PROB02': 'bcrt1sqqqq4wstyw',
|
||||
}
|
||||
BECH32_INVALID = {
|
||||
'❌_VER15_PROG41': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzc7xyq',
|
||||
'❌_VER16_PROB01': 'bcrt1sqqpl9r5c',
|
||||
'❌_VER15_PROG41': 'bcrt1sqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqajlxj8',
|
||||
'❌_VER16_PROB01': 'bcrt1sqq5r4036',
|
||||
}
|
||||
for l in BECH32_VALID:
|
||||
ad = BECH32_VALID[l]
|
||||
|
|
Loading…
Add table
Reference in a new issue