Skip to content

Commit 8765085

Browse files
committed
test: add l1 reorg tests
1 parent b38b802 commit 8765085

File tree

4 files changed

+285
-3
lines changed

4 files changed

+285
-3
lines changed

crates/node/src/test_utils/fixture.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,28 @@ impl TestFixture {
268268

269269
Ok(tx_hash)
270270
}
271+
272+
/// Reorg Anvil by a specific depth (number of blocks to rewind).
273+
pub async fn anvil_reorg(&self, depth: u64) -> eyre::Result<()> {
274+
// Ensure Anvil is running
275+
let anvil_endpoint =
276+
self.anvil_endpoint().ok_or_else(|| eyre::eyre!("Anvil is not running"))?;
277+
278+
// Create RPC client
279+
let client = alloy_rpc_client::RpcClient::new_http(anvil_endpoint.parse()?);
280+
281+
// Call anvil_reorg
282+
// Parameters: (depth, transactions)
283+
// - depth: number of blocks to rewind from current head
284+
// - transactions: empty array means reorg without adding new transactions
285+
let _: () = client
286+
.request("anvil_reorg", (depth, Vec::<String>::new()))
287+
.await?;
288+
289+
tracing::info!("Reorged Anvil by {} blocks", depth);
290+
291+
Ok(())
292+
}
271293
}
272294

273295
/// Builder for creating test fixtures with a fluent API.

crates/node/tests/l1_sync.rs

