skanda.eth
Cover photo

Demystifying Ethereum Light Clients

A technical introduction to Light Clients

skanda.eth

skanda.eth

Light Clients have been a topic of research and discussion for a long time, going all the way back to SPV mentioned in the Bitcoin Whitepaper. Today, Ethereum has come a long way in client diversity and we have many light client implementations that help us verify transactions without needing to fully sync the blockchain(none production ready). The general intuition is that running a full node that syncs the blockchain is technically and physically challenging, which has resulted in widespread reliance on centralised RPC providers like Infura and Alchemy.

This post aims to lay down core concepts that are related to Light Clients, their implementations, and a few adjacent usecases of Light Clients.

What is a Light Client?

A Light Client could be defined as a software that can be used to trustlessly interact with the blockchain, without having to rely on a full node. In an ideal world, everybody in the world would have a native light client running on their phones or browsers, that verify the state of the blockchain and send transactions.

A "Light" Spec

Altair

The Beacon Chain's Proof of Stake architecture was built to support light clients, by introducing "sync committees" in Altair. Every 27 hours or so, a subset (512) validators are randomly selected to attest the validity of the latest block on each slot. Light clients are able to use this attestation to follow the head of the chain without needing to process full blocks. Altair sync committee specs

Here, we have a data structure that optimistically stores the header attested for the light client to consume:

class LightClientOptimisticUpdate(Container):
    # Header attested to by the sync committee
    attested_header: LightClientHeader
    # Sync committee aggregate signature
    sync_aggregate: SyncAggregate
    # Slot at which the aggregate signature was created (untrusted)
    signature_slot: Slot

But we may want more data like the next sync committee, so there is a full update which tracks the next sync committee with the relevant data and proofs:

class LightClientUpdate(Container):
    # Header attested to by the sync committee
    attested_header: LightClientHeader
    # Next sync committee corresponding to attested_header.beacon.state_root
    next_sync_committee: SyncCommittee
    next_sync_committee_branch: NextSyncCommitteeBranch
    # Finalized header corresponding to attested_header.beacon.state_root
    finalized_header: LightClientHeader
    finality_branch: FinalityBranch
    # Sync committee aggregate signature
    sync_aggregate: SyncAggregate
    # Slot at which the aggregate signature was created (untrusted)
    signature_slot: Slot

There could be multiple sync comittees, since the attested block may not be finalized yet. So, we may also need another data structure to store the finalized state.

class LightClientFinalityUpdate(Container):
    # Header attested to by the sync committee
    attested_header: LightClientHeader
    # Finalized header corresponding to attested_header.beacon.state_root
    finalized_header: LightClientHeader
    finality_branch: FinalityBranch
    # Sync committee aggregate signature
    sync_aggregate: SyncAggregate
    # Slot at which the aggregate signature was created (untrusted)
    signature_slot: Slot

We also need to store the initial sync committee. Hence, we have the bootstrap structure:

class LightClientBootstrap(Container):
    # Header matching the requested beacon block root
    header: LightClientHeader
    # Current sync committee corresponding to header.beacon.state_root
    current_sync_committee: SyncCommittee
    current_sync_committee_branch: CurrentSyncCommitteeBranch

Here, you can check the Sync Aggregate section on a beacon chain slot (https://beaconcha.in/slot/9153087) to see all the data mentioned above.

Now, with these data present on the network, a light client can:

* Sync from a Trusted Block Root to a LightCLientBootstrap to get the initial sync committee

* Proceed to download the LightClientUpdate of the next sync committee and so on till the current period

* Get the final head from LightClientFinalityUpdate

* Then just follow LightClientOptimisticUpdate

Capella

Enabled tracking of execution block corresponding to the latest consensus block. This has an interesting usecase where if a beacon node is behind, it could just use a light client to obtain the latest execution block hash to be forwarded to the execution node, and the EL node could immediately sync to the latest state instead of having to apply all the blocks one by one. Capella Specs

The LightClientHeader is modified as such:

class LightClientHeader(Container):
    # Beacon block header
    beacon: BeaconBlockHeader
    # Execution payload header corresponding to beacon.body_root (from Capella onward)
    execution: ExecutionPayloadHeader
    execution_branch: ExecutionBranch

How do consensus light clients generally work?

On a high level, Light Clients can do the following:

1. We need a beacon node. This can be running on our computer or somewhere else, and then we need a checkpoint root.

2. You provide the beacon node REST API , and the block root to the light client. The light client then requests the beacon node for a snapshot a that block root. The beacon node then returns the following: Header at that checkpoint root, Sync committee and the Sync Comittee proof

3. In order to get to the curent sync committee, we would request a bunch of sync committee updates and verify them

4. Poll the new block header every 12 seconds!

Current state of Light Client Development

(From ethereum's website)

Some interesting Light Client Implementations are:

Lodestar: consensus light client in TypeScript

Helios: combined execution and consensus light client in Rust

Nimbus: consensus light client in Nim

Portal Network: Its a related project that aims to improve Light Clients requesting for all the data in a more decentralized way through three dedicated networks, instead of following a client/server model.

Links/Reading:

- Light client roadmap for Electra: https://hackmd.io/@etan-status/electra-lc

- Altair specs: https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md

- Capella specs: https://github.com/ethereum/consensus-specs/blob/dev/specs/capella/light-client/sync-protocol.md

- Lodestar specs: https://github.com/ChainSafe/lodestar/tree/unstable/packages/light-client

- Lodestar's progress on light clients: https://blog.chainsafe.io/the-road-ahead-for-ethereum-light-clients/

- Light Clients After the Merge by Etan Kissling | Devcon Bogotá

Play Video

- Networking in light clients, light client summit

Play Video

- light client summit playlist: https://www.youtube.com/watch?v=zxQvEEY9e4k&list=PLJijNYoOwnst-feT7PsCLaSdiFYzWtf7j

Collect this post as an NFT.

skanda.eth

Subscribe to skanda.eth to receive new posts directly to your inbox.

ParagraphFarcaster
Paragraph
Commented 10 months ago

New blogpost by @skanda.eth explores the evolution of Light Clients in Ethereum, tracing their roots to Bitcoin's SPV. The post explains core concepts, implementation details like Altair's sync committees, and current developments with examples such as Lodestar and Helios. Dive in for a comprehensive overview!

Demystifying Ethereum Light Clients