OSDN Git Service

fix receiver bug
[bytom/pybtm.git] / pybtm / transaction.py
1 import requests
2 import json
3 from _pysha3 import sha3_256
4 from .receiver import *
5
6 # submit_transaction broadcast raw transaction
7 # raw_transaction_str is signed transaction,
8 # test data 1:
9 #   raw_transaction_hexstr: 070100010160015e5dfc352f9247985e92b2688a9a0a0e3e45a52f633c7d2c35cf6485fc1f03a89cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8094ebdc030101160014052620b86a6d5e07311d5019dffa3864ccc8a6bd630240988348a301c86563eb16105cc0c7e12e8cd1fbc7e9031933dac05a32d2a696bc77b83f25a99a4a9458d976c5327b8004918545a3fde567f28d805f741db54e0b20e87ca3acdebdcad9a1d0f2caecf8ce0dbfc73d060807a210c6f225488347961402013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80b6f7f302011600147950bb5fcfb1c3fe14198c14ebd4ad85bb69bbc500013cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8084af5f011600141d8e1c2d71843f41e2131d7fd6df8b47e2cf56b900
10 #   submit_url: https://blockmeta.com/api/wisdom/broadcast-transaction
11 # test data 2:
12 #   raw_transaction_hexstr: 07010001015f015d2f4a8f10afbc0448779fadd916a3f1b8518ffe0b7d20fdf470d8e9b4993ef2b4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc0d8883200011600144a594e3e4cbbd87629476e7ee24c1637df66c0b76302406fd39079681118840fd6fd66cdff769f2d05d8520312e9dd559dc23c36a3cb3921e47cba233d5d2267eb0f128a908d1bab877e172e880d3f36dc6a5e5826540c202854e5c181f5a862edd190e413d75937549758ef4902e1475aac52623f0a239302013cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc09dd81001160014f63f68597df5c88a92e04229e0fd08a3584ade3b00013cffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80e1eb1701160014664f28ec6ab8826a028658dc0d0d1f94c6e20fa300
13 #   submit_url: https://blockmeta.com/api/v2/broadcast-transaction
14 def submit_transaction(raw_transaction_hexstr, submit_url):
15     raw_transaction_dict = {
16         "transaction": raw_transaction_hexstr
17     }
18     raw_transaction_json = json.dumps(raw_transaction_dict)
19     headers = {
20         "content-type": "application/json",
21         "accept": "application/json"
22     }
23     response = requests.post(submit_url, headers=headers, data=raw_transaction_json)
24     return response.text[:-1]
25
26
27 # def decode_raw_transaction(raw_transaction_str):
28 #     raw_transaction_dict = {
29 #         "raw_transaction": raw_transaction_str
30 #     }
31 #     raw_transaction_json = json.dumps(raw_transaction_dict)
32 #     headers = {
33 #         "content-type": "application/json",
34 #         "accept": "application/json"
35 #     }
36 #     url = 'http://127.0.0.1:9888/decode-raw-transaction'
37 #     response = requests.post(url, headers=headers, data=raw_transaction_json)
38 #     return {
39 #         "response": response.text[:-1]
40 #     }
41
42
43 def get_uvarint(uvarint_str):
44     uvarint_bytes = bytes.fromhex(uvarint_str)
45     x, s, i = 0, 0, 0
46     while True:
47         b = uvarint_bytes[i]
48         if b < 0x80:
49             if i > 9 or i == 9 and b > 1:
50                 return "overflow"
51             return x | int(b) << s, i + 1
52         x |= int(b & 0x7f) << s
53         s += 7
54         i += 1
55
56
57 '''
58 get_spend_output_id create tx_input spend output id
59 test data 1:
60   source_id_hexstr: 28b7b53d8dc90006bf97e0a4eaae2a72ec3d869873188698b694beaf20789f21
61   asset_id_hexstr: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
62   amount_int: 41250000000
63   source_position_int: 0
64   vmversion_int: 1
65   control_program_hexstr: 00149335b1cbd4a77b78e33315a0ed10a95b12e7ca48
66   spend_output_id_hexstr: f229ec6f403d586dc87aa2546bbe64c5f7b5f46eb13c6ee4823d03bc88a7cf17
67 '''
68 def get_spend_output_id(source_id_hexstr, asset_id_hexstr, amount_int, source_position_int, vmversion_int, control_program_hexstr):
69     amount_hexstr = amount_int.to_bytes(8, byteorder='little').hex()
70     source_position_hexstr = source_position_int.to_bytes(8, byteorder='little').hex()
71     vmversion_hexstr = vmversion_int.to_bytes(8, byteorder='little').hex()
72     cp_length_int = len(control_program_hexstr) // 2
73     cp_length_hexstr = cp_length_int.to_bytes((cp_length_int.bit_length() + 7) // 8, byteorder='little').hex()
74     sc_hexstr = source_id_hexstr + asset_id_hexstr + amount_hexstr + source_position_hexstr + vmversion_hexstr + cp_length_hexstr +  control_program_hexstr
75     innerhash_bytes = sha3_256(bytes.fromhex(sc_hexstr)).digest()
76     spend_bytes = b'entryid:output1:' + innerhash_bytes
77     spend_output_id_hexstr = sha3_256(spend_bytes).hexdigest()
78     return spend_output_id_hexstr
79
80 '''
81 get_input_id create tx input_id
82 test data 1:
83     spend_output_id_hexstr: f229ec6f403d586dc87aa2546bbe64c5f7b5f46eb13c6ee4823d03bc88a7cf17
84     input_id_hexstr: 6e3f378ed844b143a335e306f4ba26746157589c87e8fc8cba6463c566c56768
85 '''
86 def get_input_id(spend_output_id_hexstr):
87     innerhash_bytes = sha3_256(bytes.fromhex(spend_output_id_hexstr)).digest()
88     input_id_hexstr = sha3_256(b'entryid:spend1:' + innerhash_bytes).hexdigest()
89     return input_id_hexstr
90
91
92 def get_mux_id(prepare_mux_hexstr):
93     innerhash_bytes = sha3_256(bytes.fromhex(prepare_mux_hexstr)).digest()
94     mux_id_hexstr = sha3_256(b'entryid:mux1:' + innerhash_bytes).hexdigest()
95     return mux_id_hexstr
96
97
98 def get_output_id(prepare_output_id_hexstr):
99     innerhash_bytes = sha3_256(bytes.fromhex(prepare_output_id_hexstr)).digest()
100     output_id_hexstr = sha3_256(b'entryid:output1:' + innerhash_bytes).hexdigest()
101     return output_id_hexstr
102
103
104 def get_tx_id(prepare_tx_id_hexstr):
105     innerhash_bytes = sha3_256(bytes.fromhex(prepare_tx_id_hexstr)).digest()
106     tx_id_hexstr = sha3_256(b'entryid:txheader:' + innerhash_bytes).hexdigest()
107     return tx_id_hexstr
108
109
110 def get_issue_input_id(prepare_issue_hexstr):
111     innerhash_bytes = sha3_256(bytes.fromhex(prepare_issue_hexstr)).digest()
112     tx_id_hexstr = sha3_256(b'entryid:issuance1:' + innerhash_bytes).hexdigest()
113     return tx_id_hexstr
114
115
116 def get_coinbase_input_id(prepare_coinbase_input_id_hexstr):
117     innerhash_bytes = sha3_256(bytes.fromhex(prepare_coinbase_input_id_hexstr)).digest()
118     coinbase_input_id_hexstr = sha3_256(b'entryid:coinbase1:' + innerhash_bytes).hexdigest()
119     return coinbase_input_id_hexstr
120
121
122 '''
123 decode_raw_tx decode raw transaction
124 testdata 1:
125     raw_transaction_str: 070100010161015f28b7b53d8dc90006bf97e0a4eaae2a72ec3d869873188698b694beaf20789f21ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8099c4d5990100011600149335b1cbd4a77b78e33315a0ed10a95b12e7ca48630240897e2d9d24a3b5faaed0579dee7597b401491595675f897504f8945b29d836235bd2fca72a3ad0cae814628973ebcd142d9d6cc92d0b2571b69e5370a98a340c208cb7fb3086f58db9a31401b99e8c658be66134fb9034de1d5c462679270b090702013effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80f9f8bc98010116001406ce4b689ba026ffd3a7ca65d1d059546d4b78a000013dffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80c6868f01011600147929ef91997c827bebf60fa608f876ea27523c4700
126     network_str: solonet
127     transaction: 
128 {
129   "fee": 20000000,
130   "inputs": [
131     {
132       "address": "sm1qjv6mrj755aah3cenzksw6y9ftvfw0jjgk0l2mw",
133       "amount": 41250000000,
134       "asset_definition": {},
135       "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
136       "control_program": "00149335b1cbd4a77b78e33315a0ed10a95b12e7ca48",
137       "input_id": "6e3f378ed844b143a335e306f4ba26746157589c87e8fc8cba6463c566c56768",
138       "spent_output_id": "f229ec6f403d586dc87aa2546bbe64c5f7b5f46eb13c6ee4823d03bc88a7cf17",
139       "type": "spend",
140       "witness_arguments": [
141         "897e2d9d24a3b5faaed0579dee7597b401491595675f897504f8945b29d836235bd2fca72a3ad0cae814628973ebcd142d9d6cc92d0b2571b69e5370a98a340c",
142         "8cb7fb3086f58db9a31401b99e8c658be66134fb9034de1d5c462679270b0907"
143       ]
144     }
145   ],
146   "outputs": [
147     {
148       "address": "sm1qqm8yk6ym5qn0l5a8efjar5ze23k5k79qnvtslj",
149       "amount": 40930000000,
150       "asset_definition": {},
151       "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
152       "control_program": "001406ce4b689ba026ffd3a7ca65d1d059546d4b78a0",
153       "id": "74c73266730d3c6ea32e8667ef9b867068736b84be240fe9fef205fa68bb7b95",
154       "position": 0,
155       "type": "control"
156     },
157     {
158       "address": "sm1q0y57lyve0jp8h6lkp7nq37rkagn4y0z8hvh6kq",
159       "amount": 300000000,
160       "asset_definition": {},
161       "asset_id": "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
162       "control_program": "00147929ef91997c827bebf60fa608f876ea27523c47",
163       "id": "f115a833d0c302a5006032858a7ed3987f0feb2daf2a9f849384950e4766af51",
164       "position": 1,
165       "type": "control"
166     }
167   ],
168   "size": 333,
169   "time_range": 0,
170   "tx_id": "814a73dd57bae67c604f9cbc696cbc42035577423408cb9267136ed971e2bf63",
171   "version": 1
172 }
173 '''
174 def decode_raw_tx(raw_transaction_str, network_str):
175     tx = {
176         "fee": 0,
177         "inputs": [],
178         "outputs": [],
179         "size": 0,
180         "time_range": 0,
181         "tx_id": "",
182         "version": 0
183     }
184     tx['fee'] = 0
185     tx['size'] = len(raw_transaction_str) // 2
186     length = 0
187     offset = 2
188     tx['version'], length = get_uvarint(raw_transaction_str[offset:offset+18])
189     offset = offset + 2 * length
190     tx['time_range'], length = get_uvarint(raw_transaction_str[offset:offset+18])
191     offset = offset + 2 * length
192     tx_input_amount, length = get_uvarint(raw_transaction_str[offset:offset+8])
193     offset = offset + 2 * length
194     prepare_mux_hexstr = (tx_input_amount).to_bytes((tx_input_amount.bit_length() + 7) // 8, 'little').hex()
195     prepare_tx_id_hexstr = (tx['version']).to_bytes(8, 'little').hex() + (tx['time_range']).to_bytes(8, 'little').hex()
196     for _ in range(tx_input_amount):
197         _, length = get_uvarint(raw_transaction_str[offset:offset+18])
198         offset = offset + 2 * length
199         _, length = get_uvarint(raw_transaction_str[offset:offset+18])
200         offset = offset + 2 * length
201         input_type = int(raw_transaction_str[offset:offset+2], 16)
202         offset += 2
203         if input_type == 0: # issue
204             tx_input = {
205                 "amount": 0,
206                 "asset_definition": "", # TODO:fix it!!
207                 "asset_id": "",
208                 "input_id": "",
209                 "issuance_program": "",
210                 "type": "",
211                 "witness_arguments": []
212             }
213             tx_input['type'] = "issue"
214             _, length = get_uvarint(raw_transaction_str[offset:offset+18])
215             offset = offset + 2 * length
216             nonce = raw_transaction_str[offset:offset+16]
217             offset += 16
218             nonce_hash_hexstr = sha3_256(bytes.fromhex(nonce)).hexdigest()
219             tx_input['asset_id'] = raw_transaction_str[offset:offset+64]
220             offset += 64
221             tx_input['amount'], length = get_uvarint(raw_transaction_str[offset:offset+18])
222             offset = offset + 2 * length
223             _, length = get_uvarint(raw_transaction_str[offset:offset+18])
224             offset = offset + 2 * length
225             asset_definition_size, length = get_uvarint(raw_transaction_str[offset:offset+18])
226             offset = offset + 2 * length
227             tx_input['asset_definition'] = bytes.fromhex(raw_transaction_str[offset:offset+2*asset_definition_size]).decode()
228             offset = offset + 2 * asset_definition_size
229             _, length = get_uvarint(raw_transaction_str[offset:offset+18])
230             offset = offset + 2 * length
231             issuance_program_length, length = get_uvarint(raw_transaction_str[offset:offset+18])
232             offset = offset + 2 * length
233             tx_input['issuance_program'] = raw_transaction_str[offset:offset+2*issuance_program_length]
234             offset = offset + 2 * issuance_program_length
235             witness_arguments_amount, length = get_uvarint(raw_transaction_str[offset:offset+18])
236             offset = offset + 2 * length
237             for _ in range(witness_arguments_amount):
238                 argument_length, length = get_uvarint(raw_transaction_str[offset:offset+18])
239                 offset = offset + 2 * length
240                 argument = raw_transaction_str[offset:offset+2*argument_length]
241                 offset = offset + 2 * argument_length
242                 tx_input['witness_arguments'].append(argument)
243             prepare_issue_hexstr = nonce_hash_hexstr + tx_input['asset_id'] + (tx_input['amount']).to_bytes(8, byteorder='little').hex()
244             tx_input['input_id'] = get_issue_input_id(prepare_issue_hexstr)
245             tx['inputs'].append(tx_input)
246             prepare_mux_hexstr += tx_input['input_id'] + tx_input['asset_id'] + (tx_input['amount']).to_bytes(8, byteorder='little').hex() + '0000000000000000'
247             prepare_mux_hexstr += '0100000000000000' + '0151'
248             mux_id_hexstr = get_mux_id(prepare_mux_hexstr)
249         elif input_type == 1: # spend
250             tx_input = {
251                 "address": "",
252                 "amount": 0,
253                 "asset_definition": {},
254                 "asset_id": "",
255                 "control_program": "",
256                 "input_id": "",
257                 "spent_output_id": "",
258                 "type": "",
259                 "witness_arguments": []
260             }
261             tx_input['type'] = "spend"
262             _, length = get_uvarint(raw_transaction_str[offset:offset+18])
263             offset = offset + 2 * length
264             source_id = raw_transaction_str[offset:offset+64]
265             offset += 64
266             tx_input['asset_id'] = raw_transaction_str[offset:offset+64]
267             offset += 64
268             tx_input['amount'], length = get_uvarint(raw_transaction_str[offset:offset+18])
269             offset = offset + 2 * length
270             tx['fee'] += tx_input['amount']
271             source_positon, length = get_uvarint(raw_transaction_str[offset:offset+18])
272             offset = offset + 2 * length
273             vmversion, length = get_uvarint(raw_transaction_str[offset:offset+18])
274             offset = offset + 2 * length
275             control_program_length, length = get_uvarint(raw_transaction_str[offset:offset+18])
276             offset = offset + 2 * length
277             tx_input['control_program'] = raw_transaction_str[offset:offset+2*control_program_length]
278             offset = offset + 2 * control_program_length
279             tx_input['address'] = get_address(tx_input['control_program'], network_str)['address']
280             _, length = get_uvarint(raw_transaction_str[offset:offset+18])
281             offset = offset + 2 * length
282             witness_arguments_amount, length = get_uvarint(raw_transaction_str[offset:offset+18])
283             offset = offset + 2 * length
284             if witness_arguments_amount == 1:
285                 offset = offset + 2
286                 tx_input['witness_arguments'] = None
287             else: 
288                 for _ in range(witness_arguments_amount):
289                     argument_length, length = get_uvarint(raw_transaction_str[offset:offset+18])
290                     offset = offset + 2 * length
291                     argument = raw_transaction_str[offset:offset+2*argument_length]
292                     offset = offset + 2 * argument_length
293                     tx_input['witness_arguments'].append(argument)
294             tx_input['spent_output_id'] = get_spend_output_id(source_id, tx_input['asset_id'], tx_input['amount'], source_positon, vmversion, tx_input['control_program'])
295             tx_input['input_id'] = get_input_id(tx_input['spent_output_id'])
296             tx['inputs'].append(tx_input)
297             prepare_mux_hexstr += tx_input['input_id'] + tx_input['asset_id'] + (tx_input['amount']).to_bytes(8, byteorder='little').hex() + '0000000000000000'
298             prepare_mux_hexstr += '0100000000000000' + '0151'
299             mux_id_hexstr = get_mux_id(prepare_mux_hexstr)
300         elif input_type == 2: # coinbase
301             tx_input = {
302                 "amount": 0,
303                 "arbitrary": "",
304                 "asset_definition": {},
305                 "asset_id": "0000000000000000000000000000000000000000000000000000000000000000",
306                 "input_id": "",
307                 "type": "",
308                 "witness_arguments": []
309             }
310             tx_input['type'] = "coinbase"
311             arbitrary_length, length = get_uvarint(raw_transaction_str[offset:offset+18])
312             prepare_coinbase_input_id_hexstr = raw_transaction_str[offset:offset+2*length]
313             offset = offset + 2 * length
314             tx_input['arbitrary'] = raw_transaction_str[offset:offset+2*arbitrary_length]
315             prepare_coinbase_input_id_hexstr += tx_input['arbitrary']
316             offset = offset + 2 * arbitrary_length
317             tx_input['input_id'] = get_coinbase_input_id(prepare_coinbase_input_id_hexstr)
318             offset = offset + 2
319             tx['inputs'].append(tx_input)
320             prepare_mux_hexstr += tx_input['input_id'] + 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff'
321     tx_output_amount, length = get_uvarint(raw_transaction_str[offset:offset+18])
322     offset = offset + 2 * length
323     prepare_tx_id_hexstr += (tx_output_amount).to_bytes((tx_output_amount.bit_length() + 7) // 8, 'little').hex()
324     for i in range(tx_output_amount):
325         tx_output = {
326             "address": "",
327             "amount": 0,
328             "asset_definition": {},
329             "asset_id": "",
330             "control_program": "",
331             "id": "",
332             "position": 0,
333             "type": ""
334         }
335         tx_output['position'] = i
336         _, length = get_uvarint(raw_transaction_str[offset:offset+18])
337         offset = offset + 2 * length
338         _, length = get_uvarint(raw_transaction_str[offset:offset+18])
339         offset = offset + 2 * length
340         tx_output['asset_id'] = raw_transaction_str[offset:offset+64]
341         offset = offset + 64
342         tx_output['amount'], length = get_uvarint(raw_transaction_str[offset:offset+18])
343         if tx_input['type'] == "coinbase":
344             prepare_mux_hexstr = prepare_mux_hexstr + (tx_output['amount']).to_bytes(8, byteorder='little').hex() + '0000000000000000' + '0100000000000000' + '0151'
345             mux_id_hexstr = get_mux_id(prepare_mux_hexstr)
346         offset = offset + 2 * length
347         if tx_output['asset_id'] == 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff':
348             tx['fee'] -= tx_output['amount']
349         _, length = get_uvarint(raw_transaction_str[offset:offset+18])
350         offset = offset + 2 * length
351         control_program_length, length = get_uvarint(raw_transaction_str[offset:offset+18])
352         offset = offset + 2 * length
353         tx_output['control_program'] = raw_transaction_str[offset:offset+2*control_program_length]
354         offset = offset + 2 * control_program_length
355         tx_output['address'] = get_address(tx_output['control_program'], network_str)['address']
356         _, length = get_uvarint(raw_transaction_str[offset:offset+18])
357         offset = offset + 2 * length
358         prepare_output_id_hexstr = mux_id_hexstr + tx_output['asset_id'] + (tx_output['amount']).to_bytes(8, byteorder='little').hex() + (i).to_bytes(8, byteorder='little').hex() + '0100000000000000' + (control_program_length).to_bytes((control_program_length.bit_length() + 7) // 8, 'little').hex() + tx_output['control_program']
359         tx_output['id'] = get_output_id(prepare_output_id_hexstr)
360         prepare_tx_id_hexstr += tx_output['id']
361         tx_output['type'] = 'control'
362         tx['outputs'].append(tx_output)
363     if tx_input['type'] == "coinbase":
364         tx['fee'] = 0
365     tx['tx_id'] = get_tx_id(prepare_tx_id_hexstr)
366     return tx