11import re
22
3- from electroncash .address import UnknownAddress
4- from electroncash import transaction , token , bitcoin
3+ from electroncash .address import UnknownAddress , Address
4+ from electroncash import transaction , token , bitcoin , util
5+
6+ from .transaction import TransactionWithSighashUTXOS
57
68
79def extract_hex_from_libauth_extended_json_string (s ):
810 hex_regex = r'0x[0-9a-f-A-F]+'
911
1012 match = re .search (hex_regex , s )
1113 if match :
12- return match .group (0 )
14+ group = match .group (0 )
15+ group = group [2 :] if group .startswith ('0x' ) else group
16+ return group
1317
1418def extract_bigint_from_libauth_extended_json_string (s ):
1519 bigint_regex = r'[0-9]+'
1620
1721 match = re .search (bigint_regex , s )
1822 if match :
19- return match .group (0 )
20-
23+ group = match .group (0 )
24+ group = group [:- 1 ] if group .endswith ('n' ) else group
25+ return group
2126
22- def generate_electron_cash_tx_from_libauth_format (tx_data , wallet ):
27+ def generate_electron_cash_tx_from_libauth_format (tx_data , wallet , wallet_connect_address , password ):
2328
2429 transaction_data = tx_data ['transaction' ]
2530 source_outputs_data = tx_data ['sourceOutputs' ]
@@ -28,22 +33,21 @@ def generate_electron_cash_tx_from_libauth_format(tx_data, wallet):
2833 ec_inputs_bytes = list ()
2934 for index , item in enumerate (transaction_data ['inputs' ]):
3035 prevout_hash = extract_hex_from_libauth_extended_json_string (item ['outpointTransactionHash' ])
31- prevout_hash = prevout_hash [2 :] if prevout_hash .startswith ('0x' ) else prevout_hash
3236 script_sig = extract_hex_from_libauth_extended_json_string (item ['unlockingBytecode' ])
3337
38+ # in case of empty script_sig, first check if it's a contract and the contract contains the script_sig
39+ if not script_sig and ('contract' in source_outputs_data [index ].keys () and
40+ source_outputs_data [index ]['contract' ]['artifact' ]['contractName' ]):
41+ script_sig = extract_hex_from_libauth_extended_json_string (source_outputs_data [index ]['unlockingBytecode' ])
42+
3443
3544 address = None
36- locking_byte_code = extract_hex_from_libauth_extended_json_string (
45+ locking_bytecode = extract_hex_from_libauth_extended_json_string (
3746 source_outputs_data [index ]['lockingBytecode' ])
38- locking_byte_code = locking_byte_code [2 :] if locking_byte_code .startswith ('0x' ) else locking_byte_code
39- type , possible_address = transaction .get_address_from_output_script (bytes .fromhex (locking_byte_code ))
47+ type , possible_address = transaction .get_address_from_output_script (bytes .fromhex (locking_bytecode ))
4048 if type in [bitcoin .TYPE_ADDRESS , bitcoin .TYPE_PUBKEY ]:
4149 address = possible_address
4250
43- if script_sig :
44- script_sig = script_sig [2 :] if script_sig .startswith ('0x' ) else script_sig
45- else :
46- script_sig = None
4751 ec_input = {
4852 'prevout_n' : item ['outpointIndex' ],
4953 'prevout_hash' : prevout_hash ,
@@ -54,8 +58,20 @@ def generate_electron_cash_tx_from_libauth_format(tx_data, wallet):
5458 'x_pubkeys' : [],
5559 'pubkeys' : [],
5660 'signatures' : {},
61+ 'walletconnect_locking_bytecode' : locking_bytecode
5762 }
5863
64+ token_output_data = None
65+ if 'token' in source_outputs_data [index ].keys ():
66+ token_output_data = generate_token_data_output_from_token_dict (source_outputs_data [index ]['token' ])
67+ ec_input ['token_data' ] = token_output_data
68+ ec_input ['walletconnect_locking_bytecode' ] = token .wrap_spk (
69+ token_output_data , bytes .fromhex (locking_bytecode )).hex ()
70+
71+ value = extract_bigint_from_libauth_extended_json_string (source_outputs_data [index ]['valueSatoshis' ])
72+ value = int (value )
73+ ec_input ['value' ] = value
74+
5975 if script_sig :
6076 try :
6177 script_sig_data = {}
@@ -94,10 +110,9 @@ def generate_electron_cash_tx_from_libauth_format(tx_data, wallet):
94110 token_data_list = list ()
95111 for item in transaction_data ['outputs' ]:
96112 locking_bytecode = extract_hex_from_libauth_extended_json_string (item ['lockingBytecode' ])
97- locking_bytecode = locking_bytecode [2 :] if locking_bytecode .startswith ('0x' ) else locking_bytecode
98113
99114 item ['valueSatoshis' ] = extract_bigint_from_libauth_extended_json_string (item ['valueSatoshis' ])
100- value = int (item ['valueSatoshis' ][: - 1 ]) if item [ 'valueSatoshis' ]. endswith ( 'n' ) else int ( item [ 'valueSatoshis' ] )
115+ value = int (item ['valueSatoshis' ])
101116
102117 address = transaction .get_address_from_output_script (bytes .fromhex (locking_bytecode ))
103118 ec_output = address + (value ,)
@@ -106,42 +121,8 @@ def generate_electron_cash_tx_from_libauth_format(tx_data, wallet):
106121 output_bytes = b''
107122 output_bytes += bitcoin .int_to_bytes (value , 8 )
108123
109- amount = None
110124 if 'token' in item .keys ():
111- item ['token' ]['category' ] = extract_hex_from_libauth_extended_json_string (item ['token' ]['category' ])
112- category_id = item ['token' ]['category' ]
113- category_id = category_id [2 :] if category_id .startswith ('0x' ) else category_id
114- category_id_arr = bytearray .fromhex (category_id )
115- category_id_arr .reverse ()
116- category_id = bytes (category_id_arr )
117-
118- bitfield = 0
119- if 'amount' in item ['token' ].keys ():
120- item ['token' ]['amount' ] = extract_bigint_from_libauth_extended_json_string (item ['token' ]['amount' ])
121- amount = item ['token' ]['amount' ]
122- amount = int (amount ) if amount .endswith ('n' ) else int (amount )
123- if amount and amount > 0 :
124- bitfield |= token .Structure .HasAmount
125-
126- commitment = b''
127- if 'nft' in item ['token' ].keys ():
128- bitfield |= token .Structure .HasNFT
129- commitment = item ['token' ]['nft' ].get ('commitment' )
130- commitment = commitment .encode () if commitment else b''
131-
132- capability = item ['token' ]['nft' ].get ('capability' )
133-
134- if not capability or capability == 'none' :
135- bitfield |= token .Capability .NoCapability
136- elif capability == 'mutable' :
137- bitfield |= token .Capability .Mutable
138- elif capability == 'minting' :
139- bitfield |= token .Capability .Minting
140-
141- if commitment and len (commitment ) > 0 :
142- bitfield |= token .Structure .HasCommitmentLength
143-
144- token_output_data = token .OutputData (category_id , amount , commitment , bitfield )
125+ token_output_data = generate_token_data_output_from_token_dict (item ['token' ])
145126 token_data_list .append (token_output_data )
146127
147128 wrapped_locking_bytecode = token .wrap_spk (token_output_data , bytes .fromhex (locking_bytecode ))
@@ -187,4 +168,72 @@ def generate_electron_cash_tx_from_libauth_format(tx_data, wallet):
187168 ec_tx = transaction .Transaction .from_io (
188169 inputs = ec_inputs , outputs = ec_outputs , locktime = locktime , token_datas = token_data_list , version = version )
189170 ec_tx ._sign_schnorr = True
171+
172+ for index , input in enumerate (ec_tx .inputs ()):
173+ sig_placeholder = "41" + bytearray (65 ).hex ()
174+ pubkey_placeholder = "21" + bytearray (33 ).hex ()
175+
176+ address = Address .from_string (wallet_connect_address )
177+ priv_key = wallet .export_private_key (address , password )
178+ if input ['scriptSig' ] and input ['scriptSig' ].find (sig_placeholder ) != - 1 :
179+ source_output = source_outputs_data [index ]
180+ assert "contract" in source_output .keys ()
181+ assert "redeemScript" in source_output ['contract' ].keys ()
182+ redeem_script = source_output ['contract' ]['redeemScript' ]
183+ redeem_script = extract_hex_from_libauth_extended_json_string (redeem_script )
184+ input ['scriptCode' ] = redeem_script
185+
186+ ec_tx .__class__ = TransactionWithSighashUTXOS
187+ hash_type = 0x1 | 0x40 | 0x20 # SIGHASH_ALL | SIGHASH_FORKID | SIGHASH_UTXOS # Consistency
188+ type , priv_key , _ = bitcoin .deserialize_privkey (priv_key )
189+ pre_hash = bitcoin .Hash (ec_tx .serialize_preimage_bytes (index , hash_type ))
190+ sig = ec_tx ._schnorr_sign (bitcoin .public_key_from_private_key (priv_key , True ), priv_key , pre_hash )
191+ sig = '41' + util .bh2u (sig + bytes ((hash_type & 0xff ,)))
192+ input ['scriptSig' ] = input ['scriptSig' ].replace (sig_placeholder , sig )
193+ ec_tx .__class__ = transaction .Transaction
194+
195+ if input ['scriptSig' ] and input ['scriptSig' ].find (pubkey_placeholder ) != - 1 :
196+ pubkey = '21' + bitcoin .public_key_from_private_key (priv_key , True )
197+ input ['scriptSig' ] = input ['scriptSig' ].replace (pubkey_placeholder , pubkey )
198+
190199 return ec_tx
200+
201+
202+
203+ def generate_token_data_output_from_token_dict (token_dict ):
204+ token_dict ['category' ] = extract_hex_from_libauth_extended_json_string (token_dict ['category' ])
205+ category_id = token_dict ['category' ]
206+ category_id_arr = bytearray .fromhex (category_id )
207+ category_id_arr .reverse ()
208+ category_id = bytes (category_id_arr )
209+
210+ amount = None
211+ bitfield = 0
212+ if 'amount' in token_dict .keys ():
213+ token_dict ['amount' ] = extract_bigint_from_libauth_extended_json_string (token_dict ['amount' ])
214+ amount = int (token_dict ['amount' ])
215+ if amount and amount > 0 :
216+ bitfield |= token .Structure .HasAmount
217+
218+ commitment = b''
219+ if 'nft' in token_dict .keys ():
220+ bitfield |= token .Structure .HasNFT
221+ commitment = token_dict ['nft' ].get ('commitment' )
222+ commitment = extract_hex_from_libauth_extended_json_string (commitment ) if commitment else None
223+ commitment = bytes .fromhex (commitment ) if commitment else b''
224+
225+ capability = token_dict ['nft' ].get ('capability' )
226+
227+ if not capability or capability == 'none' :
228+ bitfield |= token .Capability .NoCapability
229+ elif capability == 'mutable' :
230+ bitfield |= token .Capability .Mutable
231+ elif capability == 'minting' :
232+ bitfield |= token .Capability .Minting
233+
234+ if commitment and len (commitment ) > 0 :
235+ bitfield |= token .Structure .HasCommitmentLength
236+
237+ token_output_data = token .OutputData (category_id , amount , commitment , bitfield )
238+
239+ return token_output_data
0 commit comments