@@ -4229,3 +4229,109 @@ where
42294229
42304230 Ok ( ( ) )
42314231}
4232+ #[ test_case( MemoryStorageBuilder :: default ( ) ; "memory" ) ]
4233+ #[ cfg_attr( feature = "rocksdb" , test_case( RocksDbStorageBuilder :: new( ) . await ; "rocks_db" ) ) ]
4234+ #[ cfg_attr( feature = "dynamodb" , test_case( DynamoDbStorageBuilder :: default ( ) ; "dynamo_db" ) ) ]
4235+ #[ cfg_attr( feature = "scylladb" , test_case( ScyllaDbStorageBuilder :: default ( ) ; "scylla_db" ) ) ]
4236+ #[ test_log:: test( tokio:: test) ]
4237+ async fn test_stage_block_with_message_earlier_than_cursor < B > (
4238+ mut storage_builder : B ,
4239+ ) -> anyhow:: Result < ( ) >
4240+ where
4241+ B : StorageBuilder ,
4242+ {
4243+ let mut signer = InMemorySigner :: new ( None ) ;
4244+ let receiver_public_key = signer. generate_new ( ) ;
4245+ let owner = receiver_public_key. into ( ) ;
4246+ let mut env = TestEnvironment :: new ( storage_builder. build ( ) . await ?, false , false ) . await ;
4247+ let chain_1_desc = env. add_root_chain ( 1 , owner, Amount :: from_tokens ( 10 ) ) . await ;
4248+ let chain_2_desc = env. add_root_chain ( 2 , owner, Amount :: ZERO ) . await ;
4249+ let chain_1 = chain_1_desc. id ( ) ;
4250+ let chain_2 = chain_2_desc. id ( ) ;
4251+
4252+ // Simulate a certificate sending two messages from chain_1 to chain_2.
4253+ let sender_hash = CryptoHash :: test_hash ( "sender block" ) ;
4254+
4255+ // Process the second message bundle on chain_2. This advances next_cursor_to_remove
4256+ // to height=0, index=1.
4257+ let block_proposal = make_first_block ( chain_2)
4258+ . with_incoming_bundle ( IncomingBundle {
4259+ origin : chain_1,
4260+ bundle : MessageBundle {
4261+ certificate_hash : sender_hash,
4262+ height : BlockHeight :: ZERO ,
4263+ timestamp : Timestamp :: from ( 0 ) ,
4264+ transaction_index : 1 ,
4265+ messages : vec ! [ system_credit_message( Amount :: from_tokens( 2 ) )
4266+ . to_posted( 0 , MessageKind :: Tracked ) ] ,
4267+ } ,
4268+ action : MessageAction :: Accept ,
4269+ } )
4270+ . into_first_proposal ( owner, & signer)
4271+ . await
4272+ . unwrap ( ) ;
4273+
4274+ let certificate_chain_2 = env. make_certificate ( ConfirmedBlock :: new (
4275+ BlockExecutionOutcome {
4276+ messages : vec ! [ Vec :: new( ) ] ,
4277+ previous_message_blocks : BTreeMap :: new ( ) ,
4278+ previous_event_blocks : BTreeMap :: new ( ) ,
4279+ events : vec ! [ Vec :: new( ) ] ,
4280+ blobs : vec ! [ Vec :: new( ) ] ,
4281+ state_hash : SystemExecutionState {
4282+ balance : Amount :: from_tokens ( 2 ) ,
4283+ ..env. system_execution_state ( & chain_2_desc. id ( ) )
4284+ }
4285+ . into_hash ( )
4286+ . await ,
4287+ oracle_responses : vec ! [ Vec :: new( ) ] ,
4288+ operation_results : vec ! [ ] ,
4289+ }
4290+ . with ( block_proposal. content . block ) ,
4291+ ) ) ;
4292+
4293+ env. worker ( )
4294+ . handle_confirmed_certificate ( certificate_chain_2. clone ( ) , None )
4295+ . await ?;
4296+
4297+ // Now try to stage a block with the earlier message (transaction_index: 0).
4298+ // This should fail with IncorrectMessageOrder because next_cursor_to_remove
4299+ // is now at index 1, but we're trying to process index 0.
4300+ let bad_proposed_block = make_child_block ( & certificate_chain_2. into_value ( ) )
4301+ . with_incoming_bundle ( IncomingBundle {
4302+ origin : chain_1,
4303+ bundle : MessageBundle {
4304+ certificate_hash : sender_hash,
4305+ height : BlockHeight :: ZERO ,
4306+ timestamp : Timestamp :: from ( 0 ) ,
4307+ transaction_index : 0 ,
4308+ messages : vec ! [
4309+ system_credit_message( Amount :: ONE ) . to_posted( 0 , MessageKind :: Tracked )
4310+ ] ,
4311+ } ,
4312+ action : MessageAction :: Accept ,
4313+ } ) ;
4314+
4315+ // Test stage_block_execution directly - this should fail with IncorrectMessageOrder.
4316+ assert_matches ! (
4317+ env. worker( )
4318+ . stage_block_execution( bad_proposed_block. clone( ) , None , vec![ ] )
4319+ . await ,
4320+ Err ( WorkerError :: ChainError ( chain_error) )
4321+ if matches!( * chain_error, ChainError :: IncorrectMessageOrder { .. } )
4322+ ) ;
4323+
4324+ // Also test handle_block_proposal for completeness.
4325+ let bad_proposal = bad_proposed_block
4326+ . into_first_proposal ( owner, & signer)
4327+ . await
4328+ . unwrap ( ) ;
4329+
4330+ assert_matches ! (
4331+ env. worker( ) . handle_block_proposal( bad_proposal) . await ,
4332+ Err ( WorkerError :: ChainError ( chain_error) )
4333+ if matches!( * chain_error, ChainError :: IncorrectMessageOrder { .. } )
4334+ ) ;
4335+
4336+ Ok ( ( ) )
4337+ }
0 commit comments