# Sync Committee

## Overview

Colibri uses Ethereum's Sync Committee mechanism to verify beacon chain data without maintaining the full chain state. This document explains how initialization, synchronization, and verification work in the verifier.

## Sync Committee Basics

A Sync Committee consists of 512 validators who are responsible for signing beacon block headers. The committee rotates approximately every 27 hours (256 epochs on mainnet). To verify beacon chain data, Colibri must:

1. **Obtain** the current sync committee's public keys
2. **Verify** signatures using BLS signature aggregation
3. **Update** to new committees as they rotate

## Initialization Strategies

Colibri supports two initialization modes: **with** or **without** a trusted checkpoint.

### Initialization with Trusted Checkpoint

When you provide explicit a `trusted_checkpoint`:

```javascript
const client = new Colibri({ 
  trusted_checkpoint: "0x4232db57354ddacec40adda0a502f7732ede19ba0687482a1e15ad20e5e7d1e7"
});
```

**Process:**

1. **Bootstrap Request**: The verifier requests bootstrap data for the trusted checkpoint from the Beacon API:

   ```
   GET /eth/v1/beacon/light_client/bootstrap/{block_root}
   ```
2. **Bootstrap Verification**: The response contains:
   * `header` - The beacon block header matching the trusted checkpoint
   * `currentSyncCommittee` - The 512 validator public keys active at that checkpoint
   * `currentSyncCommitteeBranch` - Merkle proof linking the sync committee to the beacon state
3. **Merkle Proof Validation**: The verifier validates that the sync committee is correctly included in the beacon state by:
   * Computing `hash_tree_root(currentSyncCommittee)` → `sync_committee_root`
   * Verifying the Merkle branch against `header.beacon.stateRoot`
   * The gindex depends on the fork (54 for Deneb, 86 for Electra)
4. **Storage**: The verified sync committee public keys are stored locally, indexed by period number

**Security:** This approach is maximally secure when you control the source of the trusted checkpoint (e.g., from your own node, a hardware wallet, or a trusted out-of-band source).

### Initialization without Trusted Checkpoint

When no `trusted_checkpoint` are provided, Colibri automatically bootstraps:

```javascript
const client = new Colibri(); // Automatic secure initialization
```

**Process:**

1. **Finality Checkpoint Query**: The verifier requests the latest finalized checkpoint using the standard Beacon API endpoint:

   ```
   GET /eth/v1/beacon/states/head/finality_checkpoints
   ```

   Response format (compatible with both Beacon APIs and Checkpointz servers):

   ```json
   {
     "data": {
       "finalized": {
         "epoch": "400504",
         "root": "0x40ed0506ff33aed37aae7a4678cdc84de1429306ac1138b7aba46dd8490f1a59"
       }
     }
   }
   ```
2. **Checkpoint Trust Establishment**: The verifier sets this root as a trusted checkpoint internally
3. **Bootstrap Flow**: Continues with the same bootstrap process as above using the fetched root

**Server Flexibility:**

* **Beacon API Compatible**: The endpoint works with standard Beacon API nodes
* **Checkpointz Servers**: Also works with dedicated Checkpointz servers
* **Configuration Choice**: Hosts can configure either `checkpointz` servers OR `beacon_apis` for checkpoint fetching
* **Automatic Fallback**: Multiple URLs enable redundancy

**Security Considerations:**

* **a Multiple Servers**: Colibri queries multiple configured servers for consensus
* **Trusted Infrastructure Options**:
  * Use your own Beacon node for maximum trust
  * Use public Checkpointz services (beaconcha.in, ethstaker.cc, etc.)
  * Use public Beacon APIs as a fallback
* **Weak Subjectivity**: This approach relies on the weak subjectivity assumption - as long as initialization happens within the weak subjectivity period (\~2 weeks for Ethereum mainnet), the network's honesty assumption holds
* **Trade-off**: Convenience vs. complete trustlessness - suitable for most applications but not for maximum security scenarios

## Sync Committee Period Transitions

Every \~27 hours, the sync committee changes. Colibri handles this automatically using Light Client Updates.

### Standard Period Transition

When verification requires a newer sync committee than currently stored:

