Skip to content

Commit

Permalink
Merge bitcoin#24576: contrib: testgen: remove redundant base58 implem…
Browse files Browse the repository at this point in the history
…entation

65c49ac test: throw `ValueError` for invalid base58 checksum (Sebastian Falbesoner)
219d2c7 contrib: testgen: use base58 methods from test framework (Sebastian Falbesoner)
605fecf scripted-diff: rename `chars` to `b58chars` in test_framework.address (Sebastian Falbesoner)
11c63e3 contrib: testgen: import OP_* constants from test framework (Sebastian Falbesoner)
7d755bb contrib: testgen: avoid need for manually setting PYTHONPATH (Sebastian Falbesoner)

Pull request description:

  This PR removes the redundant base58 implementation [contrib/testgen/base58.py](https://github.com/bitcoin/bitcoin/blob/master/contrib/testgen/base58.py) for the test generation script `gen_key_io_test_vectors.py` and uses the one from the test framework instead. Additionally, three other cleanups/improvements are done:
  - import script operator constants `OP_*` from test framework instead of manually defining them
  - add Python path to test framework directly in the script (via `sys.path.append(...)`) instead of needing the caller to specify `PYTHONPATH=...` on the command line (the same approach is done for the signet miner and the message capture scripts)
  - rename `chars` to `b58chars` in the test_framework.address module (is more explicit and makes the diff for the base58 replacement smaller)

ACKs for top commit:
  laanwj:
    Code review ACK 65c49ac

Tree-SHA512: 92e1534cc320cd56262bf455de7231c6ec821bfcd0ed58aa5718271ecec1a89df7951bf31527a2306db6398e7f2664d2ff8508200c28163c0b164d3f5aaf8b0e
  • Loading branch information
fanquake committed Apr 6, 2022
2 parents d906329 + 65c49ac commit 10f629e
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 148 deletions.
4 changes: 2 additions & 2 deletions contrib/testgen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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
./gen_key_io_test_vectors.py valid 70 > ../../src/test/data/key_io_valid.json
./gen_key_io_test_vectors.py invalid 70 > ../../src/test/data/key_io_invalid.json
115 changes: 0 additions & 115 deletions contrib/testgen/base58.py

This file was deleted.

43 changes: 20 additions & 23 deletions contrib/testgen/gen_key_io_test_vectors.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
#!/usr/bin/env python3
# Copyright (c) 2012-2021 The Bitcoin Core developers
# Copyright (c) 2012-2022 The Bitcoin Core developers
# 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/bech32(m) address and private key test vectors.
Usage:
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
./gen_key_io_test_vectors.py valid 70 > ../../src/test/data/key_io_valid.json
./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
import os

from itertools import islice
from base58 import b58encode_chk, b58decode_chk, b58chars
import os
import random
from segwit_addr import bech32_encode, decode_segwit_address, convertbits, CHARSET, Encoding
import sys

sys.path.append(os.path.join(os.path.dirname(__file__), '../../test/functional'))

from test_framework.address import base58_to_byte, byte_to_base58, b58chars # noqa: E402
from test_framework.script import OP_0, OP_1, OP_2, OP_3, OP_16, OP_DUP, OP_EQUAL, OP_EQUALVERIFY, OP_HASH160, OP_CHECKSIG # noqa: E402
from test_framework.segwit_addr import bech32_encode, decode_segwit_address, convertbits, CHARSET, Encoding # noqa: E402

# key types
PUBKEY_ADDRESS = 0
Expand All @@ -29,16 +33,6 @@
PRIVKEY_REGTEST = 239

# script
OP_0 = 0x00
OP_1 = 0x51
OP_2 = 0x52
OP_3 = 0x53
OP_16 = 0x60
OP_DUP = 0x76
OP_EQUAL = 0x87
OP_EQUALVERIFY = 0x88
OP_HASH160 = 0xa9
OP_CHECKSIG = 0xac
pubkey_prefix = (OP_DUP, OP_HASH160, 20)
pubkey_suffix = (OP_EQUALVERIFY, OP_CHECKSIG)
script_prefix = (OP_HASH160, 20)
Expand Down Expand Up @@ -114,8 +108,10 @@ def is_valid(v):
'''Check vector v for validity'''
if len(set(v) - set(b58chars)) > 0:
return is_valid_bech32(v)
result = b58decode_chk(v)
if result is None:
try:
payload, version = base58_to_byte(v)
result = bytes([version]) + payload
except ValueError: # thrown if checksum doesn't match
return is_valid_bech32(v)
for template in templates:
prefix = bytearray(template[0])
Expand All @@ -139,7 +135,8 @@ def gen_valid_base58_vector(template):
suffix = bytearray(template[2])
dst_prefix = bytearray(template[4])
dst_suffix = bytearray(template[5])
rv = b58encode_chk(prefix + payload + suffix)
assert len(prefix) == 1
rv = byte_to_base58(payload + suffix, prefix[0])
return rv, dst_prefix + payload + dst_suffix

def gen_valid_bech32_vector(template):
Expand Down Expand Up @@ -190,7 +187,8 @@ def gen_invalid_base58_vector(template):
else:
suffix = bytearray(template[2])

val = b58encode_chk(prefix + payload + suffix)
assert len(prefix) == 1
val = byte_to_base58(payload + suffix, prefix[0])
if random.randint(0,10)<1: # line corruption
if randbool(): # add random character to end
val += random.choice(b58chars)
Expand Down Expand Up @@ -250,7 +248,6 @@ def gen_invalid_vectors():
yield val,

if __name__ == '__main__':
import sys
import json
iters = {'valid':gen_valid_vectors, 'invalid':gen_invalid_vectors}
try:
Expand Down
16 changes: 8 additions & 8 deletions test/functional/test_framework/address.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class AddressType(enum.Enum):
legacy = 'legacy' # P2PKH


chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
b58chars = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'


def create_deterministic_address_bcrt1_p2tr_op_true():
Expand All @@ -59,10 +59,10 @@ def byte_to_base58(b, version):
b += hash256(b)[:4] # append checksum
value = int.from_bytes(b, 'big')
while value > 0:
result = chars[value % 58] + result
result = b58chars[value % 58] + result
value //= 58
while b[0] == 0:
result = chars[0] + result
result = b58chars[0] + result
b = b[1:]
return result

Expand All @@ -76,23 +76,23 @@ def base58_to_byte(s):
n = 0
for c in s:
n *= 58
assert c in chars
digit = chars.index(c)
assert c in b58chars
digit = b58chars.index(c)
n += digit
h = '%x' % n
if len(h) % 2:
h = '0' + h
res = n.to_bytes((n.bit_length() + 7) // 8, 'big')
pad = 0
for c in s:
if c == chars[0]:
if c == b58chars[0]:
pad += 1
else:
break
res = b'\x00' * pad + res

# Assert if the checksum is invalid
assert_equal(hash256(res[:-4])[:4], res[-4:])
if hash256(res[:-4])[:4] != res[-4:]:
raise ValueError('Invalid Base58Check checksum')

return res[1:-4], int(res[0])

Expand Down

0 comments on commit 10f629e

Please sign in to comment.