Lines changed: 263 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ async fn test_l1_sync_batch_commit() -> eyre::Result<()> {
6666
let initial_safe = initial_status.l2.fcs.safe_block_info().number;
6767

6868
// Send BatchCommit transactions
69-
for i in 0..=2 {
69+
for i in 0..=6 {
7070
let commit_batch_tx = read_test_transaction("commitBatch", &i.to_string())?;
7171
fixture.anvil_send_raw_transaction(commit_batch_tx).await?;
7272
}
@@ -187,3 +187,265 @@ async fn test_l1_sync_batch_finalized() -> eyre::Result<()> {
187187
Ok(())
188188
}
189189

190+
#[tokio::test]
191+
async fn test_l1_sync_batch_revert() -> eyre::Result<()> {
192+
reth_tracing::init_test_tracing();
193+
194+
let mut fixture = TestFixture::builder()
195+
.followers(1)
196+
.with_chain_spec(SCROLL_DEV.clone())
197+
.skip_l1_synced_notifications()
198+
.with_anvil_default_state()
199+
.with_anvil_chain_id(22222222)
200+
.build()
201+
.await?;
202+
203+
// Get initial status
204+
let initial_status = fixture.get_sequencer_status().await?;
205+
let initial_safe = initial_status.l2.fcs.safe_block_info().number;
206+
207+
// Send BatchCommit transactions
208+
for i in 0..=6 {
209+
let commit_batch_tx = read_test_transaction("commitBatch", &i.to_string())?;
210+
fixture.anvil_send_raw_transaction(commit_batch_tx).await?;
211+
}
212+
213+
fixture.l1().sync().await?;
214+
fixture.expect_event().l1_synced().await?;
215+
fixture.expect_event().batch_consolidated().await?;
216+
fixture.anvil_mine_blocks(64).await?;
217+
218+
// Wait for l1 blocks to be processed
219+
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
220+
221+
// Check that safe head was updated
222+
let new_status = fixture.get_sequencer_status().await?;
223+
assert!(
224+
new_status.l2.fcs.safe_block_info().number > initial_safe,
225+
"Safe head should advance after BatchCommit when synced and L1Synced"
226+
);
227+
228+
// Send BatchRevert transactions
229+
let revert_batch_tx = read_test_transaction("revertBatch", "0")?;
230+
fixture.anvil_send_raw_transaction(revert_batch_tx).await?;
231+
fixture.anvil_mine_blocks(64).await?;
232+
233+
// Wait for l1 blocks to be processed
234+
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
235+
236+
// Check that safe head was updated
237+
let revert_status = fixture.get_sequencer_status().await?;
238+
assert!(
239+
revert_status.l2.fcs.safe_block_info().number < new_status.l2.fcs.safe_block_info().number,
240+
"Safe head should advance after BatchCommit when synced and L1Synced"
241+
);
242+
243+
Ok(())
244+
}
245+
246+
// =============================================================================
247+
// Test Suite 2: L1 Reorg handling for different batch events
248+
// =============================================================================
249+
250+
/// Test: L1 reorg of BatchCommit after L1Synced.
251+
///
252+
/// Expected: When a BatchCommit is reorged after the node has synced (L1Synced),
253+
/// the safe head should revert to the last block of the previous BatchCommit.
254+
#[tokio::test]
255+
async fn test_l1_reorg_batch_commit() -> eyre::Result<()> {
256+
reth_tracing::init_test_tracing();
257+
258+
let mut fixture = TestFixture::builder()
259+
.followers(1)
260+
.with_chain_spec(SCROLL_DEV.clone())
261+
.skip_l1_synced_notifications()
262+
.with_anvil_default_state()
263+
.with_anvil_chain_id(22222222)
264+
.build()
265+
.await?;
266+
267+
// Send BatchCommit transactions 0-2
268+
for i in 0..=2 {
269+
let commit_batch_tx = read_test_transaction("commitBatch", &i.to_string())?;
270+
fixture.anvil_send_raw_transaction(commit_batch_tx).await?;
271+
}
272+
273+
// Trigger L1 sync
274+
fixture.l1().sync().await?;
275+
fixture.expect_event().l1_synced().await?;
276+
fixture.expect_event().batch_consolidated().await?;
277+
278+
// Check that safe head was updated to batch 2
279+
let status_after_sync = fixture.get_sequencer_status().await?;
280+
let safe_after_batch_2 = status_after_sync.l2.fcs.safe_block_info().number;
281+
tracing::info!("Safe head after batch 2: {}", safe_after_batch_2);
282+
283+
// Send BatchCommit transaction 3
284+
let commit_batch_3_tx = read_test_transaction("commitBatch", "3")?;
285+
fixture.anvil_send_raw_transaction(commit_batch_3_tx).await?;
286+
287+
// Wait for processing
288+
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
289+
290+
// Check that safe head advanced to batch 3
291+
let status_after_batch_3 = fixture.get_sequencer_status().await?;
292+
let safe_after_batch_3 = status_after_batch_3.l2.fcs.safe_block_info().number;
293+
tracing::info!("Safe head after batch 3: {}", safe_after_batch_3);
294+
assert!(
295+
safe_after_batch_3 > safe_after_batch_2,
296+
"Safe head should advance after BatchCommit when L1Synced"
297+
);
298+
299+
// Reorg to remove batch 3 (reorg depth 1)
300+
fixture.anvil_reorg(1).await?;
301+
fixture.anvil_mine_blocks(1).await?;
302+
303+
// Wait for reorg to be detected and processed
304+
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
305+
306+
// Check that safe head reverted to batch 2
307+
let status_after_reorg = fixture.get_sequencer_status().await?;
308+
let safe_after_reorg = status_after_reorg.l2.fcs.safe_block_info().number;
309+
tracing::info!("Safe head after reorg: {}", safe_after_reorg);
310+
assert_eq!(
311+
safe_after_reorg, safe_after_batch_2,
312+
"Safe head should revert to previous BatchCommit after reorg"
313+
);
314+
315+
Ok(())
316+
}
317+
318+
/// Test: L1 reorg of BatchFinalized event.
319+
///
320+
/// Expected: Reorging BatchFinalized should have no effect because we only update
321+
/// the finalized head after the BatchFinalized event is itself finalized on L1,
322+
/// meaning it can never be reorged in practice.
323+
#[tokio::test]
324+
async fn test_l1_reorg_batch_finalized() -> eyre::Result<()> {
325+
reth_tracing::init_test_tracing();
326+
327+
let mut fixture = TestFixture::builder()
328+
.followers(1)
329+
.with_chain_spec(SCROLL_DEV.clone())
330+
.skip_l1_synced_notifications()
331+
.with_anvil_default_state()
332+
.with_anvil_chain_id(22222222)
333+
.build()
334+
.await?;
335+
336+
// Send BatchCommit transactions
337+
for i in 0..=6 {
338+
let commit_batch_tx = read_test_transaction("commitBatch", &i.to_string())?;
339+
fixture.anvil_send_raw_transaction(commit_batch_tx).await?;
340+
}
341+
342+
// Send BatchFinalized transactions
343+
for i in 1..=2 {
344+
let finalize_batch_tx = read_test_transaction("finalizeBatch", &i.to_string())?;
345+
fixture.anvil_send_raw_transaction(finalize_batch_tx).await?;
346+
}
347+
348+
// Trigger L1 sync
349+
fixture.l1().sync().await?;
350+
fixture.expect_event().l1_synced().await?;
351+
352+
// Wait for consolidate and finalization
353+
fixture.expect_event().batch_consolidated().await?;
354+
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
355+
356+
// Get finalized head after batch finalization
357+
let status_after_finalize = fixture.get_sequencer_status().await?;
358+
let finalized_after = status_after_finalize.l2.fcs.finalized_block_info().number;
359+
tracing::info!("Finalized head after batch finalized: {}", finalized_after);
360+
361+
// Reorg to remove the BatchFinalized events
362+
fixture.anvil_reorg(2).await?;
363+
fixture.anvil_mine_blocks(1).await?;
364+
365+
// Wait for reorg detection
366+
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
367+
368+
// Check that finalized head hasn't changed
369+
let status_after_reorg = fixture.get_sequencer_status().await?;
370+
let finalized_after_reorg = status_after_reorg.l2.fcs.finalized_block_info().number;
371+
tracing::info!("Finalized head after reorg: {}", finalized_after_reorg);
372+
assert_eq!(
373+
finalized_after_reorg, finalized_after,
374+
"Finalized head should not change after reorg of BatchFinalized"
375+
);
376+
377+
Ok(())
378+
}
379+
380+
/// Test: L1 reorg of BatchRevert event.
381+
///
382+
/// Expected: When a BatchRevert is reorged, the safe head should be restored to
383+
/// the state before the revert was applied. If the reverted batches are re-committed
384+
/// after the reorg, the safe head should advance again.
385+
#[tokio::test]
386+
async fn test_l1_reorg_batch_revert() -> eyre::Result<()> {
387+
reth_tracing::init_test_tracing();
388+
389+
let mut fixture = TestFixture::builder()
390+
.followers(1)
391+
.with_chain_spec(SCROLL_DEV.clone())
392+
.skip_l1_synced_notifications()
393+
.with_anvil_default_state()
394+
.with_anvil_chain_id(22222222)
395+
.build()
396+
.await?;
397+
398+
// Trigger L1 sync
399+
fixture.l1().sync().await?;
400+
fixture.expect_event().l1_synced().await?;
401+
402+
// Send BatchCommit transactions
403+
for i in 0..=6 {
404+
let commit_batch_tx = read_test_transaction("commitBatch", &i.to_string())?;
405+
fixture.anvil_send_raw_transaction(commit_batch_tx).await?;
406+
// if i!=0 { fixture.expect_event().batch_consolidated().await?; }
407+
}
408+
409+
// Wait for reorg to be detected and processed
410+
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
411+
412+
// Get safe head after all commits
413+
let status_after_commits = fixture.get_sequencer_status().await?;
414+
let safe_after_commits = status_after_commits.l2.fcs.safe_block_info().number;
415+
tracing::info!("Safe head after all commits: {}", safe_after_commits);
416+
417+
// Send BatchRevert transaction
418+
let revert_batch_tx = read_test_transaction("revertBatch", "0")?;
419+
fixture.anvil_send_raw_transaction(revert_batch_tx).await?;
420+
fixture.anvil_mine_blocks(1).await?;
421+
422+
// Wait for revert to be processed
423+
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
424+
425+
// Check that safe head was reverted
426+
let status_after_revert = fixture.get_sequencer_status().await?;
427+
let safe_after_revert = status_after_revert.l2.fcs.safe_block_info().number;
428+
tracing::info!("Safe head after revert: {}", safe_after_revert);
429+
assert!(
430+
safe_after_revert < safe_after_commits,
431+
"Safe head should decrease after BatchRevert"
432+
);
433+
434+
// Reorg to remove the BatchRevert event (reorg depth 1)
435+
fixture.anvil_reorg(2).await?;
436+
fixture.anvil_mine_blocks(3).await?;
437+
438+
// Wait for reorg to be detected and processed
439+
tokio::time::sleep(tokio::time::Duration::from_secs(3)).await;
440+
441+
// Check that safe head was restored to pre-revert state
442+
let status_after_reorg = fixture.get_sequencer_status().await?;
443+
let safe_after_reorg = status_after_reorg.l2.fcs.safe_block_info().number;
444+
tracing::info!("Safe head after reorg: {}", safe_after_reorg);
445+
assert_eq!(
446+
safe_after_reorg, safe_after_commits,
447+
"Safe head should be restored after reorg of BatchRevert"
448+
);
449+
450+
Ok(())
451+
}

tests/state.json

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)