Skip to content

Commit 4df57f1

Browse files
committed
feat: add test l1 reorg
1 parent 3d0c580 commit 4df57f1

File tree

9 files changed

+1407
-10
lines changed

9 files changed

+1407
-10
lines changed

crates/node/src/test_utils/fixture.rs

Lines changed: 46 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,30 @@ impl TestFixture {
229229
pub const fn has_anvil(&self) -> bool {
230230
self.anvil.is_some()
231231
}
232+
233+
/// Send a raw transaction to Anvil.
234+
pub async fn anvil_send_raw_transaction(
235+
&self,
236+
raw_tx: impl Into<alloy_primitives::Bytes>,
237+
) -> eyre::Result<alloy_primitives::B256> {
238+
use alloy_provider::{Provider, ProviderBuilder};
239+
240+
// Ensure Anvil is running
241+
let anvil_endpoint =
242+
self.anvil_endpoint().ok_or_else(|| eyre::eyre!("Anvil is not running"))?;
243+
244+
// Create provider
245+
let provider = ProviderBuilder::new().connect_http(anvil_endpoint.parse()?);
246+
247+
// Send raw transaction
248+
let raw_tx_bytes = raw_tx.into();
249+
let pending_tx = provider.send_raw_transaction(&raw_tx_bytes).await?;
250+
251+
let tx_hash = *pending_tx.tx_hash();
252+
tracing::info!("Sent raw transaction to Anvil: {:?}", tx_hash);
253+
254+
Ok(tx_hash)
255+
}
232256
}
233257

