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:
|
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 valid 70 > ../../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 invalid 70 > ../../src/test/data/key_io_invalid.json
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
# 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:
|
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 valid 70 > ../../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 invalid 70 > ../../src/test/data/key_io_invalid.json
|
||||||
'''
|
'''
|
||||||
# 2012 Wladimir J. van der Laan
|
# 2012 Wladimir J. van der Laan
|
||||||
# Released under MIT License
|
# Released under MIT License
|
||||||
|
@ -15,7 +15,7 @@ import os
|
||||||
from itertools import islice
|
from itertools import islice
|
||||||
from base58 import b58encode_chk, b58decode_chk, b58chars
|
from base58 import b58encode_chk, b58decode_chk, b58chars
|
||||||
import random
|
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
|
# key types
|
||||||
PUBKEY_ADDRESS = 0
|
PUBKEY_ADDRESS = 0
|
||||||
|
@ -32,6 +32,7 @@ PRIVKEY_REGTEST = 239
|
||||||
OP_0 = 0x00
|
OP_0 = 0x00
|
||||||
OP_1 = 0x51
|
OP_1 = 0x51
|
||||||
OP_2 = 0x52
|
OP_2 = 0x52
|
||||||
|
OP_3 = 0x53
|
||||||
OP_16 = 0x60
|
OP_16 = 0x60
|
||||||
OP_DUP = 0x76
|
OP_DUP = 0x76
|
||||||
OP_EQUAL = 0x87
|
OP_EQUAL = 0x87
|
||||||
|
@ -44,6 +45,7 @@ script_prefix = (OP_HASH160, 20)
|
||||||
script_suffix = (OP_EQUAL,)
|
script_suffix = (OP_EQUAL,)
|
||||||
p2wpkh_prefix = (OP_0, 20)
|
p2wpkh_prefix = (OP_0, 20)
|
||||||
p2wsh_prefix = (OP_0, 32)
|
p2wsh_prefix = (OP_0, 32)
|
||||||
|
p2tr_prefix = (OP_1, 32)
|
||||||
|
|
||||||
metadata_keys = ['isPrivkey', 'chain', 'isCompressed', 'tryCaseFlip']
|
metadata_keys = ['isPrivkey', 'chain', 'isCompressed', 'tryCaseFlip']
|
||||||
# templates for valid sequences
|
# templates for valid sequences
|
||||||
|
@ -54,40 +56,58 @@ templates = [
|
||||||
((SCRIPT_ADDRESS,), 20, (), (False, 'main', None, None), script_prefix, script_suffix),
|
((SCRIPT_ADDRESS,), 20, (), (False, 'main', None, None), script_prefix, script_suffix),
|
||||||
((PUBKEY_ADDRESS_TEST,), 20, (), (False, 'test', None, None), pubkey_prefix, pubkey_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),
|
((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),
|
((PUBKEY_ADDRESS_REGTEST,), 20, (), (False, 'regtest', None, None), pubkey_prefix, pubkey_suffix),
|
||||||
((SCRIPT_ADDRESS_REGTEST,), 20, (), (False, 'regtest', None, None), script_prefix, script_suffix),
|
((SCRIPT_ADDRESS_REGTEST,), 20, (), (False, 'regtest', None, None), script_prefix, script_suffix),
|
||||||
((PRIVKEY,), 32, (), (True, 'main', False, None), (), ()),
|
((PRIVKEY,), 32, (), (True, 'main', False, None), (), ()),
|
||||||
((PRIVKEY,), 32, (1,), (True, 'main', True, None), (), ()),
|
((PRIVKEY,), 32, (1,), (True, 'main', True, None), (), ()),
|
||||||
((PRIVKEY_TEST,), 32, (), (True, 'test', False, None), (), ()),
|
((PRIVKEY_TEST,), 32, (), (True, 'test', False, None), (), ()),
|
||||||
((PRIVKEY_TEST,), 32, (1,), (True, 'test', True, 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, (), (True, 'regtest', False, None), (), ()),
|
||||||
((PRIVKEY_REGTEST,), 32, (1,), (True, 'regtest', True, None), (), ())
|
((PRIVKEY_REGTEST,), 32, (1,), (True, 'regtest', True, None), (), ())
|
||||||
]
|
]
|
||||||
# templates for valid bech32 sequences
|
# templates for valid bech32 sequences
|
||||||
bech32_templates = [
|
bech32_templates = [
|
||||||
# hrp, version, witprog_size, metadata, output_prefix
|
# hrp, version, witprog_size, metadata, encoding, output_prefix
|
||||||
('bc', 0, 20, (False, 'main', None, True), p2wpkh_prefix),
|
('bc', 0, 20, (False, 'main', None, True), Encoding.BECH32, p2wpkh_prefix),
|
||||||
('bc', 0, 32, (False, 'main', None, True), p2wsh_prefix),
|
('bc', 0, 32, (False, 'main', None, True), Encoding.BECH32, p2wsh_prefix),
|
||||||
('bc', 1, 2, (False, 'main', None, True), (OP_1, 2)),
|
('bc', 1, 32, (False, 'main', None, True), Encoding.BECH32M, p2tr_prefix),
|
||||||
('tb', 0, 20, (False, 'test', None, True), p2wpkh_prefix),
|
('bc', 2, 2, (False, 'main', None, True), Encoding.BECH32M, (OP_2, 2)),
|
||||||
('tb', 0, 32, (False, 'test', None, True), p2wsh_prefix),
|
('tb', 0, 20, (False, 'test', None, True), Encoding.BECH32, p2wpkh_prefix),
|
||||||
('tb', 2, 16, (False, 'test', None, True), (OP_2, 16)),
|
('tb', 0, 32, (False, 'test', None, True), Encoding.BECH32, p2wsh_prefix),
|
||||||
('bcrt', 0, 20, (False, 'regtest', None, True), p2wpkh_prefix),
|
('tb', 1, 32, (False, 'test', None, True), Encoding.BECH32M, p2tr_prefix),
|
||||||
('bcrt', 0, 32, (False, 'regtest', None, True), p2wsh_prefix),
|
('tb', 3, 16, (False, 'test', None, True), Encoding.BECH32M, (OP_3, 16)),
|
||||||
('bcrt', 16, 40, (False, 'regtest', None, True), (OP_16, 40))
|
('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
|
# templates for invalid bech32 sequences
|
||||||
bech32_ng_templates = [
|
bech32_ng_templates = [
|
||||||
# hrp, version, witprog_size, invalid_bech32, invalid_checksum, invalid_char
|
# hrp, version, witprog_size, encoding, invalid_bech32, invalid_checksum, invalid_char
|
||||||
('tc', 0, 20, False, False, False),
|
('tc', 0, 20, Encoding.BECH32, False, False, False),
|
||||||
('tb', 17, 32, False, False, False),
|
('bt', 1, 32, Encoding.BECH32M, False, False, False),
|
||||||
('bcrt', 3, 1, False, False, False),
|
('tb', 17, 32, Encoding.BECH32M, False, False, False),
|
||||||
('bc', 15, 41, False, False, False),
|
('bcrt', 3, 1, Encoding.BECH32M, False, False, False),
|
||||||
('tb', 0, 16, False, False, False),
|
('bc', 15, 41, Encoding.BECH32M, False, False, False),
|
||||||
('bcrt', 0, 32, True, False, False),
|
('tb', 0, 16, Encoding.BECH32, False, False, False),
|
||||||
('bc', 0, 16, True, False, False),
|
('bcrt', 0, 32, Encoding.BECH32, True, False, False),
|
||||||
('tb', 0, 32, False, True, False),
|
('bc', 0, 16, Encoding.BECH32, True, False, False),
|
||||||
('bcrt', 0, 20, False, False, True)
|
('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):
|
def is_valid(v):
|
||||||
|
@ -127,8 +147,9 @@ def gen_valid_bech32_vector(template):
|
||||||
hrp = template[0]
|
hrp = template[0]
|
||||||
witver = template[1]
|
witver = template[1]
|
||||||
witprog = bytearray(os.urandom(template[2]))
|
witprog = bytearray(os.urandom(template[2]))
|
||||||
dst_prefix = bytearray(template[4])
|
encoding = template[4]
|
||||||
rv = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5))
|
dst_prefix = bytearray(template[5])
|
||||||
|
rv = bech32_encode(hrp, [witver] + convertbits(witprog, 8, 5), encoding)
|
||||||
return rv, dst_prefix + witprog
|
return rv, dst_prefix + witprog
|
||||||
|
|
||||||
def gen_valid_vectors():
|
def gen_valid_vectors():
|
||||||
|
@ -186,22 +207,23 @@ def gen_invalid_bech32_vector(template):
|
||||||
hrp = template[0]
|
hrp = template[0]
|
||||||
witver = template[1]
|
witver = template[1]
|
||||||
witprog = bytearray(os.urandom(template[2]))
|
witprog = bytearray(os.urandom(template[2]))
|
||||||
|
encoding = template[3]
|
||||||
|
|
||||||
if no_data:
|
if no_data:
|
||||||
rv = bech32_encode(hrp, [])
|
rv = bech32_encode(hrp, [], encoding)
|
||||||
else:
|
else:
|
||||||
data = [witver] + convertbits(witprog, 8, 5)
|
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}:
|
if template[2] % 5 in {2, 4}:
|
||||||
data[-1] |= 1
|
data[-1] |= 1
|
||||||
else:
|
else:
|
||||||
data.append(0)
|
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)
|
i = len(rv) - random.randrange(1, 7)
|
||||||
rv = rv[:i] + random.choice(CHARSET.replace(rv[i], '')) + rv[i + 1:]
|
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)
|
i = len(hrp) + 1 + random.randrange(0, len(rv) - len(hrp) - 4)
|
||||||
rv = rv[:i] + rv[i:i + 4].upper() + rv[i + 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 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)).
|
* [`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 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 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 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
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
|
@ -7,15 +7,18 @@
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace bech32
|
||||||
|
{
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
||||||
typedef std::vector<uint8_t> data;
|
typedef std::vector<uint8_t> data;
|
||||||
|
|
||||||
/** The Bech32 character set for encoding. */
|
/** The Bech32 and Bech32m character set for encoding. */
|
||||||
const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||||
|
|
||||||
/** The Bech32 character set for decoding. */
|
/** The Bech32 and Bech32m character set for decoding. */
|
||||||
const int8_t CHARSET_REV[128] = {
|
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,
|
||||||
-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
|
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
|
/** 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
|
* make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher
|
||||||
* bits correspond to earlier values. */
|
* bits correspond to earlier values. */
|
||||||
|
@ -111,21 +120,24 @@ data ExpandHRP(const std::string& hrp)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Verify a checksum. */
|
/** 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,
|
// 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
|
// 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
|
// list of values would result in a new valid list. For that reason, Bech32 requires the
|
||||||
// resulting checksum to be 1 instead.
|
// resulting checksum to be 1 instead. In Bech32m, this constant was amended.
|
||||||
return PolyMod(Cat(ExpandHRP(hrp), values)) == 1;
|
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. */
|
/** 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);
|
data enc = Cat(ExpandHRP(hrp), values);
|
||||||
enc.resize(enc.size() + 6); // Append 6 zeroes
|
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);
|
data ret(6);
|
||||||
for (size_t i = 0; i < 6; ++i) {
|
for (size_t i = 0; i < 6; ++i) {
|
||||||
// Convert the 5-bit groups in mod to checksum values.
|
// 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
|
||||||
|
|
||||||
namespace bech32
|
/** 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
|
||||||
/** Encode a Bech32 string. */
|
// to return a lowercase Bech32/Bech32m string, but if given an uppercase HRP, the
|
||||||
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
|
|
||||||
// result will always be invalid.
|
// result will always be invalid.
|
||||||
for (const char& c : hrp) assert(c < 'A' || c > 'Z');
|
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);
|
data combined = Cat(values, checksum);
|
||||||
std::string ret = hrp + '1';
|
std::string ret = hrp + '1';
|
||||||
ret.reserve(ret.size() + combined.size());
|
ret.reserve(ret.size() + combined.size());
|
||||||
|
@ -155,8 +164,8 @@ std::string Encode(const std::string& hrp, const data& values) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Decode a Bech32 string. */
|
/** Decode a Bech32 or Bech32m string. */
|
||||||
std::pair<std::string, data> Decode(const std::string& str) {
|
DecodeResult Decode(const std::string& str) {
|
||||||
bool lower = false, upper = false;
|
bool lower = false, upper = false;
|
||||||
for (size_t i = 0; i < str.size(); ++i) {
|
for (size_t i = 0; i < str.size(); ++i) {
|
||||||
unsigned char c = str[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) {
|
for (size_t i = 0; i < pos; ++i) {
|
||||||
hrp += LowerCase(str[i]);
|
hrp += LowerCase(str[i]);
|
||||||
}
|
}
|
||||||
if (!VerifyChecksum(hrp, values)) {
|
Encoding result = VerifyChecksum(hrp, values);
|
||||||
return {};
|
if (result == Encoding::INVALID) return {};
|
||||||
}
|
return {result, std::move(hrp), data(values.begin(), values.end() - 6)};
|
||||||
return {hrp, data(values.begin(), values.end() - 6)};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace bech32
|
} // 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
|
// Distributed under the MIT software license, see the accompanying
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
// Bech32 is a string encoding format used in newer address types.
|
// Bech32 and Bech32m are string encoding formats used in newer
|
||||||
// The output consists of a human-readable part (alphanumeric), a
|
// address types. The outputs consist of a human-readable part
|
||||||
// separator character (1), and a base32 data section, the last
|
// (alphanumeric), a separator character (1), and a base32 data
|
||||||
// 6 characters of which are a checksum.
|
// 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
|
#ifndef BITCOIN_BECH32_H
|
||||||
#define BITCOIN_BECH32_H
|
#define BITCOIN_BECH32_H
|
||||||
|
@ -19,11 +20,29 @@
|
||||||
namespace bech32
|
namespace bech32
|
||||||
{
|
{
|
||||||
|
|
||||||
/** Encode a Bech32 string. If hrp contains uppercase characters, this will cause an assertion error. */
|
enum class Encoding {
|
||||||
std::string Encode(const std::string& hrp, const std::vector<uint8_t>& values);
|
INVALID, //!< Failed decoding
|
||||||
|
|
||||||
/** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */
|
BECH32, //!< Bech32 encoding as defined in BIP173
|
||||||
std::pair<std::string, std::vector<uint8_t>> Decode(const std::string& str);
|
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
|
} // namespace bech32
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ static void Bech32Encode(benchmark::Bench& bench)
|
||||||
tmp.reserve(1 + 32 * 8 / 5);
|
tmp.reserve(1 + 32 * 8 / 5);
|
||||||
ConvertBits<8, 5, true>([&](unsigned char c) { tmp.push_back(c); }, v.begin(), v.end());
|
ConvertBits<8, 5, true>([&](unsigned char c) { tmp.push_back(c); }, v.begin(), v.end());
|
||||||
bench.batch(v.size()).unit("byte").run([&] {
|
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};
|
std::vector<unsigned char> data = {0};
|
||||||
data.reserve(33);
|
data.reserve(33);
|
||||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
|
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
|
std::string operator()(const WitnessV0ScriptHash& id) const
|
||||||
|
@ -51,7 +51,7 @@ public:
|
||||||
std::vector<unsigned char> data = {0};
|
std::vector<unsigned char> data = {0};
|
||||||
data.reserve(53);
|
data.reserve(53);
|
||||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
|
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
|
std::string operator()(const WitnessUnknown& id) const
|
||||||
|
@ -62,7 +62,7 @@ public:
|
||||||
std::vector<unsigned char> data = {(unsigned char)id.version};
|
std::vector<unsigned char> data = {(unsigned char)id.version};
|
||||||
data.reserve(1 + (id.length * 8 + 4) / 5);
|
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);
|
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 {}; }
|
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";
|
error_str = "Invalid prefix for Base58-encoded address";
|
||||||
}
|
}
|
||||||
data.clear();
|
data.clear();
|
||||||
auto bech = bech32::Decode(str);
|
const auto dec = bech32::Decode(str);
|
||||||
if (bech.second.size() > 0) {
|
if ((dec.encoding == bech32::Encoding::BECH32 || dec.encoding == bech32::Encoding::BECH32M) && dec.data.size() > 0) {
|
||||||
|
// Bech32 decoding
|
||||||
error_str = "";
|
error_str = "";
|
||||||
|
if (dec.hrp != params.Bech32HRP()) {
|
||||||
if (bech.first != params.Bech32HRP()) {
|
|
||||||
error_str = "Invalid prefix for Bech32 address";
|
error_str = "Invalid prefix for Bech32 address";
|
||||||
return CNoDestination();
|
return CNoDestination();
|
||||||
}
|
}
|
||||||
|
int version = dec.data[0]; // The first 5 bit symbol is the witness version (0-16)
|
||||||
// Bech32 decoding
|
if (version == 0 && dec.encoding != bech32::Encoding::BECH32) {
|
||||||
int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16)
|
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.
|
// The rest of the symbols are converted witness program bytes.
|
||||||
data.reserve(((bech.second.size() - 1) * 5) / 8);
|
data.reserve(((dec.data.size() - 1) * 5) / 8);
|
||||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, bech.second.begin() + 1, bech.second.end())) {
|
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, dec.data.begin() + 1, dec.data.end())) {
|
||||||
if (version == 0) {
|
if (version == 0) {
|
||||||
{
|
{
|
||||||
WitnessV0KeyHash keyid;
|
WitnessV0KeyHash keyid;
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
BOOST_FIXTURE_TEST_SUITE(bech32_tests, BasicTestingSetup)
|
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[] = {
|
static const std::string CASES[] = {
|
||||||
"A12UEL5L",
|
"A12UEL5L",
|
||||||
|
@ -22,15 +22,35 @@ BOOST_AUTO_TEST_CASE(bip173_testvectors_valid)
|
||||||
"?1ezyfcl",
|
"?1ezyfcl",
|
||||||
};
|
};
|
||||||
for (const std::string& str : CASES) {
|
for (const std::string& str : CASES) {
|
||||||
auto ret = bech32::Decode(str);
|
const auto dec = bech32::Decode(str);
|
||||||
BOOST_CHECK(!ret.first.empty());
|
BOOST_CHECK(dec.encoding == bech32::Encoding::BECH32);
|
||||||
std::string recode = bech32::Encode(ret.first, ret.second);
|
std::string recode = bech32::Encode(bech32::Encoding::BECH32, dec.hrp, dec.data);
|
||||||
BOOST_CHECK(!recode.empty());
|
BOOST_CHECK(!recode.empty());
|
||||||
BOOST_CHECK(CaseInsensitiveEqual(str, recode));
|
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[] = {
|
static const std::string CASES[] = {
|
||||||
" 1nwldj5",
|
" 1nwldj5",
|
||||||
|
@ -49,8 +69,32 @@ BOOST_AUTO_TEST_CASE(bip173_testvectors_invalid)
|
||||||
"A12uEL5L",
|
"A12uEL5L",
|
||||||
};
|
};
|
||||||
for (const std::string& str : CASES) {
|
for (const std::string& str : CASES) {
|
||||||
auto ret = bech32::Decode(str);
|
const auto dec = bech32::Decode(str);
|
||||||
BOOST_CHECK(ret.first.empty());
|
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"
|
"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)
|
FUZZ_TARGET(bech32)
|
||||||
{
|
{
|
||||||
const std::string random_string(buffer.begin(), buffer.end());
|
const std::string random_string(buffer.begin(), buffer.end());
|
||||||
const std::pair<std::string, std::vector<uint8_t>> r1 = bech32::Decode(random_string);
|
const auto r1 = bech32::Decode(random_string);
|
||||||
if (r1.first.empty()) {
|
if (r1.hrp.empty()) {
|
||||||
assert(r1.second.empty());
|
assert(r1.encoding == bech32::Encoding::INVALID);
|
||||||
|
assert(r1.data.empty());
|
||||||
} else {
|
} else {
|
||||||
const std::string& hrp = r1.first;
|
assert(r1.encoding != bech32::Encoding::INVALID);
|
||||||
const std::vector<uint8_t>& data = r1.second;
|
const std::string reencoded = bech32::Encode(r1.encoding, r1.hrp, r1.data);
|
||||||
const std::string reencoded = bech32::Encode(hrp, data);
|
|
||||||
assert(CaseInsensitiveEqual(random_string, reencoded));
|
assert(CaseInsensitiveEqual(random_string, reencoded));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<unsigned char> input;
|
std::vector<unsigned char> input;
|
||||||
ConvertBits<8, 5, true>([&](unsigned char c) { input.push_back(c); }, buffer.begin(), buffer.end());
|
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 (input.size() + 3 + 6 <= 90) {
|
||||||
if (r2.first.empty()) {
|
// If it's possible to encode input in Bech32(m) without exceeding the 90-character limit:
|
||||||
assert(r2.second.empty());
|
for (auto encoding : {bech32::Encoding::BECH32, bech32::Encoding::BECH32M}) {
|
||||||
} else {
|
const std::string encoded = bech32::Encode(encoding, "bc", input);
|
||||||
const std::string& hrp = r2.first;
|
assert(!encoded.empty());
|
||||||
const std::vector<uint8_t>& data = r2.second;
|
const auto r2 = bech32::Decode(encoded);
|
||||||
assert(hrp == "bc");
|
assert(r2.encoding == encoding);
|
||||||
assert(data == input);
|
assert(r2.hrp == "bc");
|
||||||
|
assert(r2.data == input);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,8 +12,12 @@ from test_framework.util import (
|
||||||
)
|
)
|
||||||
|
|
||||||
BECH32_VALID = 'bcrt1qtmp74ayg7p24uslctssvjm06q5phz4yrxucgnv'
|
BECH32_VALID = 'bcrt1qtmp74ayg7p24uslctssvjm06q5phz4yrxucgnv'
|
||||||
BECH32_INVALID_SIZE = 'bcrt1sqqpl9r5c'
|
BECH32_INVALID_BECH32 = 'bcrt1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqdmchcc'
|
||||||
BECH32_INVALID_PREFIX = 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4'
|
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_VALID = 'mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn'
|
||||||
BASE58_INVALID_PREFIX = '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem'
|
BASE58_INVALID_PREFIX = '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem'
|
||||||
|
@ -40,6 +44,18 @@ class InvalidAddressErrorMessageTest(BitcoinTestFramework):
|
||||||
assert not info['isvalid']
|
assert not info['isvalid']
|
||||||
assert_equal(info['error'], 'Invalid prefix for Bech32 address')
|
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)
|
info = node.validateaddress(BECH32_VALID)
|
||||||
assert info['isvalid']
|
assert info['isvalid']
|
||||||
assert 'error' not in info
|
assert 'error' not in info
|
||||||
|
|
|
@ -2,10 +2,18 @@
|
||||||
# Copyright (c) 2017 Pieter Wuille
|
# Copyright (c) 2017 Pieter Wuille
|
||||||
# Distributed under the MIT software license, see the accompanying
|
# Distributed under the MIT software license, see the accompanying
|
||||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
# 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
|
import unittest
|
||||||
|
from enum import Enum
|
||||||
|
|
||||||
CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"
|
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):
|
def bech32_polymod(values):
|
||||||
|
@ -27,38 +35,45 @@ def bech32_hrp_expand(hrp):
|
||||||
|
|
||||||
def bech32_verify_checksum(hrp, data):
|
def bech32_verify_checksum(hrp, data):
|
||||||
"""Verify a checksum given HRP and converted data characters."""
|
"""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(encoding, hrp, data):
|
||||||
def bech32_create_checksum(hrp, data):
|
|
||||||
"""Compute the checksum values given HRP and data."""
|
"""Compute the checksum values given HRP and data."""
|
||||||
values = bech32_hrp_expand(hrp) + 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)]
|
return [(polymod >> 5 * (5 - i)) & 31 for i in range(6)]
|
||||||
|
|
||||||
|
|
||||||
def bech32_encode(hrp, data):
|
def bech32_encode(encoding, hrp, data):
|
||||||
"""Compute a Bech32 string given HRP and data values."""
|
"""Compute a Bech32 or Bech32m string given HRP and data values."""
|
||||||
combined = data + bech32_create_checksum(hrp, data)
|
combined = data + bech32_create_checksum(encoding, hrp, data)
|
||||||
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
|
return hrp + '1' + ''.join([CHARSET[d] for d in combined])
|
||||||
|
|
||||||
|
|
||||||
def bech32_decode(bech):
|
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
|
if ((any(ord(x) < 33 or ord(x) > 126 for x in bech)) or
|
||||||
(bech.lower() != bech and bech.upper() != bech)):
|
(bech.lower() != bech and bech.upper() != bech)):
|
||||||
return (None, None)
|
return (None, None, None)
|
||||||
bech = bech.lower()
|
bech = bech.lower()
|
||||||
pos = bech.rfind('1')
|
pos = bech.rfind('1')
|
||||||
if pos < 1 or pos + 7 > len(bech) or len(bech) > 90:
|
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:]):
|
if not all(x in CHARSET for x in bech[pos+1:]):
|
||||||
return (None, None)
|
return (None, None, None)
|
||||||
hrp = bech[:pos]
|
hrp = bech[:pos]
|
||||||
data = [CHARSET.find(x) for x in bech[pos+1:]]
|
data = [CHARSET.find(x) for x in bech[pos+1:]]
|
||||||
if not bech32_verify_checksum(hrp, data):
|
encoding = bech32_verify_checksum(hrp, data)
|
||||||
return (None, None)
|
if encoding is None:
|
||||||
return (hrp, data[:-6])
|
return (None, None, None)
|
||||||
|
return (encoding, hrp, data[:-6])
|
||||||
|
|
||||||
|
|
||||||
def convertbits(data, frombits, tobits, pad=True):
|
def convertbits(data, frombits, tobits, pad=True):
|
||||||
|
@ -86,7 +101,7 @@ def convertbits(data, frombits, tobits, pad=True):
|
||||||
|
|
||||||
def decode_segwit_address(hrp, addr):
|
def decode_segwit_address(hrp, addr):
|
||||||
"""Decode a segwit address."""
|
"""Decode a segwit address."""
|
||||||
hrpgot, data = bech32_decode(addr)
|
encoding, hrpgot, data = bech32_decode(addr)
|
||||||
if hrpgot != hrp:
|
if hrpgot != hrp:
|
||||||
return (None, None)
|
return (None, None)
|
||||||
decoded = convertbits(data[1:], 5, 8, False)
|
decoded = convertbits(data[1:], 5, 8, False)
|
||||||
|
@ -96,12 +111,15 @@ def decode_segwit_address(hrp, addr):
|
||||||
return (None, None)
|
return (None, None)
|
||||||
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
|
if data[0] == 0 and len(decoded) != 20 and len(decoded) != 32:
|
||||||
return (None, None)
|
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)
|
return (data[0], decoded)
|
||||||
|
|
||||||
|
|
||||||
def encode_segwit_address(hrp, witver, witprog):
|
def encode_segwit_address(hrp, witver, witprog):
|
||||||
"""Encode a segwit address."""
|
"""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):
|
if decode_segwit_address(hrp, ret) == (None, None):
|
||||||
return None
|
return None
|
||||||
return ret
|
return ret
|
||||||
|
@ -119,3 +137,5 @@ class TestFrameworkScript(unittest.TestCase):
|
||||||
# P2WSH
|
# P2WSH
|
||||||
test_python_bech32('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj')
|
test_python_bech32('bcrt1qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xueyj')
|
||||||
test_python_bech32('bcrt1qft5p2uhsdcdc3l2ua4ap5qqfg4pjaqlp250x7us7a8qqhrxrxfsqseac85')
|
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)
|
node.createwallet(wallet_name='watch_only', disable_private_keys=True)
|
||||||
wallet_watch_only = node.get_wallet_rpc('watch_only')
|
wallet_watch_only = node.get_wallet_rpc('watch_only')
|
||||||
BECH32_VALID = {
|
BECH32_VALID = {
|
||||||
'✔️_VER15_PROG40': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqn2cjv3',
|
'✔️_VER15_PROG40': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqxkg7fn',
|
||||||
'✔️_VER16_PROG03': 'bcrt1sqqqqqjq8pdp',
|
'✔️_VER16_PROG03': 'bcrt1sqqqqq8uhdgr',
|
||||||
'✔️_VER16_PROB02': 'bcrt1sqqqqqjq8pv',
|
'✔️_VER16_PROB02': 'bcrt1sqqqq4wstyw',
|
||||||
}
|
}
|
||||||
BECH32_INVALID = {
|
BECH32_INVALID = {
|
||||||
'❌_VER15_PROG41': 'bcrt10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqzc7xyq',
|
'❌_VER15_PROG41': 'bcrt1sqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqajlxj8',
|
||||||
'❌_VER16_PROB01': 'bcrt1sqqpl9r5c',
|
'❌_VER16_PROB01': 'bcrt1sqq5r4036',
|
||||||
}
|
}
|
||||||
for l in BECH32_VALID:
|
for l in BECH32_VALID:
|
||||||
ad = BECH32_VALID[l]
|
ad = BECH32_VALID[l]
|
||||||
|
|
Loading…
Add table
Reference in a new issue