|
| 1 | +## Overview |
| 2 | + |
| 3 | +Rainbow Bridge ETHβNEAR light client. An on-chain Ethereum light client contract on NEAR that tracks Ethereum beacon chain finality via sync committee updates, plus a relayer that feeds it data. |
| 4 | + |
| 5 | +## Repository Layout |
| 6 | + |
| 7 | +Two Cargo workspaces plus a standalone crate: |
| 8 | + |
| 9 | +- **`contracts/near/`** β NEAR smart contracts workspace (`eth2-client`, `eth-prover`, `eth-types`) |
| 10 | +- **`eth2near/`** β Legacy relayer workspace (7 crates) |
| 11 | +- **`relayer/`** β New relayer (standalone, Rust edition 2024) |
| 12 | + |
| 13 | +The eth2 light client lives in `contracts/near/eth2-client/`. Supporting crates: |
| 14 | +- `eth-types` β Ethereum execution/consensus type definitions (BlockHeader, BeaconBlockHeader, SyncCommittee, etc.) |
| 15 | +- `eth2-utility` β Consensus logic: network configs, fork handling, merkle proof verification, sync committee period computation |
| 16 | +- `eth2_hashing` β WASM-compatible Keccak256 (patched ethereum_hashing for NEAR SDK) |
| 17 | + |
| 18 | +## Build, Test, Lint |
| 19 | + |
| 20 | +All contract commands run from `contracts/near/`: |
| 21 | + |
| 22 | +```bash |
| 23 | +# Build (requires cargo-near and wasm32-unknown-unknown target) |
| 24 | +cargo near build reproducible-wasm --manifest-path contracts/near/eth2-client/Cargo.toml |
| 25 | + |
| 26 | +# Test (the canonical CI sequence β builds test WASM first, then runs tests twice: with and without default features) |
| 27 | +cd contracts/near/eth2-client && ./test.sh |
| 28 | + |
| 29 | +# Run all tests |
| 30 | +cd contracts/near && cargo test -p eth2-client |
| 31 | + |
| 32 | +# Run a single test |
| 33 | +cd contracts/near && cargo test -p eth2-client test_gc_headers -- --nocapture |
| 34 | + |
| 35 | +# Unit tests only (no mainnet feature, allows validate_updates=false) |
| 36 | +cd contracts/near && cargo test -p eth2-client --no-default-features -- --nocapture |
| 37 | + |
| 38 | +# Lint |
| 39 | +cd contracts/near && cargo clippy |
| 40 | +cd contracts/near && cargo fmt --check |
| 41 | +``` |
| 42 | + |
| 43 | +**Feature flags** (eth2-client): `default = ["logs", "mainnet"]` |
| 44 | +- `mainnet` β enforces `validate_updates` and `verify_bls_signatures` on init |
| 45 | +- `logs` β enables `env::log_str()` output |
| 46 | +- Unit tests marked `#[cfg(not(feature = "mainnet"))]` only run with `--no-default-features` |
| 47 | + |
| 48 | +**CI** (`.github/workflows/contracts-near.yml`): runs `make build-eth2-client`, `./test.sh`, and relayer tests. Requires `git-lfs` for test data. |
| 49 | + |
| 50 | +## Contract State Machine |
| 51 | + |
| 52 | +The eth2-client alternates between two modes: |
| 53 | + |
| 54 | +``` |
| 55 | +SubmitLightClientUpdate β submit_beacon_chain_light_client_update() |
| 56 | + validates sync committee signatures, merkle proofs, period transitions |
| 57 | + updates finalized beacon header + sync committees |
| 58 | + β transitions to SubmitHeader |
| 59 | +
|
| 60 | +SubmitHeader β submit_execution_header() (called repeatedly) |
| 61 | + accepts execution blocks in reverse order (newestβoldest) |
| 62 | + stores block_numberβblock_hash in LookupMap |
| 63 | + GCs old blocks beyond hashes_gc_threshold |
| 64 | + when chain is closed (reaches previous finalized block + 1) |
| 65 | + β transitions back to SubmitLightClientUpdate |
| 66 | +``` |
| 67 | + |
| 68 | +Light client updates can advance by at most 1 sync committee period (8192 slots β 27 hours). This bounds GC to at most one period's worth of removals per cycle. |
| 69 | + |
| 70 | +## Key Protocol Constants |
| 71 | + |
| 72 | +From `eth2-utility/src/consensus.rs`: |
| 73 | +- `SLOTS_PER_EPOCH = 32` (~6.4 min) |
| 74 | +- `EPOCHS_PER_SYNC_COMMITTEE_PERIOD = 256` |
| 75 | +- One sync committee period = 8192 slots β 27 hours |
| 76 | +- `hashes_gc_threshold` is typically 51000 (~7 days of blocks) |
| 77 | + |
| 78 | +## Storage Keys |
| 79 | + |
| 80 | +```rust |
| 81 | +StorageKey::FinalizedExecutionBlocks // LookupMap<u64, H256> β block number β hash |
| 82 | +StorageKey::FinalizedExecutionHeader // LazyOption<ExecutionHeaderInfo> |
| 83 | +StorageKey::CurrentSyncCommittee // LazyOption<SyncCommittee> |
| 84 | +StorageKey::NextSyncCommittee // LazyOption<SyncCommittee> |
| 85 | +``` |
| 86 | + |
| 87 | +`__DeprecatedUnfinalizedHeaders` and `__DeprecatedSubmitters` are leftover from V1 migration β do not reuse. |
| 88 | + |
| 89 | +## Access Control |
| 90 | + |
| 91 | +Uses `near-plugins` for roles: `DAO`, `PauseManager`, `UpgradableCodeStager/Deployer`, `UnrestrictedSubmitLightClientUpdate`, `UnrestrictedSubmitExecutionHeader`. Admin methods (`update_trusted_signer`, `update_hashes_gc_threshold`, etc.) require `Role::DAO`. |
| 92 | + |
| 93 | +## Test Data |
| 94 | + |
| 95 | +Test data lives in `contracts/near/eth2-client/src/data/` (LFS-tracked JSON files). Sepolia is the primary test network. Regenerate with: |
| 96 | +```bash |
| 97 | +cd contracts/near/eth2-client/src/data && uv run dump_sepolia_data.py |
| 98 | +``` |
| 99 | + |
| 100 | +## Migration Pattern |
| 101 | + |
| 102 | +`migrate.rs` defines the previous struct version (e.g., `Eth2ClientV1`) and a `migrate()` function that reads old state and constructs the new `Eth2Client`. When adding fields to `Eth2Client`, create a new versioned struct matching the *current* on-chain layout and set defaults for new fields in `migrate()`. |
| 103 | + |
| 104 | +## New Relayer (`relayer/`) |
| 105 | + |
| 106 | +Standalone binary with CLI subcommands: `run` (continuous), `run-job` (single execution for Cloud Run), `init`, `generate-config`, `validate-config`. Config via TOML or env vars with `RELAYER_` prefix. Uses Lighthouse types for beacon chain, Alloy for execution RPC, near-fetch for NEAR. |
0 commit comments