Skip to content

Commit

Permalink
Improve async message checks (#4706)
Browse files Browse the repository at this point in the history
* Improve async message checks

* Change checks for async messages

* Add unit tests
  • Loading branch information
Leo-Besancon authored Jun 25, 2024
1 parent 9565630 commit 77544e2
Show file tree
Hide file tree
Showing 5 changed files with 292 additions and 2 deletions.
13 changes: 11 additions & 2 deletions massa-execution-worker/src/execution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
14 changes: 14 additions & 0 deletions massa-execution-worker/src/interface_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down
267 changes: 267 additions & 0 deletions massa-execution-worker/src/tests/scenarios_mandatories.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Binary file not shown.
Binary file not shown.

0 comments on commit 77544e2

Please sign in to comment.