def generate_proof_blob(block_dict, tx_index): header = block_header(block_dict) mpt = HexaryTrie(db={}) for tx_dict in block_dict["transactions"]: key = rlp.encode(utils.parse_as_int(tx_dict['transactionIndex'])) mpt.set(key, rlp_transaction(tx_dict)) if mpt.root_hash != normalize_bytes(block_dict['transactionsRoot']): raise ValueError( "Tx trie root hash does not match. Calculated: {} Sent: {}".format( mpt.root_hash.hex(), normalize_bytes(block_dict['transactionsRoot']).hex())) mpt_key_nibbles = bytes_to_nibbles(rlp.encode(tx_index)) mpt_path, stack_indexes, stack = generate_proof(mpt, mpt_key_nibbles) proof_blob = rlp.encode([ 1, # proof_type header, tx_index, bytes(mpt_path), bytes(stack_indexes), stack, ]) return proof_blob
def test_hexary_trie_missing_traversal_node_with_traverse_from(): db = {} trie = HexaryTrie(db, prune=True) key1 = to_bytes(0x0123) trie.set( key1, b'use a value long enough that it must be hashed according to trie spec' ) key2 = to_bytes(0x1234) trie.set(key2, b'val2') # delete first child of the root root_node = trie.root_node first_child_hash = root_node.raw[0] del db[first_child_hash] # Get exception with relevant info about lookup nibbles with pytest.raises(MissingTraversalNode) as exc_info: trie.traverse_from(root_node, (0, 1, 2, 3)) exception = exc_info.value assert exception.nibbles_traversed == (0, ) assert encode_hex(first_child_hash) in str(exception) # Other keys are still traversable node = trie.traverse((1, )) assert node.value == b'val2' assert node.sub_segments == ()
def test_squash_changes_raises_correct_error_on_underlying_missing_data(): db = {} trie = HexaryTrie(db, prune=True) trie.set(b'what floats on water?', b'very small rocks') old_root_hash = trie.root_hash # what if the root node hash is missing from the beginning? del db[old_root_hash] # the appropriate exception should be raised, when squashing changes with trie.squash_changes() as memory_trie: with pytest.raises(MissingTrieNode): memory_trie[b'what floats on water?']
def test_hexary_trie_batch_save_keeps_last_root_data(): db = {} trie = HexaryTrie(db) trie.set(b'what floats on water?', b'very small rocks') old_root_hash = trie.root_hash with trie.squash_changes() as memory_trie: memory_trie.set(b'what floats on water?', b'a duck') assert trie[b'what floats on water?'] == b'a duck' old_trie = HexaryTrie(db, root_hash=old_root_hash) assert old_trie[b'what floats on water?'] == b'very small rocks'
def test_hexary_trie_batch_save_drops_last_root_data_when_pruning(): db = {} trie = HexaryTrie(db, prune=True) trie.set(b'what floats on water?', b'very small rocks') old_root_hash = trie.root_hash with trie.squash_changes() as memory_trie: memory_trie.set(b'what floats on water?', b'a duck') assert trie[b'what floats on water?'] == b'a duck' old_trie = HexaryTrie(db, root_hash=old_root_hash) with pytest.raises(KeyError): old_trie.root_node
def test_squash_changes_can_still_access_underlying_deleted_data(): db = {} trie = HexaryTrie(db, prune=True) trie.set(b'what floats on water?', b'very small rocks') old_root_hash = trie.root_hash with trie.squash_changes() as memory_trie: memory_trie.set(b'what floats on water?', b'a duck') verify_ref_count(memory_trie) # change to a root hash that the memory trie doesn't have anymore memory_trie.root_hash memory_trie.root_hash = old_root_hash assert memory_trie[b'what floats on water?'] == b'very small rocks'
def generate_proof_blob(block_dict, tx_index): header = block_header(block_dict) mpt = HexaryTrie(db={}) for tx_dict in block_dict["transactions"]: key = rlp.encode(utils.parse_as_int(tx_dict['transactionIndex'])) mpt.set(key, rlp_transaction(tx_dict)) if mpt.root_hash != normalize_bytes(block_dict['transactionsRoot']): raise ValueError( "Tx trie root hash does not match. Calculated: {} Sent: {}" .format(mpt.root_hash.hex(), normalize_bytes(block_dict['transactionsRoot']).hex())) return construct_proof_from_mpt(mpt, header, tx_index, 1)
def test_hexary_trie_batch_save_drops_last_root_data_when_pruning(): db = {} trie = HexaryTrie(db, prune=True) trie.set(b'what floats on water?', b'very small rocks') old_root_hash = trie.root_hash with trie.squash_changes() as memory_trie: memory_trie.set(b'what floats on water?', b'a duck') verify_ref_count(memory_trie) assert trie[b'what floats on water?'] == b'a duck' old_trie = HexaryTrie(db, root_hash=old_root_hash) with pytest.raises(MissingTraversalNode) as excinfo: old_trie.root_node assert encode_hex(old_root_hash) in str(excinfo.value)
def generate_proof_blob_receipt(block_dict, tx_index, url): header = block_header(block_dict) mpt = HexaryTrie(db={}) for tx_dict in block_dict["transactions"]: key = rlp.encode(utils.parse_as_int(tx_dict['transactionIndex'])) receipt = get_receipt(url, tx_dict['hash']) mpt.set(key, rlp.encode(receipt)) if mpt.root_hash != normalize_bytes(block_dict['receiptsRoot']): if MODULE_DEBUG: print("mpt.root_hash " + str(utils.encode_hex(mpt.root_hash))) print("receiptRoot " + str(normalize_bytes(utils.encode_hex(block_dict['receiptsRoot'])))) raise ValueError("Block receiptRoot hash does not match.") return construct_proof_from_mpt(mpt, header, tx_index, 2)
def test_squash_changes_raises_correct_error_on_new_deleted_data(): db = {} trie = HexaryTrie(db, prune=True) trie.set(b'what floats on water?', b'very small rocks') with trie.squash_changes() as memory_trie: memory_trie.set(b'what floats on water?', b'a duck') verify_ref_count(memory_trie) middle_root_hash = memory_trie.root_hash memory_trie.set(b'what floats on water?', b'ooooohh') memory_trie.root_hash verify_ref_count(memory_trie) # change to a root hash that the memory trie doesn't have anymore memory_trie.root_hash = middle_root_hash with pytest.raises(MissingTrieNode): memory_trie[b'what floats on water?']
def test_squash_changes_reverts_trie_root_on_exception(): db = {} trie = HexaryTrie(db, prune=True) trie.set(b'\x00', b'B' * 32) trie.set(b'\xff', b'C' * 32) old_root_hash = trie.root_hash # delete the node that will be used during trie fixup del db[trie.root_node.raw[0xf]] with pytest.raises(MissingTrieNode): with trie.squash_changes() as memory_trie: try: memory_trie[b'\x11'] = b'new val' except MissingTrieNode: assert False, "Only the squash_changes context exit should raise this exception" del memory_trie[b'\xff'] assert trie.root_hash == old_root_hash
def verify_transaction_hash(self, block_info, tx_hash): txns = block_info.transactions tx_index = 0 # generate the mpt mpt = HexaryTrie(db={}) for tx_dict in txns: if tx_dict.hash == tx_hash: tx_index = tx_dict.transactionIndex key = rlp.encode(utils.parse_as_int(tx_dict['transactionIndex'])) mpt.set(key, self.rlp_transaction(tx_dict)) # verify the tx root if mpt.root_hash != normalize_bytes(block_info.transactionsRoot): return False # generate the proof mpt_key_nibbles = bytes_to_nibbles(rlp.encode(tx_index)) proof = tuple(self.generate_proof(mpt, mpt_key_nibbles)) if HexaryTrie.get_from_proof(mpt.root_hash, rlp.encode(utils.parse_as_int(tx_index)), proof) \ != self.rlp_transaction(txns[tx_index]): return False return True
def generate_receipt_proof_blob(web3, block_dict, tx_index): header = block_header(block_dict) mpt = HexaryTrie(db={}) for tx_dict in block_dict['transactions']: key = rlp.encode(tx_dict['transactionIndex']) tx_receipt = web3.eth.getTransactionReceipt(tx_dict['hash']) mpt.set(key, rlp.encode(receipt(tx_receipt))) if mpt.root_hash != normalize_bytes(block_dict['receiptsRoot']): raise ValueError('Receipt trie root hash does not match') mpt_key_nibbles = bytes_to_nibbles(rlp.encode(tx_index)) mpt_path, stack_indexes, stack = generate_proof(mpt, mpt_key_nibbles) proof_blob = rlp.encode([ 2, # proof_type header, tx_index, compact_encode(mpt_path), bytes(stack_indexes), stack, ]) return proof_blob
print(w3.toHex(trie.root_hash)) # Generate Merkle tree txs = [] B = len(block.transactions) print("The block has %d transactions" % B) for key in range(B): tx = w3.eth.getTransaction(block.transactions[key]) raw_tx = _Transaction(tx.nonce, tx.gasPrice, tx.gas, w3.toBytes(hexstr=tx.to), tx.value, w3.toBytes(hexstr=w3.toHex(hexstr=tx.input)), tx.v, w3.toInt(tx.r), w3.toInt(tx.s)) rlp_tx = rlp.encode(raw_tx) assert tx.hash == keccak(rlp_tx) txs.append(raw_tx) trie.set(rlp.encode(key), rlp_tx) # print(rlp_tx) assert block.transactionsRoot == trie.root_hash print(w3.toHex(trie.root_hash)) # Generate Merkle tree proof for per each node proofs = [] for key in range(B): proof = trie.get_proof(rlp.encode(key)) proofs.append(proof) # Verify Merkle tree proof for per each node nodes = [] for key in range(B): node = HexaryTrie.get_from_proof(trie.root_hash, rlp.encode(key), proofs[key])
def test_hexary_trie_missing_node(): db = {} trie = HexaryTrie(db, prune=True) key1 = to_bytes(0x0123) trie.set( key1, b'use a value long enough that it must be hashed according to trie spec' ) key2 = to_bytes(0x1234) trie.set(key2, b'val2') trie_root_hash = trie.root_hash # delete first child of the root root_node = trie.root_node.raw first_child_hash = root_node[0] del db[first_child_hash] # Get exception with relevant info about key with pytest.raises(MissingTrieNode) as exc_info: trie.get(key1) message = str(exc_info.value) assert encode_hex(key1) in message assert encode_hex(trie_root_hash) in message assert encode_hex(first_child_hash) in message # Get exception when trying to write into key with shared prefix key1_shared_prefix = to_bytes(0x0234) with pytest.raises(MissingTrieNode) as set_exc_info: trie.set(key1_shared_prefix, b'val2') set_exc_message = str(set_exc_info.value) assert encode_hex(key1_shared_prefix) in set_exc_message assert encode_hex(trie_root_hash) in set_exc_message assert encode_hex(first_child_hash) in set_exc_message # Get exception when trying to delete key with missing data with pytest.raises(MissingTrieNode) as delete_exc_info: trie.delete(key1) delete_exc_message = str(delete_exc_info.value) assert encode_hex(key1) in delete_exc_message assert encode_hex(trie_root_hash) in delete_exc_message assert encode_hex(first_child_hash) in delete_exc_message # Get exception when checking if key exists with missing data key1_shared_prefix2 = to_bytes(0x0345) with pytest.raises(MissingTrieNode) as existance_exc_info: key1_shared_prefix2 in trie existance_exc_message = str(existance_exc_info.value) assert encode_hex(key1_shared_prefix2) in existance_exc_message assert encode_hex(trie_root_hash) in existance_exc_message assert encode_hex(first_child_hash) in existance_exc_message # Other keys are still accessible assert trie.get(key2) == b'val2'
from eth_utils import ( keccak, ) trie = HexaryTrie(db={}) print(w3.toHex(trie.root_hash)) print(w3.toHex(keccak(trie.root_node))) print(w3.toHex(keccak(rlp.encode(trie.root_node)))) print("1", w3.toHex(rlp.encode(1))) print("ONE", w3.toHex(rlp.encode("ONE"))) print("2", w3.toHex(rlp.encode(2))) print("TWO", w3.toHex(rlp.encode("TWO"))) trie.set(rlp.encode(1), rlp.encode("ONE")) print(w3.toHex(trie.root_hash)) print(w3.toHex(keccak(rlp.encode(trie.get_node(trie.root_hash))))) assert trie.root_hash == keccak(rlp.encode(trie.get_node(trie.root_hash))) print(trie.get_node(trie.root_hash)) trie.set(rlp.encode(2), rlp.encode("TWO")) print(w3.toHex(trie.root_hash)) print(w3.toHex(keccak(rlp.encode(trie.get_node(trie.root_hash))))) assert trie.root_hash == keccak(rlp.encode(trie.get_node(trie.root_hash))) print(trie.get_node(trie.root_hash)) trie.set(rlp.encode(3), rlp.encode("THREE")) print(w3.toHex(trie.root_hash))