def make_snapshot_from_ops_hash( cls, record_root_hash, prev_consensus_hashes ): """ Generate the consensus hash from the hash over the current ops, and all previous required consensus hashes. """ # mix into previous consensus hashes... all_hashes = prev_consensus_hashes[:] + [record_root_hash] all_hashes.sort() all_hashes_merkle_tree = pybitcoin.MerkleTree( all_hashes ) root_hash = all_hashes_merkle_tree.root() consensus_hash = StateEngine.calculate_consensus_hash( root_hash ) return consensus_hash
def block_verify(block_data): """ Given block data (a dict with 'merkleroot' hex string and 'tx' list of hex strings--i.e. a block returned from bitcoind's getblock JSON RPC method), verify that the transactions are consistent. Return True on success Return False if not. """ # verify block data txs m = pybitcoin.MerkleTree(block_data['tx']) root_hash = str(m.root()) return root_hash == str(block_data['merkleroot'])
def make_ops_snapshot(cls, serialized_ops): """ Generate a deterministic hash over the sequence of (serialized) operations. """ record_hashes = [] for serialized_op in serialized_ops: record_hash = binascii.hexlify(pybitcoin.hash.bin_double_sha256(serialized_op)) record_hashes.append(record_hash) if len(record_hashes) == 0: record_hashes.append(binascii.hexlify(pybitcoin.hash.bin_double_sha256(""))) # put records into their own Merkle tree, and mix the root with the consensus hashes. record_hashes.sort() record_merkle_tree = pybitcoin.MerkleTree(record_hashes) record_root_hash = record_merkle_tree.root() return record_root_hash
def handle_block(self, message_header, block): """ Got a block. * validate it * load its transactions * ask for each transaction's sender transaction """ if self.have_all_block_data(): self.loop_exit() return block_hash = block.calculate_hash() # is this a solicited block? if block_hash not in self.block_info.keys(): log.error("Ignoring unsolicited block %s" % block_hash) return header = self.block_info[block_hash]['header'] height = self.block_info[block_hash]['height'] log.debug("handle block %s (%s)" % (height, block_hash)) # does this block's transaction hashes match the merkle root? tx_hashes = [ block.txns[i].calculate_hash() for i in xrange(0, len(block.txns)) ] mr = pybitcoin.MerkleTree(tx_hashes).root() if mr != header['merkle_root']: log.error("Merkle root of %s (%s) mismatch: expected %s, got %s" % (block_hash, height, header['merkle_root'], mr)) return nulldata_txs = [] relindex = 0 for txindex in xrange(0, len(block.txns)): txdata = self.parse_tx(block.txns[txindex], header, block_hash, txindex) # if there is no nulldata output, then we don't care about this one. has_nulldata = False nulldata_payload = None for outp in txdata['vout']: if outp['scriptPubKey']['type'] == 'nulldata': has_nulldata = True nulldata_payload = bitcoin.deserialize_script( outp['scriptPubKey']['hex'])[1] if type(nulldata_payload) not in [str, unicode]: # this is a malformed OP_RETURN, where the varint that should follow OP_RETURN doesn't have the data behind it. # just take the data after the varint, no matter what it is (i.e. "6a52" will be "") nulldata_payload = outp['scriptPubKey']['hex'][4:] # count all txs processed self.num_txs_processed += 1 if not has_nulldata: continue # remember nulldata txdata['nulldata'] = nulldata_payload # calculate total output (part of fee; will be debited when we discover the senders) txdata['fee'] -= sum( int(out['value'] * 10**8) for out in txdata['vout']) # remember the relative tx index (i.e. the ith nulldata tx) txdata['relindex'] = relindex # do we actually want this? if self.tx_filter is not None: if not self.tx_filter(txdata): continue # yup, we want it! relindex += 1 nulldata_txs.append(txdata) self.block_info[block_hash]['txns'] = nulldata_txs self.block_info[block_hash]['num_txns'] = len(block.txns) self.block_info[block_hash]['num_senders'] = 0 # get each input's transaction sender_txhashes = [] for txn in self.block_info[block_hash]['txns']: for i in xrange(0, len(txn['vin'])): # record information about the transaction # that created this input (so we can go find # it later). inp = txn['vin'][i] sender_txid = inp['txid'] inp_sender_outp = inp['vout'] if str(sender_txid) not in sender_txhashes: sender_txhashes.append(str(sender_txid)) sinfo = self.make_sender_info(block_hash, txn, i) if not self.sender_info.has_key(sender_txid): # map outpoint for this input to the tx info self.sender_info[sender_txid] = {} # sinfo is the information from the output in # the sender-tx that funded inp self.sender_info[sender_txid][inp_sender_outp] = sinfo # update accounting... self.num_blocks_received += 1 self.block_info[block_hash]['handled'] = True log.debug("Request %s nulldata sender TXs" % len(sender_txhashes)) if self.have_all_block_data(): self.loop_exit() return
def flush_transactions(self): """ TESTING ONLY Send the bufferred list of transactions as a block. """ # next block txs = self.next_block_txs self.next_block_txs = [] # add a fake coinbase txs.append( "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff53038349040d00456c69676975730052d8f72ffabe6d6dd991088decd13e658bbecc0b2b4c87306f637828917838c02a5d95d0e1bdff9b0400000000000000002f73733331312f00906b570400000000e4050000ffffffff01bf208795000000001976a9145399c3093d31e4b0af4be1215d59b857b861ad5d88ac00000000" ) block_txs = {} block_txids = [] for tx in txs: txid = self.make_txid(tx) block_txids.append(txid) block_txs[txid] = tx version = '01000000' t_hex = "%08X" % self.time difficulty_hex = "%08X" % self.difficulty tx_merkle_tree = pybitcoin.MerkleTree(block_txs) tx_merkle_root = binascii.hexlify(tx_merkle_tree.root()) prev_block_hash = self.block_hashes[self.end_block - 1] block_header = version + prev_block_hash + tx_merkle_root + t_hex + difficulty_hex + '00000000' # NOTE: not accurate; just get *a* hash block_hash = self.make_txid(block_header) # update nextblockhash at least self.blocks[prev_block_hash]['nextblockhash'] = block_hash # next block block = { 'merkleroot': tx_merkle_root, 'nonce': 0, # mock 'previousblockhash': prev_block_hash, 'hash': block_hash, 'version': 3, 'tx': block_txids, 'chainwork': '00' * 32, # mock 'height': self.end_block, 'difficulty': Decimal(0.0), # mock 'nextblockhash': None, # to be filled in 'confirmations': None, # to be filled in 'time': self.time, # mock 'bits': 0x00000000, # mock 'size': sum([len(tx) for tx in txs]) + len(block_hash) # mock } self.block_hashes[self.end_block] = block_hash self.blocks[block_hash] = block self.txs.update(block_txs) self.time += 600 # 10 minutes self.difficulty += 1 self.end_block += 1 return [self.make_txid(tx) for tx in txs]
def handle_block(self, message_header, block): log.info("handle block message %s" % block) if self.have_all_block_data(): self.loop_exit() return block_hash = block.calculate_hash() # Is it a solicited block if block_hash not in self.block_info.keys(): log.error("Ignoring unsolicited block %s" % (block_hash)) return header = self.block_info[block_hash]['header'] height = self.block_info[block_hash]['height'] # does the block's transaction hashes match the merkle root tx_hashes = [ block.txns[i].calculate_hash() for i in xrange(0, len(block.txns)) ] mr = pybitcoin.MerkleTree(tx_hashes).root() if mr != header['merkle_root']: log.error("Merkle root %s (%s) mismatch: expected %s, got %s" % (block_hash, height, header['merkle_root'], mr)) return nulldata_txs = [] relindex = 0 log.info("The origin block is %s" % block) log.info("The initial txns format is %s " % block.txns) for txindex in xrange(0, len(block.txns)): txdata = self.parse_tx(block.txns[txindex], header, block_hash, txindex) has_nulldata = False nulldata_payload = None for outp in txdata['vout']: if outp['scriptPubkey']['type'] == 'nulldata': has_nulldata = True nulldata_payload = bitcoin.deserialize_script( outp['scriptPubkey']['hex'])[1] if type(nulldata_payload) not in [str, unicode]: log.debug("Malformed nulldata format") nulldata_payload = outp['scriptPubkey']['hex'][4:] if not has_nulldata: continue log.info('nulldata is %s ' % binascii.unhexlify(nulldata_payload)) txdata['nulldata'] = nulldata_payload txdata['relindex'] = relindex relindex += 1 nulldata_txs.append(txdata) self.block_info[block_hash]['txns'] = nulldata_txs self.block_info[block_hash]['num_txns'] = len(block.txns) self.block_info[block_hash]['num_senders'] = 0 sender_txhashes = [] for txn in self.block_info[block_hash]['txns']: for i in xrange(0, len(txn['vin'])): inp = txn['vin'][i] sender_txid = inp['txid'] inp_sender_outp = inp['vout'] if str(sender_txid) not in sender_txhashes: sender_txhashes.append(str(sender_txid)) sinfo = self.make_sender_info(block_hash, txn, i) if not self.sender_info.has_key(sender_txid): self.sender_info[sender_txid] = {} self.sender_info[sender_txid][inp_sender_outp] = sinfo # update accounting self.num_blocks_received += 1 self.block_info[block_hash]['handled'] = True log.debug("Request %s nulldata sender TXs" % len(sender_txhashes)) if self.have_all_block_data(): self.loop_exit() return
def flush_transactions( self ): """ TESTING ONLY Send the bufferred list of transactions as a block. Save the resulting transactions to a temporary file. """ # next block txs = [] if self.next_block_txs_path is not None and os.path.exists( self.next_block_txs_path ): txs = self.restore_next( self.next_block_txs_path ) os.unlink( self.next_block_txs_path ) # add a fake coinbase txs.append( "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff53038349040d00456c69676975730052d8f72ffabe6d6dd991088decd13e658bbecc0b2b4c87306f637828917838c02a5d95d0e1bdff9b0400000000000000002f73733331312f00906b570400000000e4050000ffffffff01bf208795000000001976a9145399c3093d31e4b0af4be1215d59b857b861ad5d88ac00000000" ) block_txs = {} block_txids = [] for tx in txs: txid = make_txid( str(tx) ) block_txids.append( txid ) block_txs[ txid ] = tx version = '01000000' t_hex = "%08X" % self.time difficulty_hex = "%08X" % self.difficulty tx_merkle_tree = pybitcoin.MerkleTree( block_txids ) tx_merkle_root = tx_merkle_tree.root() prev_block_hash = self.block_hashes[ self.end_block - 1 ] # next block block = { 'merkleroot': tx_merkle_root, 'nonce': 0, # mock 'previousblockhash': prev_block_hash, 'version': 3, 'tx': block_txids, 'chainwork': '00' * 32, # mock 'height': self.end_block, 'difficulty': Decimal(0.0), # mock 'nextblockhash': None, # to be filled in 'confirmations': None, # to be filled in 'time': self.time, # mock 'bits': "0x00000000", # mock 'size': sum( [len(tx) for tx in txs] ) + 32 # mock } block_header = bitcoin.main.encode(block['version'], 256, 4)[::-1] + \ block['previousblockhash'].decode('hex')[::-1] + \ block['merkleroot'].decode('hex')[::-1] + \ bitcoin.main.encode(block['time'], 256, 4)[::-1] + \ bitcoin.main.encode(int(block['bits'], 16), 256, 4)[::-1] + \ bitcoin.main.encode(block['nonce'], 256, 4)[::-1] block['hash'] = pybitcoin.bin_double_sha256( block_header )[::-1].encode('hex') block['header'] = block_header for txid in block['tx']: # update txid --> blockhash map self.txid_to_blockhash[ txid ] = block['hash'] # update nextblockhash at least self.blocks[prev_block_hash]['nextblockhash'] = block['hash'] self.block_hashes[ self.end_block ] = block['hash'] self.blocks[ block['hash'] ] = block self.txs.update( block_txs ) self.time += 600 # 10 minutes self.difficulty += 1 self.end_block += 1 if self.save_file is not None: self.save( self.save_file ) if self.spv_headers_path is not None: with open(self.spv_headers_path, "a+") as f: f.write( block_header ) f.write( "00".decode('hex') ) # our SPV client expects varint for tx count to be zero return [ make_txid( str(tx) ) for tx in txs ]