Skip to content

Instantly share code, notes, and snippets.

@0xClandestine
Created July 16, 2025 22:41
Show Gist options
  • Select an option

  • Save 0xClandestine/1b56a478c029a6d1935e00145a3d124b to your computer and use it in GitHub Desktop.

Select an option

Save 0xClandestine/1b56a478c029a6d1935e00145a3d124b to your computer and use it in GitHub Desktop.
Block hash verification example
from web3 import Web3
import rlp
from eth_utils import keccak, to_bytes, to_hex
class BlockHeaderVerifier:
def __init__(self, rpc_url=""):
self.w3 = Web3(Web3.HTTPProvider(rpc_url))
def get_block_header_fields(self, block):
"""Extract and format block header fields for RLP encoding"""
# Convert address string to bytes
miner_bytes = to_bytes(hexstr=block.miner) if isinstance(block.miner, str) else block.miner
# Build header with proper types
return [
block.parentHash, # 0. parentHash
block.sha3Uncles, # 1. ommersHash (sha3Uncles)
miner_bytes, # 2. beneficiary (miner)
block.stateRoot, # 3. stateRoot
block.transactionsRoot, # 4. transactionsRoot
block.receiptsRoot, # 5. receiptsRoot
block.logsBloom, # 6. logsBloom
block.difficulty, # 7. difficulty (0 for PoS)
block.number, # 8. number
block.gasLimit, # 9. gasLimit
block.gasUsed, # 10. gasUsed
block.timestamp, # 11. timestamp
block.extraData, # 12. extraData
block.mixHash, # 13. mixHash
block.nonce, # 14. nonce
block.baseFeePerGas, # 15. baseFeePerGas (EIP-1559)
block.withdrawalsRoot, # 16. withdrawalsRoot (EIP-4895)
block.blobGasUsed, # 17. blobGasUsed (EIP-4844)
block.excessBlobGas, # 18. excessBlobGas (EIP-4844)
block.parentBeaconBlockRoot, # 19. parentBeaconBlockRoot (EIP-4788)
block.requestsHash # 20. requestsHash (EIP-7685)
]
def decode_merkle_proof_nodes(self, proof_list):
"""Decode RPC proof nodes from hex strings to RLP items"""
decoded_nodes = []
for node in proof_list:
# Each node is an RLP-encoded hex string, decode it
decoded_nodes.append(rlp.decode(bytes(node)))
return decoded_nodes
def get_account_proof(self, address, block_number, slots=[]):
"""Get RLP encoded account proof for a given address and block number"""
# Get account proof from RPC
proof = self.w3.eth.get_proof(address, slots, block_number)
# Decode the account proof nodes
account_proof = self.decode_merkle_proof_nodes(proof['accountProof'])
# Decode storage proofs if any slots were requested
# storage_proofs = []
# for slot_data in proof['storageProof']:
# storage_proofs.append(self.decode_merkle_proof_nodes(slot_data['proof']))
# The proof structure is: [accountProof, storageProofs]
# proof_rlp = rlp.encode([account_proof, storage_proofs])
proof_rlp = rlp.encode(account_proof)
print(f"\nAccount proof RLP: 0x{proof_rlp.hex()}")
print(f"Account balance: {Web3.from_wei(proof['balance'], 'ether')} ETH")
print(f"Account nonce: {proof['nonce']}")
print(f"Account code hash: {proof['codeHash'].hex()}")
print(f"Account storage hash: {proof['storageHash'].hex()}")
return proof_rlp
def verify_block_header(self, block_number):
"""Verify block header hash for a given block number"""
# Get block
block = self.w3.eth.get_block(block_number)
# Get header fields and encode
header = self.get_block_header_fields(block)
encoded = rlp.encode(header)
computed_hash = keccak(encoded)
# Check match
if block.hash == computed_hash:
print("✅ Success: ", block.hash.hex())
else:
print("❌ Failed: ", computed_hash.hex())
print(f"\ncast keccak256 0x{encoded.hex()}")
return block.hash == computed_hash
# Example usage
if __name__ == "__main__":
verifier = BlockHeaderVerifier()
verifier.verify_block_header(22841008)
verifier.get_account_proof("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", 22841008)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment