@@ -15,7 +15,7 @@ use rollup_node_primitives::{
1515} ;
1616use rollup_node_watcher:: L1Notification ;
1717use scroll_alloy_consensus:: TxL1Message ;
18- use scroll_alloy_hardforks:: { ScrollHardfork , ScrollHardforks } ;
18+ use scroll_alloy_hardforks:: ScrollHardforks ;
1919use scroll_alloy_network:: Scroll ;
2020use scroll_db:: { Database , DatabaseError , DatabaseOperations , L1MessageStart , UnwindResult } ;
2121use scroll_network:: NewBlockWithPeer ;
@@ -79,6 +79,8 @@ pub struct ChainOrchestrator<ChainSpec, BC, P> {
7979 chain_buffer_size : usize ,
8080 /// A boolean to represent if the L1 has been synced.
8181 l1_synced : bool ,
82+ /// The L1 message queue index at which the V2 L1 message queue was enabled.
83+ l1_v2_message_queue_start_index : u64 ,
8284 /// The waker to notify when the engine driver should be polled.
8385 waker : AtomicWaker ,
8486}
9799 l2_client : P ,
98100 optimistic_sync_threshold : u64 ,
99101 chain_buffer_size : usize ,
102+ l1_v2_message_queue_start_index : u64 ,
100103 ) -> Result < Self , ChainOrchestratorError > {
101104 let chain = init_chain_from_db ( & database, & l2_client, chain_buffer_size) . await ?;
102105 Ok ( Self {
@@ -117,6 +120,7 @@ impl<
117120 optimistic_sync_threshold,
118121 chain_buffer_size,
119122 l1_synced : false ,
123+ l1_v2_message_queue_start_index,
120124 waker : AtomicWaker :: new ( ) ,
121125 } )
122126 }
@@ -534,15 +538,14 @@ impl<
534538 Box :: pin ( Self :: handle_batch_commit ( self . database . clone ( ) , batch) ) ,
535539 ) )
536540 }
537- L1Notification :: L1Message { message, block_number, block_timestamp } => {
541+ L1Notification :: L1Message { message, block_number, block_timestamp : _ } => {
538542 ChainOrchestratorFuture :: HandleL1Message ( self . handle_metered (
539543 ChainOrchestratorItem :: L1Message ,
540544 Box :: pin ( Self :: handle_l1_message (
545+ self . l1_v2_message_queue_start_index ,
541546 self . database . clone ( ) ,
542- self . chain_spec . clone ( ) ,
543547 message,
544548 block_number,
545- block_timestamp,
546549 ) ) ,
547550 ) )
548551 }
@@ -623,33 +626,15 @@ impl<
623626
624627 /// Handles an L1 message by inserting it into the database.
625628 async fn handle_l1_message (
629+ l1_v2_message_queue_start_index : u64 ,
626630 database : Arc < Database > ,
627- chain_spec : Arc < ChainSpec > ,
628631 l1_message : TxL1Message ,
629632 l1_block_number : u64 ,
630- block_timestamp : u64 ,
631633 ) -> Result < Option < ChainOrchestratorEvent > , ChainOrchestratorError > {
632634 let event = ChainOrchestratorEvent :: L1MessageCommitted ( l1_message. queue_index ) ;
633-
634- let queue_hash = if chain_spec
635- . scroll_fork_activation ( ScrollHardfork :: EuclidV2 )
636- . active_at_timestamp_or_number ( block_timestamp, l1_block_number) &&
637- l1_message. queue_index > 0
638- {
639- let index = l1_message. queue_index - 1 ;
640- let prev_queue_hash = database
641- . get_l1_message_by_index ( index)
642- . await ?
643- . map ( |m| m. queue_hash )
644- . ok_or ( DatabaseError :: L1MessageNotFound ( L1MessageStart :: Index ( index) ) ) ?;
645-
646- let mut input = prev_queue_hash. unwrap_or_default ( ) . to_vec ( ) ;
647- input. append ( & mut l1_message. tx_hash ( ) . to_vec ( ) ) ;
648- Some ( keccak256 ( input) & L1_MESSAGE_QUEUE_HASH_MASK )
649- } else {
650- None
651- } ;
652-
635+ let queue_hash =
636+ compute_l1_message_queue_hash ( & database, & l1_message, l1_v2_message_queue_start_index)
637+ . await ?;
653638 let l1_message = L1MessageEnvelope :: new ( l1_message, l1_block_number, None , queue_hash) ;
654639 database. insert_l1_message ( l1_message) . await ?;
655640 Ok ( Some ( event) )
@@ -700,6 +685,39 @@ impl<
700685 }
701686}
702687
688+ /// Computes the queue hash by taking the previous queue hash and performing a 2-to-1 hash with the
689+ /// current transaction hash using keccak. It then applies a mask to the last 32 bits as these bits
690+ /// are used to store the timestamp at which the message was enqueued in the contract. For the first
691+ /// message in the queue, the previous queue hash is zero. If the L1 message queue index is before
692+ /// migration to `L1MessageQueueV2`, the queue hash will be None.
693+ ///
694+ /// The solidity contract (`L1MessageQueueV2.sol`) implementation is defined here: <https://github.com/scroll-tech/scroll-contracts/blob/67c1bde19c1d3462abf8c175916a2bb3c89530e4/src/L1/rollup/L1MessageQueueV2.sol#L379-L403>
695+ async fn compute_l1_message_queue_hash (
696+ database : & Arc < Database > ,
697+ l1_message : & TxL1Message ,
698+ l1_v2_message_queue_start_index : u64 ,
699+ ) -> Result < Option < alloy_primitives:: FixedBytes < 32 > > , ChainOrchestratorError > {
700+ let queue_hash = if l1_message. queue_index == l1_v2_message_queue_start_index {
701+ let mut input = B256 :: default ( ) . to_vec ( ) ;
702+ input. append ( & mut l1_message. tx_hash ( ) . to_vec ( ) ) ;
703+ Some ( keccak256 ( input) & L1_MESSAGE_QUEUE_HASH_MASK )
704+ } else if l1_message. queue_index > l1_v2_message_queue_start_index {
705+ let index = l1_message. queue_index - 1 ;
706+ let mut input = database
707+ . get_l1_message_by_index ( index)
708+ . await ?
709+ . map ( |m| m. queue_hash )
710+ . ok_or ( DatabaseError :: L1MessageNotFound ( L1MessageStart :: Index ( index) ) ) ?
711+ . unwrap_or_default ( )
712+ . to_vec ( ) ;
713+ input. append ( & mut l1_message. tx_hash ( ) . to_vec ( ) ) ;
714+ Some ( keccak256 ( input) & L1_MESSAGE_QUEUE_HASH_MASK )
715+ } else {
716+ None
717+ } ;
718+ Ok ( queue_hash)
719+ }
720+
703721async fn init_chain_from_db < P : Provider < Scroll > + ' static > (
704722 database : & Arc < Database > ,
705723 l2_client : & P ,
@@ -954,6 +972,7 @@ mod test {
954972
955973 const TEST_OPTIMISTIC_SYNC_THRESHOLD : u64 = 100 ;
956974 const TEST_CHAIN_BUFFER_SIZE : usize = 2000 ;
975+ const TEST_L1_MESSAGE_QUEUE_INDEX_BOUNDARY : u64 = 953885 ;
957976
958977 /// A headers+bodies client that stores the headers and bodies in memory, with an artificial
959978 /// soft bodies response limit that is set to 20 by default.
@@ -1105,6 +1124,7 @@ mod test {
11051124 . expect ( "Failed to parse mainnet genesis block" ) ;
11061125 assertor. push_success ( & mainnet_genesis) ;
11071126 let provider = ProviderBuilder :: < _ , _ , Scroll > :: default ( ) . connect_mocked_client ( assertor) ;
1127+
11081128 let db = Arc :: new ( setup_test_db ( ) . await ) ;
11091129 (
11101130 ChainOrchestrator :: new (
@@ -1114,6 +1134,7 @@ mod test {
11141134 provider,
11151135 TEST_OPTIMISTIC_SYNC_THRESHOLD ,
11161136 TEST_CHAIN_BUFFER_SIZE ,
1137+ TEST_L1_MESSAGE_QUEUE_INDEX_BOUNDARY ,
11171138 )
11181139 . await
11191140 . unwrap ( ) ,
@@ -1274,6 +1295,8 @@ mod test {
12741295
12751296 #[ tokio:: test]
12761297 async fn test_handle_l1_message ( ) {
1298+ reth_tracing:: init_test_tracing ( ) ;
1299+
12771300 // Instantiate chain orchestrator and db
12781301 let ( mut chain_orchestrator, db) = setup_test_chain_orchestrator ( ) . await ;
12791302
@@ -1283,7 +1306,7 @@ mod test {
12831306 let mut u = Unstructured :: new ( & bytes) ;
12841307
12851308 let message = TxL1Message {
1286- queue_index : i64 :: arbitrary ( & mut u ) . unwrap ( ) . unsigned_abs ( ) ,
1309+ queue_index : TEST_L1_MESSAGE_QUEUE_INDEX_BOUNDARY - 1 ,
12871310 ..Arbitrary :: arbitrary ( & mut u) . unwrap ( )
12881311 } ;
12891312 let block_number = u64:: arbitrary ( & mut u) . unwrap ( ) ;
@@ -1309,15 +1332,18 @@ mod test {
13091332
13101333 // insert the previous L1 message in database.
13111334 chain_orchestrator. handle_l1_notification ( L1Notification :: L1Message {
1312- message : TxL1Message { queue_index : 1062109 , ..Default :: default ( ) } ,
1335+ message : TxL1Message {
1336+ queue_index : TEST_L1_MESSAGE_QUEUE_INDEX_BOUNDARY ,
1337+ ..Default :: default ( )
1338+ } ,
13131339 block_number : 1475588 ,
13141340 block_timestamp : 1745305199 ,
13151341 } ) ;
13161342 let _ = chain_orchestrator. next ( ) . await . unwrap ( ) . unwrap ( ) ;
13171343
13181344 // <https://sepolia.scrollscan.com/tx/0xd80cd61ac5d8665919da19128cc8c16d3647e1e2e278b931769e986d01c6b910>
13191345 let message = TxL1Message {
1320- queue_index : 1062110 ,
1346+ queue_index : TEST_L1_MESSAGE_QUEUE_INDEX_BOUNDARY + 1 ,
13211347 gas_limit : 168000 ,
13221348 to : address ! ( "Ba50f5340FB9F3Bd074bD638c9BE13eCB36E603d" ) ,
13231349 value : U256 :: ZERO ,
@@ -1336,7 +1362,7 @@ mod test {
13361362 db. get_l1_message_by_index ( message. queue_index ) . await . unwrap ( ) . unwrap ( ) ;
13371363
13381364 assert_eq ! (
1339- b256!( "5e48ae1092c7f912849b9935f4e66870d2034b24fb2016f506e6754900000000 " ) ,
1365+ b256!( "b2331b9010aac89f012d648fccc1f0a9aa5ef7b7b2afe21be297dd1a00000000 " ) ,
13401366 l1_message_result. queue_hash. unwrap( )
13411367 ) ;
13421368 }
@@ -1380,19 +1406,19 @@ mod test {
13801406 queue_hash : None ,
13811407 l1_block_number : 1 ,
13821408 l2_block_number : None ,
1383- ..Arbitrary :: arbitrary ( & mut u) . unwrap ( )
1409+ transaction : TxL1Message { queue_index : 1 , ..Arbitrary :: arbitrary ( & mut u) . unwrap ( ) } ,
13841410 } ;
13851411 let l1_message_block_20 = L1MessageEnvelope {
13861412 queue_hash : None ,
13871413 l1_block_number : 20 ,
13881414 l2_block_number : None ,
1389- ..Arbitrary :: arbitrary ( & mut u) . unwrap ( )
1415+ transaction : TxL1Message { queue_index : 2 , ..Arbitrary :: arbitrary ( & mut u) . unwrap ( ) } ,
13901416 } ;
13911417 let l1_message_block_30 = L1MessageEnvelope {
13921418 queue_hash : None ,
13931419 l1_block_number : 30 ,
13941420 l2_block_number : None ,
1395- ..Arbitrary :: arbitrary ( & mut u) . unwrap ( )
1421+ transaction : TxL1Message { queue_index : 3 , ..Arbitrary :: arbitrary ( & mut u) . unwrap ( ) } ,
13961422 } ;
13971423
13981424 // Index L1 messages
0 commit comments