1import unittest
2from util import *
3
4
5EC_PUBLIC_KEY_LEN = 33
6FLAG_KEY_PUBLIC, FLAG_KEY_TWEAK_SUM = 0x1, 0x4
7SCRIPTPUBKEY_P2PKH_LEN = 25
8SCRIPT_HASH160 = 0x1
9
10
11class PegoutTests(unittest.TestCase):
12
13    def path_to_c(self, path):
14        c_path = (c_uint * len(path))()
15        for i, n in enumerate(path):
16            c_path[i] = n
17        return c_path
18
19    def derive_pub_tweak(self, parent, path):
20        c_path = self.path_to_c(path)
21        key_out = POINTER(ext_key)()
22        fn = bip32_key_with_tweak_from_parent_path_alloc
23        self.assertEqual(fn(byref(parent), c_path, len(path),
24                            FLAG_KEY_PUBLIC | FLAG_KEY_TWEAK_SUM, byref(key_out)), WALLY_OK)
25        return key_out[0].pub_key, key_out[0].pub_key_tweak_sum
26
27    def generate_pegout_whitelistproof(self):
28        offline_xpub = 'tpubDAY5hwtonH4NE8zY46ZMFf6B6F3fqMis7cwfNihXXpAg6XzBZNoHAdAzAZx2peoU8nTWFqvUncXwJ9qgE5VxcnUKxdut8F6mptVmKjfiwDQ'
29        offline_counter = 8
30        pak_list = '030781ae6f87c0b3af83b7350eb38bbd22322f525046d0320f1cb45a97c05cbeb7:031f26676db48716aff0f6ac477db463715d3280e2c706b55556425831620fdcce'
31        master_online_key, master_online_key_len = make_cbuffer('06def06500e5efae3addf7e0ed1178074405587a95c49a3ef31367eec782319f')
32
33        pub_key, pub_key_len = make_cbuffer('00'*33)
34        self.assertEqual(wally_ec_public_key_from_private_key(master_online_key, master_online_key_len, pub_key, pub_key_len), WALLY_OK)
35        buf, buf_len = make_cbuffer('00'*33)
36        ret, written = wally_hex_to_bytes(utf8('031f26676db48716aff0f6ac477db463715d3280e2c706b55556425831620fdcce'), buf, buf_len)
37        self.assertEqual(pub_key, buf)
38
39        whitelist_index = 0
40
41        keys = pak_list.split(':')
42        offline_keys, offline_keys_len = make_cbuffer(keys[0])
43        online_keys, online_keys_len = make_cbuffer(keys[1])
44
45        key_out = ext_key()
46        self.assertEqual(bip32_key_from_base58(utf8(offline_xpub), byref(key_out)), WALLY_OK)
47        negated_master_pubkey, negated_master_pubkey_len = make_cbuffer('00'*EC_PUBLIC_KEY_LEN)
48        self.assertEqual(wally_ec_public_key_negate(key_out.pub_key, EC_PUBLIC_KEY_LEN, negated_master_pubkey, EC_PUBLIC_KEY_LEN), WALLY_OK)
49        buf, buf_len = make_cbuffer('00'*int(len(keys[0])/2))
50        ret, written = wally_hex_to_bytes(utf8(keys[0]), buf, buf_len)
51        self.assertEqual(negated_master_pubkey, buf)
52        pub_key, pub_tweak = self.derive_pub_tweak(key_out, [0, offline_counter])
53
54        whitelistproof, whitelistproof_len = make_cbuffer('00'*65)
55        (code, written) = wally_asset_pak_whitelistproof(online_keys, online_keys_len, offline_keys,
56                                                        offline_keys_len, whitelist_index, pub_key, len(pub_key),
57                                                        master_online_key, master_online_key_len,
58                                                        pub_tweak, len(pub_tweak), whitelistproof, whitelistproof_len)
59        self.assertEqual(code, WALLY_OK)
60        self.assertEqual(whitelistproof_len, written)
61
62        return whitelistproof, whitelistproof_len, pub_key, len(pub_key)
63
64    def generate_pegout_script(self):
65        gen_wlproof, gen_wlproof_len, pub_key, pub_key_len = self.generate_pegout_whitelistproof()
66        wl_proof = '013996e9eca65e06b3deda77fdc19b3476cd83af3ae8f543647a52b097558c33878752c52536c493ea00d446159009ce484795287aca1de8aaa52d6064b5960caa'
67        buf, buf_len = make_cbuffer('00'*int(len(wl_proof)/2))
68        ret, written = wally_hex_to_bytes(utf8(wl_proof), buf, buf_len)
69        self.assertEqual(buf, gen_wlproof)
70
71        genesis_block_hash, genesis_block_hash_len = make_cbuffer('0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206')
72
73        mainchain_script, mainchain_script_len = make_cbuffer('00'*SCRIPTPUBKEY_P2PKH_LEN)
74        self.assertEqual(wally_scriptpubkey_p2pkh_from_bytes(pub_key, pub_key_len, SCRIPT_HASH160,
75                                                             mainchain_script, mainchain_script_len)[0], WALLY_OK)
76
77        pegout_script, pegout_script_len = make_cbuffer('00'*(genesis_block_hash_len + mainchain_script_len + pub_key_len + gen_wlproof_len+5))
78        self.assertEqual(wally_elements_pegout_script_from_bytes(genesis_block_hash[::-1], genesis_block_hash_len,
79                                                                 mainchain_script, mainchain_script_len,
80                                                                 pub_key, pub_key_len,
81                                                                 gen_wlproof, gen_wlproof_len, 0,
82                                                                 pegout_script, pegout_script_len)[0], WALLY_OK)
83        return pegout_script, pegout_script_len
84
85    def test_pegout(self):
86        op_return_data = '6a2006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f1976a91420f2d8c7514c601984fffee90f988f33bd87f96f88ac2103c58ebf2840c9321e42e1859a387d42cc78241048f81ce9c911bd57b240139e9741013996e9eca65e06b3deda77fdc19b3476cd83af3ae8f543647a52b097558c33878752c52536c493ea00d446159009ce484795287aca1de8aaa52d6064b5960caa'
87        buf, buf_len = make_cbuffer('00'*int(len(op_return_data)/2))
88        ret, written = wally_hex_to_bytes(utf8(op_return_data), buf, buf_len)
89        self.assertEqual(buf, self.generate_pegout_script()[0])
90
91    def test_pegout_tx(self):
92        tx_hex = "02000000010111b13a9bc2833fcfddb53086fffb3cf7ff1c13948c876d9bd15df872f5fdefca0000000017160014355347fd5b11a57cddd5e1576fb38280a0627cf7fdffffff030125b251070e29ca19043cf33ccd7324e2ddab03ecc4ae0b5e77c4fc0e5cf6c95a01000000174876e80000a06a2006226e46111a0b59caaf126043eb5bbf28c34f3a5e332a1fc7b2b73cf188910f1976a91420f2d8c7514c601984fffee90f988f33bd87f96f88ac2103c58ebf2840c9321e42e1859a387d42cc78241048f81ce9c911bd57b240139e9741013996e9eca65e06b3deda77fdc19b3476cd83af3ae8f543647a52b097558c33878752c52536c493ea00d446159009ce484795287aca1de8aaa52d6064b5960caa0125b251070e29ca19043cf33ccd7324e2ddab03ecc4ae0b5e77c4fc0e5cf6c95a010007751ecdd907a20017a9140fbb8e55216381f7c4e7124eaa9070d8e8dc92c7870125b251070e29ca19043cf33ccd7324e2ddab03ecc4ae0b5e77c4fc0e5cf6c95a01000000000000105e0000000000000000024730440220029550293885a772c04c2a462b0afc7d9dd03d0286d37e1273d60a64b333875e0220626baf165c2ba70d5ce202bf6d2212a0bab83b457188a52db67d5ce396601d5001210324a1cbd388173f0c72616a0c8fe363daf9c016a157b14aa1dabf6d19b85df95c00000000000000"
93        asset, asset_len =  make_cbuffer('5ac9f65c0efcc4775e0baec4ec03abdde22473cd3cf33c0419ca290e0751b225')
94        txhash, txhash_len = make_cbuffer('caeffdf572f85dd19b6d878c94131cfff73cfbff8630b5ddcf3f83c29b3ab111')
95
96        tx = wally_tx(2)
97        script_sig, script_sig_len = make_cbuffer('160014355347fd5b11a57cddd5e1576fb38280a0627cf7')
98        witness_stack = wally_tx_witness_stack()
99        witness, witness_len = make_cbuffer('30440220029550293885a772c04c2a462b0afc7d9dd03d0286d37e1273d60a64b333875e0220626baf165c2ba70d5ce202bf6d2212a0bab83b457188a52db67d5ce396601d5001')
100        self.assertEqual(wally_tx_witness_stack_add(witness_stack, witness, witness_len), WALLY_OK)
101        witness, witness_len = make_cbuffer('0324a1cbd388173f0c72616a0c8fe363daf9c016a157b14aa1dabf6d19b85df95c')
102        self.assertEqual(wally_tx_witness_stack_add(witness_stack, witness, witness_len), WALLY_OK)
103        self.assertEqual(wally_tx_add_elements_raw_input(tx, txhash[::-1], txhash_len,
104                                                         0, 0xfffffffd, script_sig, script_sig_len, witness_stack,
105                                                         None, 0, None, 0, None, 0, None, 0, None, 0, None, 0, wally_tx_witness_stack(), 0), WALLY_OK)
106
107        unconfidential_satoshi, unconfidential_satoshi_len = make_cbuffer('00'*9)
108        self.assertEqual(wally_tx_confidential_value_from_satoshi(1000*10**8, unconfidential_satoshi, unconfidential_satoshi_len), WALLY_OK)
109        pegout_script, pegout_script_len = self.generate_pegout_script()
110        self.assertEqual(wally_tx_add_elements_raw_output(tx, pegout_script, pegout_script_len, b'\x01'+asset[::-1], asset_len+1,
111                                                          unconfidential_satoshi, unconfidential_satoshi_len, None, 0, None, 0, None, 0, 0), WALLY_OK)
112
113        self.assertEqual(wally_tx_confidential_value_from_satoshi(2099099999995810, unconfidential_satoshi, unconfidential_satoshi_len), WALLY_OK)
114        script, script_len = make_cbuffer('a9140fbb8e55216381f7c4e7124eaa9070d8e8dc92c787')
115        self.assertEqual(wally_tx_add_elements_raw_output(tx, script, script_len, b'\x01'+asset[::-1], asset_len+1,
116                                                          unconfidential_satoshi, unconfidential_satoshi_len, None, 0, None, 0, None, 0, 0), WALLY_OK)
117
118        self.assertEqual(wally_tx_confidential_value_from_satoshi(4190, unconfidential_satoshi, unconfidential_satoshi_len), WALLY_OK)
119        self.assertEqual(wally_tx_add_elements_raw_output(tx, None, 0, b'\x01'+asset[::-1], asset_len+1,
120                                                          unconfidential_satoshi, unconfidential_satoshi_len, None, 0, None, 0, None, 0, 0), WALLY_OK)
121
122        ret, pegout_tx_hex = wally_tx_to_hex(tx, 1)
123        self.assertEqual(ret, WALLY_OK)
124        self.assertEqual(pegout_tx_hex, tx_hex)
125
126
127if __name__ == '__main__':
128    _, val = wally_is_elements_build()
129    if val != 0:
130        unittest.main()
131