mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-07-05 05:02:06 +02:00
Merge bitcoin/bitcoin#16807: Let validateaddress locate error in Bech32 address
88cc481092
Modify copyright header on Bech32 code (Samuel Dobson)5599813b80
Add lots of comments to Bech32 (Samuel Dobson)2eb5792ec7
Add release notes for validateaddress Bech32 error detection (MeshCollider)42d6a029e5
Refactor and add more tests for validateaddress (Samuel Dobson)c4979f77c1
Add boost tests for bech32 error detection (MeshCollider)02a7bdee42
Add error_locations to validateaddress RPC (Samuel Dobson)b62b67e06c
Add Bech32 error location function (Samuel Dobson)0b06e720c0
More detailed error checking for base58 addresses (Samuel Dobson) Pull request description: Addresses (partially) #16779 - no GUI change in this PR Adds a LocateError function the bech32 library, which is then called by `validateaddress` RPC, (and then eventually from a GUI tool too, future work). I think modifying validateaddress is nicer than adding a separate RPC for this. Includes tests. Based on https://github.com/sipa/bech32/blob/master/ecc/javascript/bech32_ecc.js Credit to sipa for that code ACKs for top commit: laanwj: Code review and manually tested ACK88cc481092
ryanofsky: Code review ACK88cc481092
with caveat that I only checked the new `LocateErrors` code to try to verify it didn't have unsafe or unexpected operations or loop forever or crash. Did not try to verify behavior corresponds to the spec. In the worst case bugs here should just affect error messages not actual decoding of addresses so this seemed ok. w0xlt: tACK88cc481
Tree-SHA512: 9c7fe9745bc7527f80a30bd4c1e3034e16b96a02cc7f6c268f91bfad08a6965a8064fe44230aa3f87e4fa3c938f662ff4446bc682c83cb48c1a3f95cf4186688
This commit is contained in:
@ -12,79 +12,96 @@ from test_framework.util import (
|
||||
)
|
||||
|
||||
BECH32_VALID = 'bcrt1qtmp74ayg7p24uslctssvjm06q5phz4yrxucgnv'
|
||||
BECH32_VALID_CAPITALS = 'BCRT1QPLMTZKC2XHARPPZDLNPAQL78RSHJ68U33RAH7R'
|
||||
BECH32_VALID_MULTISIG = 'bcrt1qdg3myrgvzw7ml9q0ejxhlkyxm7vl9r56yzkfgvzclrf4hkpx9yfqhpsuks'
|
||||
|
||||
BECH32_INVALID_BECH32 = 'bcrt1p0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqdmchcc'
|
||||
BECH32_INVALID_BECH32M = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7k35mrzd'
|
||||
BECH32_INVALID_VERSION = 'bcrt130xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7vqynjegk'
|
||||
BECH32_INVALID_SIZE = 'bcrt1s0xlxvlhemja6c4dqv22uapctqupfhlxm9h8z3k2e72q4k9hcz7v8n0nx0muaewav25430mtr'
|
||||
BECH32_INVALID_V0_SIZE = 'bcrt1qw508d6qejxtdg4y5r3zarvary0c5xw7kqqq5k3my'
|
||||
BECH32_INVALID_PREFIX = 'bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx'
|
||||
BECH32_TOO_LONG = 'bcrt1q049edschfnwystcqnsvyfpj23mpsg3jcedq9xv049edschfnwystcqnsvyfpj23mpsg3jcedq9xv049edschfnwystcqnsvyfpj23m'
|
||||
BECH32_ONE_ERROR = 'bcrt1q049edschfnwystcqnsvyfpj23mpsg3jcedq9xv'
|
||||
BECH32_ONE_ERROR_CAPITALS = 'BCRT1QPLMTZKC2XHARPPZDLNPAQL78RSHJ68U32RAH7R'
|
||||
BECH32_TWO_ERRORS = 'bcrt1qax9suht3qv95sw33xavx8crpxduefdrsvgsklu' # should be bcrt1qax9suht3qv95sw33wavx8crpxduefdrsvgsklx
|
||||
BECH32_NO_SEPARATOR = 'bcrtq049ldschfnwystcqnsvyfpj23mpsg3jcedq9xv'
|
||||
BECH32_INVALID_CHAR = 'bcrt1q04oldschfnwystcqnsvyfpj23mpsg3jcedq9xv'
|
||||
BECH32_MULTISIG_TWO_ERRORS = 'bcrt1qdg3myrgvzw7ml8q0ejxhlkyxn7vl9r56yzkfgvzclrf4hkpx9yfqhpsuks'
|
||||
BECH32_WRONG_VERSION = 'bcrt1ptmp74ayg7p24uslctssvjm06q5phz4yrxucgnv'
|
||||
|
||||
BASE58_VALID = 'mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn'
|
||||
BASE58_INVALID_PREFIX = '17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem'
|
||||
BASE58_INVALID_CHECKSUM = 'mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJJfn'
|
||||
BASE58_INVALID_LENGTH = '2VKf7XKMrp4bVNVmuRbyCewkP8FhGLP2E54LHDPakr9Sq5mtU2'
|
||||
|
||||
INVALID_ADDRESS = 'asfah14i8fajz0123f'
|
||||
INVALID_ADDRESS_2 = '1q049ldschfnwystcqnsvyfpj23mpsg3jcedq9xv'
|
||||
|
||||
class InvalidAddressErrorMessageTest(BitcoinTestFramework):
|
||||
def set_test_params(self):
|
||||
self.setup_clean_chain = True
|
||||
self.num_nodes = 1
|
||||
|
||||
def check_valid(self, addr):
|
||||
info = self.nodes[0].validateaddress(addr)
|
||||
assert info['isvalid']
|
||||
assert 'error' not in info
|
||||
assert 'error_locations' not in info
|
||||
|
||||
def check_invalid(self, addr, error_str, error_locations=None):
|
||||
res = self.nodes[0].validateaddress(addr)
|
||||
assert not res['isvalid']
|
||||
assert_equal(res['error'], error_str)
|
||||
if error_locations:
|
||||
assert_equal(res['error_locations'], error_locations)
|
||||
else:
|
||||
assert_equal(res['error_locations'], [])
|
||||
|
||||
def test_validateaddress(self):
|
||||
node = self.nodes[0]
|
||||
# Invalid Bech32
|
||||
self.check_invalid(BECH32_INVALID_SIZE, 'Invalid Bech32 address data size')
|
||||
self.check_invalid(BECH32_INVALID_PREFIX, 'Invalid HRP or Base58 character in address')
|
||||
self.check_invalid(BECH32_INVALID_BECH32, 'Version 1+ witness address must use Bech32m checksum')
|
||||
self.check_invalid(BECH32_INVALID_BECH32M, 'Version 0 witness address must use Bech32 checksum')
|
||||
self.check_invalid(BECH32_INVALID_VERSION, 'Invalid Bech32 address witness version')
|
||||
self.check_invalid(BECH32_INVALID_V0_SIZE, 'Invalid Bech32 v0 address data size')
|
||||
self.check_invalid(BECH32_TOO_LONG, 'Bech32 string too long', list(range(90, 108)))
|
||||
self.check_invalid(BECH32_ONE_ERROR, 'Invalid checksum', [9])
|
||||
self.check_invalid(BECH32_TWO_ERRORS, 'Invalid checksum', [22, 43])
|
||||
self.check_invalid(BECH32_ONE_ERROR_CAPITALS, 'Invalid checksum', [38])
|
||||
self.check_invalid(BECH32_NO_SEPARATOR, 'Missing separator')
|
||||
self.check_invalid(BECH32_INVALID_CHAR, 'Invalid Base 32 character', [8])
|
||||
self.check_invalid(BECH32_MULTISIG_TWO_ERRORS, 'Invalid checksum', [19, 30])
|
||||
self.check_invalid(BECH32_WRONG_VERSION, 'Invalid checksum', [5])
|
||||
|
||||
# Bech32
|
||||
info = node.validateaddress(BECH32_INVALID_SIZE)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Invalid Bech32 address data size')
|
||||
# Valid Bech32
|
||||
self.check_valid(BECH32_VALID)
|
||||
self.check_valid(BECH32_VALID_CAPITALS)
|
||||
self.check_valid(BECH32_VALID_MULTISIG)
|
||||
|
||||
info = node.validateaddress(BECH32_INVALID_PREFIX)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Invalid prefix for Bech32 address')
|
||||
# Invalid Base58
|
||||
self.check_invalid(BASE58_INVALID_PREFIX, 'Invalid prefix for Base58-encoded address')
|
||||
self.check_invalid(BASE58_INVALID_CHECKSUM, 'Invalid checksum or length of Base58 address')
|
||||
self.check_invalid(BASE58_INVALID_LENGTH, 'Invalid checksum or length of Base58 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
|
||||
|
||||
info = node.validateaddress(BECH32_INVALID_VERSION)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Invalid Bech32 address witness version')
|
||||
|
||||
# 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
|
||||
# Valid Base58
|
||||
self.check_valid(BASE58_VALID)
|
||||
|
||||
# Invalid address format
|
||||
info = node.validateaddress(INVALID_ADDRESS)
|
||||
assert not info['isvalid']
|
||||
assert_equal(info['error'], 'Invalid address format')
|
||||
self.check_invalid(INVALID_ADDRESS, 'Invalid HRP or Base58 character in address')
|
||||
self.check_invalid(INVALID_ADDRESS_2, 'Invalid HRP or Base58 character in address')
|
||||
|
||||
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 HRP or Base58 character in 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)
|
||||
assert_raises_rpc_error(-5, "Invalid HRP or Base58 character in address", node.getaddressinfo, INVALID_ADDRESS)
|
||||
|
||||
def run_test(self):
|
||||
self.test_validateaddress()
|
||||
|
Reference in New Issue
Block a user