def doit(accept=True, in_psbt=None, finalize=False): if accept != None: need_keypress('y' if accept else 'x') if accept == False: with pytest.raises(CCUserRefused): done = None while done == None: time.sleep(0.050) done = dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None) return else: done = None while done == None: time.sleep(0.050) done = dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None) assert len(done) == 2 resp_len, chk = done psbt_out = dev.download_file(resp_len, chk) if not finalize: if in_psbt: assert BasicPSBT().parse(in_psbt) == BasicPSBT().parse(psbt_out) else: from pycoin.tx.Tx import Tx # parse it res = psbt_out assert res[0:4] != b'psbt' t = Tx.from_bin(res) assert t.version in [1, 2] return psbt_out
def test_txid_calc(num_ins, fake_txn, try_sign, dev, segwit, decode_with_bitcoind, cap_story): # verify correct txid for transactions is being calculated xp = dev.master_xpub psbt = fake_txn(num_ins, 1, xp, segwit_in=segwit) _, txn = try_sign(psbt, accept=True, finalize=True) #print('Signed; ' + B2A(txn)) time.sleep(.1) title, story = cap_story() assert '0' in story assert 'TXID' in title, story txid = story.strip() if 1: # compare to PyCoin from pycoin.tx.Tx import Tx t = Tx.from_bin(txn) assert t.id() == txid if 1: # compare to bitcoin core decoded = decode_with_bitcoind(txn) pprint(decoded) assert len(decoded['vin']) == num_ins if segwit: assert all(x['txinwitness'] for x in decoded['vin']) assert decoded['txid'] == txid
def test_bip143_attack_data_capture(num_utxo, segwit_in, try_sign, fake_txn, settings_set, settings_get, cap_story, sim_exec, hist_count): # cleanup prev runs, if very first time thru sim_exec('import history; history.OutptValueCache.clear()') hist_b4 = hist_count() assert hist_b4 == 0 # make a txn, capture the outputs of that as inputs for another txn psbt = fake_txn(1, num_utxo+3, segwit_in=segwit_in, change_outputs=range(num_utxo+2), outstyles=(['p2wpkh']*num_utxo) + ['p2wpkh-p2sh', 'p2pkh']) _, txn = try_sign(psbt, accept=True, finalize=True) open('debug/funding.psbt', 'wb').write(psbt) num_inp_utxo = (1 if segwit_in else 0) time.sleep(.1) title, story = cap_story() assert 'TXID' in title, story txid = story.strip() assert hist_count() in {128, hist_b4+num_utxo+num_inp_utxo} # compare to PyCoin from pycoin.tx.Tx import Tx t = Tx.from_bin(txn) assert t.id() == txid # expect all of new "change outputs" to be recorded (none of the non-segwit change tho) # plus the one input we "revealed" after1 = settings_get('ovc') assert len(after1) == min(30, num_utxo + num_inp_utxo) all_utxo = hist_count() assert all_utxo == hist_b4+num_utxo+num_inp_utxo # build a new PSBT based on those change outputs psbt2, raw = spend_outputs(psbt, txn) # try to sign that ... should work fine try_sign(raw, accept=True, finalize=True) time.sleep(.1) # should not affect stored data, because those values already cached assert settings_get('ovc') == after1 # any tweaks to input side's values should fail. for amt in [int(1E6), 1]: def value_tweak(spendables): assert len(spendables) > 2 spendables[0][1].coin_value += amt psbt3, raw = spend_outputs(psbt, txn, tweaker=value_tweak) with pytest.raises(CCProtoError) as ee: orig, result = try_sign(raw, accept=True, finalize=True) assert 'but PSBT claims' in str(ee), ee
def test_sdcard_signing(encoding, num_outs, try_sign_microsd, fake_txn, try_sign, dev): # exercise the txn encode/decode from sdcard xp = dev.master_xpub psbt = fake_txn(2, num_outs, xp, segwit_in=True) _, txn, txid = try_sign_microsd(psbt, finalize=True, encoding=encoding) from pycoin.tx.Tx import Tx t = Tx.from_bin(txn) assert t.id() == txid
async def getblock(self, blockhash: str, mode: int = 1): start = time.time() if mode == 2: raise NotImplementedError block_header = self.repository.headers.get_block_header(blockhash) if not block_header: return if mode == 1: txids, size = self.repository.blockchain.get_txids_by_block_hash( block_header['block_hash']) if txids: block = self._serialize_header(block_header) block.update({'tx': txids, 'size': size}) best_header = self.repository.headers.get_best_header() block['confirmations'] = best_header[ 'block_height'] - block_header['block_height'] + 1 Logger.p2p.info( 'Verbose block %s (%s) provided from local storage in %ss)', block_header['block_height'], blockhash, '{:.4f}'.format(time.time() - start)) return block p2p_block = await self._get_block(block_header, verbose=True) Logger.p2p.info('Verbose block %s (%s) provided from P2P in %ss)', block_header['block_height'], blockhash, '{:.4f}'.format(time.time() - start)) return p2p_block['verbose'] else: transactions, size = self.repository.blockchain.get_transactions_by_block_hash( blockhash) if transactions: Logger.p2p.info( 'Raw block %s (%s) provided from local storage in %ss)', block_header['block_height'], blockhash, '{:.4f}'.format(time.time() - start)) try: block = Block.parse(io.BytesIO( block_header['header_bytes']), include_transactions=False) block.set_txs([ Tx.from_bin(t['transaction_bytes']) for t in transactions ]) return block.as_hex() except: Logger.repository.error( 'Error loading block %s from repository, falling back to P2P' % blockhash) p2p_block = await self._get_block(block_header) Logger.p2p.info('Raw block %s (%s) provided from P2P in %ss)', block_header['block_height'], blockhash, '{:.4f}'.format(time.time() - start)) return binascii.hexlify(p2p_block['block_bytes']).decode()
def doit(accept=True, in_psbt=None, finalize=False, accept_ms_import=False, expect_txn=True): if accept_ms_import: # XXX would be better to do cap_story here, but that would limit test to simulator need_keypress('y') time.sleep(0.050) if accept != None: need_keypress('y' if accept else 'x', timeout=None) if accept == False: with pytest.raises(CCUserRefused): done = None while done == None: time.sleep(0.050) done = dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None) return else: done = None while done == None: time.sleep(0.00) done = dev.send_recv(CCProtocolPacker.get_signed_txn(), timeout=None) assert len(done) == 2 resp_len, chk = done psbt_out = dev.download_file(resp_len, chk) if not expect_txn: # skip checks; it's text return psbt_out if not finalize: if in_psbt: from psbt import BasicPSBT assert BasicPSBT().parse(in_psbt) != None else: from pycoin.tx.Tx import Tx # parse it res = psbt_out assert res[0:4] != b'psbt', 'still a PSBT, but asked for finalize' t = Tx.from_bin(res) assert t.version in [1, 2] return psbt_out
def spend_outputs(funding_psbt, finalized_txn, tweaker=None): # take details from PSBT that created a finalized txn (also provided) # and build a new PSBT that spends those change outputs. from pycoin.tx.Tx import Tx from pycoin.tx.TxOut import TxOut from pycoin.tx.TxIn import TxIn funding = Tx.from_bin(finalized_txn) b4 = BasicPSBT().parse(funding_psbt) # segwit change outputs only spendables = [(n, i) for n, i in enumerate(funding.tx_outs_as_spendable()) if i.script[0:2] == b'\x00\x14' and b4.outputs[n].bip32_paths ] #spendables = list(reversed(spendables)) random.shuffle(spendables) if tweaker: tweaker(spendables) nn = BasicPSBT() nn.inputs = [BasicPSBTInput(idx=i) for i in range(len(spendables))] nn.outputs = [BasicPSBTOutput(idx=0)] # copy input values from funding PSBT's output side for p_in, (f_out, sp) in zip(nn.inputs, [(b4.outputs[x], s) for x, s in spendables]): p_in.bip32_paths = f_out.bip32_paths p_in.witness_script = f_out.redeem_script with BytesIO() as fd: sp.stream(fd) p_in.witness_utxo = fd.getvalue() # build new txn: single output, no change, no miner fee act_scr = fake_dest_addr('p2wpkh') dest_out = TxOut(sum(s.coin_value for n, s in spendables), act_scr) txn = Tx(2, [s.tx_in() for _, s in spendables], [dest_out]) # put unsigned TXN into PSBT with BytesIO() as b: txn.stream(b) nn.txn = b.getvalue() with BytesIO() as rv: nn.serialize(rv) raw = rv.getvalue() open('debug/spend_outs.psbt', 'wb').write(raw) return nn, raw
def _get_transaction(self, txid: (str, bytes)): key = self.get_key(txid, prefix=TRANSACTION_PREFIX) data = self.session.get(self.storage_name + b'.' + key) if not data: return blockhash = data[-32:] if not int.from_bytes(blockhash[:8], 'little'): data = data[:-32] return { 'transaction_bytes': data, 'block_hash': blockhash, 'txid': txid, 'transaction_object': Tx.from_bin(data) }
def rawtx(self, body): tx = Tx.from_bin(body) orders = ctrl.api.get_orders_of_last_fifteen_minutes_ctl() print(orders) addr_order_dict = {i['bitaddr']: i for i in orders} addrs = list(addr_order_dict.keys()) for i in tx.txs_out: addr = i.address() if addr not in addrs: continue order = addr_order_dict[addr] if order['satoshi'] != i.coin_value: continue ctrl.api.update_order_ctl(order['order_id'], {'state': 1})
def doit(f_or_data, accept=True, finalize=False, accept_ms_import=False, complete=False, encoding='binary', del_after=0): if f_or_data[0:5] == b'psbt\xff': ip = f_or_data filename = 'memory' else: filename = f_or_data ip = open(f_or_data, 'rb').read() if ip[0:10] == b'70736274ff': ip = a2b_hex(ip.strip()) assert ip[0:5] == b'psbt\xff' psbtname = 'ftrysign' # population control from glob import glob import os pat = microsd_path(psbtname + '*.psbt') for f in glob(pat): assert 'psbt' in f os.unlink(f) if encoding == 'hex': ip = b2a_hex(ip) elif encoding == 'base64': from base64 import b64encode, b64decode ip = b64encode(ip) else: assert encoding == 'binary' with open_microsd(psbtname + '.psbt', 'wb') as sd: sd.write(ip) goto_home() pick_menu_item('Ready To Sign') time.sleep(.1) _, story = cap_story() if 'Choose PSBT file' in story: need_keypress('y') time.sleep(.1) pick_menu_item(psbtname + '.psbt') time.sleep(.1) if accept_ms_import: # XXX would be better to do cap_story here, but that would limit test to simulator need_keypress('y') time.sleep(0.050) title, story = cap_story() assert title == 'OK TO SEND?' if accept != None: need_keypress('y' if accept else 'x') if accept == False: time.sleep(0.050) # look for "Aborting..." ?? return ip, None, None # wait for it to finish for r in range(10): time.sleep(0.1) title, story = cap_story() if title == 'PSBT Signed': break else: assert False, 'timed out' txid = None lines = story.split('\n') if 'Final TXID:' in lines: txid = lines[-1] result_fname = lines[-4] else: result_fname = lines[-1] result = open_microsd(result_fname, 'rb').read() if encoding == 'hex' or finalize: result = a2b_hex(result.strip()) elif encoding == 'base64': result = b64decode(result) else: assert encoding == 'binary' in_file = microsd_path(psbtname + '.psbt') # read back final product if finalize: if del_after: if not txid: txid = re.findall('[0-9a-f]{64}', result_fname)[0] assert result_fname == txid + '.txn' assert not os.path.exists(in_file) else: assert 'final' in result_fname assert os.path.exists(in_file) from pycoin.tx.Tx import Tx # parse it a little assert result[ 0:4] != b'psbt', 'still a PSBT, but asked for finalize' t = Tx.from_bin(result) assert t.version in [1, 2] assert t.id() == txid else: assert result[0:5] == b'psbt\xff' if complete: assert '-signed' in result_fname else: assert '-part' in result_fname if del_after: assert not os.path.exists(in_file) from psbt import BasicPSBT was = BasicPSBT().parse(ip) now = BasicPSBT().parse(result) assert was.txn == now.txn assert was != now return ip, result, txid
# is the redeemScript, not the scriptPubKey. That's because when the CHECKSIG # operation happens EvalScript() will be evaluating the redeemScript, so the # corresponding SignatureHash() function will use that same script when it # replaces the scriptSig in the transaction being hashed with the script being # executed. ## override module method SignatureHash = MySignatureHash sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL) # Now sign it. We have to append the type of signature we want to the end, in # this case the usual SIGHASH_ALL. sig = seckey.sign(sighash) + bytes([SIGHASH_ALL]) # Set the scriptSig of our transaction input appropriately. txin.scriptSig = CScript([sig, txin_redeemScript]) # Verify the signature worked. This calls EvalScript() and actually executes # the opcodes in the scripts to see if everything worked out. If it doesn't an # exception will be raised. VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,)) # Done! Print the transaction to standard output with the bytes-to-hex # function. print("Transaction:\n", b2x(tx.serialize())) pycoin_tx = Tx.from_bin(tx.serialize()) print("Pycoin tx:\n", pycoin_tx.__repr__())
def doit(f_or_data, accept=True, finalize=False, accept_ms_import=False, complete=False): if f_or_data[0:5] == b'psbt\xff': ip = f_or_data filename = 'memory' else: filename = f_or_data ip = open(f_or_data, 'rb').read() if ip[0:10] == b'70736274ff': ip = a2b_hex(ip.strip()) assert ip[0:5] == b'psbt\xff' psbtname = 'ftrysign' # population control from glob import glob import os pat = microsd_path(psbtname + '*.psbt') for f in glob(pat): assert 'psbt' in f os.unlink(f) with open_microsd(psbtname + '.psbt', 'wb') as sd: sd.write(ip) goto_home() pick_menu_item('Ready To Sign') time.sleep(.1) _, story = cap_story() if 'Choose PSBT file' in story: need_keypress('y') time.sleep(.1) pick_menu_item(psbtname + '.psbt') time.sleep(.1) if accept_ms_import: # XXX would be better to do cap_story here, but that would limit test to simulator need_keypress('y') time.sleep(0.050) title, story = cap_story() assert title == 'OK TO SEND?' if accept != None: need_keypress('y' if accept else 'x') if accept == False: time.sleep(0.050) # look for "Aborting..." ?? return ip, None # wait for it to finish for r in range(10): time.sleep(0.1) title, story = cap_story() if title == 'PSBT Signed': break else: assert False, 'timed out' result_fname = story.split('\n')[-1] result = open_microsd(result_fname, 'rb').read() # read back final product if finalize: assert 'final' in result_fname from pycoin.tx.Tx import Tx # parse it a little assert result[ 0:4] != b'psbt', 'still a PSBT, but asked for finalize' t = Tx.from_bin(result) assert t.version in [1, 2] else: if complete: assert '-signed' in result_fname else: assert '-part' in result_fname from psbt import BasicPSBT was = BasicPSBT().parse(ip) now = BasicPSBT().parse(result) assert was.txn == now.txn assert was != now return ip, result
def tx_from_b64(h): d = binascii.a2b_base64(h.encode("utf8")) return Tx.from_bin(d)