1. **Light Client Update Request**: Fetch updates from the Beacon API:

   ```
   GET /eth/v1/beacon/light_client/updates?start_period={period}&count={n}
   ```
2. **Update Structure**: Each `LightClientUpdate` contains:
   * `attestedHeader` - A recent beacon block header signed by the **old** sync committee
   * `nextSyncCommittee` - The public keys for the **new** sync committee
   * `nextSyncCommitteeBranch` - Merkle proof for the new committee
   * `finalizedHeader` - A finalized beacon block header
   * `finalityBranch` - Merkle proof for finalized header
   * `syncAggregate` - BLS signature from the old committee
   * `signatureSlot` - The slot when the signature was created
3. **Verification Steps**:

   a. **Signature Verification**:

   ```
   - Compute: hash_tree_root(attestedHeader.beacon) → attested_blockhash
   - Compute: signing_root = hash_tree_root(SigningData(attested_blockhash, domain))
   - Verify: BLS_aggregate_verify(old_sync_committee_keys, signing_root, syncAggregate.signature)
   ```

   b. **Next Committee Merkle Proof**:

   ```
   - Compute: hash_tree_root(nextSyncCommittee) → next_committee_root
   - Verify: merkle_proof(nextSyncCommitteeBranch, next_committee_root, gindex=55, attestedHeader.stateRoot)
   ```

   c. **Finality Merkle Proof**:

   ```
   - Compute: hash_tree_root(finalizedHeader.beacon) → finalized_header_root
   - Verify: merkle_proof(finalityBranch, finalized_header_root, gindex=105, attestedHeader.stateRoot)
   ```
4. **Storage**: The new sync committee is stored, indexed by its period (`period = finalized_slot >> (5 + 8)` on mainnet)

### Edge Case: Delayed Finality

Sometimes finality is not reached at the exact start of a new period due to network conditions. Colibri handles this edge case:

**Scenario**: Period P+1 sync committee is known, but Period P is requested for verification

**Solution**:

1. Check if `highest_period == requested_period + 1`
2. If yes, fetch the light client update for `requested_period`
3. Extract `nextSyncCommittee` and compute its `hash_tree_root`
4. Compare against the stored `previous_sync_committee_root` from Period P+1
5. If the match is confirmed, store the keys for Period P without full signature verification

This optimization allows verification even when blocks are signed by the previous period's committee after a new period has started.

### Previous Sync Committee Root Tracking

To support the edge case above, Colibri stores an additional 32 bytes with each sync committee:

**Storage Format:**

```
[validator_pubkeys: 512 * 48 bytes] [previous_sync_committee_root: 32 bytes]
```

This `previous_sync_committee_root` is the `hash_tree_root` of the **previous period's** sync committee, allowing backward verification when needed.

## Weak Subjectivity Period (WSP) Validation

Ethereum's security model requires periodic checkpoint validation when syncing across long time gaps.

### WSP Configuration

Each chain has a configured weak subjectivity period (in epochs):

* **Mainnet**: 3,682 epochs (\~2 weeks)
* **Sepolia**: 3,682 epochs
* **Gnosis**: \~1,500 epochs
* **Chiado**: \~1,500 epochs

### When WSP Validation Triggers

When fetching sync committees across multiple periods:

```
epoch_gap = (target_period - stored_period) * 256
if epoch_gap > weak_subjectivity_epochs:
    # WSP validation required
```

### WSP Validation Process

1. **Checkpoint Query**: Request block root for the last finalized slot from Checkpointz:

   ```
   GET /eth/v1/beacon/blocks/{finality_slot}/root
   ```
2. **Comparison**: Compare the Checkpointz-provided root with the locally stored finalized root
3. **Result**:
   * ✅ **Match**: Sync continues normally
   * ❌ **Mismatch**: All local state is cleared, forcing re-initialization

**Purpose**: Prevents long-range attacks where an attacker attempts to create an alternative history by controlling old sync committees.

## Storage and State Management

### Persistent Storage

Colibri stores minimal state using a plugin storage system:

1. **Chain State** (`states_{chain_id}`):
   * An array of trusted blocks with their periods
   * Last checkpoint slot for WSP validation
   * Format: `[c4_trusted_block_t, c4_trusted_block_t, ...]`
