Skip to content

Commit 926f00e

Browse files
committed
test: add test_l1_sync_batch_revert_before_l1_synced
1 parent 10b540c commit 926f00e

File tree

2 files changed

+90
-19
lines changed

2 files changed

+90
-19
lines changed

crates/node/src/test_utils/fixture.rs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,8 @@ impl TestFixture {
269269
pub struct AnvilConfig {
270270
/// Whether to enable Anvil.
271271
pub enabled: bool,
272+
/// Optional port for Anvil.
273+
pub port: Option<u16>,
272274
/// Optional state file to load into Anvil.
273275
pub state_path: Option<PathBuf>,
274276
/// Optional chain ID for Anvil.
@@ -527,17 +529,19 @@ impl TestFixtureBuilder {
527529
/// ```
528530
pub fn with_anvil(
529531
mut self,
532+
port: Option<u16>,
530533
state_path: Option<PathBuf>,
531534
chain_id: Option<u64>,
532535
block_time: Option<u64>,
533536
slots_in_an_epoch: Option<u64>,
534537
) -> Self {
535538
self.anvil_config.enabled = true;
539+
self.anvil_config.port = port.or_else(|| Some(8544));
536540
self.anvil_config.state_path =
537541
state_path.or_else(|| Some(PathBuf::from("./tests/testdata/anvil_state.json")));
538542
self.anvil_config.chain_id = chain_id;
539543
self.anvil_config.block_time = block_time;
540-
self.anvil_config.slots_in_an_epoch = slots_in_an_epoch;
544+
self.anvil_config.slots_in_an_epoch = slots_in_an_epoch.or_else(|| Some(1));
541545
self
542546
}
543547

@@ -549,11 +553,11 @@ impl TestFixtureBuilder {
549553
// Start Anvil if requested
550554
let anvil = if self.anvil_config.enabled {
551555
let handle = Self::spawn_anvil(
552-
None,
556+
self.anvil_config.port,
553557
self.anvil_config.state_path.as_deref(),
554558
self.anvil_config.chain_id,
555559
self.anvil_config.block_time,
556-
None,
560+
self.anvil_config.slots_in_an_epoch,
557561
)
558562
.await?;
559563

@@ -655,7 +659,9 @@ impl TestFixtureBuilder {
655659
config.init_state = Some(state);
656660
}
657661

658-
config.slots_in_an_epoch = slots_in_an_epoch.unwrap_or(1);
662+
if let Some(slots) = slots_in_an_epoch {
663+
config.slots_in_an_epoch = slots;
664+
}
659665

660666
// Spawn Anvil and return the NodeHandle
661667
let (_api, handle) = anvil::spawn(config).await;

crates/node/tests/l1_sync.rs

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ async fn test_l1_sync_batch_commit() -> eyre::Result<()> {
125125
let mut fixture = TestFixture::builder()
126126
.followers(1)
127127
.skip_l1_synced_notifications() // Prevents automatic L1Synced, simulates initial sync
128-
.with_anvil(None, Some(22222222), None, None)
128+
.with_anvil(None, None, Some(22222222), None, None)
129129
.build()
130130
.await?;
131131

@@ -193,7 +193,7 @@ async fn test_l1_sync_batch_finalized() -> eyre::Result<()> {
193193
let mut fixture = TestFixture::builder()
194194
.followers(1)
195195
.skip_l1_synced_notifications()
196-
.with_anvil(None, Some(22222222), None, None)
196+
.with_anvil(None, None, Some(22222222), None, None)
197197
.build()
198198
.await?;
199199

@@ -296,28 +296,94 @@ async fn test_l1_sync_batch_finalized() -> eyre::Result<()> {
296296
Ok(())
297297
}
298298

299-
/// Test: `BatchRevert` events correctly roll back the safe head after `L1Synced`.
299+
/// Test: `BatchRevert` events are ignored before `L1Synced`.
300300
///
301301
/// # Test Flow
302-
/// 1. Start node in syncing state
302+
/// 1. Start node in syncing state (skip automatic L1Synced notifications)
303303
/// 2. Send `BatchCommit` transactions (batches 0-6) to L1
304-
/// 3. Complete L1 sync and verify safe head advanced
304+
/// 3. Verify safe head remains at genesis (events are buffered during sync)
305305
/// 4. Send a `BatchRevert` transaction to revert some batches
306-
/// 5. Verify safe head decreased to reflect the reverted state
306+
/// 5. Verify safe head still remains unchanged
307+
///
308+
/// # Expected Behavior
309+
/// During initial sync (before `L1Synced`), `BatchRevert` events should be indexed but
310+
/// NOT processed. The safe head should remain at genesis until L1 sync completes.
311+
/// This prevents the node from processing revert events out of order during catchup.
312+
#[tokio::test]
313+
async fn test_l1_sync_batch_revert_before_l1_synced() -> eyre::Result<()> {
314+
reth_tracing::init_test_tracing();
315+
316+
// Step 1: Setup node in syncing state
317+
let mut fixture = TestFixture::builder()
318+
.followers(1)
319+
.skip_l1_synced_notifications()
320+
.with_anvil(None, None, Some(22222222), None, None)
321+
.build()
322+
.await?;
323+
324+
// Record initial state
325+
let initial_status = fixture.get_status(0).await?;
326+
let initial_safe = initial_status.l2.fcs.safe_block_info().number;
327+
assert_eq!(initial_safe, 0, "Initial safe head should be at genesis (block 0)");
328+
329+
// Step 2: Send BatchCommit transactions (batches 0-6) to L1
330+
for i in 0..=6 {
331+
let commit_batch_tx = read_test_transaction("commitBatch", &i.to_string())?;
332+
fixture.anvil_inject_tx(commit_batch_tx).await?;
333+
}
334+
for _ in 1..=6 {
335+
fixture.expect_event().batch_commit_indexed().await?;
336+
}
337+
338+
// Verify safe head advanced after processing commits
339+
let new_status = fixture.get_status(0).await?;
340+
assert_eq!(
341+
initial_safe,
342+
new_status.l2.fcs.safe_block_info().number,
343+
"Safe head should not advance after BatchCommit before L1Synced"
344+
);
345+
346+
// Step 4: Send BatchRevert transaction to revert some batches
347+
let revert_batch_tx = read_test_transaction("revertBatch", "0")?;
348+
fixture.anvil_inject_tx(revert_batch_tx).await?;
349+
350+
fixture.expect_event().batch_reverted().await?;
351+
352+
// Step 5: Verify safe head decreased after revert
353+
let revert_status = fixture.get_status(0).await?;
354+
assert_eq!(
355+
revert_status.l2.fcs.safe_block_info().number,
356+
new_status.l2.fcs.safe_block_info().number,
357+
"Safe head should not change after BatchRevert before L1Synced"
358+
);
359+
360+
Ok(())
361+
}
362+
363+
/// Test: `BatchRevert` events correctly roll back the safe head after `L1Synced`.
364+
///
365+
/// # Test Flow
366+
/// 1. Start node in syncing state
367+
/// 2. Send `BatchCommit` transactions (batches 0-6) to L1 and wait for indexing
368+
/// 3. Complete L1 sync by sending `L1Synced` notification
369+
/// 4. Verify safe head advances after processing buffered commits
370+
/// 5. Send a `BatchRevert` transaction to revert some batches
371+
/// 6. Verify safe head decreases to reflect the reverted state
307372
///
308373
/// # Expected Behavior
309-
/// When a `BatchRevert` event is detected on L1 (after `L1Synced`), the node should
310-
/// roll back its safe head to the last valid batch before the reverted batches.
311-
/// This ensures the L2 state remains consistent with the canonical L1 state.
374+
/// After `L1Synced`, when a `BatchRevert` event is detected on L1, the node should
375+
/// immediately roll back its safe head to the last valid batch before the reverted
376+
/// batches. This ensures the L2 state remains consistent with the canonical L1 state
377+
/// and handles L1 sequencer coordinator initiated reverts correctly.
312378
#[tokio::test]
313-
async fn test_l1_sync_batch_revert() -> eyre::Result<()> {
379+
async fn test_l1_sync_batch_revert_after_l1_synced() -> eyre::Result<()> {
314380
reth_tracing::init_test_tracing();
315381

316382
// Step 1: Setup node in syncing state
317383
let mut fixture = TestFixture::builder()
318384
.followers(1)
319385
.skip_l1_synced_notifications()
320-
.with_anvil(None, Some(22222222), None, None)
386+
.with_anvil(None, None, Some(22222222), None, None)
321387
.build()
322388
.await?;
323389

@@ -394,7 +460,7 @@ async fn test_l1_reorg_batch_commit() -> eyre::Result<()> {
394460
let mut fixture = TestFixture::builder()
395461
.followers(1)
396462
.skip_l1_synced_notifications()
397-
.with_anvil(None, Some(22222222), None, None)
463+
.with_anvil(None, None, Some(22222222), None, Some(32))
398464
.build()
399465
.await?;
400466

@@ -435,7 +501,6 @@ async fn test_l1_reorg_batch_commit() -> eyre::Result<()> {
435501

436502
// Step 4: Perform L1 reorg to remove batches 4-6 (reorg depth 3)
437503
fixture.anvil_reorg(3).await?;
438-
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
439504

440505
// Wait for reorg detection
441506
fixture.expect_event().l1_reorg().await?;
@@ -478,7 +543,7 @@ async fn test_l1_reorg_batch_finalized() -> eyre::Result<()> {
478543
let mut fixture = TestFixture::builder()
479544
.followers(1)
480545
.skip_l1_synced_notifications()
481-
.with_anvil(None, Some(22222222), None, None)
546+
.with_anvil(None, None, Some(22222222), None, None)
482547
.build()
483548
.await?;
484549

@@ -557,7 +622,7 @@ async fn test_l1_reorg_batch_revert() -> eyre::Result<()> {
557622
let mut fixture = TestFixture::builder()
558623
.followers(1)
559624
.skip_l1_synced_notifications()
560-
.with_anvil(None, Some(22222222), None, None)
625+
.with_anvil(None, None, Some(22222222), None, None)
561626
.build()
562627
.await?;
563628

0 commit comments

Comments
 (0)