def test_extract_commitment_number_from_tx(self): raw_tx = "02000000000101bef67e4e2fb9ddeeb3461973cd4c62abb35050b1add772995b820b584a488489000000000038b02b8007e80300000000000022002052bfef0479d7b293c27e0f1eb294bea154c63a3294ef092c19af51409bce0e2ad007000000000000220020403d394747cae42e98ff01734ad5c08f82ba123d3d9a620abda88989651e2ab5d007000000000000220020748eba944fedc8827f6b06bc44678f93c0f9e6078b35c6331ed31e75f8ce0c2db80b000000000000220020c20b5d1f8584fd90443e7b7b720136174fa4b9333c261d04dbbd012635c0f419a00f0000000000002200208c48d15160397c9731df9bc3b236656efb6665fbfe92b4a6878e88a499f741c4c0c62d0000000000160014ccf1af2f2aabee14bb40fa3851ab2301de843110e0a06a00000000002200204adb4e2f00643db396dd120d4e7dc17625f5f2c11a40d857accc862d6b7dd80e04004730440220275b0c325a5e9355650dc30c0eccfbc7efb23987c24b556b9dfdd40effca18d202206caceb2c067836c51f296740c7ae807ffcbfbf1dd3a0d56b6de9a5b247985f060147304402204fd4928835db1ccdfc40f5c78ce9bd65249b16348df81f0c44328dcdefc97d630220194d3869c38bc732dd87d13d2958015e2fc16829e74cd4377f84d215c0b7060601475221023da092f6980e58d2c037173180e9a465476026ee50f96695963e8efe436f54eb21030e9f7b623d2ccc7c9bd44d66d5ce21ce504c0acf6385a132cec6d3c39fa711c152ae3e195220" tx = Transaction(raw_tx) self.assertEqual( commitment_number, extract_ctn_from_tx(tx, 0, local_payment_basepoint, remote_payment_basepoint))
def test_payto(self, mock_save_db): wallet = restore_wallet_from_text('disagree rug lemon bean unaware square alone beach tennis exhibit fix mimic', gap_limit=2, path='if_this_exists_mocking_failed_648151893', config=self.config)['wallet'] # bootstrap wallet funding_tx = Transaction('0200000000010165806607dd458280cb57bf64a16cf4be85d053145227b98c28932e953076b8e20000000000fdffffff02ac150700000000001600147e3ddfe6232e448a8390f3073c7a3b2044fd17eb102908000000000016001427fbe3707bc57e5bb63d6f15733ec88626d8188a02473044022049ce9efbab88808720aa563e2d9bc40226389ab459c4390ea3e89465665d593502206c1c7c30a2f640af1e463e5107ee4cfc0ee22664cfae3f2606a95303b54cdef80121026269e54d06f7070c1f967eb2874ba60de550dfc327a945c98eb773672d9411fd77181e00') funding_txid = funding_tx.txid() self.assertEqual('ede61d39e501d65ccf34e6300da439419c43393f793bb9a8a4b06b2d0d80a8a0', funding_txid) wallet.receive_tx_callback(funding_txid, funding_tx, TX_HEIGHT_UNCONFIRMED) cmds = Commands(config=self.config) tx_str = cmds._run( 'payto', (), destination="tltc1qsyzgpwa0vg2940u5t6l97etuvedr5dejcp84ad", amount="0.00123456", feerate=50, locktime=1972344, wallet=wallet) tx = tx_from_any(tx_str) self.assertEqual(2, len(tx.outputs())) txout = TxOutput.from_address_and_value("tltc1qsyzgpwa0vg2940u5t6l97etuvedr5dejcp84ad", 123456) self.assertTrue(txout in tx.outputs()) self.assertEqual("02000000000101a0a8800d2d6bb0a4a8b93b793f39439c4139a40d30e634cf5cd601e5391de6ed0100000000fdffffff0240e2010000000000160014810480bbaf62145abf945ebe5f657c665a3a3732462b060000000000160014a5103285eb519f826520a9f7d3227e1eaa7ec5f802473044022057a6f4b1ec63336c7d0ba233e785ec9f2e2d9c2d67617a50e069f4498ee6a3b7022032fb331e0bef06f46e9cb77bfe94413142653c4912516835e941fa7f170c1a53012103001b55f19541faaf7e6d57dd1bdb9fdc37725fc500e12f2418cc11e0aed4154978181e00", tx_str)
def on_qr(self, data): from electrum_ltc.bitcoin import base_decode, is_address data = data.strip() if is_address(data): self.set_URI(data) return if data.startswith('litecoin:'): self.set_URI(data) return if data.startswith('ln'): self.set_ln_invoice(data) return # try to decode transaction from electrum_ltc.transaction import Transaction from electrum_ltc.util import bh2u try: text = bh2u(base_decode(data, None, base=43)) tx = Transaction(text) tx.deserialize() except: tx = None if tx: self.tx_dialog(tx) return # show error self.show_error("Unable to decode QR data")
def get_input_tx(self, tx_hash): # First look up an input transaction in the wallet where it # will likely be. If co-signing a transaction it may not have # all the input txs, in which case we ask the network. tx = self.transactions.get(tx_hash) if not tx: request = ('blockchain.transaction.get', [tx_hash]) # FIXME: what if offline? tx = Transaction(self.network.synchronous_get(request)) return tx
def parse(self, raw, filename=None): # auto-detect and decode Base64 and Hex. if raw[0:10].lower() == b'70736274ff': raw = a2b_hex(raw.strip()) if raw[0:6] == b'cHNidP': raw = b64decode(raw) assert raw[0:5] == b'psbt\xff', "bad magic" self.filename = filename with io.BytesIO(raw[5:]) as fd: # globals while 1: ks = deser_compact_size(fd) if ks is None: break if ks == 0: break key = fd.read(ks) vs = deser_compact_size(fd) val = fd.read(vs) kt = key[0] if kt == PSBT_GLOBAL_UNSIGNED_TX: self.txn = val self.parsed_txn = Transaction(val.hex()) num_ins = len(self.parsed_txn.inputs()) num_outs = len(self.parsed_txn.outputs()) elif kt == PSBT_GLOBAL_XPUB: # key=(xpub) => val=(path) self.xpubs.append((key, val)) else: raise ValueError('unknown global key type: 0x%02x' % kt) assert self.txn, 'missing reqd section' self.inputs = [BasicPSBTInput(fd, idx) for idx in range(num_ins)] self.outputs = [ BasicPSBTOutput(fd, idx) for idx in range(num_outs) ] sep = fd.read(1) assert sep == b'' return self
def do_paste(self): data = self.app._clipboard.paste() if not data: self.app.show_info(_("Clipboard is empty")) return # try to decode as transaction try: raw_tx = tx_from_str(data) tx = Transaction(raw_tx) tx.deserialize() except: tx = None if tx: self.app.tx_dialog(tx) return # try to decode as URI/address self.set_URI(data)
def on_qr(self, data): if data.startswith('litecoin:'): self.set_URI(data) return # try to decode transaction from electrum_ltc.bitcoin import base_decode from electrum_ltc.transaction import Transaction try: text = base_decode(data, None, base=43).encode('hex') tx = Transaction(text) except: tx = None if tx: self.tx_dialog(tx) return # show error self.show_error("Unable to decode QR data")
def recover_tx_from_psbt(first: BasicPSBT, wallet: Abstract_Wallet) -> Transaction: # Take a PSBT object and re-construct the Electrum transaction object. # - does not include signatures, see merge_sigs_from_psbt # - any PSBT in the group could be used for this purpose; all must share tx details tx = Transaction(first.txn.hex()) tx.deserialize(force_full_parse=True) # .. add back some data that's been preserved in the PSBT, but isn't part of # of the unsigned bitcoin txn tx.is_partial_originally = True for idx, inp in enumerate(tx.inputs()): scr = first.inputs[idx].redeem_script or first.inputs[idx].witness_script # XXX should use transaction.py parse_scriptSig() here! if scr: try: M, N, __, pubkeys, __ = parse_redeemScript_multisig(scr) except NotRecognizedRedeemScript: # limitation: we can only handle M-of-N multisig here raise ValueError("Cannot handle non M-of-N multisig input") inp['pubkeys'] = pubkeys inp['x_pubkeys'] = pubkeys inp['num_sig'] = M inp['type'] = 'p2wsh' if first.inputs[idx].witness_script else 'p2sh' # bugfix: transaction.py:parse_input() puts empty dict here, but need a list inp['signatures'] = [None] * N if 'prev_tx' not in inp: # fetch info about inputs' previous txn wallet.add_hw_info(tx) if 'value' not in inp: # we'll need to know the value of the outpts used as part # of the witness data, much later... inp['value'] = inp['prev_tx'].outputs()[inp['prevout_n']].value return tx
def do_paste(self): data = self.app._clipboard.paste().strip() if not data: self.app.show_info(_("Clipboard is empty")) return # try to decode as transaction try: raw_tx = tx_from_str(data) tx = Transaction(raw_tx) tx.deserialize() except: tx = None if tx: self.app.tx_dialog(tx) return lower = data.lower() if lower.startswith('lightning:ln'): lower = lower[10:] # try to decode as URI/address if lower.startswith('ln'): self.set_ln_invoice(lower) else: self.set_URI(data)
def Tx_test(self): global GRowTransaction global GFileID global Tx_res global GIDTran global ST4del try: XTr = Transaction(GRowTransaction) except: Tx_res = 0 return print('XTr = ', XTr) Flv, FLfee, TTime = FilePP[GFileID] if (Flv + FLfee == 0.): Tx_res = 1 return try: amount, fee = Tx_IOSave[GIDTran] except: try: #is_relevant, is_mine, v, fee = self.window.wallet.get_wallet_delta(XTr) tx_hash, status, label, can_broadcast, can_rbf, amount, fee, height, conf, timestamp, exp_n = self.window.wallet.get_tx_info( XTr) if status != u'Signed': Tx_res = 0 return InputAdrs = {} for x in XTr.inputs(): InputAdrs[x['address']] = None for x in InputAdrs: InputAdrs[x] = self.window.network.synchronous_get( ('blockchain.address.listunspent', [x])) fee = 0 amount = 0 for x in XTr.inputs(): #print(InputAdrs[x['address']] ) for y in InputAdrs[x['address']]: if y['tx_hash'] == x['prevout_hash']: fee += int(y['value']) for addr, value in XTr.get_outputs(): #print('get_outputs=',addr, value , FFS.ReceivAddress ) fee -= value if addr == FFS.ReceivAddress: amount += value if len(ST4del) > 9: del Tx_IOSave[ST4del[0]] del ST4del[0] Tx_IOSave[GIDTran] = amount, fee ST4del.append(GIDTran) except: Tx_res = 0 return #fee = amount - XTr.output_value() self.window.show_transaction(XTr, u'') print("amount, fee =", amount, fee, Flv, FLfee, TTime, len(GRowTransaction)) dFlv = abs(Flv * 1e8 - amount) dFLfee = abs(FLfee * 1e8 - fee * 1000. / (len(GRowTransaction) / 2)) if dFLfee + dFlv < 1000: msg = 'MemPoolLimit' if amount > MemPoolLimit * 1e8: status, msg = None, None try: status, msg = self.window.network.broadcast(XTr) print('broadcast_try=', status, msg) except: pass print('broadcast=', status, msg) Tx_res = 1 return Tx_res = 0
def test_verify_ok_t_tx(self): """Actually mined 64 byte tx should not raise.""" t_tx = Transaction(VALID_64_BYTE_TX) t_tx_hash = t_tx.txid() self.assertEqual(MERKLE_ROOT, SPV.hash_merkle_root(MERKLE_BRANCH, t_tx_hash, 3))
def Tx_test(self): global GRowTransaction global GFileID global ST4del global Tx_IOSave try: XTr = Transaction(GRowTransaction) except: return 0 #print('XTr = ',XTr) Flv , FLfee , TTime = FFS.FilePP[GFileID] if( Flv + FLfee == 0. ): return 1 try: amount,fee = Tx_IOSave[self.IDTran] except: if not XTr.is_complete(): return 0 try: #is_relevant, is_mine, v, fee = self.window.wallet.get_wallet_delta(XTr) self.netw = Network(None) self.netw.start() InputAdrs = {} for x in XTr.inputs(): InputAdrs[x['address']]=None for x in InputAdrs: InputAdrs[x]=self.netw.synchronous_get(('blockchain.address.listunspent', [x])) fee = 0 amount = 0 for x in XTr.inputs(): print(InputAdrs[x['address']] ) for y in InputAdrs[x['address']]: if y['tx_hash'] == x['prevout_hash']: fee += int( y['value']) for addr, value in XTr.get_outputs(): print('get_outputs=',addr, value , FFS.ReceivAddress ) fee -= value if addr == FFS.ReceivAddress: amount += value if len(ST4del) > 9: del Tx_IOSave[ST4del[0]] del ST4del[0] Tx_IOSave[self.IDTran] = amount,fee ST4del.append(self.IDTran) except: return 0 dFlv = abs (Flv * 1e8 - amount ) dFLfee = abs (FLfee * 1e8 - fee * 1000. / (len(GRowTransaction)/2) ) if dFLfee + dFlv < 1000 : msg = 'MemPoolLimit' if amount > MemPoolLimit * 1e8 : status, msg = None,None try: status, msg = self.window.network.broadcast(XTr) print('broadcast_try=',status, msg) except: pass print('broadcast=',status, msg) return 1 return 0
def main(self): add_menu() welcome = 'Use the menu to scan a transaction.' droid.fullShow(qr_layout(welcome)) while True: event = droid.eventWait().result if not event: continue elif event["name"] == "key": if event["data"]["key"] == '4': if self.qr_data: self.show_qr(None) self.show_title(welcome) else: break elif event["name"] == "seed": password = self.get_password() if password is False: modal_dialog('Error','incorrect password') continue seed = wallet.get_mnemonic(password) modal_dialog('Your seed is', seed) elif event["name"] == "password": self.change_password_dialog() elif event["name"] == "xpub": mpk = wallet.get_master_public_key() self.show_qr(mpk) self.show_title('master public key') elif event["name"] == "scan": r = droid.scanBarcode() r = r.result if not r: continue data = r['extras']['SCAN_RESULT'] data = base_decode(data.encode('utf8'), None, base=43) data = ''.join(chr(ord(b)) for b in data).encode('hex') tx = Transaction(data) #except: # modal_dialog('Error', 'Cannot parse transaction') # continue if not wallet.can_sign(tx): modal_dialog('Error', 'Cannot sign this transaction') continue lines = map(lambda x: x[0] + u'\t\t' + format_satoshis(x[1]) if x[1] else x[0], tx.get_outputs()) if not modal_question('Sign?', '\n'.join(lines)): continue password = self.get_password() if password is False: modal_dialog('Error','incorrect password') continue droid.dialogCreateSpinnerProgress("Signing") droid.dialogShow() wallet.sign_transaction(tx, password) droid.dialogDismiss() data = base_encode(str(tx).decode('hex'), base=43) self.show_qr(data) self.show_title('Signed Transaction') droid.makeToast("Bye!")