diff --git a/core/src/executor.rs b/core/src/executor.rs index a0e1df5321..64187c3c37 100644 --- a/core/src/executor.rs +++ b/core/src/executor.rs @@ -6,7 +6,7 @@ use iroha_data_model::{ executor as data_model_executor, isi::InstructionBox, query::{AnyQueryBox, QueryRequest}, - transaction::{Executable, SignedTransaction}, + transaction::{base64_util::Base64Wrapper, Executable, SignedTransaction}, ValidationFail, }; use iroha_logger::trace; @@ -245,7 +245,7 @@ impl Executor { /// - Failed to execute entrypoint of the WASM blob. pub fn migrate( &mut self, - raw_executor: data_model_executor::Executor, + raw_executor: &data_model_executor::Executor, state_transaction: &mut StateTransaction<'_, '_>, authority: &AccountId, ) -> Result<(), wasm::error::Error> { @@ -276,19 +276,21 @@ impl Executor { #[derive(DebugCustom, Clone, Serialize)] #[debug(fmt = "LoadedExecutor {{ module: }}")] pub struct LoadedExecutor { - #[serde(skip)] + #[serde(serialize_with = "wasm::serialize_module_base64")] module: wasmtime::Module, - raw_executor: data_model_executor::Executor, } impl LoadedExecutor { + fn new(module: wasmtime::Module) -> Self { + Self { module } + } + fn load( engine: &wasmtime::Engine, - raw_executor: data_model_executor::Executor, + raw_executor: &data_model_executor::Executor, ) -> Result { Ok(Self { module: wasm::load_module(engine, &raw_executor.wasm)?, - raw_executor, }) } } @@ -316,18 +318,20 @@ impl<'de> DeserializeSeed<'de> for WasmSeed<'_, LoadedExecutor> { M: MapAccess<'de>, { while let Some(key) = map.next_key::()? { - if key.as_str() == "raw_executor" { - let executor: data_model_executor::Executor = map.next_value()?; - return Ok(LoadedExecutor::load(self.loader.engine, executor).unwrap()); + if key.as_str() == "module" { + let wrapper: Base64Wrapper = map.next_value()?; + let module = wasm::deserialize_module(self.loader.engine, wrapper.0) + .map_err(serde::de::Error::custom)?; + return Ok(LoadedExecutor::new(module)); } } - Err(serde::de::Error::missing_field("raw_executor")) + Err(serde::de::Error::missing_field("module")) } } deserializer.deserialize_struct( "LoadedExecutor", - &["raw_executor"], + &["module"], LoadedExecutorVisitor { loader: &self }, ) } diff --git a/core/src/smartcontracts/isi/world.rs b/core/src/smartcontracts/isi/world.rs index 4b23e49321..a310ce096c 100644 --- a/core/src/smartcontracts/isi/world.rs +++ b/core/src/smartcontracts/isi/world.rs @@ -408,7 +408,7 @@ pub mod isi { // Also it's a cheap operation. let mut upgraded_executor = state_transaction.world.executor.clone(); upgraded_executor - .migrate(raw_executor, state_transaction, authority) + .migrate(&raw_executor, state_transaction, authority) .map_err(|migration_error| { InvalidParameterError::Wasm(format!( "{:?}", diff --git a/core/src/smartcontracts/wasm.rs b/core/src/smartcontracts/wasm.rs index 269735c2c7..2b8066fcd2 100644 --- a/core/src/smartcontracts/wasm.rs +++ b/core/src/smartcontracts/wasm.rs @@ -14,12 +14,14 @@ use iroha_data_model::{ prelude::*, query::{parameters::QueryId, AnyQueryBox, QueryOutput, QueryRequest, QueryResponse}, smart_contract::payloads::{self, Validate}, + transaction::base64_util::Base64Wrapper, Level as LogLevel, ValidationFail, }; use iroha_logger::debug; // NOTE: Using error_span so that span info is logged on every event use iroha_logger::{error_span as wasm_log_span, prelude::tracing::Span}; use iroha_wasm_codec::{self as codec, WasmUsize}; +use serde::Serialize; use wasmtime::{ Caller, Config as WasmtimeConfig, Engine, Linker, Module, Store, StoreLimits, StoreLimitsBuilder, TypedFunc, @@ -127,6 +129,8 @@ pub mod error { Finalization(#[source] crate::query::store::Error), /// Failed to load module ModuleLoading(#[source] WasmtimeError), + /// Failed to deserialize module + ModuleDeserialization(#[source] WasmtimeError), /// Module could not be instantiated Instantiation(#[from] InstantiationError), /// Export error @@ -251,6 +255,23 @@ pub fn load_module(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result(module: &Module, serializer: S) -> Result +where + S: serde::Serializer, +{ + let bytes = module.serialize().map_err(serde::ser::Error::custom)?; + let wrapper = Base64Wrapper(bytes); + wrapper.serialize(serializer) +} + +/// Deserialize [`Module`]. Accepts only output of [`Module::serialize`]. +#[allow(unsafe_code)] +pub(crate) fn deserialize_module(engine: &Engine, bytes: impl AsRef<[u8]>) -> Result { + // SAFETY: `Module::deserialize` is safe when calling for bytes received from `Module::serialize`. + // We store serialization result on disk and then load it back so should be ok. + unsafe { Module::deserialize(engine, bytes).map_err(Error::ModuleDeserialization) } +} + /// Create [`Engine`] with a predefined configuration. /// /// # Panics diff --git a/data_model/src/transaction.rs b/data_model/src/transaction.rs index eb0a70aa7d..ef57fe45b1 100644 --- a/data_model/src/transaction.rs +++ b/data_model/src/transaction.rs @@ -423,6 +423,17 @@ mod base64 { } } +/// Internal module for use in `iroha_core` +#[cfg(feature = "transparent_api")] +pub mod base64_util { + use serde::{Deserialize, Serialize}; + + /// Wrapper for `Vec` which can be serialized and deserialized as base64 + #[derive(Deserialize, Serialize)] + #[serde(transparent)] + pub struct Base64Wrapper(#[serde(with = "super::base64")] pub Vec); +} + pub mod error { //! Module containing errors that can occur in transaction lifecycle pub use self::model::*;