diff --git a/docs/README.md b/docs/README.md index 200a8b31..6ec3d9ae 100644 --- a/docs/README.md +++ b/docs/README.md @@ -12,6 +12,7 @@ This documentation is designed to provide auditors and developers with a compreh - **[Deposits and Withdrawals](./deposits-and-withdrawals.md)** - Internal state management for deposits and withdrawals - **[Checkpointing](./checkpointing.md)** - Checkpoint creation, loading, and verification - **[SSZ Merklization](./ssz-merklization.md)** - SSZ binary Merkle tree structure, incremental update optimizations, and proof format +- **[Testing](./testing.md)** - Unit tests, end-to-end tests with Reth, and benchmarks ## Quick Reference diff --git a/docs/testing.md b/docs/testing.md new file mode 100644 index 00000000..1125b5e6 --- /dev/null +++ b/docs/testing.md @@ -0,0 +1,124 @@ +# Testing + +Summit has three levels of testing: unit/integration tests, end-to-end tests, and benchmarks. + +## Unit & Integration Tests + +### Test Harness + +Unit and integration tests use a simulated network (`node/src/test_harness/`) with mock components: + +- **`MockEngineNetwork`** — simulates Reth execution client behavior without a real EVM +- **Commonware simulated P2P** — in-process networking (no real sockets) +- **Helper functions** — `link_validators()`, `run_until_height()`, `register_validators()`, `join_validator()` + +Tests in `node/src/tests/` are organized by feature area: + +``` +tests/ +├── checkpointing/ +│ ├── creation.rs # Checkpoint creation +│ ├── joining.rs # Joining network with checkpoint +│ └── verification.rs # Checkpoint integrity verification +├── execution_requests/ +│ ├── deposits.rs # Validator registration +│ ├── withdrawals.rs # Validator exit +│ ├── protocol_params.rs # Parameter updates +│ ├── deposit_withdrawal_combined.rs # Mixed operations +│ └── validator_set.rs # Validator set transitions +└── syncer.rs # Block sync & cache +``` + +## End-to-End Tests + +E2E tests run real Reth instances alongside Summit. They exercise the full stack: Engine API IPC, P2P networking, consensus, finalization, and on-chain contract interactions. + +### Important: Genesis Hash + +The `eth_genesis_hash` field in the Summit genesis config must match the genesis block hash produced by Reth. If they differ, Summit will fail to initialize. This applies both to production deployments and to e2e tests — when running e2e tests, the hash in the test configuration must match the Reth genesis file. + +### Prerequisites + +- `reth` binary in PATH (see main README for setup) +- Ports 8545-8548, 3030-3060, 26600-26630 available + +### Building + +```bash +cargo build --features e2e +``` + +### Running + +Each test is a standalone binary. Pass `--log-dir` to capture per-node Reth logs (`node0.log`, `node1.log`, ...): + +```bash +cargo run --features e2e --bin stake-and-checkpoint -- --log-dir /tmp/rethlogs +cargo run --features e2e --bin stake-and-join-with-outdated-checkpoint -- --log-dir /tmp/rethlogs +cargo run --features e2e --bin withdraw-and-exit -- --log-dir /tmp/rethlogs +cargo run --features e2e --bin protocol-params -- --log-dir /tmp/rethlogs +cargo run --features e2e --bin sync-from-genesis -- --log-dir /tmp/rethlogs +``` + +### Common Configuration + +All E2E tests share: + +- **4 Reth instances** with IPC Engine API at `/tmp/reth_engine_api{0-3}.ipc` +- **4 genesis validators** (some tests add a 5th joining validator) +- **`blocks_per_epoch = 50`** (reduced from production default of 10,000 to accelerate epoch transitions) + +### Test Descriptions + +#### `stake-and-checkpoint` + +Tests checkpoint creation and joining the network with a checkpoint. + +- **Nodes**: 4 genesis + 1 joining +- **Contract**: Deposit contract (`0x00000000219ab540356cBB839Cbe05303d7705Fa`) — must be pre-deployed in the Reth genesis file. +- **Flow**: Starts 4 validators, sends a deposit to register a 5th validator, waits for a checkpoint at the configured height (default 50), then starts node4 bootstrapped from that checkpoint and a copy of node0's Reth DB. +- **Verifies**: The new node syncs from the checkpoint (not genesis) and all 5 nodes reach the stop height (default 100). + +#### `stake-and-join-with-outdated-checkpoint` + +Tests joining with an outdated checkpoint that requires partial resync. + +- **Nodes**: 4 genesis + 1 joining +- **Contract**: Deposit contract (`0x00000000219ab540356cBB839Cbe05303d7705Fa`) — must be pre-deployed in the Reth genesis file. +- **Flow**: Similar to `stake-and-checkpoint`, but uses epoch-based milestones (default `checkpoint_epoch=1`, `join_epoch=2`, `stop_epoch=4`). The joining node uses an older checkpoint and must backfill missing blocks before participating. +- **Verifies**: New validator syncs from the outdated checkpoint, catches up, and all nodes reach the stop epoch. + +#### `withdraw-and-exit` + +Tests the full validator withdrawal lifecycle. + +- **Nodes**: 4 genesis +- **Contract**: Withdrawal contract (`0x00000961Ef480Eb55e80D19ad83579A64c007002`) — must be pre-deployed in the Reth genesis file. Implements the EIP-7002 withdrawal request format. +- **Flow**: Sends a withdrawal transaction via the withdrawal contract, waits for the withdrawal delay (`VALIDATOR_WITHDRAWAL_NUM_EPOCHS + 1` epochs), then checks that the withdrawal amount arrived at the withdrawal address. +- **Verifies**: Withdrawal balance (32 ETH, with gas tolerance) transferred correctly, validator removed from consensus state. + +#### `protocol-params` + +Tests on-chain protocol parameter updates. + +- **Nodes**: 4 genesis +- **Contract**: Protocol params contract (`0x0000000000000000000000000000506172616D73`) — must be pre-deployed in the Reth genesis file with owner set to `0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266`, because only the owner can call `set_param(uint8,bytes)`. +- **Flow**: Sends two transactions to the protocol params contract — one updating `MaximumStake` to 64 ETH, another updating `EpochLength` to 100. Waits for `2 * blocks_per_epoch + 1` blocks so both changes are active. +- **Verifies**: RPC queries confirm `MaximumStake == 64_000_000_000` gwei and `EpochLength == 100`. + +#### `sync-from-genesis` + +Tests a new validator joining by syncing from genesis with no checkpoint. + +- **Nodes**: 4 genesis + 1 joining +- **Contracts**: Deposit contract (`0x00000000219ab540356cBB839Cbe05303d7705Fa`) and withdrawal contract (`0x00000961Ef480Eb55e80D19ad83579A64c007002`) — both must be pre-deployed in the Reth genesis file. +- **Flow**: First withdraws one validator (modifying the peer set from genesis config), then generates new keys for a joining validator, sends a deposit, creates a `bootstrappers.toml` for peer discovery, and starts node4 with no checkpoint. +- **Verifies**: The new node syncs the entire chain from genesis, catches up to the current epoch, and joins the active validator set. + +## Benchmarks + +### Consensus State Benchmark + +```bash +cargo bench -p summit-finalizer +``` diff --git a/node/src/args.rs b/node/src/args.rs index bbdee54c..4c2a0999 100644 --- a/node/src/args.rs +++ b/node/src/args.rs @@ -681,7 +681,7 @@ fn get_initial_state( has_pending_deposit: false, has_pending_withdrawal: false, joining_epoch: 0, - // TODO(matthias): this index is comes from the deposit contract. + // This index comes from the deposit contract. // Since there is no deposit transaction for the genesis nodes, the index will still be // 0 for the deposit contract. Right now we only use this index to avoid counting the same deposit request twice. // Since we set the index to 0 here, we cannot rely on the uniqueness. The first actual deposit request will have diff --git a/types/src/engine_client.rs b/types/src/engine_client.rs index b26a5d2d..c6230cd4 100644 --- a/types/src/engine_client.rs +++ b/types/src/engine_client.rs @@ -101,10 +101,8 @@ impl EngineClient for RethEngineClient { let payload_attributes = PayloadAttributes { timestamp, prev_randao: [0; 32].into(), - // todo(dalton): this should be the validators public key suggested_fee_recipient, withdrawals: Some(withdrawals), - // todo(dalton): we should make this something that we can associate with the simplex height parent_beacon_block_root, }; @@ -248,10 +246,8 @@ impl EngineClient for BadBlockEngineClient { let payload_attributes = PayloadAttributes { timestamp, prev_randao: [0; 32].into(), - // todo(dalton): this should be the validators public key suggested_fee_recipient, withdrawals: Some(withdrawals), - // todo(dalton): we should make this something that we can associate with the simplex height parent_beacon_block_root, };