Caching block hashes
How the `AxiomV0` contract works and how to interact with it
The AxiomV0 smart contract caches blockhashes from the Ethereum history and allows smart contracts to verify them against this cache. We explain how this works below.
Caching blockhashes
AxiomV0 caches the Keccak Merkle roots of consecutive length 1024 sequences of blocks with block numbers [1024 * x, ..., 1024 * x + 1023] for an index x in the mapping
mapping(uint32 => bytes32) public historicalRoots;Here historicalRoots[startBlockNumber] holds the hash keccak(prevHash || root || numFinal), where prevHash is the block hash of block startBlockNumber - 1, root is the Merkle root of the block hashes with index in [startBlockNumber, startBlockNumber + 1023], with block hashes after startBlockNumber + numFinal - 1 replaced by 0, and numFinal is the number of block hashes verified in this range of blocks.
Blockhashes are cached by calling the updateRecent, updateOld, or updateHistorical functions with the following function signatures:
function updateRecent(bytes calldata proofData) external;
function updateOld(
bytes32 nextRoot,
uint32 nextNumFinal,
bytes calldata proofData
) external;
function updateHistorical(
bytes32 nextRoot,
uint32 nextNumFinal,
bytes32[HISTORICAL_NUM_ROOTS] calldata roots,
bytes32[TREE_DEPTH + 1][HISTORICAL_NUM_ROOTS - 1] calldata endHashProofs,
bytes calldata proofData
) external;These functions verify a ZKP of the Keccak header chain and update historicalRoots accordingly:
updateRecentandupdateOldprove Keccak header chains of length up to1024.updateHistoricalprovides a recursive proof of the validity of Keccak header chains of length128 * 1024and then reads individual Merkle roots of 1024 blocks using the Merkle proofs inendHashProofs.
These functions emit the event
event UpdateEvent(uint32 startBlockNumber, bytes32 prevHash, bytes32 root, uint32 numFinal);for each update of a Merkle root.
Reading from the cache
To read from the block hash cache, AxiomV0 provides the isBlockHashValid method which takes in a witness that a block hash is included in the cache, formatted via
struct BlockHashWitness {
uint32 blockNumber;
bytes32 claimedBlockHash;
bytes32 prevHash;
uint32 numFinal;
bytes32[TREE_DEPTH] merkleProof;
}This method verifies that merkleProof is a valid Merkle path for the relevant block hash and checks that the Merkle root lies in the cache.
Last updated
