OSDN Git Service

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