From 71c29bee93801b1a2a9389b80ecf65884519eeef Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 13 Jul 2023 15:45:32 +0300 Subject: [PATCH 01/10] testing framework - execute step and handle result --- ...dder_mandos_constructed_with_calls_test.rs | 8 +- ...er_mandos_constructed_with_results_test.rs | 60 ------ .../multisig/tests/multisig_rust_test_v2.rs | 143 ++++++------- .../scenario/src/facade/debugger_backend.rs | 8 +- .../src/facade/scenario_world_runner.rs | 4 +- .../src/facade/scenario_world_steps.rs | 202 +++++++++++++++++- .../model/step/into_blockchain_call.rs | 4 +- .../src/scenario/model/step/step_enum.rs | 2 +- .../src/scenario/model/step/step_sc_call.rs | 70 +++++- .../src/scenario/model/step/step_sc_deploy.rs | 8 +- .../src/scenario/model/step/step_sc_query.rs | 19 +- .../src/scenario/model/step/typed_sc_call.rs | 40 +++- .../scenario/model/step/typed_sc_deploy.rs | 18 +- .../src/scenario/model/step/typed_sc_query.rs | 25 ++- .../src/scenario/model/transaction.rs | 2 + .../scenario/model/transaction/tx_error.rs | 21 ++ .../model/transaction/typed_response.rs | 34 +++ framework/scenario/src/scenario/run_list.rs | 6 +- .../src/scenario/run_trace/scenario_trace.rs | 4 +- .../scenario/run_trace/scenario_trace_file.rs | 4 +- .../scenario/src/scenario/run_vm/sc_call.rs | 9 + .../scenario/src/scenario/run_vm/sc_deploy.rs | 9 + .../scenario/src/scenario/run_vm/sc_query.rs | 35 +-- .../scenario/src/scenario/run_vm/vm_runner.rs | 8 +- .../scenario/src/scenario/scenario_runner.rs | 7 +- framework/snippets/src/interactor_sc_call.rs | 3 +- .../snippets/src/interactor_sc_deploy.rs | 6 +- framework/snippets/src/interactor_tx_spec.rs | 3 +- 28 files changed, 536 insertions(+), 226 deletions(-) delete mode 100644 contracts/examples/adder/tests/adder_mandos_constructed_with_results_test.rs create mode 100644 framework/scenario/src/scenario/model/transaction/tx_error.rs create mode 100644 framework/scenario/src/scenario/model/transaction/typed_response.rs diff --git a/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs b/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs index 1fbd0735ef..5a565c42cb 100644 --- a/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs +++ b/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs @@ -24,18 +24,22 @@ fn adder_scenario_constructed_raw() { .put_account(owner_address, Account::new().nonce(1)) .new_address(owner_address, 1, "sc:adder"), ) - .sc_deploy_step( + .sc_deploy_use_new_address( ScDeployStep::new() .from(owner_address) .contract_code("file:output/adder.wasm", &ic) .call(adder_contract.init(5u32)) .gas_limit("5,000,000") .expect(TxExpect::ok().no_result()), + |new_address| { + assert_eq!(new_address, adder_contract.to_address()); + }, ) .sc_query_step( ScQueryStep::new() .to(&adder_contract) - .call_expect(adder_contract.sum(), SingleValue::from(BigUint::from(5u32))), + .call(adder_contract.sum()) + .expect_value(SingleValue::from(BigUint::from(5u32))), ) .sc_call_step( ScCallStep::new() diff --git a/contracts/examples/adder/tests/adder_mandos_constructed_with_results_test.rs b/contracts/examples/adder/tests/adder_mandos_constructed_with_results_test.rs deleted file mode 100644 index c61e5206ea..0000000000 --- a/contracts/examples/adder/tests/adder_mandos_constructed_with_results_test.rs +++ /dev/null @@ -1,60 +0,0 @@ -use adder::*; -use multiversx_sc::storage::mappers::SingleValue; -use multiversx_sc_scenario::{api::StaticApi, num_bigint::BigUint, scenario_model::*, *}; // TODO: clean up imports - -fn world() -> ScenarioWorld { - let mut blockchain = ScenarioWorld::new(); - blockchain.set_current_dir_from_workspace("contracts/examples/adder"); - - blockchain.register_contract("file:output/adder.wasm", adder::ContractBuilder); - blockchain -} - -#[test] -fn adder_mandos_constructed() { - let mut world = world(); - let ic = world.interpreter_context(); - - let owner_address = "address:owner"; - let mut adder_contract = ContractInfo::>::new("sc:adder"); - - world.start_trace().set_state_step( - SetStateStep::new() - .put_account(owner_address, Account::new().nonce(1)) - .new_address(owner_address, 1, &adder_contract), - ); - - // deploy - let (new_address, ()) = adder_contract - .init(5u32) - .into_blockchain_call() - .from(owner_address) - .contract_code("file:output/adder.wasm", &ic) - .gas_limit("5,000,000") - .expect(TxExpect::ok().no_result()) - .execute(&mut world); - assert_eq!(new_address, adder_contract.to_address()); - - // query, gets saved in the trace - let result: SingleValue = adder_contract.sum().into_vm_query().execute(&mut world); - assert_eq!(result.into(), BigUint::from(5u32)); - - let () = adder_contract - .add(3u32) - .into_blockchain_call() - .from(owner_address) - .gas_limit(5000000) - .expect(TxExpect::ok().no_result()) - .execute(&mut world); - - world.check_state_step( - CheckStateStep::new() - .put_account(owner_address, CheckAccount::new()) - .put_account( - &adder_contract, - CheckAccount::new().check_storage("str:sum", "8"), - ), - ); - - world.write_scenario_trace("trace2.scen.json"); -} diff --git a/contracts/examples/multisig/tests/multisig_rust_test_v2.rs b/contracts/examples/multisig/tests/multisig_rust_test_v2.rs index 6dfb72dcaa..2580719cad 100644 --- a/contracts/examples/multisig/tests/multisig_rust_test_v2.rs +++ b/contracts/examples/multisig/tests/multisig_rust_test_v2.rs @@ -32,13 +32,15 @@ fn basic_setup_test() { let mut test = MultisigTestState::setup(); test.multisig_deploy(); - let board_members: MultiValueVec
= test - .multisig - .get_all_board_members() - .into_blockchain_call() - .from(&test.alice) - .expect(TxExpect::ok()) - .execute(&mut test.world); + let mut board_members = MultiValueVec::
::new(); + test.world.sc_call_use_result( + ScCallStep::new() + .from(&test.alice) + .call(test.multisig.get_all_board_members()), + |tr| { + board_members = tr.result.unwrap(); + }, + ); let expected_board_members: Vec<_> = [ test.alice.to_address(), @@ -129,15 +131,14 @@ impl MultisigTestState { .into(); let ic = &self.world.interpreter_context(); - let (_new_address, ()) = self - .multisig - .init(2u32, board) - .into_blockchain_call() - .from(self.owner.clone()) - .contract_code("file:output/multisig.wasm", ic) - .gas_limit("5,000,000") - .expect(TxExpect::ok().no_result()) - .execute(&mut self.world); + self.world.sc_deploy_step( + ScDeployStep::new() + .from(self.owner.clone()) + .contract_code("file:output/multisig.wasm", ic) + .call(self.multisig.init(2u32, board)) + .gas_limit("5,000,000") + .expect(TxExpect::ok().no_result()), + ); self } @@ -149,29 +150,25 @@ impl MultisigTestState { .put_account(&self.owner, Account::new().nonce(1)) .new_address(&self.owner, 1, &self.adder), ); - - let (_new_address, ()) = self - .adder - .init(0u64) - .into_blockchain_call() - .from(&self.owner) - .contract_code("file:test-contracts/adder.wasm", ic) - .gas_limit("5,000,000") - .expect(TxExpect::ok().no_result()) - .execute(&mut self.world); + self.world.sc_deploy_step( + ScDeployStep::new() + .from(self.owner.clone()) + .contract_code("file:test-contracts/adder.wasm", ic) + .call(self.adder.init(0u64)) + .gas_limit("5,000,000") + .expect(TxExpect::ok().no_result()), + ); self } fn multisig_sign(&mut self, action_id: usize, signer: &Address) { - let () = self - .multisig - .sign(action_id) - .into_blockchain_call() - .from(signer) - .gas_limit("5,000,000") - .expect(TxExpect::ok().no_result()) - .execute(&mut self.world); + self.world.sc_call_step( + ScCallStep::new() + .from(signer) + .call(self.multisig.sign(action_id)) + .expect(TxExpect::ok().no_result()), + ); } fn multisig_sign_multiple(&mut self, action_id: usize, signers: &[&Address]) { @@ -181,15 +178,14 @@ impl MultisigTestState { } fn multisig_perform(&mut self, action_id: usize, caller: &Address) -> Option
{ - let result: OptionalValue
= self - .multisig - .perform_action_endpoint(action_id) - .into_blockchain_call() - .from(caller) - .gas_limit("5,000,000") - .expect(TxExpect::ok()) - .execute(&mut self.world); - result.into_option() + let output: OptionalValue
= self.world.sc_call_get_result( + ScCallStep::new() + .from(caller) + .call(self.multisig.perform_action_endpoint(action_id)) + .gas_limit("5,000,000") + .expect(TxExpect::ok()), + ); + output.into_option() } fn multisig_sign_and_perform( @@ -216,18 +212,18 @@ impl MultisigTestState { )); let adder_init_args = self.adder.init(0u64).arg_buffer.into_multi_value_encoded(); - self.multisig - .propose_sc_deploy_from_source( - 0u64, - &self.adder, - CodeMetadata::DEFAULT, - adder_init_args, - ) - .into_blockchain_call() - .from(caller) - .gas_limit("5,000,000") - .expect(TxExpect::ok()) - .execute(&mut self.world) + self.world.sc_call_get_result( + ScCallStep::new() + .from(caller) + .call(self.multisig.propose_sc_deploy_from_source( + 0u64, + &self.adder, + CodeMetadata::DEFAULT, + adder_init_args, + )) + .gas_limit("5,000,000") + .expect(TxExpect::ok()), + ) } fn multisig_call_adder_add(&mut self, number: BigUint, caller: &Address, signers: &[&Address]) { @@ -237,29 +233,26 @@ impl MultisigTestState { fn multisig_propose_adder_add(&mut self, number: BigUint, caller: &Address) -> usize { let adder_call = self.adder.add(number); - self.multisig - .propose_transfer_execute( - &self.adder.to_address(), - 0u32, - adder_call.endpoint_name, - adder_call.arg_buffer.into_multi_value_encoded(), - ) - .into_blockchain_call() - .from(caller) - .gas_limit("5,000,000") - .expect(TxExpect::ok()) - .execute(&mut self.world) + self.world.sc_call_get_result( + ScCallStep::new() + .from(caller) + .call(self.multisig.propose_transfer_execute( + &self.adder.to_address(), + 0u32, + adder_call.endpoint_name, + adder_call.arg_buffer.into_multi_value_encoded(), + )) + .gas_limit("5,000,000") + .expect(TxExpect::ok()), + ) } fn adder_expect_get_sum(&mut self, expected_sum: BigUint, caller: &Address) -> BigUint { - let value: SingleValue = self - .adder - .sum() - .into_blockchain_call() - .from(caller) - .gas_limit("5,000,000") - .expect(TxExpect::ok().result(&format!("{expected_sum}"))) - .execute(&mut self.world); + let value: SingleValue = self.world.sc_query_get_result( + ScQueryStep::new() + .call(self.adder.sum()) + .expect_value(SingleValue::from(expected_sum)), + ); value.into() } } diff --git a/framework/scenario/src/facade/debugger_backend.rs b/framework/scenario/src/facade/debugger_backend.rs index 21418a5ad0..0c5f4f181a 100644 --- a/framework/scenario/src/facade/debugger_backend.rs +++ b/framework/scenario/src/facade/debugger_backend.rs @@ -28,11 +28,11 @@ impl ScenarioRunner for DebuggerBackend { self.for_each_runner_mut(|runner| runner.run_set_state_step(step)); } - fn run_sc_call_step(&mut self, step: &ScCallStep) { + fn run_sc_call_step(&mut self, step: &mut ScCallStep) { self.for_each_runner_mut(|runner| runner.run_sc_call_step(step)); } - fn run_multi_sc_call_step(&mut self, steps: &[ScCallStep]) { + fn run_multi_sc_call_step(&mut self, steps: &mut [ScCallStep]) { self.for_each_runner_mut(|runner| runner.run_multi_sc_call_step(steps)); } @@ -67,9 +67,9 @@ impl ScenarioRunner for DebuggerBackend { impl DebuggerBackend { pub(super) fn run_scenario_file(&mut self, steps_path: &Path) { - let scenario = scenario::parse_scenario(steps_path); + let mut scenario = scenario::parse_scenario(steps_path); - for step in &scenario.steps { + for step in &mut scenario.steps { match step { Step::ExternalSteps(external_steps_step) => { let parent_path = steps_path.parent().unwrap(); diff --git a/framework/scenario/src/facade/scenario_world_runner.rs b/framework/scenario/src/facade/scenario_world_runner.rs index 8722b1e36e..510b553a0e 100644 --- a/framework/scenario/src/facade/scenario_world_runner.rs +++ b/framework/scenario/src/facade/scenario_world_runner.rs @@ -30,11 +30,11 @@ impl ScenarioRunner for ScenarioWorld { self.for_each_runner_mut(|runner| runner.run_set_state_step(step)); } - fn run_sc_call_step(&mut self, step: &ScCallStep) { + fn run_sc_call_step(&mut self, step: &mut ScCallStep) { self.for_each_runner_mut(|runner| runner.run_sc_call_step(step)); } - fn run_multi_sc_call_step(&mut self, steps: &[ScCallStep]) { + fn run_multi_sc_call_step(&mut self, steps: &mut [ScCallStep]) { self.for_each_runner_mut(|runner| runner.run_multi_sc_call_step(steps)); } diff --git a/framework/scenario/src/facade/scenario_world_steps.rs b/framework/scenario/src/facade/scenario_world_steps.rs index d1a7c4b22b..bb89f90a95 100644 --- a/framework/scenario/src/facade/scenario_world_steps.rs +++ b/framework/scenario/src/facade/scenario_world_steps.rs @@ -21,17 +21,131 @@ impl ScenarioWorld { } /// Adds a SC call step, then executes it. - pub fn sc_call_step(&mut self, step: ScCallStep) -> &mut Self { - self.run_sc_call_step(&step); + pub fn sc_call_step(&mut self, mut sc_call_step: S) -> &mut Self + where + S: AsMut, + { + let base_step = sc_call_step.as_mut(); + self.run_sc_call_step(base_step); + self + } + + pub fn sc_call_use_raw_response( + &mut self, + mut sc_call_step: S, + use_raw_response: F, + ) -> &mut Self + where + S: AsMut, + F: FnOnce(&TxResponse), + { + let base_step = sc_call_step.as_mut(); + self.get_mut_debugger_backend() + .vm_runner + .perform_sc_call_update_results(base_step); + let response = unwrap_response(&base_step.response); + use_raw_response(response); self } + pub fn sc_call_use_result( + &mut self, + mut step: TypedScCall, + use_result: F, + ) -> &mut Self + where + OriginalResult: TopEncodeMulti, + RequestedResult: CodecFrom, + F: FnOnce(TypedResponse), + { + self.get_mut_debugger_backend() + .vm_runner + .perform_sc_call_update_results(&mut step.sc_call_step); + let response = unwrap_response(&step.sc_call_step.response); + let typed_response = TypedResponse::from_raw(response); + use_result(typed_response); + self + } + + pub fn sc_call_get_result( + &mut self, + mut step: TypedScCall, + ) -> RequestedResult + where + OriginalResult: TopEncodeMulti, + RequestedResult: CodecFrom, + { + self.get_mut_debugger_backend() + .vm_runner + .perform_sc_call_update_results(&mut step.sc_call_step); + let response = unwrap_response(&step.sc_call_step.response); + let typed_response = TypedResponse::from_raw(response); + typed_response.result.unwrap() + } + /// Adds a SC query step, then executes it. - pub fn sc_query_step(&mut self, step: ScQueryStep) -> &mut Self { - self.run_sc_query_step(&step); + pub fn sc_query_step(&mut self, mut sc_call_step: S) -> &mut Self + where + S: AsMut, + { + let base_step: &mut ScQueryStep = sc_call_step.as_mut(); + self.run_sc_query_step(base_step); + self + } + + pub fn sc_query_use_raw_response( + &mut self, + mut sc_call_step: S, + use_raw_response: F, + ) -> &mut Self + where + S: AsMut, + F: FnOnce(&TxResponse), + { + let base_step = sc_call_step.as_mut(); + self.get_mut_debugger_backend() + .vm_runner + .perform_sc_query_update_results(base_step); + let response = unwrap_response(&base_step.response); + use_raw_response(response); + self + } + + pub fn sc_query_use_result( + &mut self, + mut step: TypedScQuery, + use_result: F, + ) -> &mut Self + where + OriginalResult: TopEncodeMulti, + RequestedResult: CodecFrom, + F: FnOnce(TypedResponse), + { + self.get_mut_debugger_backend() + .vm_runner + .perform_sc_query_update_results(&mut step.sc_query_step); + let response = unwrap_response(&step.sc_query_step.response); + let typed_response = TypedResponse::from_raw(response); + use_result(typed_response); self } + pub fn sc_query_get_result( + &mut self, + mut step: TypedScQuery, + ) -> RequestedResult + where + OriginalResult: TopEncodeMulti, + RequestedResult: CodecFrom, + { + self.get_mut_debugger_backend() + .vm_runner + .perform_sc_query_update_results(&mut step.sc_query_step); + let response = unwrap_response(&step.sc_query_step.response); + let typed_response = TypedResponse::from_raw(response); + typed_response.result.unwrap() + } + /// Performs a SC query to a contract, leaves no scenario trace behind. /// /// Meant to be used for the test to investigate the state of the contract. @@ -43,15 +157,76 @@ impl ScenarioWorld { RequestedResult: CodecFrom, { let vm_runner = &mut self.get_mut_debugger_backend().vm_runner; - let sc_query_step = ScQueryStep::new().call(contract_call); - let tx_result = vm_runner.perform_sc_query(&sc_query_step); + let typed_sc_query = ScQueryStep::new().call(contract_call); + let tx_result = vm_runner.perform_sc_query(&typed_sc_query.sc_query_step); let mut raw_result = tx_result.result_values; RequestedResult::multi_decode_or_handle_err(&mut raw_result, PanicErrorHandler).unwrap() } /// Adds a SC deploy step, then executes it. - pub fn sc_deploy_step(&mut self, step: ScDeployStep) -> &mut Self { - self.run_sc_deploy_step(&step); + pub fn sc_deploy_step(&mut self, mut step: S) -> &mut Self + where + S: AsMut, + { + let base_step = step.as_mut(); + self.run_sc_deploy_step(base_step); + self + } + + pub fn sc_deploy_use_raw_response( + &mut self, + mut sc_deploy_step: S, + use_raw_response: F, + ) -> &mut Self + where + S: AsMut, + F: FnOnce(&TxResponse), + { + let base_step = sc_deploy_step.as_mut(); + self.get_mut_debugger_backend() + .vm_runner + .perform_sc_deploy_update_results(base_step); + let response = unwrap_response(&base_step.response); + use_raw_response(response); + self + } + + pub fn sc_deploy_use_result( + &mut self, + mut step: TypedScDeploy, + use_result: F, + ) -> &mut Self + where + OriginalResult: TopEncodeMulti, + RequestedResult: CodecFrom, + F: FnOnce(Address, TypedResponse), + { + self.get_mut_debugger_backend() + .vm_runner + .perform_sc_deploy_update_results(&mut step.sc_deploy_step); + let response = unwrap_response(&step.sc_deploy_step.response); + let new_address = unwrap_new_address(response); + let typed_response = TypedResponse::from_raw(response); + use_result(new_address, typed_response); + self + } + + pub fn sc_deploy_use_new_address( + &mut self, + mut sc_deploy_step: S, + use_new_address: F, + ) -> &mut Self + where + S: AsMut, + F: FnOnce(Address), + { + let base_step = sc_deploy_step.as_mut(); + self.get_mut_debugger_backend() + .vm_runner + .perform_sc_deploy_update_results(base_step); + let response = unwrap_response(&base_step.response); + let new_address = unwrap_new_address(response); + use_new_address(new_address); self } @@ -187,3 +362,14 @@ impl ScenarioWorld { self.dump_state_step() } } + +fn unwrap_response(opt_response: &Option) -> &TxResponse { + opt_response.as_ref().expect("response not processed") +} + +fn unwrap_new_address(response: &TxResponse) -> Address { + response + .new_deployed_address + .clone() + .expect("missing new address after deploy") +} diff --git a/framework/scenario/src/scenario/model/step/into_blockchain_call.rs b/framework/scenario/src/scenario/model/step/into_blockchain_call.rs index 0e3e5386c6..0a91bfd781 100644 --- a/framework/scenario/src/scenario/model/step/into_blockchain_call.rs +++ b/framework/scenario/src/scenario/model/step/into_blockchain_call.rs @@ -44,7 +44,7 @@ impl IntoBlockchainCall for ContractDeploy; fn into_blockchain_call(self) -> Self::BlockchainCall { - ScDeployStep::new().call(self).into() + ScDeployStep::new().call(self) } } @@ -61,6 +61,6 @@ where { type VMQuery = TypedScQuery; fn into_vm_query(self) -> Self::VMQuery { - ScQueryStep::default().call(self).into() + ScQueryStep::default().call(self) } } diff --git a/framework/scenario/src/scenario/model/step/step_enum.rs b/framework/scenario/src/scenario/model/step/step_enum.rs index ad6cde47f4..2d3082c1b9 100644 --- a/framework/scenario/src/scenario/model/step/step_enum.rs +++ b/framework/scenario/src/scenario/model/step/step_enum.rs @@ -19,7 +19,7 @@ pub struct ExternalStepsStep { pub path: String, } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum Step { ExternalSteps(ExternalStepsStep), SetState(SetStateStep), diff --git a/framework/scenario/src/scenario/model/step/step_sc_call.rs b/framework/scenario/src/scenario/model/step/step_sc_call.rs index bb5ac4dcbd..c8c1957da4 100644 --- a/framework/scenario/src/scenario/model/step/step_sc_call.rs +++ b/framework/scenario/src/scenario/model/step/step_sc_call.rs @@ -11,7 +11,9 @@ use crate::multiversx_sc::{ types::{ContractCall, ManagedArgBuffer}, }; -#[derive(Debug, Default, Clone)] +use super::TypedScCall; + +#[derive(Default)] pub struct ScCallStep { pub id: String, pub tx_id: Option, @@ -20,6 +22,7 @@ pub struct ScCallStep { pub tx: Box, pub expect: Option, pub response: Option, + pub response_handlers: Vec>, } impl ScCallStep { @@ -108,7 +111,7 @@ impl ScCallStep { /// - "to" /// - "function" /// - "arguments" - pub fn call(mut self, contract_call: CC) -> Self + pub fn call(mut self, contract_call: CC) -> TypedScCall where CC: ContractCall, { @@ -120,7 +123,7 @@ impl ScCallStep { for arg in scenario_args { self = self.argument(arg.as_str()); } - self + self.into() } /// Sets following fields based on the smart contract proxy: @@ -131,16 +134,38 @@ impl ScCallStep { /// - "out" /// - "status" set to 0 pub fn call_expect( - mut self, + self, contract_call: CC, - expect_value: ExpectedResult, - ) -> Self + expected_value: ExpectedResult, + ) -> TypedScCall where CC: ContractCall, ExpectedResult: CodecFrom + TopEncodeMulti, { - self = self.call(contract_call); - self = self.expect(format_expect(expect_value)); + let typed = self.call(contract_call); + typed.expect_value(expected_value) + } + + pub fn trigger_handler(&mut self) { + let response = self.response.clone().expect("response not yet ready"); + let mut current_handlers = std::mem::take(&mut self.response_handlers); + for handler in current_handlers.iter_mut() { + handler(&response); + } + } + + pub(crate) fn push_response_handler(&mut self, f: F) + where + F: FnMut(&TxResponse) + 'static, + { + self.response_handlers.push(Box::new(f)); + } + + pub fn with_raw_response(mut self, f: F) -> Self + where + F: FnMut(&TxResponse) + 'static, + { + self.push_response_handler(f); self } } @@ -197,3 +222,32 @@ pub(super) fn format_expect(t: T) -> TxExpect { } expect } + +impl Clone for ScCallStep { + fn clone(&self) -> Self { + Self { + id: self.id.clone(), + tx_id: self.tx_id.clone(), + explicit_tx_hash: self.explicit_tx_hash.clone(), + comment: self.comment.clone(), + tx: self.tx.clone(), + expect: self.expect.clone(), + response: self.response.clone(), + response_handlers: Vec::new(), + } + } +} + +impl std::fmt::Debug for ScCallStep { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ScCallStep") + .field("id", &self.id) + .field("tx_id", &self.tx_id) + .field("explicit_tx_hash", &self.explicit_tx_hash) + .field("comment", &self.comment) + .field("tx", &self.tx) + .field("expect", &self.expect) + .field("response", &self.response) + .finish() + } +} diff --git a/framework/scenario/src/scenario/model/step/step_sc_deploy.rs b/framework/scenario/src/scenario/model/step/step_sc_deploy.rs index 6869dd7820..a3e1cb90ff 100644 --- a/framework/scenario/src/scenario/model/step/step_sc_deploy.rs +++ b/framework/scenario/src/scenario/model/step/step_sc_deploy.rs @@ -12,7 +12,7 @@ use crate::{ use crate::multiversx_sc::types::{CodeMetadata, ContractDeploy}; -use super::convert_call_args; +use super::{convert_call_args, TypedScDeploy}; #[derive(Debug, Default, Clone)] pub struct ScDeployStep { @@ -84,12 +84,12 @@ impl ScDeployStep { pub fn call( mut self, contract_deploy: ContractDeploy, - ) -> Self { + ) -> TypedScDeploy { let (_, mandos_args) = process_contract_deploy(contract_deploy); for arg in mandos_args { self = self.argument(arg.as_str()); } - self + self.into() } } @@ -102,7 +102,7 @@ impl AsMut for ScDeployStep { /// Extracts /// - (optional) recipient (needed for contract upgrade, not yet used); /// - the arguments. -fn process_contract_deploy( +pub(crate) fn process_contract_deploy( contract_deploy: ContractDeploy, ) -> (Option, Vec) { let to_str = contract_deploy diff --git a/framework/scenario/src/scenario/model/step/step_sc_query.rs b/framework/scenario/src/scenario/model/step/step_sc_query.rs index b1d1891282..4e1de5f789 100644 --- a/framework/scenario/src/scenario/model/step/step_sc_query.rs +++ b/framework/scenario/src/scenario/model/step/step_sc_query.rs @@ -7,9 +7,10 @@ use crate::{ types::ContractCall, }, scenario::model::{AddressValue, BytesValue, TxExpect, TxQuery}, + scenario_model::TxResponse, }; -use super::{format_expect, process_contract_call}; +use super::{process_contract_call, TypedScQuery}; #[derive(Debug, Default, Clone)] pub struct ScQueryStep { @@ -19,6 +20,7 @@ pub struct ScQueryStep { pub comment: Option, pub tx: Box, pub expect: Option, + pub response: Option, } impl ScQueryStep { @@ -53,7 +55,7 @@ impl ScQueryStep { /// - "to" /// - "function" /// - "arguments" - pub fn call(mut self, contract_call: CC) -> Self + pub fn call(mut self, contract_call: CC) -> TypedScQuery where CC: ContractCall, { @@ -63,7 +65,7 @@ impl ScQueryStep { for arg in mandos_args { self = self.argument(arg.as_str()); } - self + self.into() } /// Sets following fields based on the smart contract proxy: @@ -74,16 +76,15 @@ impl ScQueryStep { /// - "out" /// - "status" set to 0 pub fn call_expect( - mut self, + self, contract_call: CC, - expect_value: ExpectedResult, - ) -> Self + expected_value: ExpectedResult, + ) -> TypedScQuery where CC: ContractCall, ExpectedResult: CodecFrom + TopEncodeMulti, { - self = self.call(contract_call); - self = self.expect(format_expect(expect_value)); - self + let typed = self.call(contract_call); + typed.expect_value(expected_value) } } diff --git a/framework/scenario/src/scenario/model/step/typed_sc_call.rs b/framework/scenario/src/scenario/model/step/typed_sc_call.rs index 7459c5ce88..83fd48128e 100644 --- a/framework/scenario/src/scenario/model/step/typed_sc_call.rs +++ b/framework/scenario/src/scenario/model/step/typed_sc_call.rs @@ -6,10 +6,10 @@ use crate::multiversx_sc::codec::{CodecFrom, TopEncodeMulti}; use crate::{ scenario::model::{AddressValue, U64Value}, - scenario_model::{BigUintValue, BytesValue, TxResponseStatus, TxExpect, TxResponse}, + scenario_model::{BigUintValue, BytesValue, TxExpect, TxResponse, TxResponseStatus}, }; -use super::ScCallStep; +use super::{format_expect, ScCallStep}; /// `SCCallStep` with explicit return type. #[derive(Default, Debug)] @@ -32,7 +32,10 @@ impl TypedScCall { } pub fn response(&self) -> &TxResponse { - self.sc_call_step.response.as_ref().unwrap() + self.sc_call_step + .response + .as_ref() + .expect("SC call result not yet available") } pub fn from(mut self, address: A) -> Self @@ -97,6 +100,37 @@ impl TypedScCall { self.sc_call_step = self.sc_call_step.expect(expect); self } + + /// Shorthand for creating a tx expect with status "Ok" and the given value. + /// + /// The given value is type-checked agains the tx return type. + pub fn expect_value(self, expected_value: ExpectedResult) -> Self + where + OriginalResult: TopEncodeMulti, + ExpectedResult: CodecFrom + TopEncodeMulti, + { + self.expect(format_expect(expected_value)) + } + + pub fn with_result_ok(mut self, mut f: F) -> Self + where + OriginalResult: TopEncodeMulti, + RequestedResult: CodecFrom, + F: FnMut(RequestedResult) + 'static, + { + self.sc_call_step.push_response_handler(move |response| { + assert!( + response.tx_error.is_success(), + "successful transaction expected" + ); + + let mut raw_result = response.out.clone(); + let Ok(decoded) = + RequestedResult::multi_decode_or_handle_err(&mut raw_result, PanicErrorHandler); + f(decoded); + }); + self + } } impl AsMut for TypedScCall { diff --git a/framework/scenario/src/scenario/model/step/typed_sc_deploy.rs b/framework/scenario/src/scenario/model/step/typed_sc_deploy.rs index 9b0b9d5717..aaa8f065ce 100644 --- a/framework/scenario/src/scenario/model/step/typed_sc_deploy.rs +++ b/framework/scenario/src/scenario/model/step/typed_sc_deploy.rs @@ -1,19 +1,20 @@ use std::marker::PhantomData; -use multiversx_sc::codec::PanicErrorHandler; +use multiversx_sc::{codec::PanicErrorHandler, types::ContractDeploy}; use crate::{ + api::StaticApi, multiversx_sc::{ codec::{CodecFrom, TopEncodeMulti}, types::{Address, CodeMetadata}, }, scenario_format::interpret_trait::InterpreterContext, - scenario_model::{TxResponse, TxResponseStatus}, + scenario_model::{BytesValue, TxResponse, TxResponseStatus}, }; use crate::scenario::model::{AddressValue, BigUintValue, TxExpect, U64Value}; -use super::ScDeployStep; +use super::{process_contract_deploy, ScDeployStep}; /// `ScDeployStep` with explicit return type. #[derive(Default, Debug)] @@ -77,6 +78,17 @@ impl TypedScDeploy { self.sc_deploy_step = self.sc_deploy_step.expect(expect); self } + + /// Sets following fields based on the smart contract proxy: + /// - "function" + /// - "arguments" + pub fn call(mut self, contract_deploy: ContractDeploy) -> Self { + let (_, mandos_args) = process_contract_deploy(contract_deploy); + for arg in mandos_args { + self.sc_deploy_step.tx.arguments.push(BytesValue::from(arg)); + } + self + } } impl AsMut for TypedScDeploy { diff --git a/framework/scenario/src/scenario/model/step/typed_sc_query.rs b/framework/scenario/src/scenario/model/step/typed_sc_query.rs index 8b2cf66e38..11459f6915 100644 --- a/framework/scenario/src/scenario/model/step/typed_sc_query.rs +++ b/framework/scenario/src/scenario/model/step/typed_sc_query.rs @@ -4,7 +4,7 @@ use crate::multiversx_sc::codec::{CodecFrom, TopEncodeMulti}; use crate::scenario::model::{AddressValue, BytesValue, TxExpect}; -use super::ScQueryStep; +use super::{format_expect, ScQueryStep}; #[derive(Debug, Default)] pub struct TypedScQuery { @@ -53,6 +53,29 @@ impl TypedScQuery { self.sc_query_step.expect = Some(expect); self } + + /// Shorthand for creating a tx expect with status "Ok" and the given value. + /// + /// The given value is type-checked agains the tx return type. + pub fn expect_value(self, expected_value: ExpectedResult) -> Self + where + OriginalResult: TopEncodeMulti, + ExpectedResult: CodecFrom + TopEncodeMulti, + { + self.expect(format_expect(expected_value)) + } +} + +impl AsMut for ScQueryStep { + fn as_mut(&mut self) -> &mut ScQueryStep { + self + } +} + +impl AsMut for TypedScQuery { + fn as_mut(&mut self) -> &mut ScQueryStep { + &mut self.sc_query_step + } } /// Helps with syntax. Allows the `TypedScQuery` to call the `execute` operation directly. diff --git a/framework/scenario/src/scenario/model/transaction.rs b/framework/scenario/src/scenario/model/transaction.rs index 1b1abb0050..3ffa8a542d 100644 --- a/framework/scenario/src/scenario/model/transaction.rs +++ b/framework/scenario/src/scenario/model/transaction.rs @@ -11,6 +11,7 @@ mod tx_response; mod tx_response_status; mod tx_transfer; mod tx_validator_reward; +mod typed_response; pub use self::log::*; pub use log_check::*; @@ -24,3 +25,4 @@ pub use tx_response::TxResponse; pub use tx_response_status::TxResponseStatus; pub use tx_transfer::*; pub use tx_validator_reward::*; +pub use typed_response::TypedResponse; diff --git a/framework/scenario/src/scenario/model/transaction/tx_error.rs b/framework/scenario/src/scenario/model/transaction/tx_error.rs new file mode 100644 index 0000000000..d0a2b5a3e4 --- /dev/null +++ b/framework/scenario/src/scenario/model/transaction/tx_error.rs @@ -0,0 +1,21 @@ +#[derive(Debug, Default, Clone)] +pub struct TxResponseStatus { + pub status: u64, + pub message: String, +} + +impl TxResponseStatus { + pub fn is_success(&self) -> bool { + self.status == 0 + } +} + +impl std::fmt::Display for TxResponseStatus { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if self.is_success() { + write!(f, "transaction successful") + } else { + write!(f, "transaction error: {}", self.message) + } + } +} diff --git a/framework/scenario/src/scenario/model/transaction/typed_response.rs b/framework/scenario/src/scenario/model/transaction/typed_response.rs new file mode 100644 index 0000000000..1a1086f717 --- /dev/null +++ b/framework/scenario/src/scenario/model/transaction/typed_response.rs @@ -0,0 +1,34 @@ +use super::{Log, TxResponse, TxResponseStatus}; +use multiversx_sc::codec::{PanicErrorHandler, TopDecodeMulti}; + +pub struct TypedResponse +where + T: TopDecodeMulti, +{ + pub result: Result, + pub logs: Vec, + pub gas: u64, + pub refund: u64, +} + +impl TypedResponse +where + T: TopDecodeMulti, +{ + pub fn from_raw(raw_response: &TxResponse) -> Self { + let result: Result = if raw_response.tx_error.is_success() { + let mut result_raw = raw_response.out.clone(); + let Ok(decoded) = T::multi_decode_or_handle_err(&mut result_raw, PanicErrorHandler); + Ok(decoded) + } else { + Err(raw_response.tx_error.clone()) + }; + + TypedResponse { + result, + logs: raw_response.logs.clone(), + gas: raw_response.gas, + refund: raw_response.refund, + } + } +} diff --git a/framework/scenario/src/scenario/run_list.rs b/framework/scenario/src/scenario/run_list.rs index 9f6fc2c926..90634b6fed 100644 --- a/framework/scenario/src/scenario/run_list.rs +++ b/framework/scenario/src/scenario/run_list.rs @@ -40,15 +40,15 @@ impl ScenarioRunner for ScenarioRunnerList { } } - fn run_sc_call_step(&mut self, step: &ScCallStep) { + fn run_sc_call_step(&mut self, step: &mut ScCallStep) { for runner in self.list.iter_mut() { runner.run_sc_call_step(step); } } - fn run_multi_sc_call_step(&mut self, steps: &[ScCallStep]) { + fn run_multi_sc_call_step(&mut self, steps: &mut [ScCallStep]) { for runner in self.list.iter_mut() { - for step in steps { + for step in steps.iter_mut() { runner.run_sc_call_step(step); } } diff --git a/framework/scenario/src/scenario/run_trace/scenario_trace.rs b/framework/scenario/src/scenario/run_trace/scenario_trace.rs index 24c5354321..1955489bb8 100644 --- a/framework/scenario/src/scenario/run_trace/scenario_trace.rs +++ b/framework/scenario/src/scenario/run_trace/scenario_trace.rs @@ -72,13 +72,13 @@ impl ScenarioRunner for ScenarioTrace { self.scenario_trace.steps.push(Step::SetState(step.clone())); } - fn run_sc_call_step(&mut self, step: &ScCallStep) { + fn run_sc_call_step(&mut self, step: &mut ScCallStep) { self.process_address_value(&step.tx.from); self.process_address_value(&step.tx.to); self.scenario_trace.steps.push(Step::ScCall(step.clone())); } - fn run_multi_sc_call_step(&mut self, steps: &[ScCallStep]) { + fn run_multi_sc_call_step(&mut self, steps: &mut [ScCallStep]) { for step in steps { self.process_address_value(&step.tx.from); self.process_address_value(&step.tx.to); diff --git a/framework/scenario/src/scenario/run_trace/scenario_trace_file.rs b/framework/scenario/src/scenario/run_trace/scenario_trace_file.rs index 67ee4b634b..a3ce99570e 100644 --- a/framework/scenario/src/scenario/run_trace/scenario_trace_file.rs +++ b/framework/scenario/src/scenario/run_trace/scenario_trace_file.rs @@ -37,11 +37,11 @@ impl ScenarioRunner for ScenarioTraceFile { self.with_tracer(|tracer| tracer.run_set_state_step(step)); } - fn run_sc_call_step(&mut self, step: &ScCallStep) { + fn run_sc_call_step(&mut self, step: &mut ScCallStep) { self.with_tracer(|tracer| tracer.run_sc_call_step(step)); } - fn run_multi_sc_call_step(&mut self, steps: &[ScCallStep]) { + fn run_multi_sc_call_step(&mut self, steps: &mut [ScCallStep]) { self.with_tracer(|tracer| tracer.run_multi_sc_call_step(steps)); } diff --git a/framework/scenario/src/scenario/run_vm/sc_call.rs b/framework/scenario/src/scenario/run_vm/sc_call.rs index 7103b69ed5..5ff417dd9e 100644 --- a/framework/scenario/src/scenario/run_vm/sc_call.rs +++ b/framework/scenario/src/scenario/run_vm/sc_call.rs @@ -1,6 +1,7 @@ use crate::{ multiversx_sc::codec::{CodecFrom, PanicErrorHandler, TopEncodeMulti}, scenario::model::{ScCallStep, TxESDT, TypedScCall}, + scenario_model::TxResponse, }; use multiversx_chain_vm::{ @@ -17,6 +18,14 @@ impl ScenarioVMRunner { self.perform_sc_call_lambda_and_check(sc_call_step, execute_current_tx_context_input); } + pub fn perform_sc_call_update_results(&mut self, sc_call_step: &mut ScCallStep) { + let tx_result = + self.perform_sc_call_lambda_and_check(sc_call_step, execute_current_tx_context_input); + let response = TxResponse::from_tx_result(tx_result); + sc_call_step.response = Some(response); + sc_call_step.trigger_handler(); + } + /// Adds a SC call step, executes it and retrieves the transaction result ("out" field). /// /// The transaction is expected to complete successfully. diff --git a/framework/scenario/src/scenario/run_vm/sc_deploy.rs b/framework/scenario/src/scenario/run_vm/sc_deploy.rs index 92c019c9cb..bbec7ac4bb 100644 --- a/framework/scenario/src/scenario/run_vm/sc_deploy.rs +++ b/framework/scenario/src/scenario/run_vm/sc_deploy.rs @@ -4,6 +4,7 @@ use crate::{ types::heap::Address, }, scenario::model::{ScDeployStep, TypedScDeploy}, + scenario_model::TxResponse, }; use multiversx_chain_vm::{ @@ -82,6 +83,14 @@ impl ScenarioVMRunner { (new_address, deser_result) } + + pub fn perform_sc_deploy_update_results(&mut self, sc_deploy_step: &mut ScDeployStep) { + let (new_address, tx_result) = self + .perform_sc_deploy_lambda_and_check(sc_deploy_step, execute_current_tx_context_input); + let mut response = TxResponse::from_tx_result(tx_result); + response.new_deployed_address = Some(new_address); + sc_deploy_step.response = Some(response); + } } fn tx_input_from_deploy(sc_deploy_step: &ScDeployStep) -> TxInput { diff --git a/framework/scenario/src/scenario/run_vm/sc_query.rs b/framework/scenario/src/scenario/run_vm/sc_query.rs index 0b96e63ddb..e3e23eec8a 100644 --- a/framework/scenario/src/scenario/run_vm/sc_query.rs +++ b/framework/scenario/src/scenario/run_vm/sc_query.rs @@ -1,12 +1,4 @@ -use crate::{ - api::StaticApi, - multiversx_sc::{ - codec::{CodecFrom, PanicErrorHandler}, - types::ContractCall, - }, - num_bigint::BigUint, - scenario::model::ScQueryStep, -}; +use crate::{num_bigint::BigUint, scenario::model::ScQueryStep, scenario_model::TxResponse}; use multiversx_chain_vm::{ tx_execution::execute_current_tx_context_input, tx_mock::{TxInput, TxResult}, @@ -20,6 +12,13 @@ impl ScenarioVMRunner { self.perform_sc_query_lambda_and_check(sc_query_step, execute_current_tx_context_input) } + pub fn perform_sc_query_update_results(&mut self, sc_query_step: &mut ScQueryStep) { + let tx_result = + self.perform_sc_query_lambda_and_check(sc_query_step, execute_current_tx_context_input); + let response = TxResponse::from_tx_result(tx_result); + sc_query_step.response = Some(response); + } + pub fn perform_sc_query_lambda(&mut self, sc_query_step: &ScQueryStep, f: F) -> TxResult where F: FnOnce(), @@ -53,24 +52,6 @@ impl ScenarioVMRunner { } } -impl ScenarioVMRunner { - /// Performs a SC query to a contract, leaves no scenario trace behind. - /// - /// Meant to be used for the test to investigate the state of the contract. - /// - /// Use `mandos_sc_query` to embed the SC query in the resulting scenario. - pub fn quick_query(&mut self, contract_call: CC) -> RequestedResult - where - CC: ContractCall, - RequestedResult: CodecFrom, - { - let sc_query_step = ScQueryStep::new().call(contract_call); - let tx_result = self.perform_sc_query(&sc_query_step); - let mut raw_result = tx_result.result_values; - RequestedResult::multi_decode_or_handle_err(&mut raw_result, PanicErrorHandler).unwrap() - } -} - fn tx_input_from_query(sc_query_step: &ScQueryStep) -> TxInput { TxInput { from: sc_query_step.tx.to.to_vm_address(), diff --git a/framework/scenario/src/scenario/run_vm/vm_runner.rs b/framework/scenario/src/scenario/run_vm/vm_runner.rs index 235553f931..4fb26c1589 100644 --- a/framework/scenario/src/scenario/run_vm/vm_runner.rs +++ b/framework/scenario/src/scenario/run_vm/vm_runner.rs @@ -32,13 +32,13 @@ impl ScenarioRunner for ScenarioVMRunner { self.perform_set_state(step); } - fn run_sc_call_step(&mut self, step: &ScCallStep) { - self.perform_sc_call(step); + fn run_sc_call_step(&mut self, step: &mut ScCallStep) { + self.perform_sc_call_update_results(step); } - fn run_multi_sc_call_step(&mut self, steps: &[ScCallStep]) { + fn run_multi_sc_call_step(&mut self, steps: &mut [ScCallStep]) { for step in steps { - self.perform_sc_call(step); + self.perform_sc_call_update_results(step); } } diff --git a/framework/scenario/src/scenario/scenario_runner.rs b/framework/scenario/src/scenario/scenario_runner.rs index 5bcb2546c5..80df7a0d8b 100644 --- a/framework/scenario/src/scenario/scenario_runner.rs +++ b/framework/scenario/src/scenario/scenario_runner.rs @@ -12,9 +12,9 @@ pub trait ScenarioRunner { fn run_set_state_step(&mut self, step: &SetStateStep); - fn run_sc_call_step(&mut self, step: &ScCallStep); + fn run_sc_call_step(&mut self, step: &mut ScCallStep); - fn run_multi_sc_call_step(&mut self, steps: &[ScCallStep]); + fn run_multi_sc_call_step(&mut self, steps: &mut [ScCallStep]); fn run_multi_sc_deploy_step(&mut self, steps: &[ScDeployStep]); @@ -32,7 +32,8 @@ pub trait ScenarioRunner { /// Utility method for running all steps in a scenario. fn run_scenario(&mut self, scenario: &Scenario) { - for step in &scenario.steps { + let mut steps = scenario.steps.clone(); + for step in steps.iter_mut() { match step { Step::ExternalSteps(external_steps_step) => { self.run_external_steps(external_steps_step); diff --git a/framework/snippets/src/interactor_sc_call.rs b/framework/snippets/src/interactor_sc_call.rs index 509ead9b3c..d74927dffe 100644 --- a/framework/snippets/src/interactor_sc_call.rs +++ b/framework/snippets/src/interactor_sc_call.rs @@ -18,6 +18,7 @@ impl Interactor { let tx = self.retrieve_tx_on_network(tx_hash.clone()).await; sc_call_step.response = Some(TxResponse::from_network_tx(tx)); + sc_call_step.trigger_handler(); if let Ok(token_identifier) = sc_call_step .response() @@ -33,7 +34,7 @@ impl Interactor { self.post_runners.run_sc_call_step(sc_call_step); } - async fn launch_sc_call(&mut self, sc_call_step: &ScCallStep) -> String { + async fn launch_sc_call(&mut self, sc_call_step: &mut ScCallStep) -> String { self.pre_runners.run_sc_call_step(sc_call_step); let sender_address = &sc_call_step.tx.from.value; diff --git a/framework/snippets/src/interactor_sc_deploy.rs b/framework/snippets/src/interactor_sc_deploy.rs index 2b9a847686..f5d4beee37 100644 --- a/framework/snippets/src/interactor_sc_deploy.rs +++ b/framework/snippets/src/interactor_sc_deploy.rs @@ -52,7 +52,11 @@ impl Interactor { let nonce = tx.nonce; sc_deploy_step.response = Some(TxResponse::from_network_tx(tx)); - let deploy_address = sc_deploy_step.response().new_deployed_address.clone().unwrap(); + let deploy_address = sc_deploy_step + .response() + .new_deployed_address + .clone() + .unwrap(); let set_state_step = SetStateStep::new().new_address( addr, diff --git a/framework/snippets/src/interactor_tx_spec.rs b/framework/snippets/src/interactor_tx_spec.rs index bc1b673635..584381c3dc 100644 --- a/framework/snippets/src/interactor_tx_spec.rs +++ b/framework/snippets/src/interactor_tx_spec.rs @@ -26,7 +26,8 @@ impl TransactionSpec for ScCallStep { } fn run_step(&self, step_runner: &mut dyn ScenarioRunner) { - step_runner.run_sc_call_step(self); + let mut clone = self.clone(); + step_runner.run_sc_call_step(&mut clone); // TODO: make mutability uniform } fn set_response(&mut self, tx_response: TxResponse) { From 81ae7125cdfebafd9570744a0f5a276cb462b4b2 Mon Sep 17 00:00:00 2001 From: Ovidiu Stinga Date: Thu, 13 Jul 2023 17:55:34 +0300 Subject: [PATCH 02/10] adapt some tests for with_result --- .../crowdfunding_esdt_mandos_rust_test.rs | 25 ++++++------ .../tests/rust_mandos_v2_test.rs | 38 ++++++++++--------- .../src/scenario/model/transaction/tx_call.rs | 4 +- framework/snippets/src/interactor_sc_call.rs | 4 +- 4 files changed, 37 insertions(+), 34 deletions(-) diff --git a/contracts/examples/crowdfunding-esdt/tests/crowdfunding_esdt_mandos_rust_test.rs b/contracts/examples/crowdfunding-esdt/tests/crowdfunding_esdt_mandos_rust_test.rs index 08afeb845e..cac193fba6 100644 --- a/contracts/examples/crowdfunding-esdt/tests/crowdfunding_esdt_mandos_rust_test.rs +++ b/contracts/examples/crowdfunding-esdt/tests/crowdfunding_esdt_mandos_rust_test.rs @@ -33,18 +33,19 @@ fn crowdfunding_scenario_rust_test() { .put_account(owner_addr, Account::new()) .new_address(owner_addr, 0, &cf_sc), ); - let (_, ()) = cf_sc - .init( - 2_000u32, - deadline, - EgldOrEsdtTokenIdentifier::esdt(cf_token_id_value), - ) - .into_blockchain_call() - .from(owner_addr) - .contract_code("file:output/crowdfunding-esdt.wasm", &ctx) - .gas_limit("5,000,000") - .expect(TxExpect::ok().no_result()) - .execute(&mut world); + + world.sc_deploy_step( + ScDeployStep::new() + .from(owner_addr) + .contract_code("file:output/crowdfunding-esdt.wasm", &ctx) + .call(cf_sc.init( + 2_000u32, + deadline, + EgldOrEsdtTokenIdentifier::esdt(cf_token_id_value), + )) + .gas_limit("5,000,000") + .expect(TxExpect::ok().no_result()), + ); // setup user accounts world diff --git a/contracts/feature-tests/rust-testing-framework-tester/tests/rust_mandos_v2_test.rs b/contracts/feature-tests/rust-testing-framework-tester/tests/rust_mandos_v2_test.rs index 1f6a2b8d17..786ecbb1cc 100644 --- a/contracts/feature-tests/rust-testing-framework-tester/tests/rust_mandos_v2_test.rs +++ b/contracts/feature-tests/rust-testing-framework-tester/tests/rust_mandos_v2_test.rs @@ -24,22 +24,24 @@ fn tester_deploy_test() { let mut adder_contract = ContractInfo::>::new("sc:contract"); - world.start_trace().set_state_step( - SetStateStep::new() - .put_account(owner_address, Account::new()) - .new_address(owner_address, 0, &adder_contract), - ); - - // deploy - let (new_address, result): (_, String) = adder_contract - .init() - .into_blockchain_call() - .from(owner_address) - .contract_code(WASM_PATH_EXPR, &ic) - .gas_limit("5,000,000") - .execute(&mut world); - assert_eq!(new_address, adder_contract.to_address()); - assert_eq!(result, "constructor-result"); - - world.write_scenario_trace("scenarios/trace-deploy.scen.json"); + world + .start_trace() + .set_state_step( + SetStateStep::new() + .put_account(owner_address, Account::new()) + .new_address(owner_address, 0, &adder_contract), + ) + .sc_deploy_use_result( + ScDeployStep::new() + .from(owner_address) + .contract_code(WASM_PATH_EXPR, &ic) + .call(adder_contract.init()) + .gas_limit("5,000,000") + .expect(TxExpect::ok()), + |address, tr: TypedResponse| { + assert_eq!(address, adder_contract.to_address()); + assert_eq!(tr.result.unwrap(), "constructor-result"); + }, + ) + .write_scenario_trace("scenarios/trace-deploy.scen.json"); } diff --git a/framework/scenario/src/scenario/model/transaction/tx_call.rs b/framework/scenario/src/scenario/model/transaction/tx_call.rs index 9d9d374aeb..fa2aa09c1a 100644 --- a/framework/scenario/src/scenario/model/transaction/tx_call.rs +++ b/framework/scenario/src/scenario/model/transaction/tx_call.rs @@ -1,5 +1,5 @@ use crate::{ - api::DebugApi, + api::StaticApi, multiversx_sc::types::{ContractCall, ContractCallWithEgld, EsdtTokenPayment}, scenario::model::{AddressValue, BigUintValue, BytesValue, U64Value}, scenario_format::{ @@ -70,7 +70,7 @@ impl IntoRaw for TxCall { } impl TxCall { - pub fn to_contract_call(&self) -> ContractCallWithEgld { + pub fn to_contract_call(&self) -> ContractCallWithEgld { let mut contract_call = ContractCallWithEgld::new( (&self.to.value).into(), self.function.as_bytes(), diff --git a/framework/snippets/src/interactor_sc_call.rs b/framework/snippets/src/interactor_sc_call.rs index d74927dffe..3518a7a7f5 100644 --- a/framework/snippets/src/interactor_sc_call.rs +++ b/framework/snippets/src/interactor_sc_call.rs @@ -1,10 +1,10 @@ use crate::{address_h256_to_erdrs, mandos_to_erdrs_address, Interactor}; use log::info; use multiversx_sc_scenario::{ + api::StaticApi, multiversx_sc::types::ContractCallWithEgld, scenario::ScenarioRunner, scenario_model::{ScCallStep, SetStateStep, TxCall, TxResponse}, - DebugApi, }; use multiversx_sdk::data::transaction::Transaction; @@ -73,7 +73,7 @@ impl Interactor { } } -fn contract_call_to_tx_data(contract_call: &ContractCallWithEgld) -> String { +fn contract_call_to_tx_data(contract_call: &ContractCallWithEgld) -> String { let mut result = String::from_utf8( contract_call .basic From 56774992dca3fe8de3881b06fd03136ca54a9b76 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Thu, 13 Jul 2023 23:12:46 +0300 Subject: [PATCH 03/10] interactor - better error reporting --- .../scenario/src/standalone/account_tool.rs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/framework/scenario/src/standalone/account_tool.rs b/framework/scenario/src/standalone/account_tool.rs index c9ece55b09..877e75fc18 100644 --- a/framework/scenario/src/standalone/account_tool.rs +++ b/framework/scenario/src/standalone/account_tool.rs @@ -23,10 +23,18 @@ pub async fn retrieve_account_as_scenario_set_state( let blockchain = CommunicationProxy::new(api); let account = blockchain.get_account(&address).await.unwrap(); - let account_esdt = blockchain.get_account_esdt_tokens(&address).await.unwrap(); - let account_esdt_roles = blockchain.get_account_esdt_roles(&address).await.unwrap(); - - let account_storage = blockchain.get_account_storage_keys(&address).await.unwrap(); + let account_esdt = blockchain + .get_account_esdt_tokens(&address) + .await + .unwrap_or_else(|err| panic!("failed to retrieve ESDT tokens for address {addr}: {err}")); + let account_esdt_roles = blockchain + .get_account_esdt_roles(&address) + .await + .unwrap_or_else(|err| panic!("failed to retrieve ESDT roles for address {addr}: {err}")); + let account_storage = blockchain + .get_account_storage_keys(&address) + .await + .unwrap_or_else(|err| panic!("failed to retrieve storage for address {addr}: {err}")); let addr_pretty = if !hex_encoded { if account.code.is_empty() { From a83a88451e3331f6885e6b907529f70501ec4bda Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 14 Jul 2023 02:56:24 +0300 Subject: [PATCH 04/10] interactors - better error reporting --- .../examples/adder/interact/src/adder_interact_state.rs | 6 +++++- .../basic-features/interact/src/bf_interact_state.rs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/contracts/examples/adder/interact/src/adder_interact_state.rs b/contracts/examples/adder/interact/src/adder_interact_state.rs index 2e055d07e7..445eb52075 100644 --- a/contracts/examples/adder/interact/src/adder_interact_state.rs +++ b/contracts/examples/adder/interact/src/adder_interact_state.rs @@ -40,7 +40,11 @@ impl State { /// Returns the adder contract pub fn adder(&self) -> AdderContract { - AdderContract::new(self.adder_address.clone().unwrap()) + AdderContract::new( + self.adder_address + .clone() + .expect("no known adder contract, deploy first"), + ) } /// Returns the adder contract with default address diff --git a/contracts/feature-tests/basic-features/interact/src/bf_interact_state.rs b/contracts/feature-tests/basic-features/interact/src/bf_interact_state.rs index f16d8e0e7c..6e904dd53c 100644 --- a/contracts/feature-tests/basic-features/interact/src/bf_interact_state.rs +++ b/contracts/feature-tests/basic-features/interact/src/bf_interact_state.rs @@ -39,7 +39,11 @@ impl State { /// Returns the contract pub fn bf_contract(&self) -> BasicFeaturesContract { - BasicFeaturesContract::new(self.bf_address.clone().unwrap()) + BasicFeaturesContract::new( + self.bf_address + .clone() + .expect("basic-features contract not yet deployed"), + ) } /// Returns the adder contract with default address From 9dd8744e6b699dce24622e47c5e8e4598b7f94ec Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 14 Jul 2023 02:57:29 +0300 Subject: [PATCH 05/10] interactor methods aligned with testing framework --- .../adder/interact/src/adder_interact.rs | 27 +-- .../interact-rs/src/multisig_interact.rs | 8 +- .../interact/src/bf_interact.rs | 84 ++++----- .../src/facade/scenario_world_steps.rs | 3 +- .../src/scenario/model/step/step_sc_query.rs | 7 +- .../scenario/model/transaction/tx_response.rs | 19 +- framework/snippets/src/interactor_sc_extra.rs | 164 ++++++++++++++++++ framework/snippets/src/interactor_vm_query.rs | 45 +++-- framework/snippets/src/lib.rs | 1 + 9 files changed, 260 insertions(+), 98 deletions(-) create mode 100644 framework/snippets/src/interactor_sc_extra.rs diff --git a/contracts/examples/adder/interact/src/adder_interact.rs b/contracts/examples/adder/interact/src/adder_interact.rs index ac4989635f..455b3dbee5 100644 --- a/contracts/examples/adder/interact/src/adder_interact.rs +++ b/contracts/examples/adder/interact/src/adder_interact.rs @@ -18,7 +18,7 @@ use multiversx_sc_snippets::{ mandos_system::ScenarioRunner, num_bigint::BigUint, scenario_format::interpret_trait::{InterpretableFrom, InterpreterContext}, - scenario_model::{IntoBlockchainCall, Scenario, TransferStep, TxExpect}, + scenario_model::{ScCallStep, ScDeployStep, Scenario, TransferStep, TxExpect}, standalone::retrieve_account_as_scenario_set_state, test_wallets, ContractInfo, }, @@ -95,15 +95,12 @@ impl AdderInteract { async fn deploy(&mut self) { self.set_state().await; - let mut typed_sc_deploy = self - .state - .default_adder() - .init(BigUint::from(0u64)) - .into_blockchain_call() + let mut typed_sc_deploy = ScDeployStep::new() + .call(self.state.default_adder().init(BigUint::from(0u64))) .from(&self.wallet_address) .code_metadata(CodeMetadata::all()) .contract_code("file:../output/adder.wasm", &InterpreterContext::default()) - .gas_limit("70,000,000") + .gas_limit("5,000,000") .expect(TxExpect::ok()); self.interactor.sc_deploy(&mut typed_sc_deploy).await; @@ -132,11 +129,8 @@ impl AdderInteract { let mut steps = Vec::new(); for _ in 0..*count { - let typed_sc_deploy = self - .state - .default_adder() - .init(BigUint::from(0u64)) - .into_blockchain_call() + let typed_sc_deploy = ScDeployStep::new() + .call(self.state.default_adder().init(0u32)) .from(&self.wallet_address) .code_metadata(CodeMetadata::all()) .contract_code("file:../output/adder.wasm", &InterpreterContext::default()) @@ -175,11 +169,8 @@ impl AdderInteract { } async fn add(&mut self, value: u64) { - let mut typed_sc_call = self - .state - .adder() - .add(BigUint::from(value)) - .into_blockchain_call() + let mut typed_sc_call = ScCallStep::new() + .call(self.state.adder().add(value)) .from(&self.wallet_address) .gas_limit("70,000,000") .expect(TxExpect::ok()); @@ -196,7 +187,7 @@ impl AdderInteract { } async fn print_sum(&mut self) { - let sum: SingleValue = self.interactor.vm_query(self.state.adder().sum()).await; + let sum: SingleValue = self.interactor.quick_query(self.state.adder().sum()).await; println!("sum: {}", sum.into()); } } diff --git a/contracts/examples/multisig/interact-rs/src/multisig_interact.rs b/contracts/examples/multisig/interact-rs/src/multisig_interact.rs index 560a193a58..70dcd4488d 100644 --- a/contracts/examples/multisig/interact-rs/src/multisig_interact.rs +++ b/contracts/examples/multisig/interact-rs/src/multisig_interact.rs @@ -318,13 +318,13 @@ impl MultisigInteract { async fn quorum_reached(&mut self, action_id: usize) -> bool { self.interactor - .vm_query(self.state.multisig().quorum_reached(action_id)) + .quick_query(self.state.multisig().quorum_reached(action_id)) .await } async fn signed(&mut self, signer: &Address, action_id: usize) -> bool { self.interactor - .vm_query(self.state.multisig().signed(signer, action_id)) + .quick_query(self.state.multisig().signed(signer, action_id)) .await } @@ -395,7 +395,7 @@ impl MultisigInteract { async fn print_quorum(&mut self) { let quorum: SingleValue = self .interactor - .vm_query(self.state.multisig().quorum()) + .quick_query(self.state.multisig().quorum()) .await; println!("quorum: {}", quorum.into()); @@ -404,7 +404,7 @@ impl MultisigInteract { async fn print_board(&mut self) { let board: SingleValue = self .interactor - .vm_query(self.state.multisig().num_board_members()) + .quick_query(self.state.multisig().num_board_members()) .await; println!("board: {}", board.into()); diff --git a/contracts/feature-tests/basic-features/interact/src/bf_interact.rs b/contracts/feature-tests/basic-features/interact/src/bf_interact.rs index c5f5db2644..98dbc514c0 100644 --- a/contracts/feature-tests/basic-features/interact/src/bf_interact.rs +++ b/contracts/feature-tests/basic-features/interact/src/bf_interact.rs @@ -16,7 +16,7 @@ use multiversx_sc_snippets::{ bech32, mandos_system::ScenarioRunner, scenario_format::interpret_trait::{InterpretableFrom, InterpreterContext}, - scenario_model::{IntoBlockchainCall, Scenario, TxExpect}, + scenario_model::{ScCallStep, ScDeployStep, Scenario, TxExpect}, standalone::retrieve_account_as_scenario_set_state, test_wallets, ContractInfo, }, @@ -95,54 +95,46 @@ impl BasicFeaturesInteract { async fn deploy(&mut self) { self.set_state().await; - let mut typed_sc_deploy = self - .state - .default_contract() - .init() - .into_blockchain_call() - .from(&self.wallet_address) - .code_metadata(CodeMetadata::all()) - .contract_code( - "file:../output/basic-features-storage-bytes.wasm", - &InterpreterContext::default(), + self.interactor + .sc_deploy_use_result( + ScDeployStep::new() + .call(self.state.default_contract().init()) + .from(&self.wallet_address) + .code_metadata(CodeMetadata::all()) + .contract_code( + "file:../output/basic-features-storage-bytes.wasm", + &InterpreterContext::default(), + ) + .gas_limit("4,000,000") + .expect(TxExpect::ok()), + |new_address, tr| { + tr.result + .unwrap_or_else(|err| panic!("deploy failed: {}", err.message)); + + let new_address_bech32 = bech32::encode(&new_address); + println!("new address: {new_address_bech32}"); + + let new_address_expr = format!("bech32:{new_address_bech32}"); + self.state.set_bf_address(&new_address_expr); + }, ) - .gas_limit("4,000,000") - .expect(TxExpect::ok()); - - self.interactor.sc_deploy(&mut typed_sc_deploy).await; - - let result = typed_sc_deploy.response().new_deployed_address(); - if result.is_err() { - println!("deploy failed: {}", result.err().unwrap()); - return; - } - - let new_address_bech32 = bech32::encode(&result.unwrap()); - println!("new address: {new_address_bech32}"); - - let new_address_expr = format!("bech32:{new_address_bech32}"); - self.state.set_bf_address(&new_address_expr); + .await; } async fn set_large_storage(&mut self, value: &[u8]) { - let mut typed_sc_call = self - .state - .bf_contract() - .store_bytes(value) - .into_blockchain_call() - .from(&self.wallet_address) - .gas_limit("600,000,000") - .expect(TxExpect::ok()); - - self.interactor.sc_call(&mut typed_sc_call).await; - - let result = typed_sc_call.response().handle_signal_error_event(); - if result.is_err() { - panic!( - "performing store_bytes failed with: {}", - result.err().unwrap() - ); - } + self.interactor + .sc_call_use_result( + ScCallStep::new() + .call(self.state.bf_contract().store_bytes(value)) + .from(&self.wallet_address) + .gas_limit("600,000,000"), + |tr| { + tr.result.unwrap_or_else(|err| { + panic!("performing store_bytes failed with: {}", err.message) + }); + }, + ) + .await; println!("successfully performed add"); } @@ -150,7 +142,7 @@ impl BasicFeaturesInteract { async fn print_length(&mut self) { let data: Vec = self .interactor - .vm_query(self.state.bf_contract().load_bytes()) + .quick_query(self.state.bf_contract().load_bytes()) .await; println!("retrieved data length: {}", data.len()); if data != self.large_storage_payload { diff --git a/framework/scenario/src/facade/scenario_world_steps.rs b/framework/scenario/src/facade/scenario_world_steps.rs index bb89f90a95..8cd90729f3 100644 --- a/framework/scenario/src/facade/scenario_world_steps.rs +++ b/framework/scenario/src/facade/scenario_world_steps.rs @@ -168,8 +168,7 @@ impl ScenarioWorld { where S: AsMut, { - let base_step = step.as_mut(); - self.run_sc_deploy_step(base_step); + self.run_sc_deploy_step(step.as_mut()); self } diff --git a/framework/scenario/src/scenario/model/step/step_sc_query.rs b/framework/scenario/src/scenario/model/step/step_sc_query.rs index 4e1de5f789..b8fc7c8076 100644 --- a/framework/scenario/src/scenario/model/step/step_sc_query.rs +++ b/framework/scenario/src/scenario/model/step/step_sc_query.rs @@ -1,4 +1,5 @@ use multiversx_sc::types::H256; +use num_traits::Zero; use crate::{ api::StaticApi, @@ -59,7 +60,11 @@ impl ScQueryStep { where CC: ContractCall, { - let (to_str, function, _, mandos_args) = process_contract_call(contract_call); + let (to_str, function, egld_value_expr, mandos_args) = process_contract_call(contract_call); + assert!( + egld_value_expr.value.is_zero(), + "cannot send EGLD value in queries" + ); self = self.to(to_str.as_str()); self = self.function(function.as_str()); for arg in mandos_args { diff --git a/framework/scenario/src/scenario/model/transaction/tx_response.rs b/framework/scenario/src/scenario/model/transaction/tx_response.rs index a4d8ea8c30..5a089b3357 100644 --- a/framework/scenario/src/scenario/model/transaction/tx_response.rs +++ b/framework/scenario/src/scenario/model/transaction/tx_response.rs @@ -1,5 +1,3 @@ -use core::panic; - use crate::multiversx_sc::types::Address; use multiversx_chain_vm::tx_mock::TxResult; use multiversx_sdk::data::transaction::{ @@ -52,6 +50,13 @@ impl TxResponse { response.process() } + pub fn from_raw_results(raw_results: Vec>) -> Self { + TxResponse { + out: raw_results, + ..Default::default() + } + } + fn process(self) -> Self { self.process_out().process_new_deployed_address() } @@ -59,10 +64,6 @@ impl TxResponse { fn process_out(mut self) -> Self { if let Some(first_scr) = self.api_scrs.get(0) { self.out = decode_scr_data_or_panic(first_scr.data.as_str()); - } else { - panic!("no smart contract results obtained") - // self.tx_error.status = 0; // TODO: Add correct status - // self.tx_error.message = "no smart contract results obtained".to_string(); } self } @@ -144,20 +145,20 @@ impl TxResponse { } // Returns the token identifier of the newly issued non-fungible token. - pub fn issue_non_fungible_new_token_identifier(&self) -> Result { + pub fn issue_non_fungible_new_token_identifier(&self) -> Result { let token_identifier_issue_scr: Option<&ApiSmartContractResult> = self .api_scrs .iter() .find(|scr| scr.sender.to_string() == SYSTEM_SC_BECH32 && scr.data.starts_with("@00@")); if token_identifier_issue_scr.is_none() { - panic!("no token identifier issue SCR found"); + return Err("no token identifier issue SCR found"); } let token_identifier_issue_scr = token_identifier_issue_scr.unwrap(); let encoded_tid = token_identifier_issue_scr.data.split('@').nth(2); if encoded_tid.is_none() { - panic!("no token identifier found in SCR"); + return Err("no token identifier found in SCR"); } Ok(String::from_utf8(hex::decode(encoded_tid.unwrap()).unwrap()).unwrap()) diff --git a/framework/snippets/src/interactor_sc_extra.rs b/framework/snippets/src/interactor_sc_extra.rs new file mode 100644 index 0000000000..9a25a0edf6 --- /dev/null +++ b/framework/snippets/src/interactor_sc_extra.rs @@ -0,0 +1,164 @@ +use crate::Interactor; +use multiversx_sc_scenario::{ + api::StaticApi, + multiversx_sc::{ + codec::{CodecFrom, TopEncodeMulti}, + types::{Address, ContractCall}, + }, + scenario_model::{ + ScCallStep, ScDeployStep, ScQueryStep, TxResponse, TypedResponse, TypedScCall, + TypedScDeploy, TypedScQuery, + }, +}; + +impl Interactor { + pub async fn sc_call_use_raw_response( + &mut self, + mut step: S, + use_raw_response: F, + ) -> &mut Self + where + S: AsMut, + F: FnOnce(&TxResponse), + { + self.sc_call(step.as_mut()).await; + let response = unwrap_response(&step.as_mut().response); + use_raw_response(response); + self + } + + pub async fn sc_call_use_result( + &mut self, + step: TypedScCall, + use_result: F, + ) -> &mut Self + where + OriginalResult: TopEncodeMulti, + RequestedResult: CodecFrom, + F: FnOnce(TypedResponse), + { + self.sc_call_use_raw_response(step, |response| { + let typed_response = TypedResponse::from_raw(response); + use_result(typed_response); + }) + .await + } + + pub async fn sc_query_use_raw_response( + &mut self, + mut step: S, + use_raw_response: F, + ) -> &mut Self + where + S: AsMut, + F: FnOnce(&TxResponse), + { + self.sc_query(step.as_mut()).await; + let response = unwrap_response(&step.as_mut().response); + use_raw_response(response); + self + } + + pub async fn sc_query_use_result( + &mut self, + step: TypedScQuery, + use_result: F, + ) -> &mut Self + where + OriginalResult: TopEncodeMulti, + RequestedResult: CodecFrom, + F: FnOnce(TypedResponse), + { + self.sc_query_use_raw_response(step, |response| { + let typed_response = TypedResponse::from_raw(response); + use_result(typed_response); + }) + .await + } + + pub async fn sc_query_get_result( + &mut self, + mut step: TypedScQuery, + ) -> RequestedResult + where + OriginalResult: TopEncodeMulti, + RequestedResult: CodecFrom, + { + self.sc_query(step.as_mut()).await; + let response = unwrap_response(&step.sc_query_step.response); + let typed_response = TypedResponse::from_raw(response); + typed_response.result.unwrap() + } + + pub async fn quick_query(&mut self, contract_call: CC) -> RequestedResult + where + CC: ContractCall, + RequestedResult: CodecFrom, + { + let mut typed_sc_query = ScQueryStep::new().call(contract_call); + self.sc_query(&mut typed_sc_query).await; + let response = unwrap_response(&typed_sc_query.sc_query_step.response); + let typed_response = TypedResponse::from_raw(response); + typed_response.result.unwrap() + } + + pub async fn sc_deploy_use_raw_response( + &mut self, + mut step: S, + use_raw_response: F, + ) -> &mut Self + where + S: AsMut, + F: FnOnce(&TxResponse), + { + self.sc_deploy(step.as_mut()).await; + let response = unwrap_response(&step.as_mut().response); + use_raw_response(response); + self + } + + pub async fn sc_deploy_use_result( + &mut self, + step: TypedScDeploy, + use_result: F, + ) -> &mut Self + where + OriginalResult: TopEncodeMulti, + RequestedResult: CodecFrom, + F: FnOnce(Address, TypedResponse), + { + self.sc_deploy_use_raw_response(step, |response| { + let new_address = unwrap_new_address(response); + let typed_response = TypedResponse::from_raw(response); + use_result(new_address, typed_response); + }) + .await + } + + pub async fn sc_deploy_use_new_address( + &mut self, + step: S, + use_new_address: F, + ) -> &mut Self + where + S: AsMut, + F: FnOnce(Address), + { + self.sc_deploy_use_raw_response(step, |response| { + let new_address = unwrap_new_address(response); + use_new_address(new_address); + }) + .await + } +} + +fn unwrap_response(opt_response: &Option) -> &TxResponse { + opt_response.as_ref().expect("response not processed") +} + +fn unwrap_new_address(response: &TxResponse) -> Address { + response + .new_deployed_address + .clone() + .expect("missing new address after deploy") +} diff --git a/framework/snippets/src/interactor_vm_query.rs b/framework/snippets/src/interactor_vm_query.rs index 1ff0a97cef..1c1128cda8 100644 --- a/framework/snippets/src/interactor_vm_query.rs +++ b/framework/snippets/src/interactor_vm_query.rs @@ -2,30 +2,30 @@ use crate::{address_h256_to_erdrs, Interactor}; use log::info; use multiversx_sc_scenario::{ api::StaticApi, - multiversx_sc::{ - codec::{CodecFrom, PanicErrorHandler}, - types::ContractCall, - }, + multiversx_sc::{codec::CodecFrom, types::ContractCall}, + scenario_model::{ScQueryStep, TxResponse}, }; use multiversx_sdk::data::vm::VmValueRequest; impl Interactor { - pub async fn vm_query(&mut self, contract_call: CC) -> RequestedResult + pub async fn sc_query(&mut self, mut step: S) -> &mut Self where - CC: ContractCall, - RequestedResult: CodecFrom, + S: AsMut, { - let full_cc = contract_call.into_normalized(); - let sc_address = address_h256_to_erdrs(&full_cc.basic.to.to_address()); + self.perform_sc_query(step.as_mut()).await; + self + } + + pub async fn perform_sc_query(&mut self, step: &mut ScQueryStep) { + let sc_address = address_h256_to_erdrs(&step.tx.to.to_address()); let req = VmValueRequest { sc_address: sc_address.clone(), - func_name: String::from_utf8(full_cc.basic.endpoint_name.to_boxed_bytes().into_vec()) - .unwrap(), - args: full_cc - .basic - .arg_buffer - .raw_arg_iter() - .map(|arg| hex::encode(arg.to_boxed_bytes().as_slice())) + func_name: step.tx.function.clone(), + args: step + .tx + .arguments + .iter() + .map(|arg| hex::encode(&arg.value)) .collect(), caller: sc_address, value: "0".to_string(), @@ -38,12 +38,21 @@ impl Interactor { info!("{:#?}", result); - let mut raw_results: Vec> = result + let raw_results: Vec> = result .data .return_data .iter() .map(|result| base64::decode(result).expect("query result base64 decode error")) .collect(); - RequestedResult::multi_decode_or_handle_err(&mut raw_results, PanicErrorHandler).unwrap() + step.response = Some(TxResponse::from_raw_results(raw_results)); + } + + #[deprecated(since = "0.42.0", note = "Was renamed to `quick_query`.")] + pub async fn vm_query(&mut self, contract_call: CC) -> RequestedResult + where + CC: ContractCall, + RequestedResult: CodecFrom, + { + self.quick_query(contract_call).await } } diff --git a/framework/snippets/src/lib.rs b/framework/snippets/src/lib.rs index c10905a259..3ba18fb9b5 100644 --- a/framework/snippets/src/lib.rs +++ b/framework/snippets/src/lib.rs @@ -5,6 +5,7 @@ mod interactor_multi_sc_process; mod interactor_retrieve; mod interactor_sc_call; mod interactor_sc_deploy; +mod interactor_sc_extra; mod interactor_sc_transfer; mod interactor_sender; mod interactor_tx_spec; From e0fd8d636058b093f3977b1d05df04b9609355f7 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 14 Jul 2023 04:20:20 +0300 Subject: [PATCH 06/10] testing framework - refactor --- .../scenario/src/facade/debugger_backend.rs | 40 ++++- .../src/facade/scenario_world_runner.rs | 6 +- .../src/facade/scenario_world_steps.rs | 156 ++++++------------ .../scenario/model/transaction/tx_response.rs | 21 ++- framework/scenario/src/scenario/run_list.rs | 8 +- .../src/scenario/run_trace/scenario_trace.rs | 6 +- .../scenario/run_trace/scenario_trace_file.rs | 6 +- .../scenario/src/scenario/run_vm/sc_call.rs | 17 +- .../scenario/src/scenario/run_vm/sc_deploy.rs | 57 ++----- .../scenario/src/scenario/run_vm/sc_query.rs | 6 +- .../scenario/src/scenario/run_vm/vm_runner.rs | 14 +- .../scenario/src/scenario/scenario_runner.rs | 6 +- .../snippets/src/interactor_multi_sc_exec.rs | 4 +- .../snippets/src/interactor_sc_deploy.rs | 2 +- framework/snippets/src/interactor_tx_spec.rs | 6 +- 15 files changed, 157 insertions(+), 198 deletions(-) diff --git a/framework/scenario/src/facade/debugger_backend.rs b/framework/scenario/src/facade/debugger_backend.rs index 0c5f4f181a..0f290efd02 100644 --- a/framework/scenario/src/facade/debugger_backend.rs +++ b/framework/scenario/src/facade/debugger_backend.rs @@ -29,23 +29,47 @@ impl ScenarioRunner for DebuggerBackend { } fn run_sc_call_step(&mut self, step: &mut ScCallStep) { - self.for_each_runner_mut(|runner| runner.run_sc_call_step(step)); + self.vm_runner.run_sc_call_step(step); + step.expect = step.response.as_ref().map(TxResponse::to_expect); + if let Some(trace) = &mut self.trace { + trace.run_sc_call_step(step); + } } fn run_multi_sc_call_step(&mut self, steps: &mut [ScCallStep]) { - self.for_each_runner_mut(|runner| runner.run_multi_sc_call_step(steps)); + self.vm_runner.run_multi_sc_call_step(steps); + for step in steps.iter_mut() { + step.expect = step.response.as_ref().map(TxResponse::to_expect); + } + if let Some(trace) = &mut self.trace { + trace.run_multi_sc_call_step(steps); + } } - fn run_multi_sc_deploy_step(&mut self, steps: &[ScDeployStep]) { - self.for_each_runner_mut(|runner| runner.run_multi_sc_deploy_step(steps)); + fn run_sc_query_step(&mut self, step: &mut ScQueryStep) { + self.vm_runner.run_sc_query_step(step); + step.expect = step.response.as_ref().map(TxResponse::to_expect); + if let Some(trace) = &mut self.trace { + trace.run_sc_query_step(step); + } } - fn run_sc_query_step(&mut self, step: &ScQueryStep) { - self.for_each_runner_mut(|runner| runner.run_sc_query_step(step)); + fn run_sc_deploy_step(&mut self, step: &mut ScDeployStep) { + self.vm_runner.run_sc_deploy_step(step); + step.expect = step.response.as_ref().map(TxResponse::to_expect); + if let Some(trace) = &mut self.trace { + trace.run_sc_deploy_step(step); + } } - fn run_sc_deploy_step(&mut self, step: &ScDeployStep) { - self.for_each_runner_mut(|runner| runner.run_sc_deploy_step(step)); + fn run_multi_sc_deploy_step(&mut self, steps: &mut [ScDeployStep]) { + self.vm_runner.run_multi_sc_deploy_step(steps); + for step in steps.iter_mut() { + step.expect = step.response.as_ref().map(TxResponse::to_expect); + } + if let Some(trace) = &mut self.trace { + trace.run_multi_sc_deploy_step(steps); + } } fn run_transfer_step(&mut self, step: &TransferStep) { diff --git a/framework/scenario/src/facade/scenario_world_runner.rs b/framework/scenario/src/facade/scenario_world_runner.rs index 510b553a0e..cd7c27d158 100644 --- a/framework/scenario/src/facade/scenario_world_runner.rs +++ b/framework/scenario/src/facade/scenario_world_runner.rs @@ -38,15 +38,15 @@ impl ScenarioRunner for ScenarioWorld { self.for_each_runner_mut(|runner| runner.run_multi_sc_call_step(steps)); } - fn run_multi_sc_deploy_step(&mut self, steps: &[ScDeployStep]) { + fn run_multi_sc_deploy_step(&mut self, steps: &mut [ScDeployStep]) { self.for_each_runner_mut(|runner| runner.run_multi_sc_deploy_step(steps)); } - fn run_sc_query_step(&mut self, step: &ScQueryStep) { + fn run_sc_query_step(&mut self, step: &mut ScQueryStep) { self.for_each_runner_mut(|runner| runner.run_sc_query_step(step)); } - fn run_sc_deploy_step(&mut self, step: &ScDeployStep) { + fn run_sc_deploy_step(&mut self, step: &mut ScDeployStep) { self.for_each_runner_mut(|runner| runner.run_sc_deploy_step(step)); } diff --git a/framework/scenario/src/facade/scenario_world_steps.rs b/framework/scenario/src/facade/scenario_world_steps.rs index 8cd90729f3..119aa9b42b 100644 --- a/framework/scenario/src/facade/scenario_world_steps.rs +++ b/framework/scenario/src/facade/scenario_world_steps.rs @@ -3,7 +3,7 @@ use multiversx_sc::types::{heap::Address, ContractCall}; use crate::{ api::StaticApi, facade::ScenarioWorld, - multiversx_sc::codec::{CodecFrom, PanicErrorHandler, TopEncodeMulti}, + multiversx_sc::codec::{CodecFrom, TopEncodeMulti}, scenario::{model::*, ScenarioRunner}, }; @@ -21,36 +21,28 @@ impl ScenarioWorld { } /// Adds a SC call step, then executes it. - pub fn sc_call_step(&mut self, mut sc_call_step: S) -> &mut Self + pub fn sc_call_step(&mut self, mut step: S) -> &mut Self where S: AsMut, { - let base_step = sc_call_step.as_mut(); - self.run_sc_call_step(base_step); + self.run_sc_call_step(step.as_mut()); self } - pub fn sc_call_use_raw_response( - &mut self, - mut sc_call_step: S, - use_raw_response: F, - ) -> &mut Self + pub fn sc_call_use_raw_response(&mut self, mut step: S, use_raw_response: F) -> &mut Self where S: AsMut, F: FnOnce(&TxResponse), { - let base_step = sc_call_step.as_mut(); - self.get_mut_debugger_backend() - .vm_runner - .perform_sc_call_update_results(base_step); - let response = unwrap_response(&base_step.response); + self.run_sc_call_step(step.as_mut()); + let response = unwrap_response(&step.as_mut().response); use_raw_response(response); self } pub fn sc_call_use_result( &mut self, - mut step: TypedScCall, + step: TypedScCall, use_result: F, ) -> &mut Self where @@ -58,13 +50,10 @@ impl ScenarioWorld { RequestedResult: CodecFrom, F: FnOnce(TypedResponse), { - self.get_mut_debugger_backend() - .vm_runner - .perform_sc_call_update_results(&mut step.sc_call_step); - let response = unwrap_response(&step.sc_call_step.response); - let typed_response = TypedResponse::from_raw(response); - use_result(typed_response); - self + self.sc_call_use_raw_response(step, |response| { + let typed_response = TypedResponse::from_raw(response); + use_result(typed_response); + }) } pub fn sc_call_get_result( @@ -75,37 +64,28 @@ impl ScenarioWorld { OriginalResult: TopEncodeMulti, RequestedResult: CodecFrom, { - self.get_mut_debugger_backend() - .vm_runner - .perform_sc_call_update_results(&mut step.sc_call_step); + self.run_sc_call_step(&mut step.sc_call_step); let response = unwrap_response(&step.sc_call_step.response); let typed_response = TypedResponse::from_raw(response); - typed_response.result.unwrap() + typed_response.result.expect("SC call failed") } /// Adds a SC query step, then executes it. - pub fn sc_query_step(&mut self, mut sc_call_step: S) -> &mut Self + pub fn sc_query_step(&mut self, mut step: S) -> &mut Self where S: AsMut, { - let base_step: &mut ScQueryStep = sc_call_step.as_mut(); - self.run_sc_query_step(base_step); + self.run_sc_query_step(step.as_mut()); self } - pub fn sc_query_use_raw_response( - &mut self, - mut sc_call_step: S, - use_raw_response: F, - ) -> &mut Self + pub fn sc_query_use_raw_response(&mut self, mut step: S, use_raw_response: F) -> &mut Self where S: AsMut, F: FnOnce(&TxResponse), { - let base_step = sc_call_step.as_mut(); - self.get_mut_debugger_backend() - .vm_runner - .perform_sc_query_update_results(base_step); + let base_step = step.as_mut(); + self.run_sc_query_step(base_step); let response = unwrap_response(&base_step.response); use_raw_response(response); self @@ -113,7 +93,7 @@ impl ScenarioWorld { pub fn sc_query_use_result( &mut self, - mut step: TypedScQuery, + step: TypedScQuery, use_result: F, ) -> &mut Self where @@ -121,13 +101,10 @@ impl ScenarioWorld { RequestedResult: CodecFrom, F: FnOnce(TypedResponse), { - self.get_mut_debugger_backend() - .vm_runner - .perform_sc_query_update_results(&mut step.sc_query_step); - let response = unwrap_response(&step.sc_query_step.response); - let typed_response = TypedResponse::from_raw(response); - use_result(typed_response); - self + self.sc_query_use_raw_response(step, |response| { + let typed_response = TypedResponse::from_raw(response); + use_result(typed_response); + }) } pub fn sc_query_get_result( @@ -138,12 +115,10 @@ impl ScenarioWorld { OriginalResult: TopEncodeMulti, RequestedResult: CodecFrom, { - self.get_mut_debugger_backend() - .vm_runner - .perform_sc_query_update_results(&mut step.sc_query_step); + self.run_sc_query_step(&mut step.sc_query_step); let response = unwrap_response(&step.sc_query_step.response); let typed_response = TypedResponse::from_raw(response); - typed_response.result.unwrap() + typed_response.result.expect("SC query failed") } /// Performs a SC query to a contract, leaves no scenario trace behind. @@ -156,11 +131,7 @@ impl ScenarioWorld { CC: ContractCall, RequestedResult: CodecFrom, { - let vm_runner = &mut self.get_mut_debugger_backend().vm_runner; - let typed_sc_query = ScQueryStep::new().call(contract_call); - let tx_result = vm_runner.perform_sc_query(&typed_sc_query.sc_query_step); - let mut raw_result = tx_result.result_values; - RequestedResult::multi_decode_or_handle_err(&mut raw_result, PanicErrorHandler).unwrap() + self.sc_query_get_result(ScQueryStep::new().call(contract_call)) } /// Adds a SC deploy step, then executes it. @@ -174,17 +145,15 @@ impl ScenarioWorld { pub fn sc_deploy_use_raw_response( &mut self, - mut sc_deploy_step: S, + mut step: S, use_raw_response: F, ) -> &mut Self where S: AsMut, F: FnOnce(&TxResponse), { - let base_step = sc_deploy_step.as_mut(); - self.get_mut_debugger_backend() - .vm_runner - .perform_sc_deploy_update_results(base_step); + let base_step = step.as_mut(); + self.run_sc_deploy_step(base_step); let response = unwrap_response(&base_step.response); use_raw_response(response); self @@ -192,7 +161,7 @@ impl ScenarioWorld { pub fn sc_deploy_use_result( &mut self, - mut step: TypedScDeploy, + step: TypedScDeploy, use_result: F, ) -> &mut Self where @@ -200,33 +169,37 @@ impl ScenarioWorld { RequestedResult: CodecFrom, F: FnOnce(Address, TypedResponse), { - self.get_mut_debugger_backend() - .vm_runner - .perform_sc_deploy_update_results(&mut step.sc_deploy_step); + self.sc_deploy_use_raw_response(step, |response| { + let new_address = unwrap_new_address(response); + let typed_response = TypedResponse::from_raw(response); + use_result(new_address, typed_response); + }) + } + + pub fn sc_deploy_get_result( + &mut self, + mut step: TypedScDeploy, + ) -> (Address, RequestedResult) + where + OriginalResult: TopEncodeMulti, + RequestedResult: CodecFrom, + { + self.run_sc_deploy_step(&mut step.sc_deploy_step); let response = unwrap_response(&step.sc_deploy_step.response); let new_address = unwrap_new_address(response); let typed_response = TypedResponse::from_raw(response); - use_result(new_address, typed_response); - self + (new_address, typed_response.result.unwrap()) } - pub fn sc_deploy_use_new_address( - &mut self, - mut sc_deploy_step: S, - use_new_address: F, - ) -> &mut Self + pub fn sc_deploy_use_new_address(&mut self, step: S, use_new_address: F) -> &mut Self where S: AsMut, F: FnOnce(Address), { - let base_step = sc_deploy_step.as_mut(); - self.get_mut_debugger_backend() - .vm_runner - .perform_sc_deploy_update_results(base_step); - let response = unwrap_response(&base_step.response); - let new_address = unwrap_new_address(response); - use_new_address(new_address); - self + self.sc_deploy_use_raw_response(step, |response| { + let new_address = unwrap_new_address(response); + use_new_address(new_address); + }) } /// Adds a simple transfer step, then executes it. @@ -263,9 +236,7 @@ impl TypedScCallExecutor for ScenarioWorld { OriginalResult: TopEncodeMulti, RequestedResult: CodecFrom, { - self.get_mut_debugger_backend() - .vm_runner - .perform_sc_call_get_result(typed_sc_call) + self.sc_call_get_result(typed_sc_call) } } @@ -278,9 +249,7 @@ impl TypedScDeployExecutor for ScenarioWorld { OriginalResult: TopEncodeMulti, RequestedResult: CodecFrom, { - self.get_mut_debugger_backend() - .vm_runner - .perform_sc_deploy_get_result(typed_sc_call) + self.sc_deploy_get_result(typed_sc_call) } } @@ -298,22 +267,7 @@ impl TypedScQueryExecutor for ScenarioWorld { OriginalResult: TopEncodeMulti, RequestedResult: CodecFrom, { - let debugger = self.get_mut_debugger_backend(); - let mut sc_query_step: ScQueryStep = typed_sc_query.into(); - let tx_result = debugger.vm_runner.perform_sc_query(&sc_query_step); - - let mut tx_expect = TxExpect::ok(); - for raw_result in &tx_result.result_values { - let result_hex_string = format!("0x{}", hex::encode(raw_result)); - tx_expect = tx_expect.result(result_hex_string.as_str()); - } - sc_query_step = sc_query_step.expect(tx_expect); - if let Some(trace) = &mut debugger.trace { - trace.run_sc_query_step(&sc_query_step); - } - - let mut raw_results = tx_result.result_values; - RequestedResult::multi_decode_or_handle_err(&mut raw_results, PanicErrorHandler).unwrap() + self.sc_query_get_result(typed_sc_query) } } diff --git a/framework/scenario/src/scenario/model/transaction/tx_response.rs b/framework/scenario/src/scenario/model/transaction/tx_response.rs index 5a089b3357..f64cf8573b 100644 --- a/framework/scenario/src/scenario/model/transaction/tx_response.rs +++ b/framework/scenario/src/scenario/model/transaction/tx_response.rs @@ -4,7 +4,7 @@ use multiversx_sdk::data::transaction::{ ApiLogs, ApiSmartContractResult, Events, TransactionOnNetwork, }; -use super::{Log, TxResponseStatus}; +use super::{Log, TxExpect, TxResponseStatus}; const LOG_IDENTIFIER_SC_DEPLOY: &str = "SCDeploy"; const LOG_IDENTIFIER_SIGNAL_ERROR: &str = "signalError"; @@ -24,6 +24,25 @@ pub struct TxResponse { } impl TxResponse { + /// Creates a scenario "expect" field based on the real response. + /// + /// Useful for creating traces that also check the results come out always the same. + pub fn to_expect(&self) -> TxExpect { + if self.tx_error.is_success() { + let mut tx_expect = TxExpect::ok(); + for raw_result in &self.out { + let result_hex_string = format!("0x{}", hex::encode(raw_result)); + tx_expect = tx_expect.result(result_hex_string.as_str()); + } + tx_expect + } else { + TxExpect::err( + self.tx_error.status, + format!("str:{}", self.tx_error.message), + ) + } + } + pub fn from_tx_result(tx_result: TxResult) -> Self { TxResponse { out: tx_result.result_values, diff --git a/framework/scenario/src/scenario/run_list.rs b/framework/scenario/src/scenario/run_list.rs index 90634b6fed..2169d7efb8 100644 --- a/framework/scenario/src/scenario/run_list.rs +++ b/framework/scenario/src/scenario/run_list.rs @@ -54,21 +54,21 @@ impl ScenarioRunner for ScenarioRunnerList { } } - fn run_multi_sc_deploy_step(&mut self, steps: &[ScDeployStep]) { + fn run_multi_sc_deploy_step(&mut self, steps: &mut [ScDeployStep]) { for runner in self.list.iter_mut() { - for step in steps { + for step in steps.iter_mut() { runner.run_sc_deploy_step(step); } } } - fn run_sc_query_step(&mut self, step: &ScQueryStep) { + fn run_sc_query_step(&mut self, step: &mut ScQueryStep) { for runner in self.list.iter_mut() { runner.run_sc_query_step(step); } } - fn run_sc_deploy_step(&mut self, step: &ScDeployStep) { + fn run_sc_deploy_step(&mut self, step: &mut ScDeployStep) { for runner in self.list.iter_mut() { runner.run_sc_deploy_step(step); } diff --git a/framework/scenario/src/scenario/run_trace/scenario_trace.rs b/framework/scenario/src/scenario/run_trace/scenario_trace.rs index 1955489bb8..d940987f56 100644 --- a/framework/scenario/src/scenario/run_trace/scenario_trace.rs +++ b/framework/scenario/src/scenario/run_trace/scenario_trace.rs @@ -86,19 +86,19 @@ impl ScenarioRunner for ScenarioTrace { } } - fn run_multi_sc_deploy_step(&mut self, steps: &[ScDeployStep]) { + fn run_multi_sc_deploy_step(&mut self, steps: &mut [ScDeployStep]) { for step in steps { self.process_address_value(&step.tx.from); self.scenario_trace.steps.push(Step::ScDeploy(step.clone())); } } - fn run_sc_query_step(&mut self, step: &ScQueryStep) { + fn run_sc_query_step(&mut self, step: &mut ScQueryStep) { self.process_address_value(&step.tx.to); self.scenario_trace.steps.push(Step::ScQuery(step.clone())); } - fn run_sc_deploy_step(&mut self, step: &ScDeployStep) { + fn run_sc_deploy_step(&mut self, step: &mut ScDeployStep) { self.process_address_value(&step.tx.from); self.scenario_trace.steps.push(Step::ScDeploy(step.clone())); } diff --git a/framework/scenario/src/scenario/run_trace/scenario_trace_file.rs b/framework/scenario/src/scenario/run_trace/scenario_trace_file.rs index a3ce99570e..28acc842e6 100644 --- a/framework/scenario/src/scenario/run_trace/scenario_trace_file.rs +++ b/framework/scenario/src/scenario/run_trace/scenario_trace_file.rs @@ -45,15 +45,15 @@ impl ScenarioRunner for ScenarioTraceFile { self.with_tracer(|tracer| tracer.run_multi_sc_call_step(steps)); } - fn run_multi_sc_deploy_step(&mut self, steps: &[ScDeployStep]) { + fn run_multi_sc_deploy_step(&mut self, steps: &mut [ScDeployStep]) { self.with_tracer(|tracer| tracer.run_multi_sc_deploy_step(steps)); } - fn run_sc_query_step(&mut self, step: &ScQueryStep) { + fn run_sc_query_step(&mut self, step: &mut ScQueryStep) { self.with_tracer(|tracer| tracer.run_sc_query_step(step)); } - fn run_sc_deploy_step(&mut self, step: &ScDeployStep) { + fn run_sc_deploy_step(&mut self, step: &mut ScDeployStep) { self.with_tracer(|tracer| tracer.run_sc_deploy_step(step)); } diff --git a/framework/scenario/src/scenario/run_vm/sc_call.rs b/framework/scenario/src/scenario/run_vm/sc_call.rs index 5ff417dd9e..e350ce21a4 100644 --- a/framework/scenario/src/scenario/run_vm/sc_call.rs +++ b/framework/scenario/src/scenario/run_vm/sc_call.rs @@ -12,18 +12,15 @@ use multiversx_chain_vm::{ use super::{check_tx_output, tx_input_util::generate_tx_hash, ScenarioVMRunner}; impl ScenarioVMRunner { - /// Adds a SC call step, as specified in the `sc_call_step` argument, then executes it. - pub fn perform_sc_call(&mut self, sc_call_step: &ScCallStep) { - let _ = - self.perform_sc_call_lambda_and_check(sc_call_step, execute_current_tx_context_input); - } - - pub fn perform_sc_call_update_results(&mut self, sc_call_step: &mut ScCallStep) { + /// Adds a SC call step, as specified in the `step` argument, then executes it. + /// + /// The result of the operation gets saved back in the step's response field. + pub fn perform_sc_call_update_results(&mut self, step: &mut ScCallStep) { let tx_result = - self.perform_sc_call_lambda_and_check(sc_call_step, execute_current_tx_context_input); + self.perform_sc_call_lambda_and_check(step, execute_current_tx_context_input); let response = TxResponse::from_tx_result(tx_result); - sc_call_step.response = Some(response); - sc_call_step.trigger_handler(); + step.response = Some(response); + step.trigger_handler(); } /// Adds a SC call step, executes it and retrieves the transaction result ("out" field). diff --git a/framework/scenario/src/scenario/run_vm/sc_deploy.rs b/framework/scenario/src/scenario/run_vm/sc_deploy.rs index bbec7ac4bb..302390a92f 100644 --- a/framework/scenario/src/scenario/run_vm/sc_deploy.rs +++ b/framework/scenario/src/scenario/run_vm/sc_deploy.rs @@ -1,10 +1,5 @@ use crate::{ - multiversx_sc::{ - codec::{CodecFrom, PanicErrorHandler, TopEncodeMulti}, - types::heap::Address, - }, - scenario::model::{ScDeployStep, TypedScDeploy}, - scenario_model::TxResponse, + multiversx_sc::types::heap::Address, scenario::model::ScDeployStep, scenario_model::TxResponse, }; use multiversx_chain_vm::{ @@ -15,6 +10,17 @@ use multiversx_chain_vm::{ use super::{check_tx_output, tx_input_util::generate_tx_hash, ScenarioVMRunner}; impl ScenarioVMRunner { + /// Adds a SC deploy step, as specified in the `step` argument, then executes it. + /// + /// The result of the operation gets saved back in the step's response field. + pub fn perform_sc_deploy_update_results(&mut self, step: &mut ScDeployStep) { + let (new_address, tx_result) = + self.perform_sc_deploy_lambda_and_check(step, execute_current_tx_context_input); + let mut response = TxResponse::from_tx_result(tx_result); + response.new_deployed_address = Some(new_address); + step.response = Some(response); + } + pub fn perform_sc_deploy_lambda( &mut self, sc_deploy_step: &ScDeployStep, @@ -52,45 +58,6 @@ impl ScenarioVMRunner { } (new_address, tx_result) } - - /// Adds a SC deploy step, as specified in the `sc_deploy_step` argument, then executes it. - pub fn perform_sc_deploy(&mut self, sc_deploy_step: &ScDeployStep) { - let _ = self - .perform_sc_deploy_lambda_and_check(sc_deploy_step, execute_current_tx_context_input); - } - - /// Adds a SC deploy step, executes it and retrieves the transaction result ("out" field). - /// - /// The transaction is expected to complete successfully. - /// - /// It takes the `contract_call` argument separately from the SC call step, - /// so we can benefit from type inference in the result. - pub fn perform_sc_deploy_get_result( - &mut self, - typed_sc_deploy: TypedScDeploy, - ) -> (Address, RequestedResult) - where - OriginalResult: TopEncodeMulti, - RequestedResult: CodecFrom, - { - let sc_deploy_step: ScDeployStep = typed_sc_deploy.into(); - let (new_address, tx_result) = - self.perform_sc_deploy_lambda(&sc_deploy_step, execute_current_tx_context_input); - let mut raw_result = tx_result.result_values; - let deser_result = - RequestedResult::multi_decode_or_handle_err(&mut raw_result, PanicErrorHandler) - .unwrap(); - - (new_address, deser_result) - } - - pub fn perform_sc_deploy_update_results(&mut self, sc_deploy_step: &mut ScDeployStep) { - let (new_address, tx_result) = self - .perform_sc_deploy_lambda_and_check(sc_deploy_step, execute_current_tx_context_input); - let mut response = TxResponse::from_tx_result(tx_result); - response.new_deployed_address = Some(new_address); - sc_deploy_step.response = Some(response); - } } fn tx_input_from_deploy(sc_deploy_step: &ScDeployStep) -> TxInput { diff --git a/framework/scenario/src/scenario/run_vm/sc_query.rs b/framework/scenario/src/scenario/run_vm/sc_query.rs index e3e23eec8a..db76103295 100644 --- a/framework/scenario/src/scenario/run_vm/sc_query.rs +++ b/framework/scenario/src/scenario/run_vm/sc_query.rs @@ -8,10 +8,8 @@ use super::{check_tx_output, tx_input_util::generate_tx_hash, ScenarioVMRunner}; impl ScenarioVMRunner { /// Adds a SC query step, as specified in the `sc_query_step` argument, then executes it. - pub fn perform_sc_query(&mut self, sc_query_step: &ScQueryStep) -> TxResult { - self.perform_sc_query_lambda_and_check(sc_query_step, execute_current_tx_context_input) - } - + /// + /// The result of the operation gets saved back in the step's response field. pub fn perform_sc_query_update_results(&mut self, sc_query_step: &mut ScQueryStep) { let tx_result = self.perform_sc_query_lambda_and_check(sc_query_step, execute_current_tx_context_input); diff --git a/framework/scenario/src/scenario/run_vm/vm_runner.rs b/framework/scenario/src/scenario/run_vm/vm_runner.rs index 4fb26c1589..605ce58df0 100644 --- a/framework/scenario/src/scenario/run_vm/vm_runner.rs +++ b/framework/scenario/src/scenario/run_vm/vm_runner.rs @@ -42,18 +42,18 @@ impl ScenarioRunner for ScenarioVMRunner { } } - fn run_multi_sc_deploy_step(&mut self, steps: &[ScDeployStep]) { - for step in steps { - self.perform_sc_deploy(step); + fn run_multi_sc_deploy_step(&mut self, steps: &mut [ScDeployStep]) { + for step in steps.iter_mut() { + self.perform_sc_deploy_update_results(step); } } - fn run_sc_query_step(&mut self, step: &ScQueryStep) { - let _ = self.perform_sc_query(step); + fn run_sc_query_step(&mut self, step: &mut ScQueryStep) { + self.perform_sc_query_update_results(step); } - fn run_sc_deploy_step(&mut self, step: &ScDeployStep) { - self.perform_sc_deploy(step); + fn run_sc_deploy_step(&mut self, step: &mut ScDeployStep) { + self.perform_sc_deploy_update_results(step); } fn run_transfer_step(&mut self, step: &TransferStep) { diff --git a/framework/scenario/src/scenario/scenario_runner.rs b/framework/scenario/src/scenario/scenario_runner.rs index 80df7a0d8b..90330cef0d 100644 --- a/framework/scenario/src/scenario/scenario_runner.rs +++ b/framework/scenario/src/scenario/scenario_runner.rs @@ -16,11 +16,11 @@ pub trait ScenarioRunner { fn run_multi_sc_call_step(&mut self, steps: &mut [ScCallStep]); - fn run_multi_sc_deploy_step(&mut self, steps: &[ScDeployStep]); + fn run_multi_sc_deploy_step(&mut self, steps: &mut [ScDeployStep]); - fn run_sc_query_step(&mut self, step: &ScQueryStep); + fn run_sc_query_step(&mut self, step: &mut ScQueryStep); - fn run_sc_deploy_step(&mut self, step: &ScDeployStep); + fn run_sc_deploy_step(&mut self, step: &mut ScDeployStep); fn run_transfer_step(&mut self, step: &TransferStep); diff --git a/framework/snippets/src/interactor_multi_sc_exec.rs b/framework/snippets/src/interactor_multi_sc_exec.rs index 9d71d83da9..b8035fbae6 100644 --- a/framework/snippets/src/interactor_multi_sc_exec.rs +++ b/framework/snippets/src/interactor_multi_sc_exec.rs @@ -8,7 +8,7 @@ use multiversx_sdk::data::transaction::Transaction; impl Interactor { pub async fn multi_sc_exec(&mut self, mut buffer: StepBuffer<'_>) { - for step in buffer.refs.iter() { + for step in buffer.refs.iter_mut() { step.run_step(&mut self.pre_runners); } @@ -22,7 +22,7 @@ impl Interactor { sc_call_step.set_response(TxResponse::from_network_tx(results.get(i).unwrap().clone())); } - for step in buffer.refs.iter() { + for step in buffer.refs.iter_mut() { step.run_step(&mut self.post_runners); } } diff --git a/framework/snippets/src/interactor_sc_deploy.rs b/framework/snippets/src/interactor_sc_deploy.rs index f5d4beee37..36f19d2bc5 100644 --- a/framework/snippets/src/interactor_sc_deploy.rs +++ b/framework/snippets/src/interactor_sc_deploy.rs @@ -26,7 +26,7 @@ impl Interactor { } } - pub async fn launch_sc_deploy(&mut self, sc_deploy_step: &ScDeployStep) -> String { + pub async fn launch_sc_deploy(&mut self, sc_deploy_step: &mut ScDeployStep) -> String { self.pre_runners.run_sc_deploy_step(sc_deploy_step); let sender_address = &sc_deploy_step.tx.from.value; diff --git a/framework/snippets/src/interactor_tx_spec.rs b/framework/snippets/src/interactor_tx_spec.rs index 584381c3dc..7b4f9d17f1 100644 --- a/framework/snippets/src/interactor_tx_spec.rs +++ b/framework/snippets/src/interactor_tx_spec.rs @@ -11,7 +11,7 @@ pub trait TransactionSpec { fn to_address(&self) -> &AddressValue; - fn run_step(&self, step_runner: &mut dyn ScenarioRunner); + fn run_step(&mut self, step_runner: &mut dyn ScenarioRunner); fn set_response(&mut self, tx_response: TxResponse); } @@ -25,7 +25,7 @@ impl TransactionSpec for ScCallStep { &self.tx.from } - fn run_step(&self, step_runner: &mut dyn ScenarioRunner) { + fn run_step(&mut self, step_runner: &mut dyn ScenarioRunner) { let mut clone = self.clone(); step_runner.run_sc_call_step(&mut clone); // TODO: make mutability uniform } @@ -44,7 +44,7 @@ impl TransactionSpec for ScDeployStep { &self.tx.from } - fn run_step(&self, step_runner: &mut dyn ScenarioRunner) { + fn run_step(&mut self, step_runner: &mut dyn ScenarioRunner) { step_runner.run_sc_deploy_step(self); } From ca05ea148366dafe38e4903cfaab3c56b3349ea7 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 14 Jul 2023 04:36:49 +0300 Subject: [PATCH 07/10] adder interactor cleanup --- .../adder/interact/src/adder_interact.rs | 68 +++++++++---------- .../interact/src/bf_interact.rs | 2 +- 2 files changed, 35 insertions(+), 35 deletions(-) diff --git a/contracts/examples/adder/interact/src/adder_interact.rs b/contracts/examples/adder/interact/src/adder_interact.rs index 455b3dbee5..4becf9a916 100644 --- a/contracts/examples/adder/interact/src/adder_interact.rs +++ b/contracts/examples/adder/interact/src/adder_interact.rs @@ -95,27 +95,27 @@ impl AdderInteract { async fn deploy(&mut self) { self.set_state().await; - let mut typed_sc_deploy = ScDeployStep::new() - .call(self.state.default_adder().init(BigUint::from(0u64))) - .from(&self.wallet_address) - .code_metadata(CodeMetadata::all()) - .contract_code("file:../output/adder.wasm", &InterpreterContext::default()) - .gas_limit("5,000,000") - .expect(TxExpect::ok()); - - self.interactor.sc_deploy(&mut typed_sc_deploy).await; - - let result = typed_sc_deploy.response().new_deployed_address(); - if result.is_err() { - println!("deploy failed: {}", result.err().unwrap()); - return; - } - - let new_address_bech32 = bech32::encode(&result.unwrap()); - println!("new address: {new_address_bech32}"); - - let new_address_expr = format!("bech32:{new_address_bech32}"); - self.state.set_adder_address(&new_address_expr); + self.interactor + .sc_deploy_use_result( + ScDeployStep::new() + .call(self.state.default_adder().init(BigUint::from(0u64))) + .from(&self.wallet_address) + .code_metadata(CodeMetadata::all()) + .contract_code("file:../output/adder.wasm", &InterpreterContext::default()) + .gas_limit("5,000,000") + .expect(TxExpect::ok()), + |new_address, tr| { + tr.result + .unwrap_or_else(|err| panic!("deploy failed: {}", err.message)); + + let new_address_bech32 = bech32::encode(&new_address); + println!("new address: {new_address_bech32}"); + + let new_address_expr = format!("bech32:{new_address_bech32}"); + self.state.set_adder_address(&new_address_expr); + }, + ) + .await; } async fn multi_deploy(&mut self, count: &u8) { @@ -169,19 +169,19 @@ impl AdderInteract { } async fn add(&mut self, value: u64) { - let mut typed_sc_call = ScCallStep::new() - .call(self.state.adder().add(value)) - .from(&self.wallet_address) - .gas_limit("70,000,000") - .expect(TxExpect::ok()); - - self.interactor.sc_call(&mut typed_sc_call).await; - - let result = typed_sc_call.response().handle_signal_error_event(); - if result.is_err() { - println!("performing add failed with: {}", result.err().unwrap()); - return; - } + self.interactor + .sc_call_use_result( + ScCallStep::new() + .call(self.state.adder().add(value)) + .from(&self.wallet_address) + .gas_limit("5,000,000"), + |tr| { + tr.result.unwrap_or_else(|err| { + panic!("performing add failed with: {}", err.message) + }); + }, + ) + .await; println!("successfully performed add"); } diff --git a/contracts/feature-tests/basic-features/interact/src/bf_interact.rs b/contracts/feature-tests/basic-features/interact/src/bf_interact.rs index 98dbc514c0..2d6b467a81 100644 --- a/contracts/feature-tests/basic-features/interact/src/bf_interact.rs +++ b/contracts/feature-tests/basic-features/interact/src/bf_interact.rs @@ -136,7 +136,7 @@ impl BasicFeaturesInteract { ) .await; - println!("successfully performed add"); + println!("successfully performed store_bytes"); } async fn print_length(&mut self) { From d34078386020909cbd79430739180723966c5c0a Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 14 Jul 2023 10:56:09 +0300 Subject: [PATCH 08/10] scenario steps expect from result - refactor, fix --- .../adder/interact/src/adder_interact.rs | 18 ++++++-- .../src/scenario/model/step/step_sc_call.rs | 28 +++---------- .../src/scenario/model/step/step_sc_deploy.rs | 9 ++++ .../src/scenario/model/step/step_sc_query.rs | 9 ++++ .../src/scenario/model/step/typed_sc_call.rs | 20 --------- .../scenario/model/transaction/tx_expect.rs | 42 +++++++++++++++++++ .../scenario/model/transaction/tx_response.rs | 10 +++-- .../scenario/model/value/value_checkable.rs | 6 +++ .../scenario/src/scenario/run_vm/sc_call.rs | 3 +- .../scenario/src/scenario/run_vm/sc_deploy.rs | 2 +- .../scenario/src/scenario/run_vm/sc_query.rs | 22 ++++------ framework/snippets/src/interactor_sc_call.rs | 3 +- .../snippets/src/interactor_sc_deploy.rs | 2 +- framework/snippets/src/interactor_tx_spec.rs | 8 ++-- framework/snippets/src/interactor_vm_query.rs | 6 ++- vm/src/tx_mock/tx_result.rs | 14 ++++--- 16 files changed, 123 insertions(+), 79 deletions(-) diff --git a/contracts/examples/adder/interact/src/adder_interact.rs b/contracts/examples/adder/interact/src/adder_interact.rs index 4becf9a916..1f5a2eb066 100644 --- a/contracts/examples/adder/interact/src/adder_interact.rs +++ b/contracts/examples/adder/interact/src/adder_interact.rs @@ -18,7 +18,7 @@ use multiversx_sc_snippets::{ mandos_system::ScenarioRunner, num_bigint::BigUint, scenario_format::interpret_trait::{InterpretableFrom, InterpreterContext}, - scenario_model::{ScCallStep, ScDeployStep, Scenario, TransferStep, TxExpect}, + scenario_model::{ScCallStep, ScDeployStep, ScQueryStep, Scenario, TransferStep, TxExpect}, standalone::retrieve_account_as_scenario_set_state, test_wallets, ContractInfo, }, @@ -174,7 +174,8 @@ impl AdderInteract { ScCallStep::new() .call(self.state.adder().add(value)) .from(&self.wallet_address) - .gas_limit("5,000,000"), + .gas_limit("5,000,000") + .expect(TxExpect::ok()), |tr| { tr.result.unwrap_or_else(|err| { panic!("performing add failed with: {}", err.message) @@ -187,7 +188,16 @@ impl AdderInteract { } async fn print_sum(&mut self) { - let sum: SingleValue = self.interactor.quick_query(self.state.adder().sum()).await; - println!("sum: {}", sum.into()); + self.interactor + .sc_query_use_result( + ScQueryStep::new() + .call(self.state.adder().sum()) + .expect(TxExpect::ok()), + |tr| { + let sum: SingleValue = tr.result.unwrap(); + println!("sum: {}", sum.into()); + }, + ) + .await; } } diff --git a/framework/scenario/src/scenario/model/step/step_sc_call.rs b/framework/scenario/src/scenario/model/step/step_sc_call.rs index c8c1957da4..64ec489149 100644 --- a/framework/scenario/src/scenario/model/step/step_sc_call.rs +++ b/framework/scenario/src/scenario/model/step/step_sc_call.rs @@ -22,7 +22,6 @@ pub struct ScCallStep { pub tx: Box, pub expect: Option, pub response: Option, - pub response_handlers: Vec>, } impl ScCallStep { @@ -146,27 +145,13 @@ impl ScCallStep { typed.expect_value(expected_value) } - pub fn trigger_handler(&mut self) { - let response = self.response.clone().expect("response not yet ready"); - let mut current_handlers = std::mem::take(&mut self.response_handlers); - for handler in current_handlers.iter_mut() { - handler(&response); + pub fn save_response(&mut self, tx_response: TxResponse) { + if let Some(expect) = &mut self.expect { + if expect.build_from_response { + expect.update_from_response(&tx_response) + } } - } - - pub(crate) fn push_response_handler(&mut self, f: F) - where - F: FnMut(&TxResponse) + 'static, - { - self.response_handlers.push(Box::new(f)); - } - - pub fn with_raw_response(mut self, f: F) -> Self - where - F: FnMut(&TxResponse) + 'static, - { - self.push_response_handler(f); - self + self.response = Some(tx_response); } } @@ -233,7 +218,6 @@ impl Clone for ScCallStep { tx: self.tx.clone(), expect: self.expect.clone(), response: self.response.clone(), - response_handlers: Vec::new(), } } } diff --git a/framework/scenario/src/scenario/model/step/step_sc_deploy.rs b/framework/scenario/src/scenario/model/step/step_sc_deploy.rs index a3e1cb90ff..bc39f0ea65 100644 --- a/framework/scenario/src/scenario/model/step/step_sc_deploy.rs +++ b/framework/scenario/src/scenario/model/step/step_sc_deploy.rs @@ -91,6 +91,15 @@ impl ScDeployStep { } self.into() } + + pub fn save_response(&mut self, response: TxResponse) { + if let Some(expect) = &mut self.expect { + if expect.build_from_response { + expect.update_from_response(&response) + } + } + self.response = Some(response); + } } impl AsMut for ScDeployStep { diff --git a/framework/scenario/src/scenario/model/step/step_sc_query.rs b/framework/scenario/src/scenario/model/step/step_sc_query.rs index b8fc7c8076..2526ca6de4 100644 --- a/framework/scenario/src/scenario/model/step/step_sc_query.rs +++ b/framework/scenario/src/scenario/model/step/step_sc_query.rs @@ -92,4 +92,13 @@ impl ScQueryStep { let typed = self.call(contract_call); typed.expect_value(expected_value) } + + pub fn save_response(&mut self, tx_response: TxResponse) { + if let Some(expect) = &mut self.expect { + if expect.build_from_response { + expect.update_from_response(&tx_response) + } + } + self.response = Some(tx_response); + } } diff --git a/framework/scenario/src/scenario/model/step/typed_sc_call.rs b/framework/scenario/src/scenario/model/step/typed_sc_call.rs index 83fd48128e..1a91f1e05d 100644 --- a/framework/scenario/src/scenario/model/step/typed_sc_call.rs +++ b/framework/scenario/src/scenario/model/step/typed_sc_call.rs @@ -111,26 +111,6 @@ impl TypedScCall { { self.expect(format_expect(expected_value)) } - - pub fn with_result_ok(mut self, mut f: F) -> Self - where - OriginalResult: TopEncodeMulti, - RequestedResult: CodecFrom, - F: FnMut(RequestedResult) + 'static, - { - self.sc_call_step.push_response_handler(move |response| { - assert!( - response.tx_error.is_success(), - "successful transaction expected" - ); - - let mut raw_result = response.out.clone(); - let Ok(decoded) = - RequestedResult::multi_decode_or_handle_err(&mut raw_result, PanicErrorHandler); - f(decoded); - }); - self - } } impl AsMut for TypedScCall { diff --git a/framework/scenario/src/scenario/model/transaction/tx_expect.rs b/framework/scenario/src/scenario/model/transaction/tx_expect.rs index 37f83811df..25832be797 100644 --- a/framework/scenario/src/scenario/model/transaction/tx_expect.rs +++ b/framework/scenario/src/scenario/model/transaction/tx_expect.rs @@ -1,11 +1,16 @@ +use multiversx_chain_vm::tx_mock::result_values_to_string; + use crate::{ scenario::model::{BytesValue, CheckLogs, CheckValue, CheckValueList, U64Value}, scenario_format::{ interpret_trait::{InterpretableFrom, InterpreterContext, IntoRaw}, serde_raw::TxExpectRaw, }, + scenario_model::Checkable, }; +use super::TxResponse; + #[derive(Debug, Clone)] pub struct TxExpect { pub out: CheckValueList, @@ -14,6 +19,7 @@ pub struct TxExpect { pub logs: CheckLogs, pub gas: CheckValue, pub refund: CheckValue, + pub build_from_response: bool, } impl TxExpect { @@ -25,6 +31,7 @@ impl TxExpect { logs: CheckLogs::Star, gas: CheckValue::Star, refund: CheckValue::Star, + build_from_response: true, } } @@ -43,11 +50,13 @@ impl TxExpect { logs: CheckLogs::Star, gas: CheckValue::Star, refund: CheckValue::Star, + build_from_response: true, } } pub fn no_result(mut self) -> Self { self.out = CheckValue::Equal(Vec::new()); + self.build_from_response = false; self } @@ -61,8 +70,40 @@ impl TxExpect { &InterpreterContext::default(), ))); self.out = CheckValue::Equal(check_results); + self.build_from_response = false; self } + + fn check_response(&self, tx_response: &TxResponse) { + assert!( + self.status.check(tx_response.tx_error.status), + "result code mismatch. Want: {}. Have: {}. Message: {}", + self.status, + tx_response.tx_error.status, + &tx_response.tx_error.message, + ); + + assert!( + self.out.check(&tx_response.out), + "bad out value. Want: [{}]. Have: [{}]", + self.out_to_string(), + result_values_to_string(&tx_response.out), + ); + + assert!( + self.message.check(tx_response.tx_error.message.as_str()), + "result message mismatch. Want: {}. Have: {}.", + &self.status, + &tx_response.tx_error.message, + ); + } + + pub(crate) fn update_from_response(&mut self, tx_response: &TxResponse) { + if self.build_from_response { + self.check_response(tx_response); + *self = tx_response.to_expect(); + } + } } impl InterpretableFrom for TxExpect { @@ -74,6 +115,7 @@ impl InterpretableFrom for TxExpect { message: CheckValue::::interpret_from(from.message, context), gas: CheckValue::::interpret_from(from.gas, context), refund: CheckValue::::interpret_from(from.refund, context), + build_from_response: false, } } } diff --git a/framework/scenario/src/scenario/model/transaction/tx_response.rs b/framework/scenario/src/scenario/model/transaction/tx_response.rs index f64cf8573b..dd3cfdc737 100644 --- a/framework/scenario/src/scenario/model/transaction/tx_response.rs +++ b/framework/scenario/src/scenario/model/transaction/tx_response.rs @@ -30,9 +30,13 @@ impl TxResponse { pub fn to_expect(&self) -> TxExpect { if self.tx_error.is_success() { let mut tx_expect = TxExpect::ok(); - for raw_result in &self.out { - let result_hex_string = format!("0x{}", hex::encode(raw_result)); - tx_expect = tx_expect.result(result_hex_string.as_str()); + if self.out.is_empty() { + tx_expect = tx_expect.no_result(); + } else { + for raw_result in &self.out { + let result_hex_string = format!("0x{}", hex::encode(raw_result)); + tx_expect = tx_expect.result(result_hex_string.as_str()); + } } tx_expect } else { diff --git a/framework/scenario/src/scenario/model/value/value_checkable.rs b/framework/scenario/src/scenario/model/value/value_checkable.rs index 7bf7fecdcb..b5a57f27e8 100644 --- a/framework/scenario/src/scenario/model/value/value_checkable.rs +++ b/framework/scenario/src/scenario/model/value/value_checkable.rs @@ -13,6 +13,12 @@ impl Checkable<&[u8]> for BytesValue { } } +impl Checkable<&str> for BytesValue { + fn check(&self, value: &str) -> bool { + self.check(value.as_bytes()) + } +} + impl Checkable<&Vec> for BytesValue { fn check(&self, value: &Vec) -> bool { &self.value == value diff --git a/framework/scenario/src/scenario/run_vm/sc_call.rs b/framework/scenario/src/scenario/run_vm/sc_call.rs index e350ce21a4..abbe9ee6ff 100644 --- a/framework/scenario/src/scenario/run_vm/sc_call.rs +++ b/framework/scenario/src/scenario/run_vm/sc_call.rs @@ -19,8 +19,7 @@ impl ScenarioVMRunner { let tx_result = self.perform_sc_call_lambda_and_check(step, execute_current_tx_context_input); let response = TxResponse::from_tx_result(tx_result); - step.response = Some(response); - step.trigger_handler(); + step.save_response(response); } /// Adds a SC call step, executes it and retrieves the transaction result ("out" field). diff --git a/framework/scenario/src/scenario/run_vm/sc_deploy.rs b/framework/scenario/src/scenario/run_vm/sc_deploy.rs index 302390a92f..67540877c6 100644 --- a/framework/scenario/src/scenario/run_vm/sc_deploy.rs +++ b/framework/scenario/src/scenario/run_vm/sc_deploy.rs @@ -18,7 +18,7 @@ impl ScenarioVMRunner { self.perform_sc_deploy_lambda_and_check(step, execute_current_tx_context_input); let mut response = TxResponse::from_tx_result(tx_result); response.new_deployed_address = Some(new_address); - step.response = Some(response); + step.save_response(response); } pub fn perform_sc_deploy_lambda( diff --git a/framework/scenario/src/scenario/run_vm/sc_query.rs b/framework/scenario/src/scenario/run_vm/sc_query.rs index db76103295..1d3476ab4e 100644 --- a/framework/scenario/src/scenario/run_vm/sc_query.rs +++ b/framework/scenario/src/scenario/run_vm/sc_query.rs @@ -10,18 +10,18 @@ impl ScenarioVMRunner { /// Adds a SC query step, as specified in the `sc_query_step` argument, then executes it. /// /// The result of the operation gets saved back in the step's response field. - pub fn perform_sc_query_update_results(&mut self, sc_query_step: &mut ScQueryStep) { + pub fn perform_sc_query_update_results(&mut self, step: &mut ScQueryStep) { let tx_result = - self.perform_sc_query_lambda_and_check(sc_query_step, execute_current_tx_context_input); + self.perform_sc_query_lambda_and_check(step, execute_current_tx_context_input); let response = TxResponse::from_tx_result(tx_result); - sc_query_step.response = Some(response); + step.save_response(response); } - pub fn perform_sc_query_lambda(&mut self, sc_query_step: &ScQueryStep, f: F) -> TxResult + pub fn perform_sc_query_lambda(&mut self, step: &ScQueryStep, f: F) -> TxResult where F: FnOnce(), { - let tx_input = tx_input_from_query(sc_query_step); + let tx_input = tx_input_from_query(step); let tx_result = self.blockchain_mock.vm.execute_sc_query_lambda( tx_input, &mut self.blockchain_mock.state, @@ -34,17 +34,13 @@ impl ScenarioVMRunner { tx_result } - pub fn perform_sc_query_lambda_and_check( - &mut self, - sc_query_step: &ScQueryStep, - f: F, - ) -> TxResult + pub fn perform_sc_query_lambda_and_check(&mut self, step: &ScQueryStep, f: F) -> TxResult where F: FnOnce(), { - let tx_result = self.perform_sc_query_lambda(sc_query_step, f); - if let Some(tx_expect) = &sc_query_step.expect { - check_tx_output(&sc_query_step.id, tx_expect, &tx_result); + let tx_result = self.perform_sc_query_lambda(step, f); + if let Some(tx_expect) = &step.expect { + check_tx_output(&step.id, tx_expect, &tx_result); } tx_result } diff --git a/framework/snippets/src/interactor_sc_call.rs b/framework/snippets/src/interactor_sc_call.rs index 3518a7a7f5..e6ffaf3450 100644 --- a/framework/snippets/src/interactor_sc_call.rs +++ b/framework/snippets/src/interactor_sc_call.rs @@ -17,8 +17,7 @@ impl Interactor { let tx_hash = self.launch_sc_call(sc_call_step).await; let tx = self.retrieve_tx_on_network(tx_hash.clone()).await; - sc_call_step.response = Some(TxResponse::from_network_tx(tx)); - sc_call_step.trigger_handler(); + sc_call_step.save_response(TxResponse::from_network_tx(tx)); if let Ok(token_identifier) = sc_call_step .response() diff --git a/framework/snippets/src/interactor_sc_deploy.rs b/framework/snippets/src/interactor_sc_deploy.rs index 36f19d2bc5..2ecda29eb1 100644 --- a/framework/snippets/src/interactor_sc_deploy.rs +++ b/framework/snippets/src/interactor_sc_deploy.rs @@ -50,7 +50,7 @@ impl Interactor { let addr = sc_deploy_step.tx.from.clone(); let nonce = tx.nonce; - sc_deploy_step.response = Some(TxResponse::from_network_tx(tx)); + sc_deploy_step.save_response(TxResponse::from_network_tx(tx)); let deploy_address = sc_deploy_step .response() diff --git a/framework/snippets/src/interactor_tx_spec.rs b/framework/snippets/src/interactor_tx_spec.rs index 7b4f9d17f1..7463d37c5e 100644 --- a/framework/snippets/src/interactor_tx_spec.rs +++ b/framework/snippets/src/interactor_tx_spec.rs @@ -30,8 +30,8 @@ impl TransactionSpec for ScCallStep { step_runner.run_sc_call_step(&mut clone); // TODO: make mutability uniform } - fn set_response(&mut self, tx_response: TxResponse) { - self.response = Some(tx_response); + fn set_response(&mut self, response: TxResponse) { + self.save_response(response); } } @@ -48,7 +48,7 @@ impl TransactionSpec for ScDeployStep { step_runner.run_sc_deploy_step(self); } - fn set_response(&mut self, tx_response: TxResponse) { - self.response = Some(tx_response); + fn set_response(&mut self, response: TxResponse) { + self.save_response(response); } } diff --git a/framework/snippets/src/interactor_vm_query.rs b/framework/snippets/src/interactor_vm_query.rs index 1c1128cda8..f4b21e2594 100644 --- a/framework/snippets/src/interactor_vm_query.rs +++ b/framework/snippets/src/interactor_vm_query.rs @@ -2,6 +2,7 @@ use crate::{address_h256_to_erdrs, Interactor}; use log::info; use multiversx_sc_scenario::{ api::StaticApi, + mandos_system::ScenarioRunner, multiversx_sc::{codec::CodecFrom, types::ContractCall}, scenario_model::{ScQueryStep, TxResponse}, }; @@ -44,7 +45,10 @@ impl Interactor { .iter() .map(|result| base64::decode(result).expect("query result base64 decode error")) .collect(); - step.response = Some(TxResponse::from_raw_results(raw_results)); + step.save_response(TxResponse::from_raw_results(raw_results)); + + self.pre_runners.run_sc_query_step(step); + self.post_runners.run_sc_query_step(step); } #[deprecated(since = "0.42.0", note = "Was renamed to `quick_query`.")] diff --git a/vm/src/tx_mock/tx_result.rs b/vm/src/tx_mock/tx_result.rs index d58586de2e..4234f3a207 100644 --- a/vm/src/tx_mock/tx_result.rs +++ b/vm/src/tx_mock/tx_result.rs @@ -142,11 +142,13 @@ impl fmt::Display for TxResult { impl TxResult { pub fn result_values_to_string(&self) -> String { - itertools::join( - self.result_values - .iter() - .map(|val| format!("0x{}", hex::encode(val))), - ", ", - ) + result_values_to_string(&self.result_values) } } + +pub fn result_values_to_string(values: &[Vec]) -> String { + itertools::join( + values.iter().map(|val| format!("0x{}", hex::encode(val))), + ", ", + ) +} From f2de2637ddf7601911b46f364346ad2e78c053e5 Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 14 Jul 2023 11:44:47 +0300 Subject: [PATCH 09/10] removed sc_deploy_use_new_address --- .../adder_mandos_constructed_with_calls_test.rs | 4 ++-- .../scenario/src/facade/scenario_world_steps.rs | 11 ----------- framework/snippets/src/interactor_sc_extra.rs | 16 ---------------- 3 files changed, 2 insertions(+), 29 deletions(-) diff --git a/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs b/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs index 5a565c42cb..2e4e1841e7 100644 --- a/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs +++ b/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs @@ -24,14 +24,14 @@ fn adder_scenario_constructed_raw() { .put_account(owner_address, Account::new().nonce(1)) .new_address(owner_address, 1, "sc:adder"), ) - .sc_deploy_use_new_address( + .sc_deploy_use_result( ScDeployStep::new() .from(owner_address) .contract_code("file:output/adder.wasm", &ic) .call(adder_contract.init(5u32)) .gas_limit("5,000,000") .expect(TxExpect::ok().no_result()), - |new_address| { + |new_address, _| { assert_eq!(new_address, adder_contract.to_address()); }, ) diff --git a/framework/scenario/src/facade/scenario_world_steps.rs b/framework/scenario/src/facade/scenario_world_steps.rs index 119aa9b42b..721dd9a572 100644 --- a/framework/scenario/src/facade/scenario_world_steps.rs +++ b/framework/scenario/src/facade/scenario_world_steps.rs @@ -191,17 +191,6 @@ impl ScenarioWorld { (new_address, typed_response.result.unwrap()) } - pub fn sc_deploy_use_new_address(&mut self, step: S, use_new_address: F) -> &mut Self - where - S: AsMut, - F: FnOnce(Address), - { - self.sc_deploy_use_raw_response(step, |response| { - let new_address = unwrap_new_address(response); - use_new_address(new_address); - }) - } - /// Adds a simple transfer step, then executes it. pub fn transfer_step(&mut self, step: TransferStep) -> &mut Self { self.run_transfer_step(&step); diff --git a/framework/snippets/src/interactor_sc_extra.rs b/framework/snippets/src/interactor_sc_extra.rs index 9a25a0edf6..df14ac9180 100644 --- a/framework/snippets/src/interactor_sc_extra.rs +++ b/framework/snippets/src/interactor_sc_extra.rs @@ -134,22 +134,6 @@ impl Interactor { }) .await } - - pub async fn sc_deploy_use_new_address( - &mut self, - step: S, - use_new_address: F, - ) -> &mut Self - where - S: AsMut, - F: FnOnce(Address), - { - self.sc_deploy_use_raw_response(step, |response| { - let new_address = unwrap_new_address(response); - use_new_address(new_address); - }) - .await - } } fn unwrap_response(opt_response: &Option) -> &TxResponse { From f19c410b4515093fd158848448748d26fe924abb Mon Sep 17 00:00:00 2001 From: Andrei Marinica Date: Fri, 14 Jul 2023 11:59:49 +0300 Subject: [PATCH 10/10] test fix --- .../adder/tests/adder_mandos_constructed_with_calls_test.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs b/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs index 2e4e1841e7..122c208010 100644 --- a/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs +++ b/contracts/examples/adder/tests/adder_mandos_constructed_with_calls_test.rs @@ -31,7 +31,7 @@ fn adder_scenario_constructed_raw() { .call(adder_contract.init(5u32)) .gas_limit("5,000,000") .expect(TxExpect::ok().no_result()), - |new_address, _| { + |new_address, _: TypedResponse<()>| { assert_eq!(new_address, adder_contract.to_address()); }, )