mirror of
https://github.com/ElementsProject/elements.git
synced 2026-08-13 12:33:42 +02:00
Merge ElementsProject/elements#1025: Introduce blech32m format for v1+ witness programs
c72c949d29blech32: copy ubsan suppression for bech32 to blech32 (Andrew Poelstra)d13fb4994bblech32: add test vectors for blech32 and blech32m (Andrew Poelstra)15a826ea27blech32: add functional tests for blech32m (Andrew Poelstra)c01e09e8c3blech32: add blech32m format and use it to decode witness v1+ addresses (Andrew Poelstra)18fcec8714naming nits (Fabian Jahr)8515f40fedAdd signet support to gen_key_io_test_vectors.py (Pieter Wuille)b3df66f82eUse Bech32m encoding for v1+ segwit addresses (Pieter Wuille)42f43a1bcbAdd Bech32m test vectors (Pieter Wuille)b1d1d94e01Implement Bech32m encoding/decoding (Pieter Wuille)c607835badBetter error messages for invalid addresses (Bezdrighin) Pull request description: Includes backports of https://github.com/bitcoin/bitcoin/pull/20832 (1 commit) and https://github.com/bitcoin/bitcoin/pull/20861 (5 commits) ACKs for top commit: gwillen: utACKc72c949. Tree-SHA512: af96a6ef31b1cab72b0350197dcb34761e9ffb2ec43685084408b5fafcda0adee1945045003194600372652f7cca65d721bda3ed9b6be7e9543d3199e2cbe145
This commit is contained in:
commit
64194498cf
24 changed files with 1327 additions and 738 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)).
|
||||
|
|
@ -46,3 +46,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.
|
||||
|
||||
|
|
@ -12,10 +12,10 @@ namespace bech32
|
|||
|
||||
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 +27,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 +117,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.
|
||||
|
|
@ -134,13 +143,13 @@ data CreateChecksum(const std::string& hrp, const data& values)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/** 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());
|
||||
|
|
@ -150,8 +159,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];
|
||||
|
|
@ -178,10 +187,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);
|
||||
|
||||
/// Exported for testing.
|
||||
uint32_t PolyMod(const std::vector<uint8_t>& v);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
// 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.
|
||||
|
||||
#include <blech32.h>
|
||||
|
||||
#include <assert.h>
|
||||
/*
|
||||
* IMPORTANT NOTE: Comments below may largely pertain for bech32, not blech32.
|
||||
* Some of these magic constants have changes.
|
||||
|
|
@ -16,10 +17,10 @@ namespace blech32
|
|||
|
||||
typedef std::vector<uint8_t> data;
|
||||
|
||||
/** The Blech32 character set for encoding. */
|
||||
/** The Blech32 and Blech32m character set for encoding. */
|
||||
const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
||||
|
||||
/** The Blech32 character set for decoding. */
|
||||
/** The Blech32 and Blech32m 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,
|
||||
|
|
@ -31,6 +32,23 @@ const int8_t CHARSET_REV[128] = {
|
|||
1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1
|
||||
};
|
||||
|
||||
// ELEMENTS: this encoding constant is a random 60-byte number generated
|
||||
// in sage. For bech32m, Pieter did a fair bit of grinding to choose
|
||||
// an optimal constant. For blech32m we did no such grinding ... but
|
||||
// having said this, our only hard design constraint is that we need to
|
||||
// avoid 1, which has special algebraic structure that leads to extension
|
||||
// attacks.
|
||||
//
|
||||
// Further, because blech32m is a longer code than bech32m, doing an
|
||||
// analogous grinding project would be intractable, even if we were
|
||||
// willing to put the time and effort in.
|
||||
|
||||
/* Determine the final constant to use for the specified encoding. */
|
||||
uint64_t EncodingConstant(Encoding encoding) {
|
||||
assert(encoding == Encoding::BLECH32 || encoding == Encoding::BLECH32M);
|
||||
return encoding == Encoding::BLECH32 ? 1 : 0x455972a3350f7a1;
|
||||
}
|
||||
|
||||
/** Concatenate two byte arrays. */
|
||||
data Cat(data x, const data& y)
|
||||
{
|
||||
|
|
@ -65,7 +83,7 @@ uint64_t PolyMod(const data& v)
|
|||
|
||||
// During the course of the loop below, `c` contains the bitpacked coefficients of the
|
||||
// polynomial constructed from just the values of v that were processed so far, mod g(x). In
|
||||
// the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of
|
||||
// the above example, `c` initially corresponds to 1 mod g(x), and after processing 2 inputs of
|
||||
// v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value
|
||||
// for `c`.
|
||||
uint64_t c = 1;
|
||||
|
|
@ -122,21 +140,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, Blech32 requires the
|
||||
// resulting checksum to be 1 instead.
|
||||
return PolyMod(Cat(ExpandHRP(hrp), values)) == 1;
|
||||
// resulting checksum to be 1 instead. In Blech32m, this constant was amended.
|
||||
const uint64_t check = PolyMod(Cat(ExpandHRP(hrp), values));
|
||||
if (check == EncodingConstant(Encoding::BLECH32)) return Encoding::BLECH32;
|
||||
if (check == EncodingConstant(Encoding::BLECH32M)) return Encoding::BLECH32M;
|
||||
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() + 12); // ELEMENTS: Append 6->12 zeroes
|
||||
uint64_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes.
|
||||
uint64_t mod = PolyMod(enc) ^ EncodingConstant(encoding); // Determine what to XOR into those 6 zeroes.
|
||||
data ret(12); // ELEMENTS: 6->12
|
||||
for (size_t i = 0; i < 12; ++i) { // ELEMENTS: 6->12
|
||||
// Convert the 5-bit groups in mod to checksum values.
|
||||
|
|
@ -145,9 +166,13 @@ data CreateChecksum(const std::string& hrp, const data& values)
|
|||
return ret;
|
||||
}
|
||||
|
||||
/** Encode a Blech32 string. */
|
||||
std::string Encode(const std::string& hrp, const data& values) {
|
||||
data checksum = CreateChecksum(hrp, values);
|
||||
/** Encode a Blech32 or Blech32m 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 Blech32/Blech32m 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(encoding, hrp, values);
|
||||
data combined = Cat(values, checksum);
|
||||
std::string ret = hrp + '1';
|
||||
ret.reserve(ret.size() + combined.size());
|
||||
|
|
@ -157,8 +182,8 @@ std::string Encode(const std::string& hrp, const data& values) {
|
|||
return ret;
|
||||
}
|
||||
|
||||
/** Decode a Blech32 string. */
|
||||
std::pair<std::string, data> Decode(const std::string& str) {
|
||||
/** Decode a Blech32 or Blech32m 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];
|
||||
|
|
@ -185,10 +210,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() - 12)};
|
||||
Encoding result = VerifyChecksum(hrp, values);
|
||||
if (result == Encoding::INVALID) return {};
|
||||
return {result, std::move(hrp), data(values.begin(), values.end() - 12)};
|
||||
}
|
||||
|
||||
} // namespace blech32
|
||||
|
|
|
|||
|
|
@ -2,12 +2,13 @@
|
|||
// 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_BLECH32_H
|
||||
#define BITCOIN_BLECH32_H
|
||||
|
|
@ -19,16 +20,34 @@
|
|||
namespace blech32
|
||||
{
|
||||
|
||||
/** Encode a Bech32 string. Returns the empty string in case of failure. */
|
||||
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);
|
||||
BLECH32, //!< Original blech32
|
||||
BLECH32M, //!< Blech32m tweaked a la bech32
|
||||
};
|
||||
|
||||
/** Encode a Blech32 or Blech32m string. If hrp contains uppercase characters, this will cause an
|
||||
* assertion error. Encoding must be one of BLECH32 or BLECH32M. */
|
||||
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 Blech32 or Blech32m string */
|
||||
DecodeResult Decode(const std::string& str);
|
||||
|
||||
/// Exported for testing.
|
||||
uint64_t PolyMod(const std::vector<uint8_t>& v);
|
||||
/// Exported for testing.
|
||||
std::vector<uint8_t> CreateChecksum(const std::string& hrp, const std::vector<uint8_t>& values);
|
||||
std::vector<uint8_t> CreateChecksum(Encoding encoding, const std::string& hrp, const std::vector<uint8_t>& values);
|
||||
|
||||
} // namespace blech32
|
||||
|
||||
|
|
|
|||
118
src/key_io.cpp
118
src/key_io.cpp
|
|
@ -17,6 +17,11 @@
|
|||
#include <string.h>
|
||||
#include <algorithm>
|
||||
|
||||
/// Maximum witness length for Bech32 addresses.
|
||||
static constexpr std::size_t BECH32_WITNESS_PROG_MAX_LEN = 40;
|
||||
/// Maximum witness length for Blech32 addresses. Note this does not include the extra 33-byte blinding key.
|
||||
static constexpr std::size_t BLECH32_WITNESS_PROG_MAX_LEN = 40;
|
||||
|
||||
namespace
|
||||
{
|
||||
class DestinationEncoder : public boost::static_visitor<std::string>
|
||||
|
|
@ -74,12 +79,12 @@ public:
|
|||
bytes.insert(bytes.end(), id.begin(), id.end());
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, bytes.begin(), bytes.end());
|
||||
const std::string& hrp = for_parent ? m_params.ParentBlech32HRP() : m_params.Blech32HRP();
|
||||
return blech32::Encode(hrp, data);
|
||||
return blech32::Encode(blech32::Encoding::BLECH32, hrp, data);
|
||||
}
|
||||
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
|
||||
const std::string& hrp = for_parent ? m_params.ParentBech32HRP() : m_params.Bech32HRP();
|
||||
return bech32::Encode(hrp, data);
|
||||
return bech32::Encode(bech32::Encoding::BECH32, hrp, data);
|
||||
}
|
||||
|
||||
std::string operator()(const WitnessV0ScriptHash& id) const
|
||||
|
|
@ -91,12 +96,12 @@ public:
|
|||
bytes.insert(bytes.end(), id.begin(), id.end());
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, bytes.begin(), bytes.end());
|
||||
const std::string& hrp = for_parent ? m_params.ParentBlech32HRP() : m_params.Blech32HRP();
|
||||
return blech32::Encode(hrp, data);
|
||||
return blech32::Encode(blech32::Encoding::BLECH32, hrp, data);
|
||||
}
|
||||
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.begin(), id.end());
|
||||
const std::string& hrp = for_parent ? m_params.ParentBech32HRP() : m_params.Bech32HRP();
|
||||
return bech32::Encode(hrp, data);
|
||||
return bech32::Encode(bech32::Encoding::BECH32, hrp, data);
|
||||
}
|
||||
|
||||
std::string operator()(const WitnessUnknown& id) const
|
||||
|
|
@ -111,23 +116,24 @@ public:
|
|||
bytes.insert(bytes.end(), id.program, id.program + id.length);
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, bytes.begin(), bytes.end());
|
||||
const std::string& hrp = for_parent ? m_params.ParentBlech32HRP() : m_params.Blech32HRP();
|
||||
return blech32::Encode(hrp, data);
|
||||
return blech32::Encode(blech32::Encoding::BLECH32M, hrp, data);
|
||||
}
|
||||
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { data.push_back(c); }, id.program, id.program + id.length);
|
||||
const std::string& hrp = for_parent ? m_params.ParentBech32HRP() : m_params.Bech32HRP();
|
||||
return bech32::Encode(hrp, data);
|
||||
return bech32::Encode(bech32::Encoding::BECH32M, hrp, data);
|
||||
}
|
||||
|
||||
std::string operator()(const CNoDestination& no) const { return {}; }
|
||||
std::string operator()(const NullData& null) const { return {}; }
|
||||
};
|
||||
|
||||
CTxDestination DecodeDestination(const std::string& str, const CChainParams& params, const bool for_parent)
|
||||
CTxDestination DecodeDestination(const std::string& str, const CChainParams& params, const bool for_parent, std::string& error_str)
|
||||
{
|
||||
std::vector<unsigned char> data;
|
||||
size_t pk_size = CPubKey::COMPRESSED_SIZE;
|
||||
uint160 hash;
|
||||
error_str = "";
|
||||
if (DecodeBase58Check(str, data, 55)) {
|
||||
// base58-encoded Bitcoin addresses.
|
||||
// Public-key-hash-addresses have version 0 (or 111 testnet).
|
||||
|
|
@ -167,16 +173,35 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
|
|||
std::copy(payload_start + pk_size, data.end(), hash.begin());
|
||||
return ScriptHash(CScriptID(hash), pubkey);
|
||||
}
|
||||
|
||||
// Set potential error message.
|
||||
// This message may be changed if the address can also be interpreted as a Bech32 address.
|
||||
error_str = "Invalid prefix for Base58-encoded address";
|
||||
}
|
||||
data.clear();
|
||||
auto bech = bech32::Decode(str);
|
||||
const auto dec = bech32::Decode(str);
|
||||
const std::string& hrp = for_parent ? params.ParentBech32HRP() : params.Bech32HRP();
|
||||
if (bech.second.size() > 0 && bech.first == hrp) {
|
||||
if ((dec.encoding == bech32::Encoding::BECH32 || dec.encoding == bech32::Encoding::BECH32M) && dec.data.size() > 0) {
|
||||
// Bech32 decoding
|
||||
int version = bech.second[0]; // The first 5 bit symbol is the witness version (0-16)
|
||||
error_str = "";
|
||||
|
||||
if (dec.hrp != hrp) {
|
||||
error_str = "Invalid prefix for Bech32 address";
|
||||
return CNoDestination();
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
@ -192,11 +217,21 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
|
|||
return scriptid;
|
||||
}
|
||||
}
|
||||
|
||||
error_str = "Invalid Bech32 v0 address data size";
|
||||
return CNoDestination();
|
||||
}
|
||||
if (version > 16 || data.size() < 2 || data.size() > 40) {
|
||||
|
||||
if (version > 16) {
|
||||
error_str = "Invalid Bech32 address witness version";
|
||||
return CNoDestination();
|
||||
}
|
||||
|
||||
if (data.size() < 2 || data.size() > BECH32_WITNESS_PROG_MAX_LEN) {
|
||||
error_str = "Invalid Bech32 address data size";
|
||||
return CNoDestination();
|
||||
}
|
||||
|
||||
WitnessUnknown unk;
|
||||
unk.version = version;
|
||||
std::copy(data.begin(), data.end(), unk.program);
|
||||
|
|
@ -208,14 +243,29 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
|
|||
data.clear();
|
||||
auto blech = blech32::Decode(str);
|
||||
const std::string& bl_hrp = for_parent ? params.ParentBlech32HRP() : params.Blech32HRP();
|
||||
if (blech.second.size() > 0 && blech.first == bl_hrp) {
|
||||
if ((blech.encoding == blech32::Encoding::BLECH32 || blech.encoding == blech32::Encoding::BLECH32M) && blech.data.size() > 0) {
|
||||
// Blech32 decoding
|
||||
int version = blech.second[0]; // The first 5 bit symbol is the witness version (0-16)
|
||||
error_str = "";
|
||||
|
||||
data.reserve(((blech.second.size() - 1) * 5) / 8);
|
||||
if (blech.hrp != bl_hrp) {
|
||||
error_str = "Invalid prefix for Blech32 address";
|
||||
return CNoDestination();
|
||||
}
|
||||
|
||||
int version = blech.data[0]; // The first 5 bit symbol is the witness version (0-16)
|
||||
if (version == 0 && blech.encoding != blech32::Encoding::BLECH32) {
|
||||
error_str = "Version 0 witness address must use Blech32 checksum";
|
||||
return CNoDestination();
|
||||
}
|
||||
if (version != 0 && blech.encoding != blech32::Encoding::BLECH32M) {
|
||||
error_str = "Version 1+ witness address must use Blech32m checksum";
|
||||
return CNoDestination();
|
||||
}
|
||||
|
||||
data.reserve(((blech.data.size() - 1) * 5) / 8);
|
||||
|
||||
// The rest of the symbols are converted blinding pubkey and witness program bytes.
|
||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, blech.second.begin() + 1, blech.second.end())) {
|
||||
if (ConvertBits<5, 8, false>([&](unsigned char c) { data.push_back(c); }, blech.data.begin() + 1, blech.data.end())) {
|
||||
// Must be long enough for blinding key and other data taken below
|
||||
if (data.size() < 34) {
|
||||
return CNoDestination();
|
||||
|
|
@ -240,11 +290,21 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
|
|||
return scriptid;
|
||||
}
|
||||
}
|
||||
|
||||
error_str = "Invalid Blech32 v0 address data size";
|
||||
return CNoDestination();
|
||||
}
|
||||
if (version > 16 || data.size() < 2 || data.size() > 40) {
|
||||
|
||||
if (version > 16) {
|
||||
error_str = "Invalid Blech32 address witness version";
|
||||
return CNoDestination();
|
||||
}
|
||||
|
||||
if (data.size() < 2 || data.size() > BLECH32_WITNESS_PROG_MAX_LEN) {
|
||||
error_str = "Invalid Blech32 address data size";
|
||||
return CNoDestination();
|
||||
}
|
||||
|
||||
WitnessUnknown unk;
|
||||
unk.version = version;
|
||||
std::copy(data.begin(), data.end(), unk.program);
|
||||
|
|
@ -254,6 +314,9 @@ CTxDestination DecodeDestination(const std::string& str, const CChainParams& par
|
|||
}
|
||||
}
|
||||
|
||||
// Set error message if address can't be interpreted as Base58 or Bech32 or Blech32.
|
||||
if (error_str.empty()) error_str = "Invalid address format";
|
||||
|
||||
return CNoDestination();
|
||||
}
|
||||
} // namespace
|
||||
|
|
@ -341,32 +404,41 @@ std::string EncodeDestination(const CTxDestination& dest)
|
|||
return boost::apply_visitor(DestinationEncoder(Params(), false), dest);
|
||||
}
|
||||
|
||||
CTxDestination DecodeDestination(const std::string& str, std::string& error_msg)
|
||||
{
|
||||
return DecodeDestination(str, Params(), false, error_msg);
|
||||
}
|
||||
|
||||
CTxDestination DecodeDestination(const std::string& str)
|
||||
{
|
||||
return DecodeDestination(str, Params(), false);
|
||||
std::string error_msg;
|
||||
return DecodeDestination(str, Params(), false, error_msg);
|
||||
}
|
||||
|
||||
bool IsValidDestinationString(const std::string& str, const CChainParams& params)
|
||||
{
|
||||
return IsValidDestination(DecodeDestination(str, params, false));
|
||||
std::string error_msg;
|
||||
return IsValidDestination(DecodeDestination(str, params, false, error_msg));
|
||||
}
|
||||
|
||||
bool IsValidDestinationString(const std::string& str)
|
||||
{
|
||||
return IsValidDestination(DecodeDestination(str, Params(), false));
|
||||
std::string error_msg;
|
||||
return IsValidDestination(DecodeDestination(str, Params(), false, error_msg));
|
||||
}
|
||||
|
||||
//
|
||||
// ELEMENTS
|
||||
|
||||
|
||||
std::string EncodeParentDestination(const CTxDestination& dest)
|
||||
{
|
||||
return boost::apply_visitor(DestinationEncoder(Params(), true), dest);
|
||||
}
|
||||
|
||||
CTxDestination DecodeParentDestination(const std::string& str)
|
||||
CTxDestination DecodeParentDestination(const std::string& str, std::string& error_msg)
|
||||
{
|
||||
return DecodeDestination(str, Params(), true);
|
||||
return DecodeDestination(str, Params(), true, error_msg);
|
||||
}
|
||||
|
||||
// ELEMENTS
|
||||
|
|
|
|||
|
|
@ -23,11 +23,12 @@ std::string EncodeExtPubKey(const CExtPubKey& extpubkey);
|
|||
|
||||
std::string EncodeDestination(const CTxDestination& dest);
|
||||
CTxDestination DecodeDestination(const std::string& str);
|
||||
CTxDestination DecodeDestination(const std::string& str, std::string& error_msg);
|
||||
bool IsValidDestinationString(const std::string& str);
|
||||
bool IsValidDestinationString(const std::string& str, const CChainParams& params);
|
||||
|
||||
// ELEMENTS
|
||||
std::string EncodeParentDestination(const CTxDestination& dest);
|
||||
CTxDestination DecodeParentDestination(const std::string& str);
|
||||
CTxDestination DecodeParentDestination(const std::string& str, std::string& error_msg);
|
||||
|
||||
#endif // BITCOIN_KEY_IO_H
|
||||
|
|
|
|||
|
|
@ -43,9 +43,9 @@ static RPCHelpMan validateaddress()
|
|||
RPCResult{
|
||||
RPCResult::Type::OBJ, "", "",
|
||||
{
|
||||
{RPCResult::Type::BOOL, "isvalid", "If the address is valid or not. If not, this is the only property returned."},
|
||||
{RPCResult::Type::BOOL, "isvalid_parent", "If the address is valid or not for parent chain. Valid or not, no additional details will be appended unlike `isvalid`"},
|
||||
{RPCResult::Type::STR, "address", "The address validated"},
|
||||
{RPCResult::Type::BOOL, "isvalid", "If the address is valid or not"},
|
||||
{RPCResult::Type::BOOL, "isvalid_parent", "If the address is valid or not for parent chain"},
|
||||
{RPCResult::Type::STR, "address", "The bitcoin address validated"},
|
||||
{RPCResult::Type::STR_HEX, "scriptPubKey", "The hex-encoded scriptPubKey generated by the address"},
|
||||
{RPCResult::Type::BOOL, "isscript", "If the key is a script"},
|
||||
{RPCResult::Type::BOOL, "iswitness", "If the address is a witness address"},
|
||||
|
|
@ -58,6 +58,8 @@ static RPCHelpMan validateaddress()
|
|||
{RPCResult::Type::STR, "address", ""},
|
||||
{RPCResult::Type::STR_HEX, "scriptPubKey", ""},
|
||||
}},
|
||||
{RPCResult::Type::STR, "error", /* optional */ true, "Error message, if any"},
|
||||
{RPCResult::Type::STR, "error_parent", /* optional */ true, "Error message, if any"},
|
||||
}
|
||||
},
|
||||
RPCExamples{
|
||||
|
|
@ -66,15 +68,19 @@ static RPCHelpMan validateaddress()
|
|||
},
|
||||
[&](const RPCHelpMan& self, const JSONRPCRequest& request) -> UniValue
|
||||
{
|
||||
CTxDestination dest = DecodeDestination(request.params[0].get_str());
|
||||
CTxDestination parent_dest = DecodeParentDestination(request.params[0].get_str());
|
||||
std::string error_msg;
|
||||
CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
|
||||
std::string error_msg_parent;
|
||||
CTxDestination parent_dest = DecodeParentDestination(request.params[0].get_str(), error_msg_parent);
|
||||
bool isValid = IsValidDestination(dest);
|
||||
bool is_valid_parent = IsValidDestination(parent_dest);
|
||||
CHECK_NONFATAL(isValid == error_msg.empty());
|
||||
CHECK_NONFATAL(is_valid_parent == error_msg_parent.empty());
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
ret.pushKV("isvalid", isValid);
|
||||
ret.pushKV("isvalid_parent", is_valid_parent);
|
||||
if (isValid)
|
||||
{
|
||||
if (isValid) {
|
||||
std::string currentAddress = EncodeDestination(dest);
|
||||
ret.pushKV("address", currentAddress);
|
||||
|
||||
|
|
@ -100,6 +106,11 @@ static RPCHelpMan validateaddress()
|
|||
parent_info.pushKVs(blind_detail);
|
||||
ret.pushKV("parent_address_info", parent_info);
|
||||
}
|
||||
if (!isValid && !is_valid_parent) {
|
||||
ret.pushKV("error", error_msg);
|
||||
ret.pushKV("error_parent", error_msg_parent);
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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,11 +6,100 @@
|
|||
#include <random.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <test/util/setup_common.h>
|
||||
#include <test/util/str.h>
|
||||
|
||||
#include <boost/test/unit_test.hpp>
|
||||
|
||||
BOOST_FIXTURE_TEST_SUITE(blech32_tests, BasicTestingSetup)
|
||||
|
||||
BOOST_AUTO_TEST_CASE(blech32_testvectors_valid)
|
||||
{
|
||||
static const std::string CASES[] = {
|
||||
"A133NZFWEYK7UT",
|
||||
"a133nzfweyk7ut",
|
||||
"an83characterlonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio195jhgldwsn5j",
|
||||
"abcdef1qpzry9x8gf2tvdw0s3jn54khce6mua7lgmcn7l7t7xve",
|
||||
"11qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq3xldwlutcw2l",
|
||||
"split1checkupstagehandshakeupstreamerranterredcaperredegneyqml9esp",
|
||||
"?19dv34t3p4s35",
|
||||
};
|
||||
for (const std::string& str : CASES) {
|
||||
const auto dec = blech32::Decode(str);
|
||||
BOOST_CHECK(dec.encoding == blech32::Encoding::BLECH32);
|
||||
std::string recode = blech32::Encode(blech32::Encoding::BLECH32, dec.hrp, dec.data);
|
||||
BOOST_CHECK(!recode.empty());
|
||||
BOOST_CHECK(CaseInsensitiveEqual(str, recode));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(blech32m_testvectors_valid)
|
||||
{
|
||||
static const std::string CASES[] = {
|
||||
"A1EYL4VXQ3HRPT",
|
||||
"a1eyl4vxq3hrpt",
|
||||
"an83characterlonghumanreadablepartthatcontainsthetheexcludedcharactersbioandnumber11yatn6l85muud",
|
||||
"abcdef1l7aum6echk45nj3s0wdvt2fg8x9yrzpqg8m5pqg67zq3",
|
||||
"11llllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllllll09wxh8ajdvxv",
|
||||
"split1checkupstagehandshakeupstreamerranterredcaperred3alwpgz2yydp",
|
||||
"?1dcqxsrg55dv5"
|
||||
};
|
||||
for (const std::string& str : CASES) {
|
||||
const auto dec = blech32::Decode(str);
|
||||
BOOST_CHECK(dec.encoding == blech32::Encoding::BLECH32M);
|
||||
std::string recode = blech32::Encode(blech32::Encoding::BLECH32M, dec.hrp, dec.data);
|
||||
BOOST_CHECK(!recode.empty());
|
||||
BOOST_CHECK(CaseInsensitiveEqual(str, recode));
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(blech32m_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 = blech32::Decode(str);
|
||||
BOOST_CHECK(dec.encoding == blech32::Encoding::INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(blech32_testvectors_invalid)
|
||||
{
|
||||
static const std::string CASES[] = {
|
||||
" 1nwldj5",
|
||||
"\x7f""1axkwrx",
|
||||
"\x80""1eym55h",
|
||||
"an84characterslonghumanreadablepartthatcontainsthenumber1andtheexcludedcharactersbio1569pvx",
|
||||
"pzry9x0s0muk",
|
||||
"1pzry9x0s0muk",
|
||||
"x1b4n0q5v",
|
||||
"li1dgmt3",
|
||||
"de1lg7wt\xff",
|
||||
"A1G7SGD8",
|
||||
"10a06t8",
|
||||
"1qzzfhee",
|
||||
"a12UEL5L",
|
||||
"A12uEL5L",
|
||||
};
|
||||
for (const std::string& str : CASES) {
|
||||
const auto dec = blech32::Decode(str);
|
||||
BOOST_CHECK(dec.encoding == blech32::Encoding::INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(blech32_polymod_sanity)
|
||||
{
|
||||
std::vector<unsigned char> data(40);
|
||||
|
|
@ -36,7 +125,7 @@ BOOST_AUTO_TEST_CASE(blech32_checksum)
|
|||
std::vector<unsigned char> vector{7,2,3,4,5,6,7,8,9,234,123,213,16};
|
||||
std::vector<unsigned char> b32;
|
||||
ConvertBits<8, 5, true>([&](unsigned char c) { b32.push_back(c); }, vector.begin(), vector.end());
|
||||
std::vector<unsigned char> cs = blech32::CreateChecksum("lq", b32);
|
||||
std::vector<unsigned char> cs = blech32::CreateChecksum(blech32::Encoding::BLECH32, "lq", b32);
|
||||
|
||||
std::vector<unsigned char> expected_cs{22,13,13,5,4,4,23,7,28,21,30,12};
|
||||
for (size_t i = 0; i < expected_cs.size(); i++) {
|
||||
|
|
|
|||
|
|
@ -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 @@
|
|||
void test_one_input(const std::vector<uint8_t>& buffer)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4222,13 +4222,19 @@ RPCHelpMan getaddressinfo()
|
|||
|
||||
LOCK(pwallet->cs_wallet);
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
CTxDestination dest = DecodeDestination(request.params[0].get_str());
|
||||
std::string error_msg;
|
||||
CTxDestination dest = DecodeDestination(request.params[0].get_str(), error_msg);
|
||||
|
||||
// Make sure the destination is valid
|
||||
if (!IsValidDestination(dest)) {
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid address");
|
||||
// Set generic error message in case 'DecodeDestination' didn't set it
|
||||
if (error_msg.empty()) error_msg = "Invalid address";
|
||||
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, error_msg);
|
||||
}
|
||||
|
||||
UniValue ret(UniValue::VOBJ);
|
||||
|
||||
std::string currentAddress = EncodeDestination(dest);
|
||||
ret.pushKV("address", currentAddress);
|
||||
|
||||
|
|
@ -5492,9 +5498,10 @@ static RPCHelpMan sendtomainchain_base()
|
|||
|
||||
EnsureWalletIsUnlocked(pwallet);
|
||||
|
||||
CTxDestination parent_address = DecodeParentDestination(request.params[0].get_str());
|
||||
std::string error_str;
|
||||
CTxDestination parent_address = DecodeParentDestination(request.params[0].get_str(), error_str);
|
||||
if (!IsValidDestination(parent_address))
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Invalid Bitcoin address");
|
||||
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Invalid Bitcoin address: %s", error_str));
|
||||
|
||||
CAmount nAmount = AmountFromValue(request.params[1]);
|
||||
if (nAmount <= 0)
|
||||
|
|
|
|||
130
test/functional/rpc_invalid_address_message.py
Executable file
130
test/functional/rpc_invalid_address_message.py
Executable file
|
|
@ -0,0 +1,130 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2020 The Bitcoin Core developers
|
||||
# Distributed under the MIT software license, see the accompanying
|
||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||
"""Test error messages for 'getaddressinfo' and 'validateaddress' RPC commands."""
|
||||
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
assert_raises_rpc_error,
|
||||
)
|
||||
|
||||
BECH32_VALID = 'ert1qtmp74ayg7p24uslctssvjm06q5phz4yr7gdkdv'
|
||||
BECH32_INVALID_BECH32 = 'ert1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqugsf3u'
|
||||
BECH32_INVALID_BECH32M = 'ert1qw508d6qejxtdg4y5r3zarvary0c5xw7kfqwaud'
|
||||
BECH32_INVALID_VERSION = 'ert130xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq4q68pj'
|
||||
BECH32_INVALID_SIZE = 'ert1s0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav25pltc58'
|
||||
BECH32_INVALID_V0_SIZE = 'ert1qw508d6qejxtdg4y5r3zarvary0c5xw7kqq2287l0'
|
||||
BECH32_INVALID_PREFIX = 'bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'
|
||||
|
||||
BASE58_VALID = '2dcjQH4DQC3pMcSQkMkSQyPPEr7rZ6Ga4GR'
|
||||
BASE58_INVALID_PREFIX = '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem'
|
||||
|
||||
INVALID_ADDRESS = 'asfah14i8fajz0123f'
|
||||
|
||||
## ELEMENTS
|
||||
BLECH32_VALID = 'el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsmxh4xcjh2rse'
|
||||
BLECH32_INVALID_BLECH32 = 'el1pq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsxguu9nrdg2pc'
|
||||
BLECH32_INVALID_BLECH32M = 'el1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsnnmzrstzt7de'
|
||||
BLECH32_INVALID_VERSION = 'ert130xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vq4q68pj'
|
||||
BLECH32_INVALID_SIZE = 'el1pq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfsqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqpe9jfn0gypaj'
|
||||
BLECH32_INVALID_V0_SIZE = 'ert1qw508d6qejxtdg4y5r3zarvary0c5xw7kqq2287l0'
|
||||
BLECH32_INVALID_PREFIX = 'lq1qq0umk3pez693jrrlxz9ndlkuwne93gdu9g83mhhzuyf46e3mdzfpva0w48gqgzgrklncnm0k5zeyw8my2ypfscmm3q74jvv3r'
|
||||
|
||||
|
||||
class InvalidAddressErrorMessageTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
|
||||
def skip_test_if_missing_module(self):
|
||||
self.skip_if_no_wallet()
|
||||
|
||||
def test_validateaddress(self):
|
||||
node = self.nodes[0]
|
||||
|
||||
# Bech32
|
||||
info = node.validateaddress(BECH32_INVALID_SIZE)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Invalid Bech32 address data size')
|
||||
|
||||
info = node.validateaddress(BECH32_INVALID_PREFIX)
|
||||
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
|
||||
|
||||
# Base58
|
||||
info = node.validateaddress(BASE58_INVALID_PREFIX)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Invalid prefix for Base58-encoded address')
|
||||
|
||||
info = node.validateaddress(BASE58_VALID)
|
||||
assert info['isvalid']
|
||||
assert 'error' not in info
|
||||
|
||||
# Invalid address format
|
||||
info = node.validateaddress(INVALID_ADDRESS)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Invalid address format')
|
||||
|
||||
## ELEMENTS
|
||||
info = node.validateaddress(BLECH32_INVALID_SIZE)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Invalid Blech32 address data size')
|
||||
|
||||
info = node.validateaddress(BLECH32_INVALID_PREFIX)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Invalid prefix for Blech32 address')
|
||||
|
||||
info = node.validateaddress(BLECH32_INVALID_BLECH32)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Version 1+ witness address must use Blech32m checksum')
|
||||
|
||||
info = node.validateaddress(BLECH32_INVALID_BLECH32M)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Version 0 witness address must use Blech32 checksum')
|
||||
|
||||
info = node.validateaddress(BLECH32_VALID)
|
||||
assert info['isvalid']
|
||||
assert 'error' not in info
|
||||
|
||||
def test_getaddressinfo(self):
|
||||
node = self.nodes[0]
|
||||
|
||||
assert_raises_rpc_error(-5, "Invalid Bech32 address data size", node.getaddressinfo, BECH32_INVALID_SIZE)
|
||||
|
||||
assert_raises_rpc_error(-5, "Invalid prefix for Bech32 address", node.getaddressinfo, BECH32_INVALID_PREFIX)
|
||||
|
||||
assert_raises_rpc_error(-5, "Invalid prefix for Base58-encoded address", node.getaddressinfo, BASE58_INVALID_PREFIX)
|
||||
|
||||
assert_raises_rpc_error(-5, "Invalid address format", node.getaddressinfo, INVALID_ADDRESS)
|
||||
|
||||
## ELEMENTS
|
||||
assert_raises_rpc_error(-5, "Invalid Blech32 address data size", node.getaddressinfo, BLECH32_INVALID_SIZE)
|
||||
|
||||
assert_raises_rpc_error(-5, "Invalid prefix for Blech32 address", node.getaddressinfo, BLECH32_INVALID_PREFIX)
|
||||
|
||||
def run_test(self):
|
||||
self.test_validateaddress()
|
||||
self.test_getaddressinfo()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
InvalidAddressErrorMessageTest().main()
|
||||
|
|
@ -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')
|
||||
|
|
|
|||
|
|
@ -159,6 +159,7 @@ BASE_SCRIPTS = [
|
|||
'wallet_keypool_topup.py',
|
||||
'wallet_keypool_topup.py --descriptors',
|
||||
'interface_zmq.py',
|
||||
'rpc_invalid_address_message.py',
|
||||
'interface_bitcoin_cli.py',
|
||||
'mempool_resurrect.py',
|
||||
'wallet_txn_doublespend.py --mineblock',
|
||||
|
|
|
|||
|
|
@ -567,7 +567,7 @@ class WalletTest(BitcoinTestFramework):
|
|||
assert_equal(total_txs, len(self.nodes[0].listtransactions("*", 99999)))
|
||||
|
||||
# Test getaddressinfo on external address. Note that these addresses are taken from disablewallet.py
|
||||
assert_raises_rpc_error(-5, "Invalid address", self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy")
|
||||
assert_raises_rpc_error(-5, "Invalid prefix for Base58-encoded address", self.nodes[0].getaddressinfo, "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy")
|
||||
address_info = self.nodes[0].getaddressinfo("CTEuA2rzWUDTGt6jFwmAtGtWWqtRgP7NwY4qbGGyht8QpgLvpQmbRHtzeF1yTV1rmJ9GiHhJoqnrbUYT")
|
||||
assert_equal(address_info['address'], "CTEuA2rzWUDTGt6jFwmAtGtWWqtRgP7NwY4qbGGyht8QpgLvpQmbRHtzeF1yTV1rmJ9GiHhJoqnrbUYT")
|
||||
assert_equal(address_info["scriptPubKey"], "76a914dc389a2145ec3cd4fe37bd6a11653456168cfa2c88ac")
|
||||
|
|
|
|||
|
|
@ -141,13 +141,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': 'ert10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqygadtm',
|
||||
'✔️_VER16_PROG03': 'ert1sqqqqq78f8sy',
|
||||
'✔️_VER16_PROB02': 'ert1sqqqq8l77hs',
|
||||
'✔️_VER15_PROG40': 'ert10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq35dpwe',
|
||||
'✔️_VER16_PROG03': 'ert1sqqqqqtmet4x',
|
||||
'✔️_VER16_PROB02': 'ert1sqqqqjrwjjj',
|
||||
}
|
||||
BECH32_INVALID = {
|
||||
'❌_VER15_PROG41': 'ert10qqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqkk69my',
|
||||
'❌_VER16_PROB01': 'ert1sqqpx5djq',
|
||||
'❌_VER15_PROG41': 'ert1sqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqfum9dr',
|
||||
'❌_VER16_PROB01': 'ert1sqq56yphz',
|
||||
}
|
||||
for l in BECH32_VALID:
|
||||
ad = BECH32_VALID[l]
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ implicit-unsigned-integer-truncation:leveldb/*
|
|||
implicit-integer-sign-change:crc32c/*
|
||||
|
||||
implicit-signed-integer-truncation:script/interpreter.cpp
|
||||
implicit-integer-sign-change:blech32.cpp
|
||||
implicit-integer-sign-change:primitives/block.h
|
||||
implicit-integer-sign-change:primitives/confidential.cpp
|
||||
implicit-integer-sign-change:primitives/confidential.h
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue