From 409669e00093314d87e26fa3f5ad3cf3774a4730 Mon Sep 17 00:00:00 2001 From: Davide Grilli Date: Mon, 9 Mar 2026 11:11:41 +0100 Subject: [PATCH] test: add tests/ folder with SecretScan verification links - Add pytest test suite for all 5 address types (P2PK, P2PKH, P2SH, P2WPKH, P2TR) - Test output fields, address prefixes per network, key lengths and uniqueness - Each test file prints the SecretScan URL for manual address verification - Add pytest to requirements.txt --- requirements.txt | 1 + tests/__init__.py | 0 tests/test_p2pk.py | 67 ++++++++++++++++++++++++++++++++++++++ tests/test_p2pkh.py | 66 +++++++++++++++++++++++++++++++++++++ tests/test_p2sh.py | 77 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_p2tr.py | 59 +++++++++++++++++++++++++++++++++ tests/test_p2wpkh.py | 60 ++++++++++++++++++++++++++++++++++ 7 files changed, 330 insertions(+) create mode 100644 tests/__init__.py create mode 100644 tests/test_p2pk.py create mode 100644 tests/test_p2pkh.py create mode 100644 tests/test_p2sh.py create mode 100644 tests/test_p2tr.py create mode 100644 tests/test_p2wpkh.py diff --git a/requirements.txt b/requirements.txt index 3009014..d6edba9 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ base58==2.1.1 bech32==1.2.0 ecdsa==0.19.0 six==1.17.0 +pytest diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_p2pk.py b/tests/test_p2pk.py new file mode 100644 index 0000000..f983545 --- /dev/null +++ b/tests/test_p2pk.py @@ -0,0 +1,67 @@ +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + +from src.p2pk import generate_p2pk + +SECRETSCAN_URL = "https://secretscan.org/Bitcoin?address={}" + +NETWORKS = ['mainnet', 'testnet', 'regtest'] + + +def test_p2pk_fields(): + result = generate_p2pk('mainnet', compressed=True) + assert set(result.keys()) == {'network', 'script_type', 'private_key_hex', 'private_key_wif', 'public_key_hex'} + + +def test_p2pk_script_type(): + result = generate_p2pk('mainnet') + assert result['script_type'] == 'p2pk' + + +def test_p2pk_private_key_length(): + result = generate_p2pk('mainnet') + assert len(result['private_key_hex']) == 64 + + +def test_p2pk_compressed_pubkey_length(): + result = generate_p2pk('mainnet', compressed=True) + assert len(result['public_key_hex']) == 66 + assert result['public_key_hex'][:2] in ('02', '03') + + +def test_p2pk_uncompressed_pubkey_length(): + result = generate_p2pk('mainnet', compressed=False) + assert len(result['public_key_hex']) == 130 + assert result['public_key_hex'][:2] == '04' + + +def test_p2pk_wif_mainnet(): + result = generate_p2pk('mainnet', compressed=True) + assert result['private_key_wif'][0] in ('K', 'L') + + +def test_p2pk_wif_testnet(): + result = generate_p2pk('testnet', compressed=True) + assert result['private_key_wif'][0] == 'c' + + +def test_p2pk_all_networks(): + for network in NETWORKS: + result = generate_p2pk(network) + assert result['network'] == network + + +def test_p2pk_unique_keys(): + r1 = generate_p2pk('mainnet') + r2 = generate_p2pk('mainnet') + assert r1['private_key_hex'] != r2['private_key_hex'] + + +def test_p2pk_print_secretscan(capsys): + # P2PK has no address — print the public key for manual reference + result = generate_p2pk('mainnet') + print(f"\n[P2PK] Public key: {result['public_key_hex']}") + print(f"[P2PK] SecretScan (search by pubkey): https://secretscan.org/") + captured = capsys.readouterr() + assert 'secretscan.org' in captured.out diff --git a/tests/test_p2pkh.py b/tests/test_p2pkh.py new file mode 100644 index 0000000..1c05c51 --- /dev/null +++ b/tests/test_p2pkh.py @@ -0,0 +1,66 @@ +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + +from src.p2pkh import generate_legacy_address + +SECRETSCAN_URL = "https://secretscan.org/Bitcoin?address={}" + +NETWORKS = ['mainnet', 'testnet', 'regtest'] + + +def test_p2pkh_fields(): + result = generate_legacy_address('mainnet') + assert set(result.keys()) == {'network', 'script_type', 'private_key_hex', 'private_key_wif', 'public_key_hex', 'address'} + + +def test_p2pkh_script_type(): + result = generate_legacy_address('mainnet') + assert result['script_type'] == 'p2pkh' + + +def test_p2pkh_address_mainnet(): + result = generate_legacy_address('mainnet') + assert result['address'].startswith('1') + + +def test_p2pkh_address_testnet(): + result = generate_legacy_address('testnet') + assert result['address'][0] in ('m', 'n') + + +def test_p2pkh_address_regtest(): + result = generate_legacy_address('regtest') + assert result['address'][0] in ('m', 'n') + + +def test_p2pkh_private_key_length(): + result = generate_legacy_address('mainnet') + assert len(result['private_key_hex']) == 64 + + +def test_p2pkh_compressed_pubkey(): + result = generate_legacy_address('mainnet', compressed=True) + assert len(result['public_key_hex']) == 66 + assert result['public_key_hex'][:2] in ('02', '03') + + +def test_p2pkh_uncompressed_pubkey(): + result = generate_legacy_address('mainnet', compressed=False) + assert len(result['public_key_hex']) == 130 + assert result['public_key_hex'][:2] == '04' + + +def test_p2pkh_unique_addresses(): + r1 = generate_legacy_address('mainnet') + r2 = generate_legacy_address('mainnet') + assert r1['address'] != r2['address'] + + +def test_p2pkh_print_secretscan(capsys): + result = generate_legacy_address('mainnet') + url = SECRETSCAN_URL.format(result['address']) + print(f"\n[P2PKH] Address: {result['address']}") + print(f"[P2PKH] Verify on SecretScan: {url}") + captured = capsys.readouterr() + assert 'secretscan.org' in captured.out diff --git a/tests/test_p2sh.py b/tests/test_p2sh.py new file mode 100644 index 0000000..b847330 --- /dev/null +++ b/tests/test_p2sh.py @@ -0,0 +1,77 @@ +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + +from src.p2sh import generate_p2sh_multisig + +SECRETSCAN_URL = "https://secretscan.org/Bitcoin?address={}" + +NETWORKS = ['mainnet', 'testnet', 'regtest'] + + +def test_p2sh_fields(): + result = generate_p2sh_multisig('mainnet', m=2, n=3) + assert set(result.keys()) == {'network', 'script_type', 'm', 'n', 'redeem_script_hex', 'participants', 'address'} + + +def test_p2sh_script_type(): + result = generate_p2sh_multisig('mainnet', m=2, n=3) + assert result['script_type'] == 'p2sh-multisig' + + +def test_p2sh_address_mainnet(): + result = generate_p2sh_multisig('mainnet', m=2, n=3) + assert result['address'].startswith('3') + + +def test_p2sh_address_testnet(): + result = generate_p2sh_multisig('testnet', m=2, n=3) + assert result['address'].startswith('2') + + +def test_p2sh_address_regtest(): + result = generate_p2sh_multisig('regtest', m=2, n=3) + assert result['address'].startswith('2') + + +def test_p2sh_participants_count(): + result = generate_p2sh_multisig('mainnet', m=2, n=3) + assert len(result['participants']) == 3 + assert result['m'] == 2 + assert result['n'] == 3 + + +def test_p2sh_participant_fields(): + result = generate_p2sh_multisig('mainnet', m=2, n=3) + for p in result['participants']: + assert 'private_key_hex' in p + assert 'private_key_wif' in p + assert 'public_key_hex' in p + assert len(p['private_key_hex']) == 64 + + +def test_p2sh_redeem_script_not_empty(): + result = generate_p2sh_multisig('mainnet', m=2, n=3) + assert len(result['redeem_script_hex']) > 0 + + +def test_p2sh_1of1(): + result = generate_p2sh_multisig('mainnet', m=1, n=1) + assert result['m'] == 1 + assert result['n'] == 1 + assert len(result['participants']) == 1 + + +def test_p2sh_unique_addresses(): + r1 = generate_p2sh_multisig('mainnet', m=2, n=3) + r2 = generate_p2sh_multisig('mainnet', m=2, n=3) + assert r1['address'] != r2['address'] + + +def test_p2sh_print_secretscan(capsys): + result = generate_p2sh_multisig('mainnet', m=2, n=3) + url = SECRETSCAN_URL.format(result['address']) + print(f"\n[P2SH] Address: {result['address']}") + print(f"[P2SH] Verify on SecretScan: {url}") + captured = capsys.readouterr() + assert 'secretscan.org' in captured.out diff --git a/tests/test_p2tr.py b/tests/test_p2tr.py new file mode 100644 index 0000000..b3044e3 --- /dev/null +++ b/tests/test_p2tr.py @@ -0,0 +1,59 @@ +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + +from src.p2tr import generate_p2tr_address + +SECRETSCAN_URL = "https://secretscan.org/Bitcoin?address={}" + +NETWORKS = ['mainnet', 'testnet', 'regtest'] + + +def test_p2tr_fields(): + result = generate_p2tr_address('mainnet') + assert set(result.keys()) == {'network', 'script_type', 'private_key_hex', 'private_key_wif', 'internal_pubkey_x_hex', 'address'} + + +def test_p2tr_script_type(): + result = generate_p2tr_address('mainnet') + assert result['script_type'] == 'p2tr' + + +def test_p2tr_address_mainnet(): + result = generate_p2tr_address('mainnet') + assert result['address'].startswith('bc1p') + + +def test_p2tr_address_testnet(): + result = generate_p2tr_address('testnet') + assert result['address'].startswith('tb1p') + + +def test_p2tr_address_regtest(): + result = generate_p2tr_address('regtest') + assert result['address'].startswith('bcrt1p') + + +def test_p2tr_private_key_length(): + result = generate_p2tr_address('mainnet') + assert len(result['private_key_hex']) == 64 + + +def test_p2tr_internal_pubkey_length(): + result = generate_p2tr_address('mainnet') + assert len(result['internal_pubkey_x_hex']) == 64 # x-only pubkey, 32 bytes + + +def test_p2tr_unique_addresses(): + r1 = generate_p2tr_address('mainnet') + r2 = generate_p2tr_address('mainnet') + assert r1['address'] != r2['address'] + + +def test_p2tr_print_secretscan(capsys): + result = generate_p2tr_address('mainnet') + url = SECRETSCAN_URL.format(result['address']) + print(f"\n[P2TR] Address: {result['address']}") + print(f"[P2TR] Verify on SecretScan: {url}") + captured = capsys.readouterr() + assert 'secretscan.org' in captured.out diff --git a/tests/test_p2wpkh.py b/tests/test_p2wpkh.py new file mode 100644 index 0000000..b3b0c13 --- /dev/null +++ b/tests/test_p2wpkh.py @@ -0,0 +1,60 @@ +import sys +import os +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) + +from src.p2wpkh import generate_segwit_address + +SECRETSCAN_URL = "https://secretscan.org/Bitcoin?address={}" + +NETWORKS = ['mainnet', 'testnet', 'regtest'] + + +def test_p2wpkh_fields(): + result = generate_segwit_address('mainnet') + assert set(result.keys()) == {'network', 'script_type', 'private_key_hex', 'private_key_wif', 'public_key_hex', 'address'} + + +def test_p2wpkh_script_type(): + result = generate_segwit_address('mainnet') + assert result['script_type'] == 'p2wpkh' + + +def test_p2wpkh_address_mainnet(): + result = generate_segwit_address('mainnet') + assert result['address'].startswith('bc1q') + + +def test_p2wpkh_address_testnet(): + result = generate_segwit_address('testnet') + assert result['address'].startswith('tb1q') + + +def test_p2wpkh_address_regtest(): + result = generate_segwit_address('regtest') + assert result['address'].startswith('bcrt1q') + + +def test_p2wpkh_private_key_length(): + result = generate_segwit_address('mainnet') + assert len(result['private_key_hex']) == 64 + + +def test_p2wpkh_compressed_pubkey(): + result = generate_segwit_address('mainnet', compressed=True) + assert len(result['public_key_hex']) == 66 + assert result['public_key_hex'][:2] in ('02', '03') + + +def test_p2wpkh_unique_addresses(): + r1 = generate_segwit_address('mainnet') + r2 = generate_segwit_address('mainnet') + assert r1['address'] != r2['address'] + + +def test_p2wpkh_print_secretscan(capsys): + result = generate_segwit_address('mainnet') + url = SECRETSCAN_URL.format(result['address']) + print(f"\n[P2WPKH] Address: {result['address']}") + print(f"[P2WPKH] Verify on SecretScan: {url}") + captured = capsys.readouterr() + assert 'secretscan.org' in captured.out