@@ -36,6 +36,7 @@ use rollup_node_sequencer::L1MessageInclusionMode;
3636use rollup_node_watcher:: L1Notification ;
3737use scroll_alloy_consensus:: TxL1Message ;
3838use scroll_alloy_rpc_types:: Transaction as ScrollAlloyTransaction ;
39+ use scroll_db:: L1MessageStart ;
3940use scroll_network:: { NewBlockWithPeer , SCROLL_MAINNET } ;
4041use scroll_wire:: { ScrollWireConfig , ScrollWireProtocolHandler } ;
4142use std:: { path:: PathBuf , sync:: Arc , time:: Duration } ;
@@ -1180,7 +1181,7 @@ async fn can_handle_l1_message_reorg() -> eyre::Result<()> {
11801181 for i in 1 ..=10 {
11811182 node0_rnm_handle. build_block ( ) . await ;
11821183 let b = wait_for_block_sequenced_5s ( & mut node0_rnm_events, i) . await ?;
1183- println ! ( "Sequenced block {} {:?} ", b. header. number, b. header. hash_slow( ) ) ;
1184+ tracing :: info! ( target : "scroll::test ", block_number = ? b. header. number, block_hash = ? b. header. hash_slow( ) , "Sequenced block" ) ;
11841185 }
11851186
11861187 // Assert that the follower node has received all 10 blocks from the sequencer node.
@@ -1281,6 +1282,147 @@ async fn can_handle_l1_message_reorg() -> eyre::Result<()> {
12811282 Ok ( ( ) )
12821283}
12831284
1285+ /// Tests that a follower node correctly rejects L2 blocks containing L1 messages it hasn't received
1286+ /// yet.
1287+ ///
1288+ /// This test verifies the security mechanism that prevents nodes from processing blocks with
1289+ /// unknown L1 messages, ensuring L2 chain consistency.
1290+ ///
1291+ /// # Test scenario
1292+ /// 1. Sets up two nodes: a sequencer and a follower
1293+ /// 2. The sequencer builds 10 initial blocks that are successfully imported by the follower
1294+ /// 3. An L1 message is sent only to the sequencer (not to the follower)
1295+ /// 4. The sequencer includes this L1 message in block 11 and continues building blocks up to block
1296+ /// 15
1297+ /// 5. The follower detects the unknown L1 message and stops processing at block 10
1298+ /// 6. Once the L1 message is finally sent to the follower, it can process the previously rejected
1299+ /// blocks
1300+ /// 7. The test confirms both nodes are synchronized at block 16 after the follower catches up
1301+ ///
1302+ /// # Key verification points
1303+ /// - The follower correctly identifies missing L1 messages with a `L1MessageMissingInDatabase`
1304+ /// event
1305+ /// - Block processing halts at the last valid block when an unknown L1 message is encountered
1306+ /// - The follower can resume processing and catch up once it receives the missing L1 message
1307+ /// - This prevents nodes from accepting blocks with L1 messages they cannot validate
1308+ #[ tokio:: test]
1309+ async fn can_reject_l2_block_with_unknown_l1_message ( ) -> eyre:: Result < ( ) > {
1310+ reth_tracing:: init_test_tracing ( ) ;
1311+ color_eyre:: install ( ) ?;
1312+ let chain_spec = ( * SCROLL_DEV ) . clone ( ) ;
1313+
1314+ // Launch 2 nodes: node0=sequencer and node1=follower.
1315+ let config = default_sequencer_test_scroll_rollup_node_config ( ) ;
1316+ let ( mut nodes, _tasks, _) = setup_engine ( config, 2 , chain_spec. clone ( ) , false , false ) . await ?;
1317+ let node0 = nodes. remove ( 0 ) ;
1318+ let node1 = nodes. remove ( 0 ) ;
1319+
1320+ // Get handles
1321+ let node0_rnm_handle = node0. inner . add_ons_handle . rollup_manager_handle . clone ( ) ;
1322+ let mut node0_rnm_events = node0_rnm_handle. get_event_listener ( ) . await ?;
1323+ let node0_l1_watcher_tx = node0. inner . add_ons_handle . l1_watcher_tx . as_ref ( ) . unwrap ( ) ;
1324+
1325+ let node1_rnm_handle = node1. inner . add_ons_handle . rollup_manager_handle . clone ( ) ;
1326+ let mut node1_rnm_events = node1_rnm_handle. get_event_listener ( ) . await ?;
1327+ let node1_l1_watcher_tx = node1. inner . add_ons_handle . l1_watcher_tx . as_ref ( ) . unwrap ( ) ;
1328+
1329+ // Let the sequencer build 10 blocks before performing the reorg process.
1330+ for i in 1 ..=10 {
1331+ node0_rnm_handle. build_block ( ) . await ;
1332+ let b = wait_for_block_sequenced_5s ( & mut node0_rnm_events, i) . await ?;
1333+ tracing:: info!( target: "scroll::test" , block_number = ?b. header. number, block_hash = ?b. header. hash_slow( ) , "Sequenced block" )
1334+ }
1335+
1336+ // Assert that the follower node has received all 10 blocks from the sequencer node.
1337+ wait_for_block_imported_5s ( & mut node1_rnm_events, 10 ) . await ?;
1338+
1339+ // Send a L1 message and wait for it to be indexed.
1340+ let l1_message_notification = L1Notification :: L1Message {
1341+ message : TxL1Message {
1342+ queue_index : 0 ,
1343+ gas_limit : 21000 ,
1344+ to : Default :: default ( ) ,
1345+ value : Default :: default ( ) ,
1346+ sender : address ! ( "f39Fd6e51aad88F6F4ce6aB8827279cffFb92266" ) ,
1347+ input : Default :: default ( ) ,
1348+ } ,
1349+ block_number : 10 ,
1350+ block_timestamp : 0 ,
1351+ } ;
1352+
1353+ // Send the L1 message to the sequencer node but not to follower node.
1354+ node0_l1_watcher_tx. send ( Arc :: new ( l1_message_notification. clone ( ) ) ) . await ?;
1355+ node0_l1_watcher_tx. send ( Arc :: new ( L1Notification :: NewBlock ( 10 ) ) ) . await ?;
1356+ wait_for_event_5s (
1357+ & mut node0_rnm_events,
1358+ RollupManagerEvent :: ChainOrchestratorEvent ( ChainOrchestratorEvent :: L1MessageCommitted ( 0 ) ) ,
1359+ )
1360+ . await ?;
1361+
1362+ // Build block that contains the L1 message.
1363+ node0_rnm_handle. build_block ( ) . await ;
1364+ wait_for_event_predicate_5s ( & mut node0_rnm_events, |e| {
1365+ if let RollupManagerEvent :: BlockSequenced ( block) = e {
1366+ if block. header . number == 11 &&
1367+ block. body . transactions . len ( ) == 1 &&
1368+ block. body . transactions . iter ( ) . any ( |tx| tx. is_l1_message ( ) )
1369+ {
1370+ return true ;
1371+ }
1372+ }
1373+
1374+ false
1375+ } )
1376+ . await ?;
1377+
1378+ for i in 12 ..=15 {
1379+ node0_rnm_handle. build_block ( ) . await ;
1380+ wait_for_block_sequenced_5s ( & mut node0_rnm_events, i) . await ?;
1381+ }
1382+
1383+ wait_for_event_5s (
1384+ & mut node1_rnm_events,
1385+ RollupManagerEvent :: L1MessageMissingInDatabase {
1386+ start : L1MessageStart :: Hash ( b256 ! (
1387+ "0x0a2f8e75392ab51a26a2af835042c614eb141cd934fe1bdd4934c10f2fe17e98"
1388+ ) ) ,
1389+ } ,
1390+ )
1391+ . await ?;
1392+
1393+ // follower node should not import block 15
1394+ // follower node doesn't know about the L1 message so stops processing the chain at block 10
1395+ assert_eq ! ( latest_block( & node1) . await ?. header. number, 10 ) ;
1396+
1397+ // Finally send L1 the L1 message to follower node.
1398+ node1_l1_watcher_tx. send ( Arc :: new ( l1_message_notification) ) . await ?;
1399+ node1_l1_watcher_tx. send ( Arc :: new ( L1Notification :: NewBlock ( 10 ) ) ) . await ?;
1400+ wait_for_event_5s (
1401+ & mut node1_rnm_events,
1402+ RollupManagerEvent :: ChainOrchestratorEvent ( ChainOrchestratorEvent :: L1MessageCommitted ( 0 ) ) ,
1403+ )
1404+ . await ?;
1405+
1406+ // Produce another block and send to follower node.
1407+ node0_rnm_handle. build_block ( ) . await ;
1408+ wait_for_block_sequenced_5s ( & mut node0_rnm_events, 16 ) . await ?;
1409+
1410+ // Assert that the follower node has received the latest block from the sequencer node and
1411+ // processed the missing chain before.
1412+ // This is possible now because it has received the L1 message.
1413+ wait_for_block_imported_5s ( & mut node1_rnm_events, 16 ) . await ?;
1414+
1415+ // Assert both nodes are at block 16.
1416+ let node0_latest_block = latest_block ( & node0) . await ?;
1417+ assert_eq ! ( node0_latest_block. header. number, 16 ) ;
1418+ assert_eq ! (
1419+ node0_latest_block. header. hash_slow( ) ,
1420+ latest_block( & node1) . await ?. header. hash_slow( )
1421+ ) ;
1422+
1423+ Ok ( ( ) )
1424+ }
1425+
12841426#[ tokio:: test]
12851427async fn can_gossip_over_eth_wire ( ) -> eyre:: Result < ( ) > {
12861428 reth_tracing:: init_test_tracing ( ) ;
@@ -1570,10 +1712,11 @@ async fn wait_for_event_predicate(
15701712 maybe_event = event_stream. next( ) => {
15711713 match maybe_event {
15721714 Some ( e) if predicate( e. clone( ) ) => {
1715+ tracing:: debug!( target: "scroll::test" , event = ?e, "Received event" ) ;
15731716 return Ok ( ( ) ) ;
15741717 }
15751718 Some ( e) => {
1576- tracing:: debug!( target: "TODO:nodeX " , "ignoring event {:?}" , e ) ;
1719+ tracing:: debug!( target: "scroll::test " , event = ?e , "Ignoring event" ) ;
15771720 } , // Ignore other events
15781721 None => return Err ( eyre:: eyre!( "Event stream ended unexpectedly" ) ) ,
15791722 }
0 commit comments