diff --git a/Cargo.lock b/Cargo.lock index d719df3ab56..1bbc894ea59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3753,6 +3753,7 @@ dependencies = [ "pallet-balances", "pallet-gear", "pallet-gear-messenger", + "pallet-gear-program", "pallet-gear-rpc-runtime-api", "pallet-sudo", "pallet-timestamp", diff --git a/examples/autoreply/src/lib.rs b/examples/autoreply/src/lib.rs index 90a59205f62..0c98a3a8179 100644 --- a/examples/autoreply/src/lib.rs +++ b/examples/autoreply/src/lib.rs @@ -18,6 +18,8 @@ #![no_std] +extern crate alloc; + #[cfg(feature = "std")] mod code { include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); @@ -31,6 +33,7 @@ mod wasm; #[cfg(test)] mod tests { + use alloc::vec::Vec; use gstd::ActorId; use gtest::{Program, System}; @@ -58,7 +61,9 @@ mod tests { assert!(!res.main_failed()); // Check whether the auto-reply was received - let reply_received: bool = prog2.read_state().expect("Failed to read state"); + let reply_received: bool = prog2 + .read_state(Vec::::default()) + .expect("Failed to read state"); assert!(reply_received); } } diff --git a/examples/new-meta/tests/read_state.rs b/examples/new-meta/tests/read_state.rs index 7ee30cc09bb..9c7fffdc380 100644 --- a/examples/new-meta/tests/read_state.rs +++ b/examples/new-meta/tests/read_state.rs @@ -10,7 +10,7 @@ fn read_state_bytes_returns_full_state() { let program = initialize_current_program(&system); let actual_state = program - .read_state_bytes() + .read_state_bytes(Default::default()) .expect("Unable to read program state"); let expected_state = Wallet::test_sequence().encode(); @@ -26,7 +26,12 @@ fn read_state_bytes_with_wasm_func_returns_transformed_state() { assert!(META_EXPORTS_V1.contains(&FUNC_NAME)); let actual_state = program - .read_state_bytes_using_wasm(FUNC_NAME, META_WASM_V1.to_vec(), state_args_encoded!()) + .read_state_bytes_using_wasm( + Default::default(), + FUNC_NAME, + META_WASM_V1.to_vec(), + state_args_encoded!(), + ) .expect("Unable to read program state"); let expected_state = Wallet::test_sequence().first().encode(); @@ -47,6 +52,7 @@ fn read_state_bytes_with_parameterized_wasm_func_returns_transformed_state() { let actual_state = program .read_state_bytes_using_wasm( + Default::default(), FUNC_NAME, META_WASM_V2.to_vec(), state_args_encoded!(&other_person), @@ -73,6 +79,7 @@ fn read_state_bytes_with_two_args_wasm_func_returns_transformed_state() { let actual_state = program .read_state_bytes_using_wasm( + Default::default(), FUNC_NAME, META_WASM_V2.to_vec(), state_args_encoded!(name.clone(), surname.clone()), @@ -92,7 +99,9 @@ fn read_state_returns_full_state() { let system = System::new(); let program = initialize_current_program(&system); - let actual_state: Vec = program.read_state().expect("Unable to read program state"); + let actual_state: Vec = program + .read_state(Vec::::default()) + .expect("Unable to read program state"); let expected_state = Wallet::test_sequence(); @@ -107,7 +116,12 @@ fn read_state_with_wasm_func_returns_transformed_state() { assert!(META_EXPORTS_V1.contains(&FUNC_NAME)); let actual_state: Option = program - .read_state_using_wasm(FUNC_NAME, META_WASM_V1.to_vec(), state_args!()) + .read_state_using_wasm( + Vec::::default(), + FUNC_NAME, + META_WASM_V1.to_vec(), + state_args!(), + ) .expect("Unable to read program state"); let expected_state = Wallet::test_sequence().first().cloned(); @@ -128,6 +142,7 @@ fn read_state_with_parameterized_wasm_func_returns_transformed_state() { let actual_state: Option = program .read_state_using_wasm( + Vec::::default(), FUNC_NAME, META_WASM_V2.to_vec(), state_args!(other_person.clone()), @@ -153,6 +168,7 @@ fn read_state_with_two_args_wasm_func_returns_transformed_state() { let actual_state: Option = program .read_state_using_wasm( + Vec::::default(), FUNC_NAME, META_WASM_V2.to_vec(), state_args!(name.clone(), surname.clone()), diff --git a/gtest/src/manager.rs b/gtest/src/manager.rs index 011db58dc03..4a45dda5dae 100644 --- a/gtest/src/manager.rs +++ b/gtest/src/manager.rs @@ -455,7 +455,11 @@ impl ExtManager { /// Call non-void meta function from actor stored in manager. /// Warning! This is a static call that doesn't change actors pages data. - pub(crate) fn read_state_bytes(&mut self, program_id: &ProgramId) -> Result> { + pub(crate) fn read_state_bytes( + &mut self, + payload: Vec, + program_id: &ProgramId, + ) -> Result> { let (actor, _balance) = self .actors .get_mut(program_id) @@ -467,7 +471,7 @@ impl ExtManager { program.code().clone(), Some(program.allocations().clone()), Some(*program_id), - Default::default(), + payload, u64::MAX, self.block_info, ) @@ -483,6 +487,7 @@ impl ExtManager { pub(crate) fn read_state_bytes_using_wasm( &mut self, + payload: Vec, program_id: &ProgramId, fn_name: &str, wasm: Vec, @@ -500,7 +505,7 @@ impl ExtManager { .0; let mut mapping_code_payload = args.unwrap_or_default(); - mapping_code_payload.append(&mut self.read_state_bytes(program_id)?); + mapping_code_payload.append(&mut self.read_state_bytes(payload, program_id)?); core_processor::informational::execute_for_reply::( String::from(fn_name), diff --git a/gtest/src/program.rs b/gtest/src/program.rs index dca23cfb66a..674e92f1531 100644 --- a/gtest/src/program.rs +++ b/gtest/src/program.rs @@ -446,10 +446,10 @@ impl<'a> Program<'a> { } /// Reads the program’s state as a byte vector. - pub fn read_state_bytes(&self) -> Result> { + pub fn read_state_bytes(&self, payload: Vec) -> Result> { self.manager .borrow_mut() - .with_externalities(|this| this.read_state_bytes(&self.id)) + .with_externalities(|this| this.read_state_bytes(payload, &self.id)) } /// Reads the program’s transformed state as a byte vector. The transformed @@ -482,36 +482,37 @@ impl<'a> Program<'a> { /// # let ARG_2 = 0u8; /// //Read state bytes with no arguments passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, Option::>::None)?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, Option::>::None)?; /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, state_args_encoded!())?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, state_args_encoded!())?; /// // Read state bytes with one argument passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, Some(ARG_1.encode()))?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, Some(ARG_1.encode()))?; /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, state_args_encoded!(ARG_1))?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, state_args_encoded!(ARG_1))?; /// // Read state bytes with multiple arguments passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, Some((ARG_1, ARG_2).encode()))?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, Some((ARG_1, ARG_2).encode()))?; /// # let WASM = vec![]; - /// let _ = program.read_state_bytes_using_wasm("fn_name", WASM, state_args_encoded!(ARG_1, ARG_2))?; + /// let _ = program.read_state_bytes_using_wasm(Default::default(), "fn_name", WASM, state_args_encoded!(ARG_1, ARG_2))?; /// # Ok(()) /// # } /// ``` pub fn read_state_bytes_using_wasm( &self, + payload: Vec, fn_name: &str, wasm: Vec, args: Option>, ) -> Result> { self.manager.borrow_mut().with_externalities(|this| { - this.read_state_bytes_using_wasm(&self.id, fn_name, wasm, args) + this.read_state_bytes_using_wasm(payload, &self.id, fn_name, wasm, args) }) } /// Reads and decodes the program's state . - pub fn read_state(&self) -> Result { - let state_bytes = self.read_state_bytes()?; + pub fn read_state(&self, payload: P) -> Result { + let state_bytes = self.read_state_bytes(payload.encode())?; D::decode(&mut state_bytes.as_ref()).map_err(Into::into) } @@ -544,30 +545,32 @@ impl<'a> Program<'a> { /// # let ARG_2 = 0u8; /// //Read state bytes with no arguments passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, Option::<()>::None)?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, Option::<()>::None)?; /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, state_args!())?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, state_args!())?; /// // Read state bytes with one argument passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, Some(ARG_1))?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, Some(ARG_1))?; /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, state_args!(ARG_1))?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, state_args!(ARG_1))?; /// // Read state bytes with multiple arguments passed to wasm. /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, Some((ARG_1, ARG_2)))?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, Some((ARG_1, ARG_2)))?; /// # let WASM = vec![]; - /// let _ = program.read_state_using_wasm("fn_name", WASM, state_args!(ARG_1, ARG_2))?; + /// let _ = program.read_state_using_wasm(Vec::::default(), "fn_name", WASM, state_args!(ARG_1, ARG_2))?; /// # Ok(()) /// # } /// ``` - pub fn read_state_using_wasm( + pub fn read_state_using_wasm( &self, + payload: P, fn_name: &str, wasm: Vec, argument: Option, ) -> Result { let argument_bytes = argument.map(|arg| arg.encode()); - let state_bytes = self.read_state_bytes_using_wasm(fn_name, wasm, argument_bytes)?; + let state_bytes = + self.read_state_bytes_using_wasm(payload.encode(), fn_name, wasm, argument_bytes)?; D::decode(&mut state_bytes.as_ref()).map_err(Into::into) } diff --git a/node/authorship/Cargo.toml b/node/authorship/Cargo.toml index 7b474e043a6..2e59fc84df7 100644 --- a/node/authorship/Cargo.toml +++ b/node/authorship/Cargo.toml @@ -42,6 +42,7 @@ frame-system = { workspace = true, features = ["std"] } prometheus-endpoint.workspace = true [dev-dependencies] +common = { workspace = true, features = ["std"] } sc-transaction-pool.workspace = true frame-support = { workspace = true, features = ["std"] } sp-io = { workspace = true, features = ["std"] } @@ -54,8 +55,9 @@ pallet-timestamp = { workspace = true, features = ["std"] } pallet-balances = { workspace = true, features = ["std"] } pallet-gear = { workspace = true, features = ["std"] } pallet-gear-messenger = { workspace = true, features = ["std"] } +pallet-gear-program = { workspace = true, features = ["std"] } testing = {workspace = true, features = ["vara-native"] } vara-runtime = { workspace = true, features = ["std", "dev"] } -demo-mul-by-const.workspace = true +demo-mul-by-const = { workspace = true, features = ["debug"] } env_logger.workspace = true service = { workspace = true, features = ["dev", "vara-native"] } diff --git a/node/authorship/src/tests.rs b/node/authorship/src/tests.rs index afbc17b4bbd..c845fbbf692 100644 --- a/node/authorship/src/tests.rs +++ b/node/authorship/src/tests.rs @@ -23,10 +23,12 @@ use crate::authorship::*; -use codec::Encode; +use codec::{Decode, Encode}; +use common::Program; use core::convert::TryFrom; use frame_support::{storage::storage_prefix, traits::PalletInfoAccess}; use futures::executor::block_on; +use runtime_primitives::BlockNumber; use sc_client_api::Backend; use sc_transaction_pool::BasicPool; use sc_transaction_pool_api::{ @@ -53,6 +55,7 @@ use testing::{ use vara_runtime::{AccountId, Runtime, RuntimeCall, UncheckedExtrinsic, SLOT_DURATION, VERSION}; const SOURCE: TransactionSource = TransactionSource::External; +const DEFAULT_GAS_LIMIT: u64 = 865_000_000; fn chain_event(header: B::Header) -> ChainEvent where @@ -91,7 +94,7 @@ fn checked_extrinsics(n: u32, signer: AccountId, nonce: &mut u32) -> Vec::name().as_bytes(), + "ProgramStorage".as_bytes(), + ); + let mut iter_args = IterArgs::default(); + iter_args.prefix = Some(&programs_prefix); + + // The fact that 2 init messages out of 5 have been processed means + // that there should be 2 inited programs. + let inited_count = state.pairs(iter_args).unwrap().fold(0u32, |count, pair| { + let value = match pair { + Ok((_key, value)) => value, + _ => return count, + }; + + match Program::::decode(&mut &value[..]) { + Ok(p) if p.is_initialized() => count + 1, + _ => count, + } + }); + assert_eq!(inited_count, 2); } #[test] diff --git a/pallets/gear/src/runtime_api.rs b/pallets/gear/src/runtime_api.rs index 8e615abd448..dd0a224f212 100644 --- a/pallets/gear/src/runtime_api.rs +++ b/pallets/gear/src/runtime_api.rs @@ -17,9 +17,10 @@ // along with this program. If not, see . use super::*; -use crate::queue::QueueStep; +use crate::queue::{ActorResult, QueueStep}; use common::ActiveProgram; use core::convert::TryFrom; +use core_processor::common::PrechargedDispatch; use gear_core::{code::TryNewCodeConfig, pages::WasmPage}; use gear_wasm_instrument::syscalls::SysCallName; @@ -126,10 +127,20 @@ where }; let actor_id = queued_dispatch.destination(); + let dispatch_id = queued_dispatch.id(); + let dispatch_reply = queued_dispatch.reply_details().is_some(); + + let balance = CurrencyOf::::free_balance(&::from_origin( + actor_id.into_origin(), + )); - let actor = ext_manager - .get_actor(actor_id) - .ok_or_else(|| b"Program not found in the storage".to_vec())?; + let get_actor_data = |precharged_dispatch: PrechargedDispatch| { + // At this point gas counters should be changed accordingly so fetch the program data. + match Self::get_active_actor_data(actor_id, dispatch_id, dispatch_reply) { + ActorResult::Data(data) => Ok((precharged_dispatch, data)), + ActorResult::Continue => Err(precharged_dispatch), + } + }; let dispatch_id = queued_dispatch.id(); let success_reply = queued_dispatch @@ -146,8 +157,8 @@ where ext_manager: &mut ext_manager, gas_limit, dispatch: queued_dispatch, - balance: actor.balance, - get_actor_data: |dispatch| Ok((dispatch, actor.executable_data)), + balance: balance.unique_saturated_into(), + get_actor_data, }; let journal = step.execute().unwrap_or_else(|e| unreachable!("{e:?}")); diff --git a/pallets/gear/src/tests.rs b/pallets/gear/src/tests.rs index 47b57d1186b..94852a652e4 100644 --- a/pallets/gear/src/tests.rs +++ b/pallets/gear/src/tests.rs @@ -14317,6 +14317,45 @@ fn gear_block_number_math_adds_up() { }) } +#[test] +fn test_gas_info_of_terminated_program() { + use demo_constructor::{Calls, Scheme}; + + init_logger(); + new_test_ext().execute_with(|| { + // Dies in init + let init_dead = Calls::builder().panic("Die in init"); + let handle_dead = Calls::builder().panic("Called after being terminated!"); + let (_, pid_dead) = utils::submit_constructor_with_args( + USER_1, + b"salt1", + Scheme::predefined(init_dead, handle_dead, Calls::default()), + 0, + ); + + // Sends in handle message do dead program + let handle_proxy = Calls::builder().send(pid_dead.into_bytes(), []); + let (_, proxy_pid) = utils::submit_constructor_with_args( + USER_1, + b"salt2", + Scheme::predefined(Calls::default(), handle_proxy, Calls::default()), + 0, + ); + + run_to_next_block(None); + + let _gas_info = Gear::calculate_gas_info( + USER_1.into_origin(), + HandleKind::Handle(proxy_pid), + EMPTY_PAYLOAD.to_vec(), + 0, + true, + true, + ) + .expect("failed getting gas info"); + }) +} + mod utils { #![allow(unused)]