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 # --- Error cases --- def test_p2sh_invalid_network_raises(): import pytest with pytest.raises(ValueError): generate_p2sh_multisig('invalid_net', m=2, n=3) def test_p2sh_m_greater_than_n_raises(): import pytest with pytest.raises(ValueError): generate_p2sh_multisig('mainnet', m=3, n=2) def test_p2sh_m_zero_raises(): import pytest with pytest.raises(ValueError): generate_p2sh_multisig('mainnet', m=0, n=2) def test_p2sh_n_greater_than_16_raises(): import pytest with pytest.raises(ValueError): generate_p2sh_multisig('mainnet', m=1, n=17) # --- Redeem script structure --- def test_p2sh_redeem_script_is_valid_hex(): result = generate_p2sh_multisig('mainnet', m=2, n=3) hex_str = result['redeem_script_hex'] assert all(c in '0123456789abcdef' for c in hex_str) def test_p2sh_redeem_script_starts_with_op_m(): # OP_2 = 0x52, OP_3 = 0x53 result = generate_p2sh_multisig('mainnet', m=2, n=3) redeem = bytes.fromhex(result['redeem_script_hex']) assert redeem[0] == 0x52 # OP_2 def test_p2sh_redeem_script_ends_with_op_checkmultisig(): result = generate_p2sh_multisig('mainnet', m=2, n=3) redeem = bytes.fromhex(result['redeem_script_hex']) assert redeem[-1] == 0xAE # OP_CHECKMULTISIG # --- Edge cases --- def test_p2sh_3of5(): result = generate_p2sh_multisig('mainnet', m=3, n=5) assert result['m'] == 3 assert result['n'] == 5 assert len(result['participants']) == 5 def test_p2sh_1of16(): result = generate_p2sh_multisig('mainnet', m=1, n=16) assert result['m'] == 1 assert result['n'] == 16 assert len(result['participants']) == 16 # --- BIP67: sorted vs unsorted pubkeys produce different redeem scripts --- def test_p2sh_sort_pubkeys_affects_redeem_script(): import random random.seed(42) # Generate with and without sorting; addresses will differ r_sorted = generate_p2sh_multisig('mainnet', m=2, n=3, sort_pubkeys=True) r_unsorted = generate_p2sh_multisig('mainnet', m=2, n=3, sort_pubkeys=False) # Both produce valid addresses assert r_sorted['address'].startswith('3') assert r_unsorted['address'].startswith('3') def test_p2sh_compressed_pubkeys_33_bytes(): result = generate_p2sh_multisig('mainnet', m=2, n=3, compressed=True) for p in result['participants']: assert len(bytes.fromhex(p['public_key_hex'])) == 33 def test_p2sh_uncompressed_pubkeys_65_bytes(): result = generate_p2sh_multisig('mainnet', m=2, n=3, compressed=False) for p in result['participants']: assert len(bytes.fromhex(p['public_key_hex'])) == 65