diff --git a/massa-execution-worker/src/execution.rs b/massa-execution-worker/src/execution.rs index 7cae632a4f9..e6c1bc98700 100644 --- a/massa-execution-worker/src/execution.rs +++ b/massa-execution-worker/src/execution.rs @@ -1175,10 +1175,19 @@ impl ExecutionState { // load and execute the compiled module // IMPORTANT: do not keep a lock here as `run_function` uses the `get_module` interface - let module = self + let Ok(module) = self .module_cache .write() - .load_module(&bytecode, message.max_gas)?; + .load_module(&bytecode, message.max_gas) + else { + let err = + ExecutionError::RuntimeError("could not load module for async execution".into()); + let mut context = context_guard!(self); + context.reset_to_snapshot(context_snapshot, err.clone()); + context.cancel_async_message(&message); + return Err(err); + }; + let response = massa_sc_runtime::run_function( &*self.execution_interface, module, diff --git a/massa-execution-worker/src/interface_impl.rs b/massa-execution-worker/src/interface_impl.rs index 4dd2f716310..fb33689fecf 100644 --- a/massa-execution-worker/src/interface_impl.rs +++ b/massa-execution-worker/src/interface_impl.rs @@ -1183,6 +1183,11 @@ impl Interface for InterfaceImpl { if validity_end.1 >= self.config.thread_count { bail!("validity end thread exceeds the configuration thread count") } + + if max_gas < self.config.gas_costs.max_instance_cost { + bail!("max gas is lower than the minimum instance cost") + } + let target_addr = Address::from_str(target_address)?; // check that the target address is an SC address @@ -1200,6 +1205,15 @@ impl Interface for InterfaceImpl { let mut execution_context = context_guard!(self); let emission_slot = execution_context.slot; + + if Slot::new(validity_end.0, validity_end.1) < Slot::new(validity_start.0, validity_start.1) + { + bail!("validity end is earlier than the validity start") + } + if Slot::new(validity_end.0, validity_end.1) < emission_slot { + bail!("validity end is earlier than the current slot") + } + let emission_index = execution_context.created_message_index; let sender = execution_context.get_current_address()?; let coins = Amount::from_raw(raw_coins); diff --git a/massa-execution-worker/src/tests/scenarios_mandatories.rs b/massa-execution-worker/src/tests/scenarios_mandatories.rs index b80355791cd..154ea28d3cb 100644 --- a/massa-execution-worker/src/tests/scenarios_mandatories.rs +++ b/massa-execution-worker/src/tests/scenarios_mandatories.rs @@ -758,6 +758,273 @@ fn send_and_receive_async_message() { assert_eq!(events[0].data, "message correctly received: 42,42,42,42"); } +#[test] +fn send_and_receive_async_message_expired() { + let exec_cfg = ExecutionConfig::default(); + let finalized_waitpoint = WaitPoint::new(); + let mut foreign_controllers = ExecutionForeignControllers::new_with_mocks(); + selector_boilerplate(&mut foreign_controllers.selector_controller); + foreign_controllers + .selector_controller + .set_expectations(|selector_controller| { + selector_controller + .expect_get_producer() + .returning(move |_| { + Ok(Address::from_public_key( + &KeyPair::from_str(TEST_SK_1).unwrap().get_public_key(), + )) + }); + }); + + foreign_controllers + .ledger_controller + .set_expectations(|ledger_controller| { + ledger_controller + .expect_get_balance() + .returning(move |_| Some(Amount::from_str("100").unwrap())); + + ledger_controller + .expect_entry_exists() + .times(2) + .returning(move |_| false); + + ledger_controller + .expect_entry_exists() + .returning(move |_| true); + }); + let saved_bytecode = Arc::new(RwLock::new(None)); + let finalized_waitpoint_trigger_handle = finalized_waitpoint.get_trigger_handle(); + + // Expected message from SC: send_message.ts (see massa unit tests src repo) + foreign_controllers + .final_state + .write() + .expect_finalize() + .times(1) + .with(predicate::eq(Slot::new(1, 0)), predicate::always()) + .returning(move |_, changes| { + //println!("changes S (1 0): {:?}", changes); + assert_eq!( + changes.async_pool_changes, + AsyncPoolChanges(BTreeMap::new()) + ); + finalized_waitpoint_trigger_handle.trigger(); + }); + + final_state_boilerplate( + &mut foreign_controllers.final_state, + foreign_controllers.db.clone(), + &foreign_controllers.selector_controller, + &mut foreign_controllers.ledger_controller, + Some(saved_bytecode), + None, + None, + ); + let mut universe = ExecutionTestUniverse::new(foreign_controllers, exec_cfg.clone()); + + // load bytecodes + universe.deploy_bytecode_block( + &KeyPair::from_str(TEST_SK_1).unwrap(), + Slot::new(1, 0), + include_bytes!("./wasm/send_message_expired.wasm"), + include_bytes!("./wasm/receive_message.wasm"), + ); + println!("waiting for finalized"); + finalized_waitpoint.wait(); + + // retrieve events emitted by smart contracts + let events = universe + .module_controller + .get_filtered_sc_output_event(EventFilter { + start: Some(Slot::new(1, 0)), + end: Some(Slot::new(1, 1)), + ..Default::default() + }); + // match the events + assert!(events.len() == 1, "One event was expected"); + assert!(events[0] + .data + .contains("validity end is earlier than the validity start")); +} + +#[test] +fn send_and_receive_async_message_expired_2() { + let exec_cfg = ExecutionConfig::default(); + let finalized_waitpoint = WaitPoint::new(); + let mut foreign_controllers = ExecutionForeignControllers::new_with_mocks(); + selector_boilerplate(&mut foreign_controllers.selector_controller); + foreign_controllers + .selector_controller + .set_expectations(|selector_controller| { + selector_controller + .expect_get_producer() + .returning(move |_| { + Ok(Address::from_public_key( + &KeyPair::from_str(TEST_SK_1).unwrap().get_public_key(), + )) + }); + }); + + foreign_controllers + .ledger_controller + .set_expectations(|ledger_controller| { + ledger_controller + .expect_get_balance() + .returning(move |_| Some(Amount::from_str("100").unwrap())); + + ledger_controller + .expect_entry_exists() + .times(2) + .returning(move |_| false); + + ledger_controller + .expect_entry_exists() + .returning(move |_| true); + }); + let saved_bytecode = Arc::new(RwLock::new(None)); + let finalized_waitpoint_trigger_handle = finalized_waitpoint.get_trigger_handle(); + + // Expected message from SC: send_message.ts (see massa unit tests src repo) + foreign_controllers + .final_state + .write() + .expect_finalize() + .times(1) + .with(predicate::eq(Slot::new(1, 0)), predicate::always()) + .returning(move |_, changes| { + assert_eq!( + changes.async_pool_changes, + AsyncPoolChanges(BTreeMap::new()) + ); + finalized_waitpoint_trigger_handle.trigger(); + }); + + final_state_boilerplate( + &mut foreign_controllers.final_state, + foreign_controllers.db.clone(), + &foreign_controllers.selector_controller, + &mut foreign_controllers.ledger_controller, + Some(saved_bytecode), + None, + None, + ); + let mut universe = ExecutionTestUniverse::new(foreign_controllers, exec_cfg.clone()); + + // load bytecodes + universe.deploy_bytecode_block( + &KeyPair::from_str(TEST_SK_1).unwrap(), + Slot::new(1, 0), + include_bytes!("./wasm/send_message_expired_2.wasm"), + include_bytes!("./wasm/receive_message.wasm"), + ); + println!("waiting for finalized"); + finalized_waitpoint.wait(); + + // retrieve events emitted by smart contracts + let events = universe + .module_controller + .get_filtered_sc_output_event(EventFilter { + start: Some(Slot::new(1, 0)), + end: Some(Slot::new(1, 1)), + ..Default::default() + }); + // match the events + assert!(events.len() == 1, "One event was expected"); + assert!(events[0] + .data + .contains("validity end is earlier than the current slot")); +} + +#[test] +fn send_and_receive_async_message_without_init_gas() { + let mut exec_cfg = ExecutionConfig::default(); + exec_cfg.gas_costs.max_instance_cost = 4000000; + + let finalized_waitpoint = WaitPoint::new(); + let mut foreign_controllers = ExecutionForeignControllers::new_with_mocks(); + selector_boilerplate(&mut foreign_controllers.selector_controller); + foreign_controllers + .selector_controller + .set_expectations(|selector_controller| { + selector_controller + .expect_get_producer() + .returning(move |_| { + Ok(Address::from_public_key( + &KeyPair::from_str(TEST_SK_1).unwrap().get_public_key(), + )) + }); + }); + + foreign_controllers + .ledger_controller + .set_expectations(|ledger_controller| { + ledger_controller + .expect_get_balance() + .returning(move |_| Some(Amount::from_str("100").unwrap())); + + ledger_controller + .expect_entry_exists() + .times(2) + .returning(move |_| false); + + ledger_controller + .expect_entry_exists() + .returning(move |_| true); + }); + let saved_bytecode = Arc::new(RwLock::new(None)); + let finalized_waitpoint_trigger_handle = finalized_waitpoint.get_trigger_handle(); + + // Expected message from SC: send_message.ts (see massa unit tests src repo) + foreign_controllers + .final_state + .write() + .expect_finalize() + .times(1) + .with(predicate::eq(Slot::new(1, 0)), predicate::always()) + .returning(move |_, changes| { + assert_eq!( + changes.async_pool_changes, + AsyncPoolChanges(BTreeMap::new()) + ); + finalized_waitpoint_trigger_handle.trigger(); + }); + + final_state_boilerplate( + &mut foreign_controllers.final_state, + foreign_controllers.db.clone(), + &foreign_controllers.selector_controller, + &mut foreign_controllers.ledger_controller, + Some(saved_bytecode), + None, + None, + ); + let mut universe = ExecutionTestUniverse::new(foreign_controllers, exec_cfg.clone()); + + // load bytecodes + universe.deploy_bytecode_block( + &KeyPair::from_str(TEST_SK_1).unwrap(), + Slot::new(1, 0), + include_bytes!("./wasm/send_message.wasm"), + include_bytes!("./wasm/receive_message.wasm"), + ); + println!("waiting for finalized"); + finalized_waitpoint.wait(); + + // retrieve events emitted by smart contracts + let events = universe + .module_controller + .get_filtered_sc_output_event(EventFilter { + start: Some(Slot::new(1, 0)), + end: Some(Slot::new(1, 1)), + ..Default::default() + }); + // match the events + assert!(events.len() == 1, "One event was expected"); + assert!(events[0] + .data + .contains("max gas is lower than the minimum instance cost")); +} + #[test] fn cancel_async_message() { let exec_cfg = ExecutionConfig::default(); diff --git a/massa-execution-worker/src/tests/wasm/send_message_expired.wasm b/massa-execution-worker/src/tests/wasm/send_message_expired.wasm new file mode 100644 index 00000000000..edfcc3c351f Binary files /dev/null and b/massa-execution-worker/src/tests/wasm/send_message_expired.wasm differ diff --git a/massa-execution-worker/src/tests/wasm/send_message_expired_2.wasm b/massa-execution-worker/src/tests/wasm/send_message_expired_2.wasm new file mode 100644 index 00000000000..6ada5cee98c Binary files /dev/null and b/massa-execution-worker/src/tests/wasm/send_message_expired_2.wasm differ