def get_transaction(self, txid): url = '{api_url}/tx/{txid}?verbose=3'.format(api_url=self.url, txid=txid) try: LOG.info('GET %s' % url) r = requests.get(url) data = r.json() except Exception as ex: LOG.error('Unable to get transaction %s from BTC.com: %s' % (txid, ex)) return { 'error': 'Unable to get transaction %s from BTC.com' % txid } data = data['data'] if data['data'] is not None else {} # todo check key names , test by setting testnet wrong on explorers tx = TX() tx.txid = txid tx.wtxid = data['witness_hash'] tx.lock_time = data['lock_time'] tx.block_height = data[ 'block_height'] if 'block_height' in data and data[ 'block_height'] != -1 else None tx.confirmations = data[ 'confirmations'] if 'confirmations' in data else None for item in data['inputs']: tx_input = TxInput() tx_input.address = item['prev_addresses'][0] if len( item['prev_addresses']) > 0 else None tx_input.value = item['prev_value'] tx_input.txid = item['prev_tx_hash'] tx_input.n = item['prev_position'] if item[ 'prev_position'] is not -1 else None tx_input.script = item['script_hex'] tx_input.sequence = item['sequence'] tx.inputs.append(tx_input) for i, item in enumerate(data['outputs']): tx_output = TxOutput() tx_output.address = item['addresses'][0] if len( item['addresses']) > 0 else None tx_output.value = item['value'] tx_output.n = i tx_output.spent = False if item['spent_by_tx'] is None else True tx_output.script = item['script_hex'] if item['script_hex'][:2] == '6a': tx_output.op_return = tx.decode_op_return(item['script_hex']) tx.outputs.append(tx_output) return {'transaction': tx.json_encodable()}
def test_op_return_script_with_strings_of_various_lengths(self): for x in range(1, 81): message = 'a' * x script = op_return_script(hex_data=binascii.hexlify(message.encode()).decode()) print(message) print(script) assert TX().decode_op_return(hex_data=script) == message
def get_transaction(self, txid): url = self.url + '/tx/' + str(txid) try: LOG.info('GET %s' % url) r = requests.get(url) data = r.json() except Exception as ex: LOG.error('Unable to get transaction %s from %s: %s' % (txid, self.url, ex)) return { 'error': 'Unable to get transaction %s from %s' % (txid, self.url) } tx = TX() tx.txid = txid tx.block_height = data['blockheight'] if 'blockheight' in data else None tx.lock_time = data['locktime'] for item in data['vin']: tx_input = TxInput() tx_input.address = item['addr'] if 'addr' in item else None tx_input.value = item['valueSat'] if 'valueSat' in item else 0 tx_input.txid = item['txid'] if 'txid' in item else None tx_input.n = item['n'] if 'coinbase' not in item else None tx_input.script = item['scriptSig'][ 'hex'] if 'scriptSig' in item else None if 'coinbase' in item: tx_input.script = item['coinbase'] tx_input.sequence = item['sequence'] tx.inputs.append(tx_input) for item in data['vout']: tx_output = TxOutput() tx_output.address = item['scriptPubKey']['addresses'][ 0] if 'addresses' in item['scriptPubKey'] else None tx_output.value = int(float(item['value']) * 1e8) tx_output.n = item['n'] tx_output.spent = True if 'spentTxId' in item and item[ 'spentTxId'] is not None else False tx_output.script = item['scriptPubKey']['hex'] if item['scriptPubKey']['hex'][:2] == '6a': tx_output.op_return = tx.decode_op_return( item['scriptPubKey']['hex']) tx.outputs.append(tx_output) tx.confirmations = data[ 'confirmations'] if 'confirmations' in data else None return {'transaction': tx.json_encodable()}
def test_op_return_script_with_random_string(self): for x in range(10000): print('') random_length = randint(1, 81) random_string = "".join(choice('abcdefghijklmnopqrstuvwxyz') for i in range(random_length)) script = op_return_script(hex_data=binascii.hexlify(random_string.encode()).decode()) print(random_string) print(script) assert TX().decode_op_return(hex_data=script) == random_string
def get_transaction(self, txid): url = '{api_url}/rawtx/{txid}'.format(api_url=self.url, txid=txid) try: LOG.info('GET %s' % url) r = requests.get(url) data = r.json() except Exception as ex: LOG.error('Unable to get tx %s from Blockchain.info: %s' % (txid, ex)) return {'error': 'Unable to get tx %s from Blockchain.info' % txid} tx = TX() tx.txid = txid tx.lock_time = data['lock_time'] tx.block_height = data[ 'block_height'] if 'block_height' in data else None tx.confirmations = self.get_latest_block_height( ) - tx.block_height + 1 if tx.block_height is not None else 0 for item in data['inputs']: tx_input = TxInput() tx_input.address = item['prev_out'][ 'addr'] if 'prev_out' in item else None tx_input.value = item['prev_out'][ 'value'] if 'prev_out' in item else 0 tx_input.n = item['prev_out']['n'] if 'prev_out' in item else None tx_input.txid = '' # Blockchain.info does not provide the txid of a tx input only their own tx_index, can be resolved for example via https://testnet.blockchain.info/tx-index/197277768?format=json but this would require too many http requests!!! tx_input.script = item['script'] tx_input.sequence = item['sequence'] tx.inputs.append(tx_input) for item in data['out']: tx_output = TxOutput() tx_output.address = item['addr'] if 'addr' in item else None tx_output.value = item['value'] tx_output.n = item['n'] tx_output.spent = item['spent'] tx_output.script = item['script'] if item['script'][:2] == '6a': tx_output.op_return = tx.decode_op_return(item['script']) tx.outputs.append(tx_output) return {'transaction': tx.json_encodable()}
def parse_transaction(self, data, latest_block_height=None): if latest_block_height is None: url = self.url + '/blocks/tip/height' LOG.info('GET %s' % url) try: r = requests.get(url) latest_block_height = int(r.text) except Exception as ex: LOG.error( 'Unable to get latest block_height from Blockstream.info: %s' % ex) return { 'error': 'Unable to get latest block_height from Blockstream.info' } tx = TX() tx.txid = data['txid'] tx.lock_time = data['locktime'] tx.block_height = data['status'][ 'block_height'] if 'block_height' in data['status'] else None tx.confirmations = latest_block_height - tx.block_height + 1 if tx.block_height is not None else 0 for item in data['vin']: tx_input = TxInput() tx_input.address = item['prevout']['scriptpubkey_address'] if item[ 'prevout'] is not None else None tx_input.value = item['prevout']['value'] if item[ 'prevout'] is not None else 0 tx_input.n = item['vout'] if item['is_coinbase'] is False else None tx_input.txid = item['txid'] tx_input.script = item['scriptsig'] tx_input.sequence = item['sequence'] tx.inputs.append(tx_input) for i, item in enumerate(data['vout']): tx_output = TxOutput() tx_output.address = item[ 'scriptpubkey_address'] if 'scriptpubkey_address' in item else None tx_output.value = item['value'] tx_output.n = i tx_output.spent = None # Blockstream does not provide information if a tx output has been spent tx_output.script = item['scriptpubkey'] if item['scriptpubkey'][:2] == '6a': tx_output.op_return = tx.decode_op_return(item['scriptpubkey']) tx.outputs.append(tx_output) return tx
def get_transactions(self, address): limit = 10 # number of tx given by insight is 10 n_tx = None transactions = [] i = 0 while n_tx is None or len(transactions) < n_tx: url = self.url + '/addrs/' + address + '/txs?from=' + str( limit * i) + '&to=' + str(limit * (i + 1)) try: LOG.info('GET %s' % url) r = requests.get(url) data = r.json() except Exception as ex: LOG.error( 'Unable to get transactions of address %s from %s: %s' % (address, url, ex)) return { 'error': 'Unable to get transactions of address %s from %s' % (address, url) } if all(key in data for key in ('totalItems', 'items')): n_tx = data['totalItems'] transactions += data['items'] i += 1 else: return {'error': 'Received Invalid data: %s' % data} txs = [] for transaction in transactions: tx = TX() tx.txid = transaction['txid'] tx.lock_time = transaction['locktime'] tx.confirmations = transaction['confirmations'] tx.block_height = transaction['blockheight'] for item in transaction['vin']: tx_input = TxInput() tx_input.address = item['addr'] if 'addr' in item else None tx_input.value = item['valueSat'] if 'value' in item else 0 tx_input.txid = item['txid'] if 'txid' in item else None tx_input.n = item['vout'] if 'coinbase' not in item else None tx_input.script = item['scriptSig'][ 'hex'] if 'scriptSig' in item else None if 'coinbase' in item: tx_input.script = item['coinbase'] tx_input.sequence = item['sequence'] tx.inputs.append(tx_input) for item in transaction['vout']: tx_output = TxOutput() tx_output.address = item['scriptPubKey']['addresses'][ 0] if 'addresses' in item['scriptPubKey'] else None tx_output.value = int( int(item['value'][:-9]) * 1e8 + int(item['value'][-8:])) tx_output.n = item['n'] tx_output.spent = True if 'spentTxId' in item and item[ 'spentTxId'] is not None else False tx_output.script = item['scriptPubKey']['hex'] if item['scriptPubKey']['hex'][:2] == '6a': tx_output.op_return = tx.decode_op_return( item['scriptPubKey']['hex']) tx.outputs.append(tx_output) # Only add confirmed txs if tx.block_height != -1: txs.insert(0, tx.to_dict(address)) else: # subtract 1 from total txs because it is unconfirmed n_tx -= 1 if n_tx != len(txs): return { 'error': 'Not all transactions are retrieved! expected {expected} but only got {received}' .format(expected=n_tx, received=len(txs)) } else: return {'transactions': txs}
def get_transactions(self, address): pagesize = 50 # max 50 for BTC.com n_tx = None transactions = [] page = 1 while n_tx is None or len(transactions) < n_tx: url = '{api_url}/address/{address}/tx?page={page}&pagesize={pagesize}&verbose=3'.format( api_url=self.url, address=address, page=page, pagesize=pagesize) try: LOG.info('GET %s' % url) r = requests.get(url) data = r.json() except Exception as ex: LOG.error( 'Unable to get transactions of address %s from BTC.com: %s' % (address, ex)) return { 'error': 'Unable to get transactions of address %s from BTC.com' % address } data = data['data'] if data['data'] is not None else {} if all(key in data for key in ('total_count', 'list')): n_tx = data['total_count'] transactions += data['list'] page += 1 else: return {'error': 'Received invalid data: %s' % data} if len(transactions) < n_tx: sleep(1) txs = [] for transaction in transactions: tx = TX() tx.txid = transaction['hash'] tx.block_height = transaction['block_height'] tx.confirmations = transaction['confirmations'] tx.wtxid = transaction['witness_hash'] tx.lock_time = transaction['lock_time'] for item in transaction['inputs']: tx_input = TxInput() tx_input.address = item['prev_addresses'][0] if len( item['prev_addresses']) > 0 else None tx_input.value = item['prev_value'] tx_input.txid = item['prev_tx_hash'] tx_input.n = item['prev_position'] if item[ 'prev_position'] is not -1 else None tx_input.script = item['script_hex'] tx_input.sequence = item['sequence'] tx.inputs.append(tx_input) for i, item in enumerate(transaction['outputs']): tx_output = TxOutput() tx_output.address = item['addresses'][0] if len( item['addresses']) > 0 else None tx_output.value = item['value'] tx_output.n = i tx_output.spent = False if item['spent_by_tx'] is None else True tx_output.script = item['script_hex'] if item['script_hex'][:2] == '6a': tx_output.op_return = tx.decode_op_return( item['script_hex']) tx.outputs.append(tx_output) # Only append confirmed transactions if tx.block_height is not -1: txs.append(tx.to_dict(address)) else: # subtract 1 from total txs because it is unconfirmed n_tx -= 1 if n_tx != len(txs): return { 'error': 'BTC.com: Not all transactions are retrieved! expected {expected} but only got {received}' .format(expected=n_tx, received=len(txs)) } else: return {'transactions': txs}
def get_transactions(self, address): limit = 50 # max number of tx given by blockchain.info is 50 n_tx = None transactions = [] latest_block_height = self.get_latest_block_height() if latest_block_height is None: return {'error': 'Unable to get latest block height'} i = 0 while n_tx is None or len(transactions) < n_tx: url = '{api_url}/address/{address}?format=json&limit={limit}&offset={offset}'.format( api_url=self.url, address=address, limit=limit, offset=limit * i) try: LOG.info('GET %s' % url) r = requests.get(url) data = r.json() except Exception as ex: LOG.error( 'Unable to get transactions of address %s from %s: %s' % (address, url, ex)) return { 'error': 'Unable to get transactions of address %s from %s' % (address, url) } if all(key in data for key in ('n_tx', 'txs')): n_tx = data['n_tx'] transactions += data['txs'] i += 1 else: return {'error': 'Received Invalid data: %s' % data} if len(transactions) < n_tx: sleep(1) txs = [] for transaction in transactions: tx = TX() tx.txid = transaction['hash'] tx.lock_time = transaction['lock_time'] tx.block_height = transaction[ 'block_height'] if 'block_height' in transaction else None tx.confirmations = (latest_block_height - tx.block_height ) + 1 if 'block_height' in transaction else 0 for item in transaction['inputs']: tx_input = TxInput() tx_input.address = item['prev_out'][ 'addr'] if 'prev_out' in item else None tx_input.value = item['prev_out'][ 'value'] if 'prev_out' in item else 0 tx_input.txid = '' # Blockchain.info does not provide the txid of a tx input only their own tx_index, can be resolved for example via https://testnet.blockchain.info/tx-index/197277768?format=json but this would require too many http requests!!! tx_input.n = item['prev_out'][ 'n'] if 'prev_out' in item else None tx_input.script = item['script'] tx_input.sequence = item['sequence'] tx.inputs.append(tx_input) for item in transaction['out']: tx_output = TxOutput() tx_output.address = item['addr'] if 'addr' in item else None tx_output.value = item['value'] tx_output.n = item['n'] tx_output.spent = item['spent'] tx_output.script = item['script'] if item['script'][:2] == '6a': tx_output.op_return = tx.decode_op_return(item['script']) tx.outputs.append(tx_output) # Only append confirmed transactions if tx.block_height is not None: txs.insert(0, tx.to_dict(address)) else: # subtract 1 from total txs because it is unconfirmed n_tx -= 1 if n_tx != len(txs): return { 'error': 'Not all transactions are retrieved! expected {expected} but only got {received}' .format(expected=n_tx, received=len(txs)) } else: return {'transactions': txs}
def get_transaction(self, txid): url = '{api_url}/get_tx/{network}/{txid}'.format(api_url=self.url, network=self.network, txid=txid) try: LOG.info('GET %s' % url) r = requests.get(url) data = r.json() except Exception as ex: LOG.error('Unable to get transaction %s from Chain.so: %s' % (txid, ex)) return { 'error': 'Unable to get transaction %s from Chain.so' % txid } if 'data' not in data: LOG.error('Invalid response data from Chain.so: %s' % data) return {'error': 'Invalid response data from Chain.so: %s' % data} data = data['data'] tx = TX() tx.txid = txid block_data = self.get_block_by_hash(block_hash=data['blockhash']) if not ('block' in block_data and 'height' in block_data['block']): LOG.error( 'Unable to get block %s to get the block height from chain.so' % data['blockhash']) return { 'error': 'Unable to get block %s to get the block height from chain.so' % data['blockhash'] } tx.block_height = block_data['block']['height'] tx.confirmations = data['confirmations'] for item in data['inputs']: tx_input = TxInput() tx_input.address = item[ 'address'] if item['address'] != 'coinbase' else None tx_input.value = btc2satoshis( btc=item['value']) if item['address'] != 'coinbase' else 0 tx_input.n = item['from_output']['output_no'] if item[ 'from_output'] is not None else None tx_input.txid = item['from_output']['txid'] if item[ 'from_output'] is not None else None tx_input.script = item['script'] tx_input.sequence = None # Chain.so does not provide the sequence tx.inputs.append(tx_input) for item in data['outputs']: tx_output = TxOutput() tx_output.address = item[ 'address'] if item['address'] != 'nonstandard' else None tx_output.value = btc2satoshis(btc=item['value']) tx_output.n = item['output_no'] tx_output.spent = None # Chain.so does not provide information if an output has been spent already or not tx_output.script = item['script'] if item['script'][:10] == 'OP_RETURN ': tx_output.op_return = binascii.unhexlify(tx_output.script[10:]) # Sometimes the unhexed data is encoded in another coded than utf-8 which could cause problems when converting to json later try: tx_output.op_return = tx_output.op_return.decode('utf-8') except UnicodeDecodeError: try: tx_output.op_return = tx_output.op_return.decode( 'cp1252') except Exception as ex: LOG.error( 'Unable to decode OP_RETURN data %s in utf-8 or cp1252: %s' % (tx_output.op_return, ex)) tx_output.op_return = 'Unable to decode hex data' tx.outputs.append(tx_output) return {'transaction': tx.json_encodable()}