From df808b16497704e4aa36a79ceea671d6c42b131f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marin=20Ver=C5=A1i=C4=87?= Date: Tue, 29 Oct 2024 17:31:12 +0100 Subject: [PATCH] perf: directly provide payload to wasm triggers and smart contracts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marin Veršić --- crates/iroha/src/secrecy.rs | 2 +- crates/iroha_core/src/smartcontracts/wasm.rs | 107 ++++++------------ crates/iroha_crypto/src/hash.rs | 2 +- crates/iroha_executor/src/lib.rs | 43 ++++--- crates/iroha_smart_contract/src/lib.rs | 17 +-- .../src/entrypoint.rs | 4 +- crates/iroha_trigger/src/lib.rs | 28 ++--- crates/iroha_trigger_derive/src/entrypoint.rs | 4 +- scripts/tests/consistency.sh | 14 +-- 9 files changed, 93 insertions(+), 128 deletions(-) diff --git a/crates/iroha/src/secrecy.rs b/crates/iroha/src/secrecy.rs index 19b4db79dcc..cdba906f476 100644 --- a/crates/iroha/src/secrecy.rs +++ b/crates/iroha/src/secrecy.rs @@ -12,7 +12,7 @@ impl SecretString { } } -const REDACTED: &'static str = "[REDACTED]"; +const REDACTED: &str = "[REDACTED]"; impl Serialize for SecretString { fn serialize(&self, serializer: S) -> Result { diff --git a/crates/iroha_core/src/smartcontracts/wasm.rs b/crates/iroha_core/src/smartcontracts/wasm.rs index 450621f5fdc..d9c532a7f90 100644 --- a/crates/iroha_core/src/smartcontracts/wasm.rs +++ b/crates/iroha_core/src/smartcontracts/wasm.rs @@ -47,8 +47,6 @@ const WASM_MODULE: &str = "iroha"; mod export { pub const EXECUTE_ISI: &str = "execute_instruction"; pub const EXECUTE_QUERY: &str = "execute_query"; - pub const GET_SMART_CONTRACT_CONTEXT: &str = "get_smart_contract_context"; - pub const GET_TRIGGER_CONTEXT: &str = "get_trigger_context"; pub const SET_DATA_MODEL: &str = "set_data_model"; pub const DBG: &str = "dbg"; @@ -771,11 +769,11 @@ where let instance = self.instantiate_module(module, &mut store)?; let validate_fn = Self::get_typed_func(&instance, &mut store, validate_fn_name)?; - let payload = Self::get_validate_payload(&instance, &mut store); + let context = Self::get_validate_context(&instance, &mut store); // NOTE: This function takes ownership of the pointer let offset = validate_fn - .call(&mut store, payload) + .call(&mut store, context) .map_err(ExportFnCallError::from)?; let memory = @@ -797,19 +795,19 @@ where Ok(validation_res) } - fn get_validate_payload( + fn get_validate_context( instance: &Instance, store: &mut Store>>, ) -> WasmUsize { let state = store.data(); - let payload = payloads::Validate { + let context = payloads::Validate { context: payloads::ExecutorContext { authority: state.authority.clone(), curr_block: state.specific_state.curr_block, }, target: state.specific_state.to_validate.clone(), }; - Runtime::encode_payload(instance, store, payload) + Self::encode_payload(instance, store, context) } } @@ -940,14 +938,15 @@ impl<'wrld, 'block: 'wrld, 'state: 'block> Runtime, ) -> Result<()> { let mut store = self.create_store(state); - let smart_contract = self.create_smart_contract(&mut store, bytes)?; + let instance = self.create_smart_contract(&mut store, bytes)?; let main_fn: TypedFunc<_, ()> = - Self::get_typed_func(&smart_contract, &mut store, import::SMART_CONTRACT_MAIN)?; + Self::get_typed_func(&instance, &mut store, import::SMART_CONTRACT_MAIN)?; + let context = Self::get_smart_contract_context(&instance, &mut store); // NOTE: This function takes ownership of the pointer main_fn - .call(&mut store, ()) + .call(&mut store, context) .map_err(ExportFnCallError::from)?; let mut state = store.into_data(); let executed_queries = state.take_executed_queries(); @@ -956,12 +955,16 @@ impl<'wrld, 'block: 'wrld, 'state: 'block> Runtime payloads::SmartContractContext { - payloads::SmartContractContext { + fn get_smart_contract_context( + instance: &Instance, + store: &mut Store>, + ) -> WasmUsize { + let state = store.data(); + let payload = payloads::SmartContractContext { authority: state.authority.clone(), curr_block: state.state.0.curr_block, - } + }; + Runtime::encode_payload(instance, store, payload) } } @@ -1019,10 +1022,11 @@ impl<'wrld, 'block: 'wrld, 'state: 'block> Runtime = Self::get_typed_func(&instance, &mut store, import::TRIGGER_MAIN)?; + let context = Self::get_trigger_context(&instance, &mut store); // NOTE: This function takes ownership of the pointer main_fn - .call(&mut store, ()) + .call(&mut store, context) .map_err(ExportFnCallError::from)?; let mut state = store.into_data(); @@ -1032,14 +1036,18 @@ impl<'wrld, 'block: 'wrld, 'state: 'block> Runtime payloads::TriggerContext { - payloads::TriggerContext { + fn get_trigger_context( + instance: &Instance, + store: &mut Store>, + ) -> WasmUsize { + let state = store.data(); + let payload = payloads::TriggerContext { id: state.specific_state.id.clone(), authority: state.authority.clone(), curr_block: state.state.0.curr_block, event: state.specific_state.triggering_event.clone(), - } + }; + Runtime::encode_payload(instance, store, payload) } } @@ -1302,24 +1310,24 @@ impl<'wrld, 'block, 'state> Runtime = Self::get_typed_func(&instance, &mut store, import::EXECUTOR_MIGRATE)?; - let payload = Self::get_migrate_payload(&instance, &mut store); + let context = Self::get_migrate_context(&instance, &mut store); migrate_fn - .call(&mut store, payload) + .call(&mut store, context) .map_err(ExportFnCallError::from)?; Ok(()) } - fn get_migrate_payload( + fn get_migrate_context( instance: &Instance, store: &mut Store, Migrate>>, ) -> WasmUsize { - let payload = payloads::ExecutorContext { + let context = payloads::ExecutorContext { authority: store.data().authority.clone(), curr_block: store.data().state.0.curr_block, }; - Self::encode_payload(instance, store, payload) + Self::encode_payload(instance, store, context) } } @@ -1436,7 +1444,6 @@ impl<'wrld, 'block, 'state> RuntimeBuilder, export::EXECUTE_ISI => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_instruction(caller, offset, len), export::EXECUTE_QUERY => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_query(caller, offset, len), - export::GET_SMART_CONTRACT_CONTEXT => |caller: ::wasmtime::Caller>| Runtime::get_smart_contract_context(caller), )?; Ok(linker) }) @@ -1456,7 +1463,6 @@ impl<'wrld, 'block, 'state> RuntimeBuilder create_imports!(linker, state::Trigger<'wrld, 'block, 'state>, export::EXECUTE_ISI => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_instruction(caller, offset, len), export::EXECUTE_QUERY => |caller: ::wasmtime::Caller>, offset, len| Runtime::execute_query(caller, offset, len), - export::GET_TRIGGER_CONTEXT => |caller: ::wasmtime::Caller>| Runtime::get_trigger_context(caller), )?; Ok(linker) }) @@ -1895,53 +1901,4 @@ mod tests { Ok(()) } - - #[test] - async fn trigger_related_func_is_not_linked_for_smart_contract() -> Result<(), Error> { - let (authority, _authority_keypair) = gen_account_in("wonderland"); - let kura = Kura::blank_kura_for_testing(); - let query_handle = LiveQueryStore::start_test(); - let state = State::new(world_with_test_account(&authority), kura, query_handle); - - let wat = format!( - r#" - (module - ;; Import host function to execute - (import "iroha" "{get_trigger_payload_fn_name}" - (func $exec_fn (param) (result i32))) - - {memory_and_alloc} - - ;; Function which starts the smartcontract execution - (func (export "{main_fn_name}") (param) - (call $exec_fn) - - ;; No use of return values - drop)) - "#, - main_fn_name = import::SMART_CONTRACT_MAIN, - get_trigger_payload_fn_name = export::GET_TRIGGER_CONTEXT, - // this test doesn't use the memory - memory_and_alloc = memory_and_alloc(""), - ); - - let mut runtime = RuntimeBuilder::::new().build()?; - let block_header = ValidBlock::new_dummy(&KeyPair::random().into_parts().1) - .as_ref() - .header(); - let mut state_block = state.block(block_header); - let mut state_transaction = state_block.transaction(); - let err = runtime - .execute(&mut state_transaction, authority, wat) - .expect_err("Execution should fail"); - state_transaction.apply(); - state_block.commit(); - - assert!(matches!( - err, - Error::Instantiation(InstantiationError::Linker(_)) - )); - - Ok(()) - } } diff --git a/crates/iroha_crypto/src/hash.rs b/crates/iroha_crypto/src/hash.rs index 2c390b68e99..cb446a175e2 100644 --- a/crates/iroha_crypto/src/hash.rs +++ b/crates/iroha_crypto/src/hash.rs @@ -278,7 +278,7 @@ impl FromStr for HashOf { type Err = ParseError; fn from_str(s: &str) -> Result { - Ok(s.parse::().map(Self::from_untyped_unchecked)?) + s.parse::().map(Self::from_untyped_unchecked) } } diff --git a/crates/iroha_executor/src/lib.rs b/crates/iroha_executor/src/lib.rs index a35f01f0ec2..f7998cea092 100644 --- a/crates/iroha_executor/src/lib.rs +++ b/crates/iroha_executor/src/lib.rs @@ -31,35 +31,51 @@ pub mod log { } /// Get context for `validate_transaction()` entrypoint. +/// +/// # Safety +/// +/// It's safe to call this function as long as it's safe to construct, from the given +/// pointer, byte array of prefix length and `Box<[u8]>` containing the encoded object #[cfg(not(test))] -pub fn decode_execute_transaction_context( +pub unsafe fn decode_execute_transaction_context( context: *const u8, ) -> payloads::Validate { - // Safety: ownership of the provided context is transferred into `_decode_from_raw` - unsafe { decode_with_length_prefix_from_raw(context) } + decode_with_length_prefix_from_raw(context) } /// Get context for `validate_instruction()` entrypoint. +/// +/// # Safety +/// +/// It's safe to call this function as long as it's safe to construct, from the given +/// pointer, byte array of prefix length and `Box<[u8]>` containing the encoded object #[cfg(not(test))] -pub fn decode_execute_instruction_context( +pub unsafe fn decode_execute_instruction_context( context: *const u8, ) -> payloads::Validate { - // Safety: ownership of the provided context is transferred into `_decode_from_raw` - unsafe { decode_with_length_prefix_from_raw(context) } + decode_with_length_prefix_from_raw(context) } /// Get context for `validate_query()` entrypoint. +/// +/// # Safety +/// +/// It's safe to call this function as long as it's safe to construct, from the given +/// pointer, byte array of prefix length and `Box<[u8]>` containing the encoded object #[cfg(not(test))] -pub fn decode_validate_query_context(context: *const u8) -> payloads::Validate { - // Safety: ownership of the provided context is transferred into `_decode_from_raw` - unsafe { decode_with_length_prefix_from_raw(context) } +pub unsafe fn decode_validate_query_context(context: *const u8) -> payloads::Validate { + decode_with_length_prefix_from_raw(context) } /// Get context for `migrate()` entrypoint. +/// +/// # Safety +/// +/// It's safe to call this function as long as it's safe to construct, from the given +/// pointer, byte array of prefix length and `Box<[u8]>` containing the encoded object #[cfg(not(test))] -pub fn decode_migrate_context(context: *const u8) -> payloads::ExecutorContext { - // Safety: ownership of the provided context is transferred into `_decode_from_raw` - unsafe { decode_with_length_prefix_from_raw(context) } +pub unsafe fn decode_migrate_context(context: *const u8) -> payloads::ExecutorContext { + decode_with_length_prefix_from_raw(context) } /// Set new [`ExecutorDataModel`]. @@ -227,8 +243,7 @@ impl DataModelBuilder { let account_permissions = host .query(FindPermissionsByAccountId::new(account.id().clone())) .execute() - .unwrap() - .into_iter(); + .unwrap(); for permission in account_permissions.map(|permission| permission.unwrap()) { if !self.permissions.contains(permission.name()) { diff --git a/crates/iroha_smart_contract/src/lib.rs b/crates/iroha_smart_contract/src/lib.rs index 08131cde857..003f6af6d2e 100644 --- a/crates/iroha_smart_contract/src/lib.rs +++ b/crates/iroha_smart_contract/src/lib.rs @@ -226,10 +226,17 @@ pub fn stub_getrandom(_dest: &mut [u8]) -> Result<(), getrandom::Error> { } /// Get context for smart contract `main()` entrypoint. +/// +/// # Safety +/// +/// It's safe to call this function as long as it's safe to construct, from the given +/// pointer, byte array of prefix length and `Box<[u8]>` containing the encoded object #[cfg(not(test))] -pub fn get_smart_contract_context() -> data_model::smart_contract::payloads::SmartContractContext { +pub unsafe fn decode_smart_contract_context( + context: *const u8, +) -> data_model::smart_contract::payloads::SmartContractContext { // Safety: ownership of the returned result is transferred into `_decode_from_raw` - unsafe { decode_with_length_prefix_from_raw(host::get_smart_contract_context()) } + decode_with_length_prefix_from_raw(context) } #[cfg(not(test))] @@ -253,12 +260,6 @@ mod host { /// This function doesn't take ownership of the provided allocation /// but it does transfer ownership of the result to the caller pub(super) fn execute_instruction(ptr: *const u8, len: usize) -> *const u8; - - /// Get context for smart contract `main()` entrypoint. - /// # Warning - /// - /// This function transfers ownership of the result to the caller - pub(super) fn get_smart_contract_context() -> *const u8; } } diff --git a/crates/iroha_smart_contract_derive/src/entrypoint.rs b/crates/iroha_smart_contract_derive/src/entrypoint.rs index 4184c4b2c3f..659abf7f4c8 100644 --- a/crates/iroha_smart_contract_derive/src/entrypoint.rs +++ b/crates/iroha_smart_contract_derive/src/entrypoint.rs @@ -34,9 +34,9 @@ pub fn impl_entrypoint(emitter: &mut Emitter, item: syn::ItemFn) -> TokenStream /// Smart contract entrypoint #[no_mangle] #[doc(hidden)] - unsafe extern "C" fn #main_fn_name() { + unsafe extern "C" fn #main_fn_name(context: *const u8) { let host = ::iroha_smart_contract::Iroha; - let context = ::iroha_smart_contract::get_smart_contract_context(); + let context = ::iroha_trigger::decode_trigger_context(context); #fn_name(host, context) } diff --git a/crates/iroha_trigger/src/lib.rs b/crates/iroha_trigger/src/lib.rs index ffa36a49186..bea08890eaa 100644 --- a/crates/iroha_trigger/src/lib.rs +++ b/crates/iroha_trigger/src/lib.rs @@ -12,26 +12,18 @@ pub mod log { pub use iroha_smart_contract_utils::{debug, error, event, info, log::*, trace, warn}; } +/// Get context for smart contract `main()` entrypoint. +/// +/// # Safety +/// +/// It's safe to call this function as long as it's safe to construct, from the given +/// pointer, byte array of prefix length and `Box<[u8]>` containing the encoded object #[cfg(not(test))] -mod host { - #[link(wasm_import_module = "iroha")] - extern "C" { - /// Get context for trigger `main()` entrypoint. - /// - /// # Warning - /// - /// This function does transfer ownership of the result to the caller - pub(super) fn get_trigger_context() -> *const u8; - } -} - -/// Get context for trigger `main()` entrypoint. -#[cfg(not(test))] -pub fn get_trigger_context() -> data_model::smart_contract::payloads::TriggerContext { +pub unsafe fn decode_trigger_context( + context: *const u8, +) -> data_model::smart_contract::payloads::TriggerContext { // Safety: ownership of the returned result is transferred into `_decode_from_raw` - unsafe { - iroha_smart_contract_utils::decode_with_length_prefix_from_raw(host::get_trigger_context()) - } + iroha_smart_contract_utils::decode_with_length_prefix_from_raw(context) } pub mod prelude { diff --git a/crates/iroha_trigger_derive/src/entrypoint.rs b/crates/iroha_trigger_derive/src/entrypoint.rs index 2337268151d..a2b4e6dbe48 100644 --- a/crates/iroha_trigger_derive/src/entrypoint.rs +++ b/crates/iroha_trigger_derive/src/entrypoint.rs @@ -34,9 +34,9 @@ pub fn impl_entrypoint(emitter: &mut Emitter, item: syn::ItemFn) -> TokenStream /// Smart contract entrypoint #[no_mangle] #[doc(hidden)] - unsafe extern "C" fn #main_fn_name() { + unsafe extern "C" fn #main_fn_name(context: *const u8) { let host = ::iroha_trigger::smart_contract::Iroha; - let context = ::iroha_trigger::get_trigger_context(); + let context = ::iroha_trigger::decode_trigger_context(context); #fn_name(host, context) } diff --git a/scripts/tests/consistency.sh b/scripts/tests/consistency.sh index 36c55204180..ca23bc9c0d8 100755 --- a/scripts/tests/consistency.sh +++ b/scripts/tests/consistency.sh @@ -3,14 +3,14 @@ set -e case $1 in "genesis") - cargo run --release --bin kagami -- genesis generate --executor executor.wasm --wasm-dir libs --genesis-public-key ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4 | diff - defaults/genesis.json || { - echo 'Please re-generate the default genesis with `cargo run --package iroha_kagami --release --bin kagami -- genesis --executor executor.wasm --wasm-dir libs --genesis-public-key ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4 > ./defaults/genesis.json`' + cargo run --package iroha_kagami --bin kagami --release -- genesis generate --executor executor.wasm --wasm-dir libs --genesis-public-key ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4 | diff - defaults/genesis.json || { + echo 'Please re-generate the default genesis with `cargo run --package iroha_kagami --bin kagami --release -- genesis --executor executor.wasm --wasm-dir libs --genesis-public-key ed01204164BF554923ECE1FD412D241036D863A6AE430476C898248B8237D77534CFC4 > ./defaults/genesis.json`' echo 'The assumption here is that the authority of the default genesis transaction is `iroha_test_samples::SAMPLE_GENESIS_ACCOUNT_ID`' exit 1 };; "schema") - cargo run --release --bin kagami -- schema | diff - docs/source/references/schema.json || { - echo 'Please re-generate schema with `cargo run --package iroha_kagami --release --bin kagami -- schema > docs/source/references/schema.json`' + cargo run --package iroha_kagami --bin kagami --release -- schema | diff - docs/source/references/schema.json || { + echo 'Please re-generate schema with `cargo run --package iroha_kagami --bin kagami --release -- schema > docs/source/references/schema.json`' exit 1 };; "docker-compose") @@ -25,15 +25,15 @@ case $1 in } command_base_for_single() { - echo "cargo run --package iroha_swarm --release --bin iroha_swarm -- -p 1 -s Iroha -H -c ./defaults -i hyperledger/iroha:local -b ." + echo "cargo run --package iroha_swarm --bin iroha_swarm --release -- -p 1 -s Iroha -H -c ./defaults -i hyperledger/iroha:local -b ." } command_base_for_multiple_local() { - echo "cargo run --package iroha_swarm --release --bin iroha_swarm -- -p 4 -s Iroha -H -c ./defaults -i hyperledger/iroha:local -b ." + echo "cargo run --package iroha_swarm --bin iroha_swarm --release -- -p 4 -s Iroha -H -c ./defaults -i hyperledger/iroha:local -b ." } command_base_for_default() { - echo "cargo run --package iroha_swarm --release --bin iroha_swarm -- -p 4 -s Iroha -H -c ./defaults -i hyperledger/iroha:dev" + echo "cargo run --package iroha_swarm --bin iroha_swarm --release -- -p 4 -s Iroha -H -c ./defaults -i hyperledger/iroha:dev" }