1import unittest
2from util import *
3
4VER_MAIN_PUBLIC = 0x0488B21E
5VER_TEST_PUBLIC = 0x043587CF
6
7FLAG_KEY_PUBLIC = 0x1
8
9ADDRESS_TYPE_P2PKH       = 0x01
10ADDRESS_TYPE_P2SH_P2WPKH = 0x02
11ADDRESS_TYPE_P2WPKH      = 0x04
12
13ADDRESS_VERSION_P2PKH_MAINNET = 0x00
14ADDRESS_VERSION_P2PKH_TESTNET = 0x6F
15ADDRESS_VERSION_P2SH_MAINNET  = 0x05
16ADDRESS_VERSION_P2SH_TESTNET  = 0xC4
17
18NETWORK_BITCOIN_MAINNET = 0x01
19NETWORK_BITCOIN_TESTNET = 0x02
20NETWORK_LIQUID_MAINNET  = 0x03
21NETWORK_LIQUID_REGTEST  = 0x04
22
23
24# Vector from test_bip32.py. We only need an xpub to derive addresses.
25vec = {
26
27    'm/0H/1': {
28        # xpub6ASuArnXKPbfEwhqN6e3mwBcDTgzisQN1wXN9BJcM47sSikHjJf3UFHKkNAWbWMiGj7Wf5uMash7SyYq527Hqck2AxYysAA7xmALppuCkwQ
29        FLAG_KEY_PUBLIC:  '0488B21E025C1BD648000000012A7857'
30                          '631386BA23DACAC34180DD1983734E44'
31                          '4FDBF774041578E9B6ADB37C1903501E'
32                          '454BF00751F24B1B489AA925215D66AF'
33                          '2234E3891C3B21A52BEDB3CD711C6F6E2AF7',
34
35        "address_legacy": '1JQheacLPdM5ySCkrZkV66G2ApAXe1mqLj',
36
37        # OP_DUP OP_HASH160 [pub_key_hash] OP_EQUALVERIFY OP_CHECKSIG
38        'scriptpubkey_legacy': '76a914bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe88ac',
39
40        "address_p2sh_segwit": '3DymAvEWH38HuzHZ3VwLus673bNZnYwNXu',
41
42        # OP_HASH160 [script_hash] OP_EQUAL
43        'scriptpubkey_p2sh_segwit': 'a91486cc442a97817c245ce90ed0d31d6dbcde3841f987',
44
45        "address_segwit": 'bc1qhm6697d9d2224vfyt8mj4kw03ncec7a7fdafvt',
46
47        ## OP_0 [pub_key_hash]
48        'scriptpubkey_segwit': '0014bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe',
49    },
50
51    'm/1H/1': {
52        # tpubDApXh6cD2fZ7WjtgpHd8yrWyYaneiFuRZa7fVjMkgxsmC1QzoXW8cgx9zQFJ81Jx4deRGfRE7yXA9A3STsxXj4CKEZJHYgpMYikkas9DBTP
53        FLAG_KEY_PUBLIC:  '043587CF025C1BD648000000012A7857'
54                          '631386BA23DACAC34180DD1983734E44'
55                          '4FDBF774041578E9B6ADB37C1903501E'
56                          '454BF00751F24B1B489AA925215D66AF'
57                          '2234E3891C3B21A52BEDB3CD711C6F6E2AF7',
58
59        "address_legacy": 'mxvewdhKCenLkYgNa8irv1UM2omEWPMdEE',
60
61        # OP_DUP OP_HASH160 [pub_key_hash] OP_EQUALVERIFY OP_CHECKSIG
62        'scriptpubkey_legacy': '76a914bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe88ac',
63
64        "address_p2sh_segwit": '2N5XyEfAXtVde7mv6idZDXp5NFwajYEj9TD',
65
66        # OP_HASH160 [script_hash] OP_EQUAL
67        'scriptpubkey_p2sh_segwit': 'a91486cc442a97817c245ce90ed0d31d6dbcde3841f987',
68
69        "address_segwit": 'tb1qhm6697d9d2224vfyt8mj4kw03ncec7a7rtx6hc',
70
71        ## OP_0 [pub_key_hash]
72        'scriptpubkey_segwit': '0014bef5a2f9a56a94aab12459f72ad9cf8cf19c7bbe',
73    }
74
75}
76
77class AddressTests(unittest.TestCase):
78
79    SERIALIZED_LEN = 4 + 1 + 4 + 4 + 32 + 33
80
81    def unserialize_key(self, buf, buf_len):
82        key_out = ext_key()
83        ret = bip32_key_unserialize(buf, buf_len, byref(key_out))
84        return ret, key_out
85
86    def get_test_key(self, vec, path):
87        buf, buf_len = make_cbuffer(vec[path][FLAG_KEY_PUBLIC])
88        ret, key_out = self.unserialize_key(buf, self.SERIALIZED_LEN)
89        self.assertEqual(ret, WALLY_OK)
90
91        return key_out
92
93    def test_address_vectors(self):
94        self.do_test_vector(vec, 'm/0H/1', NETWORK_BITCOIN_MAINNET)
95        self.do_test_vector(vec, 'm/1H/1', NETWORK_BITCOIN_TESTNET) # Testnet
96
97    def do_test_vector(self, vec, path, network):
98        key = self.get_test_key(vec, path)
99
100        # Address type flag is mandatory
101        version = ADDRESS_VERSION_P2PKH_MAINNET if network == NETWORK_BITCOIN_MAINNET else ADDRESS_VERSION_P2PKH_TESTNET
102        ret, new_addr = wally_bip32_key_to_address(key, 0, version)
103        self.assertEqual(ret, WALLY_EINVAL)
104
105        # Obtain legacy address (P2PKH)
106        ret, new_addr = wally_bip32_key_to_address(key, ADDRESS_TYPE_P2PKH, version)
107        self.assertEqual(ret, WALLY_OK)
108        self.assertEqual(new_addr, vec[path]['address_legacy'])
109
110        # Obtain wrapped SegWit address (P2SH_P2WPKH)
111        version = ADDRESS_VERSION_P2SH_MAINNET if network == NETWORK_BITCOIN_MAINNET else ADDRESS_VERSION_P2SH_TESTNET
112        ret, new_addr = wally_bip32_key_to_address(key, ADDRESS_TYPE_P2SH_P2WPKH, version)
113        self.assertEqual(ret, WALLY_OK)
114        self.assertEqual(new_addr, vec[path]['address_p2sh_segwit'])
115
116        # wally_bip32_key_to_address does not support bech32 native SegWit (P2WPKH)
117        ret, new_addr = wally_bip32_key_to_address(key, ADDRESS_TYPE_P2WPKH, version)
118        self.assertEqual(ret, WALLY_EINVAL)
119
120        # Obtain native SegWit address (P2WPKH)
121        bech32_prefix = 'bc' if network == NETWORK_BITCOIN_MAINNET else 'tb'
122        ret, new_addr = wally_bip32_key_to_addr_segwit(key, utf8(bech32_prefix), 0)
123        self.assertEqual(ret, WALLY_OK)
124        self.assertEqual(new_addr, vec[path]['address_segwit'])
125
126        # Parse legacy address (P2PKH):
127        out, out_len = make_cbuffer('00' * (25))
128        ret, written = wally_address_to_scriptpubkey(utf8(vec[path]['address_legacy']), network, out, out_len)
129
130        self.assertEqual(ret, WALLY_OK)
131        self.assertEqual(hexlify(out[0:written]), utf8(vec[path]['scriptpubkey_legacy']))
132
133        # Get address for P2PKH scriptPubKey
134        ret, new_addr = wally_scriptpubkey_to_address(out, written, network)
135        self.assertEqual(ret, WALLY_OK)
136        self.assertEqual(new_addr, vec[path]['address_legacy'])
137
138        # Parse wrapped SegWit address (P2SH_P2WPKH):
139        out, out_len = make_cbuffer('00' * (25))
140        ret, written = wally_address_to_scriptpubkey(utf8(vec[path]['address_p2sh_segwit']), network, out, out_len)
141
142        self.assertEqual(ret, WALLY_OK)
143        self.assertEqual(hexlify(out[0:written]), utf8(vec[path]['scriptpubkey_p2sh_segwit']))
144
145        # Get address for P2SH scriptPubKey
146        ret, new_addr = wally_scriptpubkey_to_address(out, written, network)
147        self.assertEqual(ret, WALLY_OK)
148        self.assertEqual(new_addr, vec[path]['address_p2sh_segwit'])
149
150        # Parse native SegWit address (P2WPKH):
151        out, out_len = make_cbuffer('00' * (100))
152        ret, written = wally_addr_segwit_to_bytes(utf8(vec[path]['address_segwit']), utf8(bech32_prefix), 0, out, out_len)
153        self.assertEqual(ret, WALLY_OK)
154        self.assertEqual(hexlify(out[0:written]), utf8(vec[path]['scriptpubkey_segwit']))
155
156    def test_address_scriptpubkey_liquid(self):
157        """Check that addresses can be converted to and from scriptpubkeys for Liquid"""
158        for addr, scriptpubkey, network in [
159            ('XYtnYoGoSeE9ouMEVi6mfeujhjT2VnJncA', 'a914ec51ffb65120594389733bf8625f542446d97f7987', NETWORK_LIQUID_REGTEST),
160            ('H5nswXhfo8AMt159sgA5FWT35De34hVR4o', 'a914f80278b2011573a2ac59c83fadf929b0fc57ad0187', NETWORK_LIQUID_MAINNET),
161        ]:
162            out, out_len = make_cbuffer('00' * (100))
163            ret, written = wally_address_to_scriptpubkey(utf8(addr), network, out, out_len)
164            self.assertEqual(ret, WALLY_OK)
165            self.assertEqual(hexlify(out[0:written]), utf8(scriptpubkey))
166
167            ret, new_addr = wally_scriptpubkey_to_address(out, written, network)
168            self.assertEqual(ret, WALLY_OK)
169            self.assertEqual(utf8(new_addr), utf8(addr))
170
171
172if __name__ == '__main__':
173    unittest.main()
174