A smart contract that verifies the integrity of post-Capella historical blockhash
es via SSZ proofs. These SSZ proofs are made possible by the introduction of SSZ beacon block roots in EIP-4788.
The contract provides the following functions to verify and access historical blockhash values.
isBlockhashVerified(blockNumber)
: Returnsblockhash
associated with the providedblockNumber
if the_blockhash
has been verified before.verifyCurrentBlock()
: Using an SSZ beacon block root (from the EIP 4788 ring buffer) for a given blockx
, validates theblockhash
of blockx
.verifyRecentHistoricalBlock()
: Using an SSZ beacon block root (from the EIP 4788 ring buffer) for a given blockx
, validates ablockhash
of block within the range[x - 8192, x - 1]
.verifyHistoricalBlock()
: Using an SSZ beacon block root (from the EIP 4788 ring buffer) for a given blockx
, validates ablockhash
of block within the range[CAPELLA_INIT_BLOCK, x - 8193]
.
We report execution gas and calldata benchmarks below. Valid proofs will have same calldata sizes below as of the Deneb hardfork. These benchmarks may change as the result of future hardforks.
Execution Gas | Calldata Size | |
---|---|---|
verifyCurrentBlock() |
70851 | 1124 |
verifyHistoricalBlock() |
88131 | 1860 |
verifyRecentHistoricalBlock() |
111315 | 2852 |
We now explain how we prove Ethereum blockhashes into the beacon block root with SSZ proofs. This section assumes an understanding of SSZ Merkleization.
As described in the spec and annotated spec, verifying inclusion proofs into SSZ Merkleized data structures requires giving a valid Merkle proof into the root and also checking the correct field is chosen by constraining the generalized index as described here.
To verify an SSZ proof with respect to a generalized index, we separate it into the following two components:
- Local Index: A 0-indexed number indicating location within the structure (either struct or Vector/List), calculated as
generalized_index % prev_power_of_two(generalized_index)
.- For a Container (struct), this is its position in the struct.
- For a Vector or List, it is its index.
- Tree Height: Height of the type's SSZ Merkle tree. This should also be the length of any Merkle proof for the type. Calculated as
floor(log2(generalized_index))
.- For a Container (struct), can also be calculated as
ceil(log2(amount_of_fields))
. - For a Vector, can also be calculated as
ceil(log2(capacity))
. - For a List, can also be calculated as
ceil(log2(max_length)) + 1
. The+ 1
is for the length mix-in.
- For a Container (struct), can also be calculated as
During verification, the local index's binary representation encodes the left/right path of the Merkle proof, while the tree height constrains the length of the Merkle proof.
The beacon block root commits to the entire history of Ethereum blockhashes after the Capella hard fork via its commitment to the beacon state and the following BeaconState
fields:
latest_execution_payload_header
-- contains the most recent Ethereum blockhash.state_roots
-- contains roots of the beacon states and hence Ethereum blockhashes for the past 8192 slots.historical_summaries
-- contains Merkle roots of state and block roots in groups of 8192 back to the Capella hard fork.
This contract uses SSZ proofs for entries of these fields to verify any blockhash after the Capella hard fork into a post-Deneb beacon block root. These proofs take the form of successive Merkle proofs of fields into Merkleized SSZ structs which together form a path from the blockhash in question to the beacon block root. In addition, the block number associated with the blockhash is also validated to be able to index each blockhash by its respective block number in contract storage. In the rest of this section, we specify the path and the relevant local indices for each of the three cases above.
In all cases, we begin by proving the state_root
into the beacon block root, which is available in the EVM via EIP-4788. This proof has a local index of 3 and a tree height of 3.
pub struct BeaconBlock {
pub slot: Slot,
pub proposer_index: ValidatorIndex,
pub parent_root: Root,
**** pub state_root: Root, ****
pub body: Root
}
The remainder of the SSZ verification follows different flows for each of the three cases, which directly map to the three contract functions.
In this case, we need to first prove inclusion of the latest_execution_payload_header
field of the BeaconState
via the field shown below.
pub struct BeaconState {
/** <...> **/
pub next_sync_committee: SyncCommittee<SYNC_COMMITTEE_SIZE>,
**** pub latest_execution_payload_header: ExecutionPayloadHeader, ****
pub next_withdrawal_index: WithdrawalIndex,
/** <...> **/
}
Then, we can prove both the blockhash
field and the block_number
field of the ExecutionPayloadHeader
.
pub struct ExecutionPayloadHeader {
pub parent_hash: Hash32,
pub fee_recipient: ExecutionAddress,
pub state_root: Bytes32,
pub receipts_root: Bytes32,
pub logs_bloom: ByteVector<BYTES_PER_LOGS_BLOOM>,
pub prev_randao: Bytes32,
**** pub block_number: u64, ****
pub gas_limit: u64,
pub gas_used: u64,
pub timestamp: u64,
pub extra_data: ByteList<MAX_EXTRA_DATA_BYTES>,
pub base_fee_per_gas: U256,
**** pub block_hash: Hash32, ****
pub transactions_root: Root,
pub withdrawals_root: Root,
pub blob_gas_used: u64,
pub excess_blob_gas: u64,
}
The corresponding local indices and tree heights for a proof of a blockhash
for slot n
into the BeaconBlock
root for slot n
are:
Merkleized Data Structure | Leaf Node | Root Node | Local Index | Tree Height | |
---|---|---|---|---|---|
BeaconState Root Proof |
BeaconBlock for slot n |
BeaconState Root |
BeaconBlock Root |
3 | 3 |
ExecutionPayload Root Proof |
BeaconState for slot n |
ExecutionPayload Root |
BeaconState Root |
24 | 5 |
Blockhash Proof | ExecutionPayload for slot n |
blockhash |
ExecutionPayload Root |
6 | 5 |
Block Number Proof | ExecutionPayload for slot n |
block_number |
ExecutionPayload Root |
12 | 5 |
In this case, the historic state root we are interested in lies in the state_roots
vector, and its index within the vector is historic_state_root_slot % 8192
. We must first give a proof from an element of the state_roots
vector to the current state root:
/// Current slot instance
pub struct BeaconState {
/** <...>> **/
pub block_roots: Vector<Root, SLOTS_PER_HISTORICAL_ROOT>,
**** pub state_roots: Vector<Root, SLOTS_PER_HISTORICAL_ROOT>, ****
pub historical_roots: List<Root, HISTORICAL_ROOTS_LIMIT>,
/** <...> **/
}
Next, we need to prove inclusion of the latest_execution_payload_header
field of the BeaconState
via the field shown below.
/// Historic slot instance
pub struct BeaconState {
/** <...> **/
pub next_sync_committee: SyncCommittee<SYNC_COMMITTEE_SIZE>,
**** pub latest_execution_payload_header:
ExecutionPayloadHeader<BYTES_PER_LOGS_BLOOM, MAX_EXTRA_DATA_BYTES>, ****
pub next_withdrawal_index: WithdrawalIndex,
/** <...> **/
}
Finally, we can prove both the blockhash
field and the block_number
field of the ExecutionPayloadHeader
.
/// Historic slot instance
pub struct ExecutionPayloadHeader {
/** <...> **/
pub prev_randao: Bytes32,
**** pub block_number: u64, ****
pub gas_limit: u64,
/** <...> **/
pub base_fee_per_gas: U256,
**** pub block_hash: Hash32, ****
pub transactions_root: Root,
/** <...> **/
}
To summarize, to prove the blockhash
and block_number
for slot x
into a BeaconBlock
root for slot n
where n - 8192 <= x < n
, we require the following sequence of proofs with corresponding local indices and tree heights:
Merkleized Data Structure | Leaf Node | Root Node | Local Index | Tree Height | |
---|---|---|---|---|---|
BeaconState Root Proof |
BeaconBlock for slot n |
BeaconState Root |
BeaconBlock Root |
3 | 3 |
Historical BeaconState Root Proof |
BeaconState for slot n |
Historical BeaconState Root |
BeaconState Root |
49152 + (x % 8192) |
18 |
ExecutionPayload Root Proof |
BeaconState for slot x |
ExecutionPayload Root |
BeaconState Root |
24 | 5 |
Blockhash Proof | ExecutionPayload for slot x |
blockhash |
ExecutionPayload Root |
6 | 5 |
Block Number Proof | ExecutionPayload for slot x |
block_number |
ExecutionPayload Root |
12 | 5 |
In this case, the historic state root we are interested in is committed to in the historical_summaries
field of the BeaconState
. Thus, we must give a proof from the state_summary_root
of the HistoricalSummary
at index (historic_state_root_slot - CAPELLA_INIT_SLOT) / 8192
of historical_summaries
to the current beacon state root.
/// Current slot instance
pub struct BeaconState {
/** <...> **/
pub next_withdrawal_validator_index: ValidatorIndex,
**** pub historical_summaries: List<HistoricalSummary, HISTORICAL_ROOTS_LIMIT>, ****
}
Next, we must prove the historic state root at index historic_state_root_slot % 8192
of the state_roots
vector into the state_summary_root
.
/// Merkleization slot instance
pub struct BeaconState {
/** <...> **/
pub block_roots: Vector<Root, SLOTS_PER_HISTORICAL_ROOT>,
**** pub state_roots: Vector<Root, SLOTS_PER_HISTORICAL_ROOT>, ****
pub historical_roots: List<Root, HISTORICAL_ROOTS_LIMIT>,
/** <...> **/
}
Next, we need to prove inclusion of the latest_execution_payload_header
field of the BeaconState
via the field shown below.
/// Historic slot instance
pub struct BeaconState {
/** <...> **/
pub next_sync_committee: SyncCommittee<SYNC_COMMITTEE_SIZE>,
**** pub latest_execution_payload_header:
ExecutionPayloadHeader<BYTES_PER_LOGS_BLOOM, MAX_EXTRA_DATA_BYTES>, ****
pub next_withdrawal_index: WithdrawalIndex,
/** <...> **/
}
Finally, we can prove both the blockhash
field and the block_number
field of the ExecutionPayloadHeader
.
/// Historic slot instance
pub struct ExecutionPayloadHeader {
/** <...> **/
pub prev_randao: Bytes32,
**** pub block_number: u64, ****
pub gas_limit: u64,
/** <...> **/
pub base_fee_per_gas: U256,
**** pub block_hash: Hash32, ****
pub transactions_root: Root,
/** <...> **/
}
To summarize, to prove the blockhash
and block_number
for slot x
into the BeaconBlock
root for slot n
with CAPELLA_INIT_SLOT <= x < n - 8192
, we must generate the following SSZ proofs with corresponding local indices and tree heights:
Merkleized Data Structure | Leaf Node | Root Node | Local Index | Tree Height | |
---|---|---|---|---|---|
BeaconState Root Proof |
BeaconBlock for slot n |
BeaconState Root |
BeaconBlock Root |
3 | 3 |
HistoricalSummary Root Proof |
BeaconState for slot n |
state_summary_root |
BeaconState Root |
1811939328 + 2 * ((x - CAPELLA_INIT_SLOT) / 8192) + 1 |
31 |
Historical BeaconState Root Proof |
BeaconState for merkleization slot of slot n |
Historical BeaconState Root |
state_summary_root |
x % 8192 |
13 |
ExecutionPayload Root Proof |
BeaconState for slot x |
ExecutionPayload Root |
BeaconState Root |
24 | 5 |
Blockhash Proof | ExecutionPayload for slot x |
blockhash |
ExecutionPayload Root |
6 | 5 |
Block Number Proof | ExecutionPayload for slot x |
block_number |
ExecutionPayload Root |
12 | 5 |
To run:
cp crates/eth_proof_backend/.env.example crates/eth_proof_backend/.env
Then, fill out the relevant environment variables. Setting OPTIMISM_RPC_URL
is optional.
source crates/eth_proof_backend/.env
cd crates && cargo run --release
By default, the server exposes two endpoints:
Generates a set of SSZ proofs to prove a blockhash into a specified beacon block root.
Parameter | Required | Description |
---|---|---|
prove_into_block |
Yes | The block whose SSZ beacon block root the proof should be generated against |
prove_from_block |
Yes | The block whose blockhash to verify |
# CurrentBlock
curl http://localhost:3000/generate_blockhash_proof?prove_from_block=20822991&prove_into_block=20822991
Click to view full JSON response
{
"ssz_proof": {
"type": "CurrentBlock",
"block_number_proof": {
"branch": [
"0x80c3c90100000000000000000000000000000000000000000000000000000000",
"0x77983d96ffbe0ce6d774c5549ec83b5db92b9d58acb9000907e1769ab65a3058",
"0x0a2fd60fe409181685a3c646ab7f52ae45469aa4e79731d9a1f571d55640972f",
"0x3440525e894c563e08cb064ad356008bf2f2137dfcafec62e2c7e1224ab30fa0",
"0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c"
],
"generalized_index": 38,
"leaf": "0xcfbb3d0100000000000000000000000000000000000000000000000000000000",
"local_index": 6,
"root": "0x8fb9b7f0da7bf188eace48eae9570ab5bf71b7799e3af991fbffd6f921f322e3"
},
"blockhash_proof": {
"branch": [
"0xdcbf672fc4595064fe10422dcb17202536b2da53dd27b2f2a49bc97efa9bc1ad",
"0x06ccf7eb988b9e6db8b9df95cd19e4b913e43bfd13292d2c9937ccbabcef4d54",
"0xac999fc800596f5b57fb7f9df3b68824852ea3768880751413cbf0dc724c53d7",
"0xd6df8fe94969670a54855026c77387b7fb23e279c098352f62e1bc10d65b33e1",
"0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c"
],
"generalized_index": 44,
"leaf": "0x3b60459d7b4f659bd642c1308d95edb028ab5285284fd5dba9f1c78820cfa3b7",
"local_index": 12,
"root": "0x8fb9b7f0da7bf188eace48eae9570ab5bf71b7799e3af991fbffd6f921f322e3"
},
"curr_state_root_proof": {
"branch": [
"0xb1a87aaa635efd3d4c7a40551b1babf5db79e5bb59d396fe607677d8ca8b4882",
"0x2e9f58cbfdc738f60d5f9c304e3480b923239610fa650ff309259defd96a0d19",
"0x9364f9f48c2ba1865e7a34a780ff34f6abe74442bb037dd4921d342c95ebd607"
],
"generalized_index": 11,
"leaf": "0x712394bfa792683f38e6b167e14806cbc80b7a9c6ad95ffb21e9bf800a734002",
"local_index": 3,
"root": "0x1eb75a280a75f6f05cafefa43ea93daf78857398c626be53692290f8a71a03a2"
},
"execution_payload_proof": {
"branch": [
"0xebcf9c0300000000000000000000000000000000000000000000000000000000",
"0x494ba097b2500f1a224013727c11891bef1d485a22300de649ec05ef3bfbb8d3",
"0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71",
"0x29ccd141f1ea891ed7494cb00dd790509edbcbadc851f98c469c3f1c77a998da",
"0x10bf96a30efcb70cd4697d810f171dbbe777ab63cb75413530a04b4c8a876038"
],
"generalized_index": 56,
"leaf": "0x8fb9b7f0da7bf188eace48eae9570ab5bf71b7799e3af991fbffd6f921f322e3",
"local_index": 24,
"root": "0x712394bfa792683f38e6b167e14806cbc80b7a9c6ad95ffb21e9bf800a734002"
}
}
}
# RecentHistoricalBlock
curl http://localhost:3000/generate_blockhash_proof?prove_from_block=20822990&prove_into_block=20822991
Click to view full JSON response
{
"ssz_proof": {
"type": "RecentHistoricalBlock",
"block_number_proof": {
"branch": [
"0x80c3c90100000000000000000000000000000000000000000000000000000000",
"0x8ad18636bfece80d0fce01fb3a59c56243f5912ef62be03456970041432b13f6",
"0x9be456c7bdbad4b85162bc9dcd4db39a74bbea2fa749ea9289cfd309f769cd8b",
"0xf9407b400e423e51179b826fbdda5e2a010f5678645d2c05c5242035b96546a1",
"0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c"
],
"generalized_index": 38,
"leaf": "0xcebb3d0100000000000000000000000000000000000000000000000000000000",
"local_index": 6,
"root": "0x47cfb64e2ec4487e330772d23c89c4a049e485987b4e2a988088376999e2fa11"
},
"blockhash_proof": {
"branch": [
"0x98029a19c321f46cbff4d0b2b445b89458ede54dd95a26cc1c792c1401a4503b",
"0x52ce89921faea95a8dfe83542bd3199f9b71d7e5679e9437b3f246a466a00377",
"0xaca01d9075023a3e0b341dec552dbdee7de1793e5635dde9fd82bc4d24416c2b",
"0xa92b8c3225173e91464cc33e1933f082b9187dc785bb5be3469582d0769d3963",
"0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c"
],
"generalized_index": 44,
"leaf": "0x2b3c66dda429b5b6ab1163f3f22bde73d65c4de83aaa3315e8f06e25b92ddd4d",
"local_index": 12,
"root": "0x47cfb64e2ec4487e330772d23c89c4a049e485987b4e2a988088376999e2fa11"
},
"curr_state_root_proof": {
"branch": [
"0xb1a87aaa635efd3d4c7a40551b1babf5db79e5bb59d396fe607677d8ca8b4882",
"0x2e9f58cbfdc738f60d5f9c304e3480b923239610fa650ff309259defd96a0d19",
"0x9364f9f48c2ba1865e7a34a780ff34f6abe74442bb037dd4921d342c95ebd607"
],
"generalized_index": 11,
"leaf": "0x712394bfa792683f38e6b167e14806cbc80b7a9c6ad95ffb21e9bf800a734002",
"local_index": 3,
"root": "0x1eb75a280a75f6f05cafefa43ea93daf78857398c626be53692290f8a71a03a2"
},
"execution_payload_proof": {
"branch": [
"0xdbcf9c0300000000000000000000000000000000000000000000000000000000",
"0xc603268447c8c1aeb38a961121ca741c51079383dfe007aa081d9bcf281ca304",
"0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71",
"0x3d5656788fc03388c192654b925321cc432287151339c1b3cd0f6b3c199b537d",
"0x16b63f5157b92c8ef524a615c58f273eea60cdd881f05032dda20181fce95b7c"
],
"generalized_index": 56,
"leaf": "0x47cfb64e2ec4487e330772d23c89c4a049e485987b4e2a988088376999e2fa11",
"local_index": 24,
"root": "0xf0b06a34175f0ed35e352dc74b53724180d00736a028b8c6dce2ed1da3a3f274"
},
"hist_state_root_proof": {
"branch": [
"0x596f5d6200a0d9250830b702ab8b00df7845c76df0dd43f6ad1a00d9f3b423d5",
"0x75157b3abce3f02e58172edef173be3ae981d5ddaefec47a65a3185bd9d9f3c2",
"0x07383ca5dfea8649338facc70f0372de63e603e1756bda99be56fc2208e5146f",
"0xe54342ab3e010a702674640629b4d1fac8525041ded12d3b250043a2c976b066",
"0xa4213188f6fe43cc3c24743e98aa344145e8f76c1bd5bd92bfdb0c4c18e6db10",
"0xa9d167f8822a43cf4bfc53a98615a4ec89938cf3d0563f5ee6ac8b75d217a7e0",
"0x5171f832f8c26e481e506e8a977d2836471f495175f5fcb18dd2a47fdac54555",
"0x2c25fcaa72819f1d855dd9ad638c9f150e0354c4b9ce90987e98b5921d0d66bb",
"0xe18acb7cb9ace0245f98f4aa6c531856ddd54e52b167f10464dd6d71c31ddfff",
"0x95815ed47c984880cbf646de6dff6c4a3601ea10315c69169dda044514ee9193",
"0x1cf19aa1bde06cc1ee88c8aa6003839bd4748441dc88b57ffd67c51dc122249e",
"0xba55c3beba604dc6e2dd73e1df66bfff23449db37f56c786b216ae79ac723fed",
"0xa0edabcc6649e34ab70018032a8d98b6bb9919bc5ff3719f612c19998c5d7c05",
"0x4df6b89755125d4f6c5575039a04e22301a5a49ee893c1d27e559e3eeab73da7",
"0x2c108473d5e0d40b194a4d6a5e2ba4882e503af8b001f2f5be8aa46faa9fe408",
"0x0a6a24a63d62eb67ee71bed2d625e0520ed44087a8be73a233bbe69861960ed2",
"0x361b87228d7163a25d6bbb0a02f8ad64928339f60852460b094ece5f98066207",
"0x4ac58bec03213877dc0995ae186e9d5add2eec166b9efb87a709d410be4fb490"
],
"generalized_index": 316572,
"leaf": "0xf0b06a34175f0ed35e352dc74b53724180d00736a028b8c6dce2ed1da3a3f274",
"local_index": 54428,
"root": "0x712394bfa792683f38e6b167e14806cbc80b7a9c6ad95ffb21e9bf800a734002"
}
}
}
# HistoricalBlock
curl http://localhost:3000/generate_blockhash_proof?prove_from_block=19822990&prove_into_block=20822991
Click to view full JSON response
{
"ssz_proof": {
"type": "HistoricalBlock",
"block_number_proof": {
"branch": [
"0x80c3c90100000000000000000000000000000000000000000000000000000000",
"0xaf7381970e9e3b08f030d0529819700835df102fed86e2ebec9c9af6b1aad28c",
"0x9b6b3994257f8d86a64fac883070d93c492e042ff3e443e7274671f3aebce3bf",
"0xf82210390d1c63d1dd68b9f12cda46fd3f294f2984b79cd2f5990c555f85e263",
"0x9a355fd99247fa5e3eb6f397f1ee992f4f1c694e78b21971a79f1088de1ff37d"
],
"generalized_index": 38,
"leaf": "0x8e792e0100000000000000000000000000000000000000000000000000000000",
"local_index": 6,
"root": "0xac67f42c2523e56aa7b2d46746a3926d5cc06360d24c657380ff4af3648585f9"
},
"blockhash_proof": {
"branch": [
"0x6fd9b3096bff0d1790bd3d993050839a045e70a9d85d6ef1ebb18d6aabf12d29",
"0x5c586a0e6af7b31e2014b5414f57c3c85b428051d38cb7b354e2c669ce26edae",
"0xb0b0e0e758010a3b8b3d53d84ddbbe658ba5bee943d366bbd5ee7dba32ab04a6",
"0xd1769faa1257912c2f0338e7ef0ae9eb4820a27c0704d1a3e1c2791554043f79",
"0x9a355fd99247fa5e3eb6f397f1ee992f4f1c694e78b21971a79f1088de1ff37d"
],
"generalized_index": 44,
"leaf": "0x8fc43310e6d60aafc7a6c7fdcfd1467dfb58db96581270cdbca6e8e7466bb585",
"local_index": 12,
"root": "0xac67f42c2523e56aa7b2d46746a3926d5cc06360d24c657380ff4af3648585f9"
},
"curr_state_root_proof": {
"branch": [
"0xb1a87aaa635efd3d4c7a40551b1babf5db79e5bb59d396fe607677d8ca8b4882",
"0x2e9f58cbfdc738f60d5f9c304e3480b923239610fa650ff309259defd96a0d19",
"0x9364f9f48c2ba1865e7a34a780ff34f6abe74442bb037dd4921d342c95ebd607"
],
"generalized_index": 11,
"leaf": "0x712394bfa792683f38e6b167e14806cbc80b7a9c6ad95ffb21e9bf800a734002",
"local_index": 3,
"root": "0x1eb75a280a75f6f05cafefa43ea93daf78857398c626be53692290f8a71a03a2"
},
"execution_payload_proof": {
"branch": [
"0xdbaba80200000000000000000000000000000000000000000000000000000000",
"0xb0486920ef8aba6826718e8a0a813789c5ae6ad4a84ac2b2c9dbed30e41e0751",
"0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71",
"0xe4c68cda69610de6088c48a749708c1b12c77067dee406a9924237d99c036249",
"0xfde3524e6a06fdeb50db5e4f2d6adae3c13ec5ea1eea65f81b500f090ab99a74"
],
"generalized_index": 56,
"leaf": "0xac67f42c2523e56aa7b2d46746a3926d5cc06360d24c657380ff4af3648585f9",
"local_index": 24,
"root": "0x728f7db1f406a5d1f98b3d9c2bc62edb9182527abc748d8415180cf6b2491c2d"
},
"hist_state_root_proof": {
"branch": [
"0xec380a819e9e26740fa118c1e6281b3deac3d47efd45a6996eecbe0d4e8f6d70",
"0x3f6f4ecc22df9b83f082fc1341faea26a0fa7a8bb3a420d2c629fffa4b27ab4a",
"0x8d54e8265340b97a6e6183e2bff270b20a60c2d4dffd3d482b2ed1aa9a773d1f",
"0x001952aede8e188f0c5d9e9d145f3576ff4c2c3b363b239da6bb62da91ccdc4f",
"0xc58e281926c1e7d6e23381f3e09ac27c49327f86565532fd841ef46d2058e0db",
"0x61112b64de15e9386e7aee1be8d241ae18417a773bce3facb81b59267d152980",
"0x0f686be18c68088aaa61f7b1f0d5386c80e3d5dfda37ff98bad194f6a58240af",
"0xf17c5345ec04e3c10ab012d93360360de9dcfa1abc0038fd05c100b1085732b1",
"0x23bea9a4b88d9fd162ea680457da45090c2ff825416f16d3c74c653a4a943c3c",
"0x79c1d461ede6dc15c3dbdfdd77cfe24fba9b969f034486a17104ac426f368bc0",
"0x3c02e2495f90dc3bb37b7fb81f37c19d95c21c1b53a2f71284e171ac246f01cb",
"0x41c1acc60eaf7131b6fc313459253abb2beddb96830945a6d3146cc94cddf4fc",
"0x5c2537f54ae4c5b54af1f7a8564efa78e28cf7c64f0cff5e473f9b67c6b4c3a0"
],
"generalized_index": 15370,
"leaf": "0x728f7db1f406a5d1f98b3d9c2bc62edb9182527abc748d8415180cf6b2491c2d",
"local_index": 7178,
"root": "0x613db5a8b14db003c77131a55c17f8748595e532638ee49d13dd754b2b2e065a"
},
"summary_root_proof": {
"branch": [
"0xa0b3a50fdc44cb6162ef553e1591f992b59eefadeb9498012b670f4fb591c394",
"0xf2c1ae8710393719edcdf51d080a5f9c65c73ccf2eecd78ac61eb5735d1cb366",
"0xf0ca49ac2aab4092fa15fea6d5105e545e5eccd21d5f0ad00335d8ea4dd23b44",
"0x9eecd9026ae89c0dd2c86927eab644a9d1a70cf366e70c7fc4b7db0f56c2d148",
"0x29a110731687d206696fedc8d4c2149a6fd7474aeaaf01b33adbc9232f98fa89",
"0x4798d0e92891c6bff8e0828a487fb7668a6ad8649993fa1e6f9f09d496f6d232",
"0x0003ded0742f04543164cad80410644f47c79048500aa1cc88996c94098d406a",
"0xd5fb5effea691d833f3c53c00b390c08aaffa8294e48c7790f4d4ab18c4d9430",
"0xd24a1778b3e27acf1fd4e5b0eb98626964d6892f8427b9c12f85832c8240c14a",
"0x460485af574848585860d57ea2bd50835e0b5a2a296e0dcb014483c82debb54f",
"0x506d86582d252405b840018792cad2bf1259f1ef5aa5f887e13cb2f0094f51e1",
"0xffff0ad7e659772f9534c195c815efc4014ef1e1daed4404c06385d11192e92b",
"0x6cf04127db05441cd833107a52be852868890e4317e6a02ab47683aa75964220",
"0xb7d05f875f140027ef5118a2247bbb84ce8f2f0f1123623085daf7960c329f5f",
"0xdf6af5f5bbdb6be9ef8aa618e4bf8073960867171e29676f8b284dea6a08a85e",
"0xb58d900f5e182e3c50ef74969ea16c7726c549757cc23523c369587da7293784",
"0xd49a7502ffcfb0340b1d7885688500ca308161a7f96b62df9d083b71fcc8f2bb",
"0x8fe6b1689256c0d385f42f5bbe2027a22c1996e110ba97c171d3e5948de92beb",
"0x8d0d63c39ebade8509e0ae3c9c3876fb5fa112be18f905ecacfecb92057603ab",
"0x95eec8b2e541cad4e91de38385f2e046619f54496c2382cb6cacd5b98c26f5a4",
"0xf893e908917775b62bff23294dbbe3a1cd8e6cc1c35b4801887b646a6f81f17f",
"0xcddba7b592e3133393c16194fac7431abf2f5485ed711db282183c819e08ebaa",
"0x8a8d7fe3af8caa085a7639a832001457dfb9128a8061142ad0335629ff23ff9c",
"0xfeb3c337d7a51a6fbf00b9e34c52e1c9195c969bd4e7a0bfd51d5c5bed9c1167",
"0xe71f0aa83cc32edfbefa9f4d3e0174ca85182eec9f3a09f6a6c0df6377a510d7",
"0xd201000000000000000000000000000000000000000000000000000000000000",
"0xf937140000000000000000000000000000000000000000000000000000000000",
"0xca0823dc4fc61b0baf3fe9a24c98d71b94ef1cfd4987ea92c295254baa20212b",
"0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71",
"0x29ccd141f1ea891ed7494cb00dd790509edbcbadc851f98c469c3f1c77a998da",
"0x10bf96a30efcb70cd4697d810f171dbbe777ab63cb75413530a04b4c8a876038"
],
"generalized_index": 3959423663,
"leaf": "0x613db5a8b14db003c77131a55c17f8748595e532638ee49d13dd754b2b2e065a",
"local_index": 1811940015,
"root": "0x712394bfa792683f38e6b167e14806cbc80b7a9c6ad95ffb21e9bf800a734002"
}
}
}
Generates a set of SSZ proofs to prove a blockhash into a recent beacon block root that is likely to be available in the EIP-4788 contract.
Parameter | Required | Description |
---|---|---|
eip4788_timestamp |
No | The EIP-4788 timestamp of the SSZ beacon block root to generate the proof against. If not provided, an SSZ beacon block root near the tip of the chain will be used which ensures that the proof will be verifiable on-chain for as long as possible. |
prove_from_block |
Yes | The block whose blockhash to verify |
verifier_chain |
Yes | The chain on which the proof will be verified. Options: "mainnet", "optimism" |
# CurrentBlock, with set timestamp
curl http://localhost:3000/generate_eip4788_blockhash_proof?prove_from_block=20829376&verifier_chain=mainnet&eip4788_timestamp=1727288531
Click to view full JSON response
{
"type": "CurrentBlock",
"eip4788_timestamp": 1727288531,
"ssz_proof": {
"block_number_proof": {
"branch": [
"0x80c3c90100000000000000000000000000000000000000000000000000000000",
"0x592b5fc1b6cf000a0da322d778be6239f99a9781d39d66aab5ce4ee40de5ac47",
"0x861acbedf86e2c6d512a3e3267b82b3d280ceed221f76f996cc0ef83cb856735",
"0xf0eaafdb7905960c326039103091f977e0fb5437977eaad48d47348b2f4ebfb1",
"0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c"
],
"generalized_index": 38,
"leaf": "0xc0d43d0100000000000000000000000000000000000000000000000000000000",
"local_index": 6,
"root": "0xefbec0b3a6f719f88b116cbb102a0980e3e8bbe511b727dfdc6d611db8b9f4d8"
},
"blockhash_proof": {
"branch": [
"0xc253ac822a789eb41dacdc3b115b3869fd26c1aafec8f78da96aa0684233cae7",
"0x074eca38742b369f633f96a1cd67f518dab0993c355eeb21ff3e22d79b93e24d",
"0xe9f3e28f3b42ee5f1ec5152cef5bc55bc1abcb034525a3354f8dd0cbcc330d46",
"0xd09eb93f051658fcf0fe55087527c73621ca94f75fa652264c9dfd051999638c",
"0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c"
],
"generalized_index": 44,
"leaf": "0xb8d4fdea35b4e115e3335e8fc6361062f22fb41ed587703be5137335f423db10",
"local_index": 12,
"root": "0xefbec0b3a6f719f88b116cbb102a0980e3e8bbe511b727dfdc6d611db8b9f4d8"
},
"curr_state_root_proof": {
"branch": [
"0x4c077a041dae0b8d0de8090186f4ea806aef952dd1050d54f031c908b4b174d9",
"0x67ce6e4299218f7814d4715edcf4702c8fdd957a42ca72979e39f8af7e459020",
"0x6bf7e8cda1c50f5c971727e6a056c96ffd052f7618f110045d2e148e4d4c9744"
],
"generalized_index": 11,
"leaf": "0x657906374cfd95a9ea194f44ad6c9ac8cbe26c5838304951e4746093c160c40f",
"local_index": 3,
"root": "0x8c1345f0832349abb5a8c0321c699b0b821fe78718743a3709cf3fec4060f700"
},
"execution_payload_proof": {
"branch": [
"0xfb5e9e0300000000000000000000000000000000000000000000000000000000",
"0x2dbd2627cabfadd446cf9adbff2b01573ffd3289bd6c45b73c0255956a362836",
"0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71",
"0x2ff4bdfcb5e65d88c12b8d64e141759ac58f1d0f851ed720658fd914a01891db",
"0xf542bff4bd4fa9b5e1ebb6e8a96a6e7d6d69d7769da32d7999ddcc5c72aa974b"
],
"generalized_index": 56,
"leaf": "0xefbec0b3a6f719f88b116cbb102a0980e3e8bbe511b727dfdc6d611db8b9f4d8",
"local_index": 24,
"root": "0x657906374cfd95a9ea194f44ad6c9ac8cbe26c5838304951e4746093c160c40f"
}
}
}
# RecentHistoricalBlock, with no set timestamp (will potentially return different output every time)
curl http://localhost:3000/generate_eip4788_blockhash_proof?prove_from_block=20822990&verifier_chain=mainnet
Click to view full JSON response
{
"eip4788_timestamp": 1727288531,
"ssz_proof": {
"type": "RecentHistoricalBlock",
"blockhash_proof": {
"branch": [
"0xdcbf672fc4595064fe10422dcb17202536b2da53dd27b2f2a49bc97efa9bc1ad",
"0x06ccf7eb988b9e6db8b9df95cd19e4b913e43bfd13292d2c9937ccbabcef4d54",
"0xac999fc800596f5b57fb7f9df3b68824852ea3768880751413cbf0dc724c53d7",
"0xd6df8fe94969670a54855026c77387b7fb23e279c098352f62e1bc10d65b33e1",
"0x536d98837f2dd165a55d5eeae91485954472d56f246df256bf3cae19352a123c",
"0xebcf9c0300000000000000000000000000000000000000000000000000000000",
"0x494ba097b2500f1a224013727c11891bef1d485a22300de649ec05ef3bfbb8d3",
"0xdb56114e00fdd4c1f85c892bf35ac9a89289aaecb1ebd0a96cde606a748b5d71",
"0x29ccd141f1ea891ed7494cb00dd790509edbcbadc851f98c469c3f1c77a998da",
"0x10bf96a30efcb70cd4697d810f171dbbe777ab63cb75413530a04b4c8a876038"
],
"generalized_index": 1804,
"leaf": "0x3b60459d7b4f659bd642c1308d95edb028ab5285284fd5dba9f1c78820cfa3b7",
"local_index": 780,
"root": "0x712394bfa792683f38e6b167e14806cbc80b7a9c6ad95ffb21e9bf800a734002"
},
"curr_state_root_proof": {
"branch": [
"0x4c077a041dae0b8d0de8090186f4ea806aef952dd1050d54f031c908b4b174d9",
"0x67ce6e4299218f7814d4715edcf4702c8fdd957a42ca72979e39f8af7e459020",
"0x6bf7e8cda1c50f5c971727e6a056c96ffd052f7618f110045d2e148e4d4c9744"
],
"generalized_index": 11,
"leaf": "0x657906374cfd95a9ea194f44ad6c9ac8cbe26c5838304951e4746093c160c40f",
"local_index": 3,
"root": "0x8c1345f0832349abb5a8c0321c699b0b821fe78718743a3709cf3fec4060f700"
},
"hist_state_root_proof": {
"branch": [
"0xf0b06a34175f0ed35e352dc74b53724180d00736a028b8c6dce2ed1da3a3f274",
"0xaba74180bf30c153e39899da14a3787999ee48f8b503650ef80c2d04ecea0b28",
"0x07383ca5dfea8649338facc70f0372de63e603e1756bda99be56fc2208e5146f",
"0xe54342ab3e010a702674640629b4d1fac8525041ded12d3b250043a2c976b066",
"0xa4213188f6fe43cc3c24743e98aa344145e8f76c1bd5bd92bfdb0c4c18e6db10",
"0xa81cf8fe6fed4768c46016b9480c53e7e40a5e09aaf03c28717015c772c77fbe",
"0xc9a3d0c632c35efc4b5b72df6486be5bbf360689faf0f02dfbac15c16c90e125",
"0x2c25fcaa72819f1d855dd9ad638c9f150e0354c4b9ce90987e98b5921d0d66bb",
"0x749572112712fba58d75f118107527eed37559e2fa178ae6ebd79820ed68472b",
"0x7fd2903598cde6eedb9e888b743eba9affa5db74a457e64ef63bddaf5b38cadc",
"0x1cf19aa1bde06cc1ee88c8aa6003839bd4748441dc88b57ffd67c51dc122249e",
"0xc2988998bee099bb1145e7a53069a5a3efe669bb5683c80899ecae554cbf99fa",
"0x2bb70fa40139164f1a6170f2d5ef2d5951fef21acbd47859dec636d0aee85484",
"0x4df6b89755125d4f6c5575039a04e22301a5a49ee893c1d27e559e3eeab73da7",
"0x7c69b7a7a54b577cf0d81a1bec691bb9ccf6fd6d5a7ae9b68f342946bc737574",
"0x2bd214f7c45aad91c7e39e26b68a412bfb51b4423583afac3e20bd105f4a9d81",
"0x6dbfd5a26370267aed7266d749afae5c7786db0f561b75d194fcdc6d4f986d70",
"0x41e616f226f52a784d3db865449174ddf372408d478abdd31517684d73d4d478"
],
"generalized_index": 316573,
"leaf": "0x712394bfa792683f38e6b167e14806cbc80b7a9c6ad95ffb21e9bf800a734002",
"local_index": 54429,
"root": "0x657906374cfd95a9ea194f44ad6c9ac8cbe26c5838304951e4746093c160c40f"
}
}
}
The GET /generate_eip4788_blockhash_proof
endpoint contains all the data necessary to construct the contract calls for the verifyCurrentBlock()
, verifyRecentHistoricalBlock()
, and verifyHistoricalBlock()
functions. The .ssz_proof.type
field indicates which function to use.
verifyCurrentBlock() arg |
Response Field |
---|---|
timestamp |
.eip4788_timestamp |
currentStateRootProof.leaf |
.ssz_proof.curr_state_root_proof.leaf |
currentStateRootProof.proof |
.ssz_proof.curr_state_root_proof.branch |
executionPayloadRootProof.leaf |
.ssz_proof.execution_payload_proof.leaf |
executionPayloadRootProof.proof |
.ssz_proof.execution_payload_proof.branch |
blockNumberProof.leaf |
.ssz_proof.block_number_proof.leaf |
blockNumberProof.proof |
.ssz_proof.block_number_proof.branch |
blockhashProof.leaf |
.ssz_proof.blockhash_proof.leaf |
blockhashProof.proof |
.ssz_proof.blockhash_proof.branch |
verifyRecentHistoricalBlock() arg |
Response Field |
---|---|
timestamp |
.eip4788_timestamp |
currentStateRootProof.leaf |
.ssz_proof.curr_state_root_proof.leaf |
currentStateRootProof.proof |
.ssz_proof.curr_state_root_proof.branch |
historicalStateRootProof.leaf |
.ssz_proof.hist_state_root_proof.leaf |
historicalStateRootProof.proof |
.ssz_proof.hist_state_root_proof.branch |
historicalStateRootLocalIndex |
.ssz_proof.hist_state_root_proof.local_index |
executionPayloadRootProof.leaf |
.ssz_proof.execution_payload_proof.leaf |
executionPayloadRootProof.proof |
.ssz_proof.execution_payload_proof.branch |
blockNumberProof.leaf |
.ssz_proof.block_number_proof.leaf |
blockNumberProof.proof |
.ssz_proof.block_number_proof.branch |
blockhashProof.leaf |
.ssz_proof.blockhash_proof.leaf |
blockhashProof.proof |
.ssz_proof.blockhash_proof.branch |
verifyHistoricalBlock() arg |
Response Field |
---|---|
timestamp |
.eip4788_timestamp |
currentStateRootProof.leaf |
.ssz_proof.curr_state_root_proof.leaf |
currentStateRootProof.proof |
.ssz_proof.curr_state_root_proof.branch |
summaryRootProof.leaf |
.ssz_proof.summary_root_proof.leaf |
summaryRootProof.proof |
.ssz_proof.summary_root_proof.branch |
stateSummaryRootLocalIndex |
.ssz_proof.summary_root_proof.local_index |
historicalStateRootProof.leaf |
.ssz_proof.hist_state_root_proof.leaf |
historicalStateRootProof.proof |
.ssz_proof.hist_state_root_proof.branch |
historicalStateRootLocalIndex |
.ssz_proof.hist_state_root_proof.local_index |
executionPayloadRootProof.leaf |
.ssz_proof.execution_payload_proof.leaf |
executionPayloadRootProof.proof |
.ssz_proof.execution_payload_proof.branch |
blockNumberProof.leaf |
.ssz_proof.block_number_proof.leaf |
blockNumberProof.proof |
.ssz_proof.block_number_proof.branch |
blockhashProof.leaf |
.ssz_proof.blockhash_proof.leaf |
blockhashProof.proof |
.ssz_proof.blockhash_proof.branch |
Future Ethereum hardforks may change the format of the beacon block and beacon state, which may change the generalized indices of different fields committed to in the beacon block root as discussed here. There a few possible forms such a change may take:
- The most likely type of change involves appending fields to structs in the beacon state or block. If appending fields causes the number of fields in a struct to exceed a new power of two, the SSZ tree height for Merkleization of that field will increase by 1. This means the generalized indices of the fields in the struct will also need to be updated. As of September 2024, this is expected to happen in the Pectra hard fork.
- Another possible change is a substantial restructuring of beacon state or block fields that affects the structure of SSZ proofs for blockhashes. A historical example of this was the shift of historical
BeaconState
roots from thehistorical_roots
List to thehistorical_summaries
List in the Capella hardfork. A potential future example is the transition to theStableContainer
SSZ struct proposed in EIP-7495.
In both cases, the blockhash verifier logic contained in this repo will need to be updated, which we recommend to be done by updating and redeploying the contract. In addition, if the verifier needs to support configurations from multiple incompatible network upgrades, then the verifier will also need to be extended to branch accordingly.