Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add HistoricalSummariesBlockProof for headers from Capella onwards #291

Merged
Merged
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 64 additions & 3 deletions history/history-network.md
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,22 @@ BlockProofHistoricalRoots = Container[
slot: Slot # Slot of BeaconBlock, used to calculate the historical_roots index
]

BlockHeaderProof = Union[None, BlockProofHistoricalHashesAccumulator, BlockProofHistoricalRoots]
# Proof that EL block_hash is in BeaconBlock -> BeaconBlockBody -> ExecutionPayload
ExecutionBlockProofCapella = List[Bytes32, limit=12]

# Proof that BeaconBlock root is part of historical_summaries and thus canonical
# For Capella and onwards
BeaconBlockProofHistoricalSummaries = Vector[Bytes32, 13]

# Proof for EL BlockHeader for Capella and onwards
BlockProofHistoricalSummaries = Container[
beaconBlockProof: BeaconBlockProofHistoricalSummaries, # Proof that the BeaconBlock is part of the historical_summaries and thus part of the canonical chain
beaconBlockRoot: Bytes32, # hash_tree_root of BeaconBlock used to verify the proofs
executionBlockProof: ExecutionBlockProofCapella, # Proof that EL BlockHash is part of the BeaconBlock
slot: Slot # Slot of BeaconBlock, used to calculate the historical_summaries index
]

BlockHeaderProof = Union[None, BlockProofHistoricalHashesAccumulator, BlockProofHistoricalRoots, BlockProofHistoricalSummaries]

BlockHeaderWithProof = Container(
header: ByteList[MAX_HEADER_LENGTH], # RLP encoded header in SSZ ByteList
Expand All @@ -185,8 +200,8 @@ BlockHeaderWithProof = Container(
For pre-merge headers, clients SHOULD NOT accept headers without a proof
as there is the `BlockProofHistoricalHashesAccumulator` solution available.
For post-merge until Capella headers, clients SHOULD NOT accept headers without a proof as there is the `BlockProofHistoricalRoots` solution available.

For post Capella headers, there is currently no proof solution and clients SHOULD
For Capella and onwards headers, clients SHOULD NOT accept headers without a proof as there is the `BlockProofHistoricalSummaries` solution available.
For headers that are not yet part of the last period, clients SHOULD
accept headers without a proof.

##### Block Header by Hash
Expand Down Expand Up @@ -412,3 +427,49 @@ flowchart LR
ExecutionBlockProof --> Proof2([verify_merkle_multiproof])
beaconBlockRoot --> Proof2 --> block_hash
```

#### BlockProofHistoricalSummaries

The `BlockProofHistoricalSummaries` is an SSZ container which holds two Merkle proofs as specified in the [SSZ Merke proofs specification](https://github.com/ethereum/consensus-specs/blob/dev/ssz/merkle-proofs.md#merkle-multiproofs):
- `BeaconBlockProofHistoricalSummaries`
- `ExecutionBlockProof`

Additionally the SSZ container holds a `BeaconBlock` hash_tree_root and a slot.

This chain of two proofs allows for verifying that an EL `BlockHeader` is part of the canonical chain. The only requirement is having access to the beacon chain `historical_summaries`.
The `historical_summaries` is a [`BeaconState` field](https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/beacon-chain.md#beaconstate) that was introduced since the Capella fork. It gets updated every period (8192 slots). The `BlockProofHistoricalSummaries` MUST be used to verify blocks from the Capella fork onwards.

The Portal beacon network [provides access](./beacon-chain/beacon-network.md#historicalsummaries) to an up to date `historical_summaries` object.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can lead to confusion that the Portal Beacon Network is the only option to get these.

I think we should either

  • not mention the Portal Beacon Network
  • or mention multiple sources implementations could get this data, for example an EL client wouldn't run the Portal Beacon Network as it already has access to this data from their CL client.

The reason I mention this as it is a question I had to answer multiple times to EL layer teams and is consistently a cause of confusion

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps I can alter it to:

"The historical_summaries can be taken from the CL BeaconState (Link to beacon API endpoint) or the Portal beacon network also provides access to an up to date historical_summaries object."

And hopefully in a secondary PR I can add a specific API endpoint for it :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like a good solution to me because then it is clear you aren't forced to use Portal beacon but it is 1 of 2 options, which should prevent confusion

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adjusted in ec0200c


The relationship of the beacon chain structures that are used for the `BlockProofHistoricalSummaries` can be seen below:

```mermaid
flowchart LR
BeaconBlock -- contains --> BeaconBlockBody -- contains --> ExecutionPayload -- contains --> block_hash
state.block_roots -- hash_tree_root --> HistoricalSummary --> historical_summaries
BeaconBlock -- hash_tree_root --> state.block_roots
```

The first proof, the `BeaconBlockProofHistoricalSummaries`, is to prove that the `BeaconBlock` is part of the `historical_summaries` and thus part of the canonical chain.

In order to verify this proof, the `BeaconBlock` root from the container is provided as leaf and the matching `HistoricalSummary.block_summary_root` is provided as root. The matching `historical_summaries` index can be calculated from the slot that is provided and the index can then be used to lookup the `HistoricalSummary` from the `historical_summaries`.

The second proof, the `ExecutionBlockProof`, is to prove that the EL block hash is part of the `BeaconBlock`.

In order to verify this part of the proof, the EL block hash is provided as leaf and the `BeaconBlock` root as root.

Relationship of proof building can be seen here:
```mermaid
flowchart LR
BeaconBlock -- build_proof --> ExecutionBlockProof
state.block_roots -- build_proof --> BeaconBlockProofHistoricalSummaries
```

And the verification path:
```mermaid
flowchart LR
BeaconBlockProofHistoricalSummaries --> Proof1([verify_merkle_multiproof])
HistoricalSummary.block_summary_root --> Proof1 --> beaconBlockRoot
ExecutionBlockProof --> Proof2([verify_merkle_multiproof])
beaconBlockRoot --> Proof2 --> block_hash
```
Loading