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 docs related to Starlight > Ethereum sync using BEEFY #718

Open
wants to merge 2 commits into
base: girazoki-add-altair-diagram
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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
116 changes: 116 additions & 0 deletions solo-chains/docs/bridge/etbereum-to-solo-chain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# Ethereum State Verification Diagram

## Altair Sync protocol

### Preliminaries
- Ethereum epochs consis of 32 slots, where each slot is 12s
- Ethereum distributes all validators in attestation 32 attestation committees (one per slot)
- At the beginning of each epoch, each validator is assigned a slot
- Ethereum pseudo-randomly selects, among all validators, a block producer for a slot n
- The block produced for slot n needs to be attested by the attestation committee for slot n


### The need for a sync protocol
- For a light client, determining the canonical chain can become expensive (as we need to verify a bunch of attestions)
- In order to verify a block header, you need the information of the previous header

### The Altair sync protocol
- A sync committe made up of 512 pseudo-randomly chosen validators is selected every 256 epochs (approx, 27hours)
- Each block header includes the aggregated signature of the validators in the sync committee,
- Each header lists the current and the next sync committee
- Sync committee members are rewarded with 0.1ETH for their service, and they are charged that amount if they dont sign
- 2/3rds quorum needs to be reached
- For now sync committee members are not slashed if the vote for a malicious header (although subject to change after EIP-7657)

### Verification PseudoCode

```python
def validate_light_client_update(snapshot: LightClientSnapshot,
update: LightClientUpdate,
genesis_validators_root: Root) -> None:
# Verify update slot is larger than snapshot slot
assert update.header.slot > snapshot.header.slot

# Verify update does not skip a sync committee period
snapshot_period = compute_epoch_at_slot(snapshot.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
update_period = compute_epoch_at_slot(update.header.slot) // EPOCHS_PER_SYNC_COMMITTEE_PERIOD
assert update_period in (snapshot_period, snapshot_period + 1)

# Verify update header root is the finalized root of the finality header, if specified
if update.finality_header == BeaconBlockHeader():
signed_header = update.header
assert update.finality_branch == [Bytes32() for _ in range(floorlog2(FINALIZED_ROOT_INDEX))]
else:
signed_header = update.finality_header
assert is_valid_merkle_branch(
leaf=hash_tree_root(update.header),
branch=update.finality_branch,
depth=floorlog2(FINALIZED_ROOT_INDEX),
index=get_subtree_index(FINALIZED_ROOT_INDEX),
root=update.finality_header.state_root,
)

# Verify update next sync committee if the update period incremented
if update_period == snapshot_period:
sync_committee = snapshot.current_sync_committee
assert update.next_sync_committee_branch == [Bytes32() for _ in range(floorlog2(NEXT_SYNC_COMMITTEE_INDEX))]
else:
sync_committee = snapshot.next_sync_committee
assert is_valid_merkle_branch(
leaf=hash_tree_root(update.next_sync_committee),
branch=update.next_sync_committee_branch,
depth=floorlog2(NEXT_SYNC_COMMITTEE_INDEX),
index=get_subtree_index(NEXT_SYNC_COMMITTEE_INDEX),
root=update.header.state_root,
)

# Verify sync committee has sufficient participants
assert sum(update.sync_committee_bits) >= MIN_SYNC_COMMITTEE_PARTICIPANTS

# Verify sync committee aggregate signature
participant_pubkeys = [pubkey for (bit, pubkey) in zip(update.sync_committee_bits, sync_committee.pubkeys) if bit]
domain = compute_domain(DOMAIN_SYNC_COMMITTEE, update.fork_version, genesis_validators_root)
signing_root = compute_signing_root(signed_header, domain)
assert bls.FastAggregateVerify(participant_pubkeys, signing_root, update.sync_committee_signature)
```

## Transmiting Ethereum Headers to Starlight

### The relayers job to transmit Ethereum's header
1. The relayer node needs to collect the aggergated sync committee signature embeedded into the Ethereum block
2. The relayer needs to aggregrate the public keys responsible for generating that block.
3. If the sync committee changes, the relayer needs to generate a merkle proof proving that the sync committee has changed.
4. The relayer presents all these components to the appropriate pallet in starlight

### Eth state receiver pallets job
1. If the relayer claims the sync committee changes, verify the merkle proof to make sure the new sync committee is correct.
2. Perform a verification of the validity of the committee members that signed the aggregated message
3. Perform a verification of the aggregated signature
4. If all of those pass, accept the header, with the new **state root ready to receive storage proofs**

### Overall Diagram ETH state validity Reception

<p align="center">
<img src="images/beacon_chain_altair.drawio.png" width="1000">
</p>



## Proving symbiotic validator selection to starlight (or any other Ethereum state)

### The relayers job to transmit symbiotic validators
1. The relayer node checks the latest accepted header in starlight, and it generates a storage proof of the validators storage item in the symbiotic smart contract
2. The relayer sends this information to the Symbiotic validator receiver pallet

### The Symbiotic validator Receiver pallets job to verify the validators