2. **Sync Committee State** (`sync_{chain_id}_{period}`):
   * Validator public keys (512 \* 48 = 24,576 bytes)
   * Optional: Deserialized BLS keys (512 \* 96 = 49,152 bytes)
   * Previous sync committee root (32 bytes)
   * Total: \~24KB per period (or \~49KB with deserialization)

### State Lifecycle

**Initialization:**

```
No state → Checkpointz query → Bootstrap → Store Period N sync committee
```

**Normal Operation:**

```
Need Period N+k → Fetch k light client updates → Verify chain → Store each period
```

**WSP Exceeded:**

```
Gap too large → Checkpointz validation → Match: continue | Mismatch: clear all state
```

### Storage Cleanup

Colibri automatically manages storage by keeping only the most recent periods (configurable limit). Oldest periods are deleted when the limit is exceeded, but the earliest and latest periods are always preserved.

## Security Properties

### Trust Assumptions

**With Trusted Checkpoint:**

* ✅ Fully trustless after initial checkpoint
* ✅ No dependency on external services
* ⚠️ Requires secure out-of-band checkpoint source

**Without Trusted Checkpoint:**

* ✅ Convenient automatic initialization
* ✅ Multiple Checkpointz servers provide redundancy
* ⚠️ Relies on the weak subjectivity assumption
* ⚠️ Initial trust in Checkpointz infrastructure

### Verification Guarantees

Once initialized, Colibri provides:

* **Cryptographic proofs** for all beacon chain data
* **BLS signature verification** for sync committee attestations
* **Merkle proofs** for state inclusion
* **Automatic period transitions** with full verification
* **WSP validation** prevents long-range attacks

### Attack Resistance

**Sync Committee Compromise**: An attacker controlling 2/3 of a sync committee can sign invalid blocks, but:

* Cannot forge signatures for future periods
* Limited to their committee's 27-hour window
* WSP validation catches attempts to fork history

**Long-Range Attacks**: Prevented by WSP validation requiring external checkpoint validation

**Eclipse Attacks**: Multiple Checkpointz servers and Beacon APIs provide redundancy

## Implementation Details

### SSZ Types

**LightClientBootstrap:**

```
Container:
  header: LightClientHeader
  currentSyncCommittee: SyncCommittee
  currentSyncCommitteeBranch: Vector[Bytes32, depth]
```

**LightClientUpdate:**

```
Container:
  attestedHeader: LightClientHeader
  nextSyncCommittee: SyncCommittee  
  nextSyncCommitteeBranch: Vector[Bytes32, depth]
  finalizedHeader: LightClientHeader
  finalityBranch: Vector[Bytes32, depth]
  syncAggregate: SyncAggregate
  signatureSlot: uint64
```

**Merkle Branch Depths:**

* Deneb: `nextSyncCommitteeBranch` = 5, `currentSyncCommitteeBranch` = 5, `finalityBranch` = 6
* Electra: `nextSyncCommitteeBranch` = 6, `currentSyncCommitteeBranch` = 6, `finalityBranch` = 7

### Period Calculation

```c
// Mainnet configuration:
// slots_per_epoch = 32 (5 bits)
// epochs_per_period = 256 (8 bits)

uint32_t period = slot >> (5 + 8); // slot / (32 * 256)
```

### BLS Signature Aggregation

Colibri uses BLS signature aggregation to efficiently verify that 2/3 of the sync committee signed a block:

1. Extract participating validator indices from `syncAggregate.syncCommitteeBits`
2. Aggregate public keys of participating validators
3. Compute the signing root with domain separation
4. Verify the aggregated signature against the aggregated public key

## Further Reading

* [Ethereum Beacon Chain Specification](https://github.com/ethereum/consensus-specs)
* [Light Client Specification](https://github.com/ethereum/consensus-specs/blob/dev/specs/altair/light-client/sync-protocol.md)
* [Weak Subjectivity Guide](https://notes.ethereum.org/@adiasg/weak-subjectvity-eth2)
* [Checkpointz Server](https://github.com/ethpandaops/checkpointz)


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://corpus-core.gitbook.io/specification-colibri-stateless/introduction/architecture/sync.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