234258
/// Builder for creating test fixtures with a fluent API.
@@ -466,7 +490,7 @@ impl TestFixtureBuilder {
466490
/// Enable Anvil with the default state file (`tests/anvil_state.json`).
467491
pub fn with_anvil_default_state(mut self) -> Self {
468492
self.enable_anvil = true;
469-
self.anvil_state_path = Some(PathBuf::from("tests/anvil_state.json"));
493+
self.anvil_state_path = Some(PathBuf::from("./tests/testdata/anvil_state.json"));
470494
self
471495
}
472496

@@ -491,19 +515,25 @@ impl TestFixtureBuilder {
491515

492516
/// Build the test fixture.
493517
pub async fn build(self) -> eyre::Result<TestFixture> {
494-
let config = self.config;
518+
let mut config = self.config;
495519
let chain_spec = self.chain_spec.unwrap_or_else(|| SCROLL_DEV.clone());
496520

497521
// Start Anvil if requested
498522
let anvil = if self.enable_anvil {
499-
Some(
523+
let anvil = Some(
500524
Self::spawn_anvil(
501525
self.anvil_state_path.as_deref(),
502526
self.anvil_chain_id,
503527
self.anvil_block_time,
504528
)
505529
.await?,
506-
)
530+
);
531+
if let Some(ref handle) = anvil {
532+
let endpoint = handle.http_endpoint();
533+
config.l1_provider_args.url = endpoint.parse::<reqwest::Url>().ok();
534+
config.blob_provider_args.anvil_url = endpoint.parse::<reqwest::Url>().ok();
535+
}
536+
anvil
507537
} else {
508538
None
509539
};
@@ -579,11 +609,18 @@ impl TestFixtureBuilder {
579609
config.block_time = Some(std::time::Duration::from_secs(time));
580610
}
581611

582-
// Note: State loading from file is not yet implemented in this version
583-
// The anvil::NodeConfig API for loading state is different from alloy_node_bindings
584-
// TODO: Implement state loading once we understand the correct API
585-
if state_path.is_some() {
586-
tracing::warn!("State loading is not yet implemented with anvil crate");
612+
// Load state from file if provided
613+
if let Some(path) = state_path {
614+
let state = anvil::eth::backend::db::SerializableState::load(path)
615+
.map_err(|e| {
616+
eyre::eyre!(
617+
"Failed to load Anvil state from {}: {:?}",
618+
path.display(),
619+
e
620+
)
621+
})?;
622+
tracing::info!("Loaded Anvil state from: {}", path.display());
623+
config.init_state = Some(state);
587624
}
588625

589626
// Spawn Anvil and return the NodeHandle
Lines changed: 291 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,291 @@
1+
# L1 Multi-Mode Testing Documentation
2+
3+
## Overview
4+
5+
This document describes the test suite for Issue #420: Testing multi-mode L1 event consumption, reorg handling, and node restart scenarios.
6+
7+
**Related Issue**: https://github.com/scroll-tech/rollup-node/issues/420
8+
9+
## Test Coverage
10+
11+
### 1. Event Consumption in Different Sync States
12+
13+
The test suite covers the following scenarios as specified in the issue:
14+
15+
| Event | Sync State | Test Function | Expected Outcome |
16+
| ---------------- | ---------- | ------------- | ---------------- |
17+
| BatchCommit | Syncing | `test_batch_commit_while_syncing` | No change (events only processed when finalized) |
18+
| BatchCommit | Synced | `test_batch_commit_while_synced` | Updates safe head immediately |
19+
| BatchFinalized | Syncing | `test_batch_finalized_while_syncing` | Triggers unprocessed BatchCommit events, updates safe and finalized heads |
20+
| BatchFinalized | Synced | `test_batch_finalized_while_synced` | Updates finalized head only |
21+
| BatchRevert | Syncing | `test_batch_revert_while_syncing` | No effect |
22+
| BatchRevert | Synced | `test_batch_revert_while_synced` | Updates safe head to last block of previous batch |
23+
24+
### 2. L1 Reorg Handling
25+
26+
Tests for handling L1 reorgs of various events:
27+
28+
| Reorged Event | Test Function | Expected Outcome |
29+
| ------------------ | ------------- | ---------------- |
30+
| BatchCommit | `test_l1_reorg_batch_commit` | Updates safe head to last block of previous BatchCommit |
31+
| BatchFinalized | `test_l1_reorg_batch_finalized_has_no_effect` | No change (finalized events can't be reorged) |
32+
33+
### 3. Node Shutdown and Restart
34+
35+
- `test_node_restart_after_l1_reorg`: Tests node restart after L1 reorg (requires implementation)
36+
37+
### 4. Anvil Integration
38+
39+
- `test_with_anvil_l1_events`: Demonstrates using real Anvil instance for L1 interactions
40+
41+
## Current Status
42+
43+
### ✅ Completed
44+
45+
1. **Test structure created** - All test functions are defined with proper documentation
46+
2. **Anvil integration** - TestFixture now supports Anvil with configurable state files
47+
3. **Test data prepared**:
48+
- `tests/anvil_state.json` - Anvil initial state with deployed L1 contracts
49+
- `crates/node/tests/testdata/test_transactions.json` - Raw transaction data
50+
- `crates/node/tests/testdata/batch_0_calldata.bin` - Real batch calldata
51+
- `crates/node/tests/testdata/batch_1_calldata.bin` - Real batch calldata
52+
53+
### 🚧 Needs Implementation
54+
55+
1. **Node restart testing infrastructure**:
56+
- Persistent database across test runs
57+
- Ability to stop and restart ChainOrchestrator
58+
- Reorg detection on startup
59+
60+
2. **Anvil state file loading**:
61+
- The current `spawn_anvil` implementation logs a warning about state loading
62+
- Need to implement proper state loading from `tests/anvil_state.json`
63+
- See `crates/node/src/test_utils/fixture.rs:582-587`
64+
65+
3. **Enhanced L1 event helpers**:
66+
- Consider adding `BatchRangeReverted` event helper
67+
- Add more granular reorg testing utilities
68+
69+
4. **Real L1 contract interactions**:
70+
- Send transactions to Anvil L1 contracts (ScrollChain, MessageQueue)
71+
- Parse real contract events
72+
- Test end-to-end flow from contract call to rollup node event processing
73+
74+
## Running the Tests
75+
76+
### Run all L1 multi-mode tests:
77+
78+
```bash
79+
cargo test --test l1_multi_mode
80+
```
81+
82+
### Run a specific test:
83+
84+
```bash
85+
cargo test --test l1_multi_mode test_batch_commit_while_syncing
86+
```
87+
88+
### Run tests with logging:
89+
90+
```bash
91+
RUST_LOG=debug cargo test --test l1_multi_mode -- --nocapture
92+
```
93+
94+
### Run Anvil integration tests:
95+
96+
```bash
97+
# These tests are ignored by default
98+
cargo test --test l1_multi_mode test_with_anvil_l1_events -- --ignored
99+
```
100+
101+
## Next Steps
102+
103+
### Phase 1: Fix Compilation Issues
104+
105+
Some tests may have compilation errors due to:
106+
- Rust toolchain version mismatch (proc macro ABI issues)
107+
- Missing trait imports
108+
109+
**Action**: Run `cargo clean` and rebuild with the correct toolchain version.
110+
111+
### Phase 2: Implement Anvil State Loading
112+
113+
Update `TestFixtureBuilder::spawn_anvil` to properly load state from JSON:
114+
115+
```rust
116+
async fn spawn_anvil(
117+
state_path: Option<&std::path::Path>,
118+
chain_id: Option<u64>,
119+
block_time: Option<u64>,
120+
) -> eyre::Result<anvil::NodeHandle> {
121+
let mut config = anvil::NodeConfig::default();
122+
123+
if let Some(id) = chain_id {
124+
config.chain_id = Some(id);
125+
}
126+
127+
if let Some(time) = block_time {
128+
config.block_time = Some(std::time::Duration::from_secs(time));
129+
}
130+
131+
// TODO: Implement state loading
132+
// Research the correct Anvil API for loading state from file
133+
134+
let (_api, handle) = anvil::spawn(config).await;
135+
Ok(handle)
136+
}
137+
```
138+
139+
### Phase 3: Enhance Test Infrastructure
140+
141+
1. **Add persistent database support**:
142+
```rust
143+
pub fn with_persistent_db(mut self, path: impl Into<PathBuf>) -> Self {
144+
// Implementation needed
145+
}
146+
```
147+
148+
2. **Add node restart capability**:
149+
```rust
150+
pub async fn restart_node(&mut self, index: usize) -> eyre::Result<()> {
151+
// Implementation needed
152+
}
153+
```
154+
155+
3. **Add more event assertions**:
156+
```rust
157+
impl EventAssertions {
158+
pub async fn batch_reverted(self) -> eyre::Result<()> {
159+
// Implementation needed
160+
}
161+
162+
pub async fn l1_reorg(self) -> eyre::Result<()> {
163+
// Implementation needed
164+
}
165+
}
166+
```
167+
168+
### Phase 4: Real L1 Contract Testing
169+
170+
Use the `test_transactions.json` data to:
171+
172+
1. Send real transactions to Anvil contracts
173+
2. Trigger actual BatchCommit/BatchFinalized events
174+
3. Verify the rollup node processes them correctly
175+
176+
Example structure:
177+
178+
```rust
179+
#[tokio::test]
180+
async fn test_real_l1_batch_commit() -> eyre::Result<()> {
181+
let mut fixture = TestFixture::builder()
182+
.sequencer()
183+
.with_anvil_default_state()
184+
.with_anvil_chain_id(1337)
185+
.build()
186+
.await?;
187+
188+
// Get provider for Anvil
189+
let anvil = fixture.anvil.as_ref().unwrap();
190+
let provider = ProviderBuilder::new()
191+
.with_recommended_fillers()
192+
.on_http(anvil.endpoint().parse()?);
193+
194+
// Load and send transaction from test_transactions.json
195+
let tx_data = load_test_transaction("batch_commit_tx_0")?;
196+
let tx_hash = provider.send_raw_transaction(&tx_data).await?.tx_hash();
197+
198+
// Wait for rollup node to process the event
199+
fixture.expect_event().batch_consolidated().await?;
200+
201+
Ok(())
202+
}
203+
```
204+
205+
## Environment Setup
206+
207+
### Required Files
208+
209+
1. `tests/anvil_state.json` - ✅ Already created
210+
2. `tests/anvil.env` - ✅ Already created
211+
3. `crates/node/tests/testdata/test_transactions.json` - ✅ Already created
212+
4. `crates/node/tests/testdata/batch_0_calldata.bin` - ✅ Already exists
213+
5. `crates/node/tests/testdata/batch_1_calldata.bin` - ✅ Already exists
214+
215+
### Contract Addresses (from anvil.env)
216+
217+
Key L1 contract addresses deployed in Anvil:
218+
219+
- **L1_SCROLL_CHAIN_PROXY**: `0x5FC8d32690cc91D4c39d9d3abcBD16989F875707`
220+
- **L1_MESSAGE_QUEUE_V2_PROXY**: `0xDc64a140Aa3E981100a9becA4E685f962f0cF6C9`
221+
- **L1_SYSTEM_CONFIG_PROXY**: `0x9fE46736679d2D9a65F0992F2272dE9f3c7fa6e0`
222+
223+
## Testing Matrix
224+
225+
Use this matrix to track test implementation progress:
226+
227+
| Category | Test | Status | Notes |
228+
|----------|------|--------|-------|
229+
| **Sync State** | BatchCommit - Syncing || Implemented |
230+
| | BatchCommit - Synced || Implemented |
231+
| | BatchFinalized - Syncing || Implemented |
232+
| | BatchFinalized - Synced || Implemented |
233+
| | BatchRevert - Syncing || Implemented |
234+
| | BatchRevert - Synced || Implemented |
235+
| **L1 Reorg** | BatchCommit Reorg || Implemented |
236+
| | BatchFinalized Reorg || Implemented |
237+
| | BatchRevert Reorg || TODO |
238+
| | BatchRangeRevert Reorg || TODO |
239+
| **Node Restart** | Restart after reorg | 🚧 | Needs infrastructure |
240+
| | Restart with unfinalized events || TODO |
241+
| **Anvil Integration** | Real contract events | 🚧 | Partial implementation |
242+
243+
## Troubleshooting
244+
245+
### Proc Macro ABI Mismatch
246+
247+
If you see errors like:
248+
```
249+
proc macro server error: mismatched ABI
250+
```
251+
252+
**Solution**:
253+
```bash
254+
cargo clean
255+
cargo build --tests
256+
```
257+
258+
### Missing Batch Calldata Files
259+
260+
If tests fail with "No such file or directory" for batch calldata:
261+
262+
**Solution**: Ensure you're running tests from the project root:
263+
```bash
264+
cd /Users/yiweichi/Scroll/rollup-node
265+
cargo test --test l1_multi_mode
266+
```
267+
268+
### Anvil Connection Issues
269+
270+
If Anvil tests time out:
271+
272+
**Solution**: Check that the Anvil instance is starting correctly and listening on the expected port.
273+
274+
## Contributing
275+
276+
When adding new tests:
277+
278+
1. Follow the existing test structure and naming conventions
279+
2. Add comprehensive documentation comments
280+
3. Update this README with new test coverage
281+
4. Ensure tests are idempotent and don't depend on execution order
282+
5. Use the builder pattern for complex test setup
283+
284+
## References
285+
286+
- [Issue #420](https://github.com/scroll-tech/rollup-node/issues/420) - Original issue
287+
- [TestFixture Documentation](crates/node/src/test_utils/fixture.rs)
288+
- [L1 Helper Functions](crates/node/src/test_utils/l1_helpers.rs)
289+
- [Event Assertions](crates/node/src/test_utils/event_utils.rs)
290+
291+

0 commit comments

Comments
 (0)