1. The symbiotic validator pallet receiver asks the Ethereum state receiver pallet about the correctness of the validators.
2. The ethereum state pallet receiver repplies with a yes or no, depending on the validity of the storage proof against the storage root stored
3. If accepted, the next validator set is stored on-chain
4. In the next session, the new era will get started

### Overall Diagram Validator Information passing

<p align="center">
<img src="images/relayer_proof_starlight.drawio.png" width="1000">
</p>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 changes: 73 additions & 0 deletions solo-chains/docs/bridge/solo-chain-to-ethereum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# State synchronization flow from Starlight to Ethereum

## BEEFY

BEEFY (Binary Enhanced Extra Fast Yield) is a light client protocol for Polkadot that enables efficient finality proofs using [Merkle Mountain Ranges](https://spec.polkadot.network/sect-finality#defn-mmr). It reduces the amount of data that needs to be relayed between chains by focusing on specific validator signatures.

On its core, BEEFY allows components of a remote network (such as Ethereum) to verify the state (finality proofs) created by the origin chain (Polkadot, Starlight, etc.).

The goals of BEEFY (according to Polkadot’s spec) are:

- Allow customization of signature schemes to adapt to different target chains.
- Minimize the size of the finality proof and the effort required by a light client to follow finality.
- Unify data types and use backward-compatible versioning so that the protocol can be extended (additional payload, different signature schemes) without breaking existing light clients.

BEEFY consists of two components:

a. **Consensus Extension** on GRANDPA finalization (voting round).

b. An efficient **protocol** for convincing on-chain/ off-chain light clients about the finality vote (relayer).

## Voting round

A “voting round” on BEEFY is basically the time gap where BEEFY validators vote over a specific block (round number). More specifically, they vote for the Commitment for that block number.

The current round ends when the next round starts, which might happen when:

1. Either the node collects `2/3rd + 1` valid votes for that round.
2. Or the node receives a BEEFY Justification for a block greater than the current best BEEFY block.

Each node determines what it believes the next round number is, based on the following:

1. Best GRANDPA finalized block number (`best_grandpa`).
2. Best BEEFY finalized block number (`best_beefy`).
3. Starting block of the current session (`session_start`).

See [the formula](https://spec.polkadot.network/sect-finality#defn-beefy-round-number) in Polkadot’s spec for more detailed explanation.

Something important to note is that while BEEFY **can** vote on top of GRANDPA-finalized blocks, it does **not have to**. BEEFY can select **any recent block** as a checkpoint (based on the above criteria), even if it hasn't yet been finalized by GRANDPA. This allows BEEFY to provide faster and more efficient finality for external chains, especially when the full GRANDPA finality process is too slow for cross-chain communication purposes.

## Vote Message

On each BEEFY round, validators create a direct vote and gossip it to its peers. As part of a vote, validators sign over the Payload present inside the Beefy Commitment. You can check the full definition [here](https://spec.polkadot.network/chap-networking#defn-msg-beefy-gossip).

The Payload is basically the Merkle root of the whole MMR generated. The leaf data inside It contains several fields for each past block (full definition [here](https://spec.polkadot.network/sect-finality#defn-beefy-payload)).

## Relayer’s role

To submit BEEFY proofs to Ethereum (or any other remote network), the relayer first collects all the BEEFY gossiped votes of the origin chain’s validators, and convert them to a **Signed Commitment Witnesses** data structure, which is composed by the BEEFY commitment itself, a randomly selected validator signature, and an initial bitfield claiming which validators have signed the commitment.

```code
signedCommitmentWitnesses = (beefyCommitment, validatorBooleanArray, randomValidatorSignature)
```

After building the `signedCommitmentWitnesses` message, the relayer starts interacting with the light client on Ethereum (`BeefyClient.sol`) and performs some steps, which in case of Snowbridge are the following ones:

1. `submitInitial` - In the first transaction, the relayer submits the `signedCommitmentWitnesses` message.
2. The relayer then waits [MAX_SEED_LOOKAHEAD](https://eth2book.info/bellatrix/part3/config/preset/#max_seed_lookahead) blocks.
3. `commitPrevRandao` - The relayer submits a second transaction to reveal and commit to a random seed, derived from Ethereum's [RANDAO](https://eips.ethereum.org/EIPS/eip-4399).
4. The relayer requests from the light client a bitfield with N*N*randomly chosen validators sampled from the initial bitfield.
5. `submitFinal` - The relayer sends a third and final transaction with signatures for all the validators specified in the final bitfield

After all the previous steps, the light client verifies all validator signatures in the third transaction and checks if:

1. The provided validators are in the current validator set
2. The provided validators are in the final bitfield
3. The provided validators have signed the beefy commitment

If those checks succeed and the third transaction is valid, then the payload inside the BEEFY commitment is applied.

## Starlight → Relayer → Ethereum: overall workflow diagram
<p align="center">
<img src="images/starlight_ethereum_beefy_sync.drawio.png" width="1000">
</p>
Loading