def recv_tx(self, ec, tx, node): if ec: print "Error with new transaction:", ec return tx_hash = bitcoin.hash_transaction(tx) self.memory_buffer.receive(tx, bind(self.store_tx, _1, tx_hash)) # Re-subscribe to new transactions from node node.subscribe_transaction(bind(self.recv_tx, _1, _2, node))
def handle_handshake(ec, node, hs): if ec: error_exit(ec) # 1. Connected to other node # 2. Performed version/verack handshake process # Node is now ready and communicating with us node.send_get_blocks(create_get_blocks_message(), bitcoin.bind(handle_send_get_blocks, bitcoin._1)) node.subscribe_inventory( bitcoin.bind(receive_inv, bitcoin._1, bitcoin._2, node)) hs.fetch_network_address(show_ip)
def recv_tx(self, tx, handle_store): tx_hash = str(bitcoin.hash_transaction(tx)) desc = (tx_hash, [], []) for input in tx.inputs: prevout = input.previous_output desc[1].append((str(prevout.hash), prevout.index)) for idx, output in enumerate(tx.outputs): address = bitcoin.payment_address() if address.extract(output.output_script): desc[2].append((idx, str(address))) self.txpool.store(tx, bind(self.confirmed, _1, desc), bind(self.mempool_stored, _1, desc, handle_store))
def block_header(self, ec, blk_head, statement_line, info): info["timestamp"] = blk_head.timestamp info["block_hash"] = str(bitcoin.hash_block_header(blk_head)) tx_hash = bitcoin.hash_digest(info["tx_hash"]) self.chain.fetch_transaction(tx_hash, bitcoin.bind(self.load_tx, bitcoin._1, bitcoin._2, statement_line, info))
def load_pool_tx(self, ec, tx, info): if self.stop_on_error(ec): return # block_hash = mempool:5 # inputs (load from prevtx) # outputs (load from tx) # raw_output_script (load from tx) # height is always None # value (get from finish_if_done) self.load_tx(tx, info) if info["is_input"] == 0: our_output = tx.outputs[info["index"]] info["value"] = our_output.value # Save serialised output script in case this output is unspent info["raw_output_script"] = \ str(bitcoin.save_script(our_output.output_script)) else: assert (info["is_input"] == 1) info.previous_output = tx.inputs[info["index"]].previous_output # If all the inputs are loaded if self.inputs_all_loaded(info["inputs"]): # We are the sole input assert (info["is_input"] == 1) # No more inputs left to load # This info has finished loading info["height"] = None info["block_hash"] = "mempool" self.finish_if_done() create_handler = lambda prevout_index, input_index: \ bind(self.load_input_pool_tx, _1, _2, prevout_index, info, input_index) self.fetch_input_txs(tx, info, create_handler)
def tx_index(self, ec, block_depth, offset, entry, info): if self.stop_on_error(ec): return info["height"] = block_depth # And now for the block hash self.chain.fetch_block_header_by_depth( block_depth, bind(self.block_header, _1, _2, entry, info))
def load_tx(self, ec, tx, statement_line, info): outputs = [] for tx_out in tx.outputs: script = tx_out.output_script if script.type() == bitcoin.payment_type.pubkey_hash: pkh = bitcoin.short_hash(str(script.operations()[2].data)) outputs.append(bitcoin.public_key_hash_to_address(pkh)) else: outputs.append("Unknown") info["outputs"] = outputs info["inputs"] = [None for i in range(len(tx.inputs))] if info["is_input"] == 1: info["inputs"][info["index"]] = self.address else: our_output = tx.outputs[info["index"]] info["value"] = our_output.value with statement_line.lock: statement_line.raw_output_script = \ str(bitcoin.save_script(our_output.output_script)) if not [empty_in for empty_in in info["inputs"] if empty_in is None]: # We have the sole input assert (info["is_input"] == 1) with statement_line.lock: statement_line.input_loaded = info self.finish_if_done() for tx_idx, tx_in in enumerate(tx.inputs): if info["is_input"] == 1 and info["index"] == tx_idx: continue self.chain.fetch_transaction( tx_in.previous_output.hash, bitcoin.bind(self.load_input, bitcoin._1, bitcoin._2, tx_in.previous_output.index, statement_line, info, tx_idx))
def tx_index(self, ec, block_depth, offset, entry, info): if self.stop_on_error(ec): return info["height"] = block_depth # And now for the block hash self.chain.fetch_block_header_by_depth(block_depth, bind(self.block_header, _1, _2, entry, info))
def load_pool_tx(self, ec, tx, info): if self.stop_on_error(ec): return # block_hash = mempool:5 # inputs (load from prevtx) # outputs (load from tx) # raw_output_script (load from tx) # height is always None # value (get from finish_if_done) self.load_tx(tx, info) if info["is_input"] == 0: our_output = tx.outputs[info["index"]] info["value"] = our_output.value # Save serialised output script in case this output is unspent info["raw_output_script"] = \ str(bitcoin.save_script(our_output.output_script)) else: assert(info["is_input"] == 1) info.previous_output = tx.inputs[info["index"]].previous_output # If all the inputs are loaded if self.inputs_all_loaded(info["inputs"]): # We are the sole input assert(info["is_input"] == 1) # No more inputs left to load # This info has finished loading info["height"] = None info["block_hash"] = "mempool" self.finish_if_done() create_handler = lambda prevout_index, input_index: \ bind(self.load_input_pool_tx, _1, _2, prevout_index, info, input_index) self.fetch_input_txs(tx, info, create_handler)
def load_tx(self, ec, tx, statement_line, info): outputs = [] for tx_out in tx.outputs: script = tx_out.output_script if script.type() == bitcoin.payment_type.pubkey_hash: pkh = bitcoin.short_hash(str(script.operations()[2].data)) outputs.append(bitcoin.public_key_hash_to_address(pkh)) else: outputs.append("Unknown") info["outputs"] = outputs info["inputs"] = [None for i in range(len(tx.inputs))] if info["is_input"] == 1: info["inputs"][info["index"]] = self.address else: our_output = tx.outputs[info["index"]] info["value"] = our_output.value with statement_line.lock: statement_line.raw_output_script = \ str(bitcoin.save_script(our_output.output_script)) if not [empty_in for empty_in in info["inputs"] if empty_in is None]: # We have the sole input assert(info["is_input"] == 1) with statement_line.lock: statement_line.input_loaded = info self.finish_if_done() for tx_idx, tx_in in enumerate(tx.inputs): if info["is_input"] == 1 and info["index"] == tx_idx: continue self.chain.fetch_transaction( tx_in.previous_output.hash, bitcoin.bind(self.load_input, bitcoin._1, bitcoin._2, tx_in.previous_output.index, statement_line, info, tx_idx) )
def monitor_tx(self, node): # We will be notified here when connected to new bitcoin nodes # Here we subscribe to new transactions from them which we # add to the transaction_pool. That way we can track which # transactions we are interested in. node.subscribe_transaction(bind(self.recv_tx, _1, _2, node)) # Re-subscribe to next new node self.protocol.subscribe_channel(self.monitor_tx)
def subscribe(self, request): address = str(request["params"][0]) service = self.backend.mempool_service chain = self.backend.blockchain txpool = self.backend.transaction_pool memory_buff = self.backend.memory_buffer history.payment_history(service, chain, txpool, memory_buff, address, bind(self.construct, _1, _2, request))
def load_tx_info(self, point, statement_line, is_input): info = {} info["tx_hash"] = str(point.hash) info["index"] = point.index info["is_input"] = 1 if is_input else 0 self.chain.fetch_transaction_index(point.hash, bitcoin.bind(self.tx_index, bitcoin._1, bitcoin._2, bitcoin._3, statement_line, info))
def block_header(self, ec, blk_head, statement_line, info): info["timestamp"] = blk_head.timestamp info["block_hash"] = str(bitcoin.hash_block_header(blk_head)) tx_hash = bitcoin.hash_digest(info["tx_hash"]) self.chain.fetch_transaction( tx_hash, bitcoin.bind(self.load_tx, bitcoin._1, bitcoin._2, statement_line, info))
def block_header(self, ec, blk_head, entry, info): if self.stop_on_error(ec): return info["timestamp"] = blk_head.timestamp info["block_hash"] = str(bitcoin.hash_block_header(blk_head)) tx_hash = bitcoin.hash_digest(info["tx_hash"]) # Now load the actual main transaction for this input or output self.chain.fetch_transaction(tx_hash, bind(self.load_chain_tx, _1, _2, entry, info))
def load_tx_info(self, point, statement_line, is_input): info = {} info["tx_hash"] = str(point.hash) info["index"] = point.index info["is_input"] = 1 if is_input else 0 self.chain.fetch_transaction_index( point.hash, bitcoin.bind(self.tx_index, bitcoin._1, bitcoin._2, bitcoin._3, statement_line, info))
def start_loading(self, membuf_result, output_points): if len(membuf_result) == 0 and len(output_points) == 0: self.handle_finish([]) self.stopped() # Create a bunch of entry lines which are outputs and # then their corresponding input (if it exists) for outpoint in output_points: entry = PaymentEntry(outpoint) with self.lock: self.statement.append(entry) # Attempt to fetch the spend of this output self.chain.fetch_spend(outpoint, bind(self.load_spend, _1, _2, entry)) self.load_tx_info(outpoint, entry, False) # Load memory pool transactions with self.lock: self.membuf_result = membuf_result for info in self.membuf_result: self.txpool.fetch(bitcoin.hash_digest(info["tx_hash"]), bind(self.load_pool_tx, _1, _2, info))
def block_header(self, ec, blk_head, entry, info): if self.stop_on_error(ec): return info["timestamp"] = blk_head.timestamp info["block_hash"] = str(bitcoin.hash_block_header(blk_head)) tx_hash = bitcoin.hash_digest(info["tx_hash"]) # Now load the actual main transaction for this input or output self.chain.fetch_transaction( tx_hash, bind(self.load_chain_tx, _1, _2, entry, info))
def load_tx_info(self, point, entry, is_input): info = {} info["tx_hash"] = str(point.hash) info["index"] = point.index info["is_input"] = 1 if is_input else 0 # Before loading the transaction, Stratum requires the hash # of the parent block, so we load the block depth and then # fetch the block header and hash it. self.chain.fetch_transaction_index(point.hash, bind(self.tx_index, _1, _2, _3, entry, info))
def start_loading(self, ec, output_points): with self.lock: for outpoint in output_points: statement_line = StatementLine(outpoint) self.statement.append(statement_line) self.chain.fetch_spend(outpoint, bitcoin.bind(self.load_spend, bitcoin._1, bitcoin._2, statement_line)) self.load_tx_info(outpoint, statement_line, False)
def start_loading(self, ec, output_points): with self.lock: for outpoint in output_points: statement_line = StatementLine(outpoint) self.statement.append(statement_line) self.chain.fetch_spend( outpoint, bitcoin.bind(self.load_spend, bitcoin._1, bitcoin._2, statement_line)) self.load_tx_info(outpoint, statement_line, False)
def start(self, address, handle_finish): self.statement = [] self.membuf_result = None self.address = address self.handle_finish = handle_finish address = bitcoin.payment_address(address) # To begin we fetch all the outputs (payments in) # associated with this address self.chain.fetch_outputs(address, bind(self.check_membuf, _1, _2))
def load_tx_info(self, point, entry, is_input): info = {} info["tx_hash"] = str(point.hash) info["index"] = point.index info["is_input"] = 1 if is_input else 0 # Before loading the transaction, Stratum requires the hash # of the parent block, so we load the block depth and then # fetch the block header and hash it. self.chain.fetch_transaction_index( point.hash, bind(self.tx_index, _1, _2, _3, entry, info))
def notify(self, affected_addrs): service = self.backend.mempool_service chain = self.backend.blockchain txpool = self.backend.transaction_pool memory_buff = self.backend.memory_buffer for address in affected_addrs: response = {"id": None, "method": "blockchain.address.subscribe", "params": [str(address)]} history.payment_history(service, chain, txpool, memory_buff, address, bind(self.send_notify, _1, _2, response))
def notify(self, affected_addrs): service = self.backend.mempool_service chain = self.backend.blockchain txpool = self.backend.transaction_pool memory_buff = self.backend.memory_buffer for address in affected_addrs: response = { "id": None, "method": "blockchain.address.subscribe", "params": [str(address)] } history.payment_history(service, chain, txpool, memory_buff, address, bind(self.send_notify, _1, _2, response))
def receive_inv(ec, inv, node): if ec: error_exit(ec) print "Received:" for ivv in inv.inventories: if ivv.type == bitcoin.inventory_type.block: print ivv.hash else: print "--" # Re-subscribe to receive further inventory packets. # Bitcoin nodes can respond with any number of hashes split over # any number of batches. node.subscribe_inventory( bitcoin.bind(receive_inv, bitcoin._1, bitcoin._2, node))
def load_chain_tx(self, ec, tx, entry, info): if self.stop_on_error(ec): return self.load_tx(tx, info) if info["is_input"] == 0: our_output = tx.outputs[info["index"]] info["value"] = our_output.value # Save serialised output script in case this output is unspent with entry.lock: entry.raw_output_script = \ str(bitcoin.save_script(our_output.output_script)) # If all the inputs are loaded if self.inputs_all_loaded(info["inputs"]): # We are the sole input assert(info["is_input"] == 1) with entry.lock: entry.input_loaded = info self.finish_if_done() create_handler = lambda prevout_index, input_index: \ bind(self.load_input_chain_tx, _1, _2, prevout_index, entry, info, input_index) self.fetch_input_txs(tx, info, create_handler)
def load_chain_tx(self, ec, tx, entry, info): if self.stop_on_error(ec): return self.load_tx(tx, info) if info["is_input"] == 0: our_output = tx.outputs[info["index"]] info["value"] = our_output.value # Save serialised output script in case this output is unspent with entry.lock: entry.raw_output_script = \ str(bitcoin.save_script(our_output.output_script)) # If all the inputs are loaded if self.inputs_all_loaded(info["inputs"]): # We are the sole input assert (info["is_input"] == 1) with entry.lock: entry.input_loaded = info self.finish_if_done() create_handler = lambda prevout_index, input_index: \ bind(self.load_input_chain_tx, _1, _2, prevout_index, entry, info, input_index) self.fetch_input_txs(tx, info, create_handler)
def check_membuf(self, ec, output_points): if self.stop_on_error(ec): return self.membuf.check(output_points, self.address, bind(self.start_loading, _1, output_points))
def handle_init(ec, hs, net): if ec: error_exit(ec) # Main program thread begins here hs.connect(net, "localhost", 8333, bitcoin.bind(handle_handshake, bitcoin._1, bitcoin._2, hs))
def tx_index(self, ec, block_depth, offset, statement_line, info): info["height"] = block_depth self.chain.fetch_block_header_by_depth( block_depth, bitcoin.bind(self.block_header, bitcoin._1, bitcoin._2, statement_line, info) )
def payment_history(service, chain, txpool, membuf, address, finish): _history.payment_history(service.internal_ptr, chain.internal_ptr, txpool.internal_ptr, membuf.internal_ptr, str(address), bind(wrap_finish, finish, _1, _2))
import bitcoin def start_polling(ec, node, poll): if ec: print ec return poll.query(node) if __name__ == "__main__": s1 = bitcoin.async_service(1) s2 = bitcoin.async_service(1) chain = bitcoin.bdb_blockchain(s1, "database") poll = bitcoin.poller(chain) net = bitcoin.network(s2) hs = bitcoin.handshake(s2) hs.connect(net, "localhost", 8333, bitcoin.bind(start_polling, bitcoin._1, bitcoin._2, poll)) raw_input()
def tx_index(self, ec, block_depth, offset, statement_line, info): info["height"] = block_depth self.chain.fetch_block_header_by_depth( block_depth, bitcoin.bind(self.block_header, bitcoin._1, bitcoin._2, statement_line, info))
if ec: error_exit(ec) print addr.ip def handle_handshake(ec, node, hs): if ec: error_exit(ec) # 1. Connected to other node # 2. Performed version/verack handshake process # Node is now ready and communicating with us node.send_get_blocks(create_get_blocks_message(), bitcoin.bind(handle_send_get_blocks, bitcoin._1)) node.subscribe_inventory( bitcoin.bind(receive_inv, bitcoin._1, bitcoin._2, node)) hs.fetch_network_address(show_ip) def handle_init(ec, hs, net): if ec: error_exit(ec) # Main program thread begins here hs.connect(net, "localhost", 8333, bitcoin.bind(handle_handshake, bitcoin._1, bitcoin._2, hs)) if __name__ == "__main__": s = bitcoin.async_service(1) net = bitcoin.network(s) hs = bitcoin.handshake(s) hs.start(bitcoin.bind(handle_init, bitcoin._1, hs, net)) raw_input()