def serial_number_to_tx(serial_number, bitcoind_proxy, proxy=None): """ Convert a serial number into its transaction in the blockchain. Use an untrusted bitcoind connection to get the list of transactions, and use trusted SPV headers to ensure that the transaction obtained is on the main chain. @bitcoind_proxy must be a BitcoindConnection (from virtualchain.lib.session) Return the SPV-verified transaction object (as a dict) on success Return None on error """ proxy = get_default_proxy() if proxy is None else proxy parts = serial_number.split('-') block_id, tx_index = int(parts[0]), int(parts[1]) timeout = 1.0 while True: try: block_hash = bitcoind_proxy.getblockhash(block_id) block_data = bitcoind_proxy.getblock(block_hash) break except Exception as e: log.error('Unable to obtain block data; retrying...') time.sleep(timeout) timeout = timeout * 2 + random.random() * timeout rc = SPVClient.sync_header_chain(proxy.spv_headers_path, bitcoind_proxy.opts['bitcoind_server'], block_id) if not rc: msg = 'Failed to synchronize SPV header chain up to {}' log.error(msg.format(block_id)) return None # verify block header rc = SPVClient.block_header_verify(proxy.spv_headers_path, block_id, block_hash, block_data) if not rc: msg = 'Failed to verify block header for {} against SPV headers' log.error(msg.format(block_id)) return None # verify block txs rc = SPVClient.block_verify(block_data, block_data['tx']) if not rc: msg = 'Failed to verify block transaction IDs for {} against SPV headers' log.error(msg.format(block_id)) return None # sanity check if tx_index >= len(block_data['tx']): msg = 'Serial number {} references non-existant transaction {} (out of {} txs)' log.error(msg.format(serial_number, tx_index, len(block_data['tx']))) return None # obtain transaction txid = block_data['tx'][tx_index] tx = bitcoind_proxy.getrawtransaction(txid, 1) # verify tx rc = SPVClient.tx_verify(block_data['tx'], tx) if not rc: msg = 'Failed to verify block transaction {} against SPV headers' log.error(msg.format(txid)) return None # verify tx index if tx_index != SPVClient.tx_index(block_data['tx'], tx): msg = ('TX index mismatch: serial number identifies ' 'transaction number {} ({}), but got transaction {}') log.error( msg.format( tx_index, block_data['tx'][tx_index], block_data['tx'][SPVClient.tx_index(block_data['tx'], tx)])) return None # success! return tx
% (serial_number, tx_index, len(block_data['tx']))) return None # obtain transaction txid = block_data['tx'][tx_index] tx = bitcoind_proxy.getrawtransaction(txid, 1) # verify tx rc = SPVClient.tx_verify(block_data['tx'], tx) if not rc: log.error("Failed to verify block transaction %s against SPV headers" % txid) return None # verify tx index if tx_index != SPVClient.tx_index(block_data['tx'], tx): log.error("TX index mismatch: serial number identifies transaction number %s (%s), but got transaction %s" % \ (tx_index, block_data['tx'][tx_index], block_data['tx'][ SPVClient.tx_index(block_data['tx'], tx) ])) return None # success! return tx def parse_tx_op_return(tx): """ Given a transaction, locate its OP_RETURN and parse out its opcode and payload. Return (opcode, payload) on success Return (None, None) if there is no OP_RETURN, or if it's not a blockchain ID operation. """