From a702bd539cc6d40f1dda9915982dfbdb6593dbdd Mon Sep 17 00:00:00 2001 From: Graydon Hoare Date: Thu, 7 Mar 2024 13:14:48 -0800 Subject: [PATCH] WIP Add an eager per-host ModuleCache --- .../common/cost_types/wasm_insn_exec.rs | 4 +- soroban-env-host/src/budget/wasmi_helper.rs | 9 +- soroban-env-host/src/host.rs | 17 +- soroban-env-host/src/host/lifecycle.rs | 52 +- soroban-env-host/src/vm.rs | 489 ++---------------- 5 files changed, 85 insertions(+), 486 deletions(-) diff --git a/soroban-env-host/benches/common/cost_types/wasm_insn_exec.rs b/soroban-env-host/benches/common/cost_types/wasm_insn_exec.rs index d54f4435b..2b86f53e8 100644 --- a/soroban-env-host/benches/common/cost_types/wasm_insn_exec.rs +++ b/soroban-env-host/benches/common/cost_types/wasm_insn_exec.rs @@ -55,7 +55,7 @@ pub fn wasm_module_with_n_globals(n: usize) -> Vec { pub fn wasm_module_with_n_imports(n: usize) -> Vec { let mut me = ModEmitter::default(); let names = Vm::get_all_host_functions(); - for (module,name,arity) in names.iter().take(n) { + for (module, name, arity) in names.iter().take(n) { if *module == "t" { continue; } @@ -157,7 +157,7 @@ pub fn wasm_module_with_n_types(mut n: usize) -> Vec { } pub fn wasm_module_with_n_elem_segments(n: usize) -> Vec { - let me = ModEmitter::from_configs(1,n as u32); + let me = ModEmitter::from_configs(1, n as u32); let mut fe = me.func(Arity(0), 0); fe.push(Symbol::try_from_small_str("pass").unwrap()); let (mut me, f) = fe.finish(); diff --git a/soroban-env-host/src/budget/wasmi_helper.rs b/soroban-env-host/src/budget/wasmi_helper.rs index b0c94e379..b976d4c0e 100644 --- a/soroban-env-host/src/budget/wasmi_helper.rs +++ b/soroban-env-host/src/budget/wasmi_helper.rs @@ -1,5 +1,8 @@ use crate::{ - budget::AsBudget, host::error::TryBorrowOrErr, xdr::ContractCostType, Host, HostError, + budget::{AsBudget, Budget}, + host::error::TryBorrowOrErr, + xdr::ContractCostType, + Host, HostError, }; use wasmi::{errors, FuelConsumptionMode, FuelCosts, ResourceLimiter}; @@ -110,9 +113,9 @@ pub(crate) fn load_calibrated_fuel_costs() -> FuelCosts { fuel_costs } -pub(crate) fn get_wasmi_config(host: &Host) -> Result { +pub(crate) fn get_wasmi_config(budget: &Budget) -> Result { let mut config = wasmi::Config::default(); - let fuel_costs = host.as_budget().0.try_borrow_or_err()?.fuel_costs; + let fuel_costs = budget.0.try_borrow_or_err()?.fuel_costs; // Turn off most optional wasm features, leaving on some post-MVP features // commonly enabled by Rust and Clang. Make sure all unused features are diff --git a/soroban-env-host/src/host.rs b/soroban-env-host/src/host.rs index 33b8aecc4..48a21b5ab 100644 --- a/soroban-env-host/src/host.rs +++ b/soroban-env-host/src/host.rs @@ -10,6 +10,7 @@ use crate::{ impl_wrapping_obj_to_num, num::*, storage::Storage, + vm::ModuleCache, xdr::{ int128_helpers, AccountId, Asset, ContractCostType, ContractEventType, ContractExecutable, ContractIdPreimage, ContractIdPreimageFromAddress, CreateContractArgs, Duration, Hash, @@ -91,6 +92,7 @@ pub struct CoverageScoreboard { #[derive(Clone, Default)] struct HostImpl { + module_cache: RefCell>, source_account: RefCell>, ledger: RefCell>, objects: RefCell>, @@ -198,7 +200,12 @@ macro_rules! impl_checked_borrow_helpers { } }; } - +impl_checked_borrow_helpers!( + module_cache, + Option, + try_borrow_module_cache, + try_borrow_module_cache_mut +); impl_checked_borrow_helpers!( source_account, Option, @@ -327,6 +334,7 @@ impl Host { #[cfg(all(not(target_family = "wasm"), feature = "tracy"))] let _client = tracy_client::Client::start(); Self(Rc::new(HostImpl { + module_cache: RefCell::new(None), source_account: RefCell::new(None), ledger: RefCell::new(None), objects: Default::default(), @@ -357,6 +365,13 @@ impl Host { })) } + pub fn maybe_add_module_cache(&self) -> Result<(), HostError> { + if self.get_ledger_protocol_version()? > ModuleCache::MIN_LEDGER_VERSION { + *self.try_borrow_module_cache_mut()? = Some(ModuleCache::new(self)?); + } + Ok(()) + } + pub fn set_source_account(&self, source_account: AccountId) -> Result<(), HostError> { *self.try_borrow_source_account_mut()? = Some(source_account); Ok(()) diff --git a/soroban-env-host/src/host/lifecycle.rs b/soroban-env-host/src/host/lifecycle.rs index ca02fec20..005235649 100644 --- a/soroban-env-host/src/host/lifecycle.rs +++ b/soroban-env-host/src/host/lifecycle.rs @@ -6,12 +6,12 @@ use crate::{ metered_write_xdr, ContractReentryMode, CreateContractArgs, }, xdr::{ - Asset, ContractCodeCostInputs, ContractCodeEntry, ContractCodeEntryExt, - ContractCodeEntryV1, ContractDataDurability, ContractExecutable, ContractIdPreimage, + Asset, ContractCodeEntry, ContractCodeEntryExt, ContractCodeEntryV1, + ContractDataDurability, ContractExecutable, ContractIdPreimage, ContractIdPreimageFromAddress, ExtensionPoint, Hash, LedgerKey, LedgerKeyContractCode, ScAddress, ScErrorCode, ScErrorType, }, - AddressObject, BytesObject, Host, HostError, Symbol, TryFromVal, Vm, + AddressObject, BytesObject, Host, HostError, Symbol, TryFromVal, }; use std::rc::Rc; @@ -165,7 +165,7 @@ impl Host { ) })?; - let cost_inputs: ContractCodeCostInputs; + let mut ext: ContractCodeEntryExt = ContractCodeEntryExt::V0; // Instantiate a temporary / throwaway VM using this wasm. This will do // both quick checks like "does this wasm have the right protocol number @@ -179,26 +179,27 @@ impl Host { // Allow a zero-byte contract when testing, as this is used to make // native test contracts behave like wasm. They will never be // instantiated, this is just to exercise their storage logic. - cost_inputs = ContractCodeCostInputs { - n_instructions: 0, - n_functions: 0, - n_globals: 0, - n_table_entries: 0, - n_types: 0, - n_data_segments: 0, - n_elem_segments: 0, - n_imports: 0, - n_exports: 0, - n_memory_pages: 0, - }; } else { - let check_vm = Vm::new( - self, - Hash(hash_bytes.metered_clone(self)?), - wasm_bytes_m.as_slice(), - None, - )?; - cost_inputs = check_vm.get_contract_code_cost_inputs(); + let config = crate::budget::get_wasmi_config(self.as_budget())?; + let engine = wasmi::Engine::new(&config); + let module = + crate::vm::ParsedModule::new(self, &engine, wasm_bytes_m.as_slice(), None)?; + // We introduce refined instantiation cost models at the same protocol as we introduce module caching. + if self.get_ledger_protocol_version()? >= super::ModuleCache::MIN_LEDGER_VERSION { + if let Some(cost_inputs) = module.cost_inputs.clone() { + ext = ContractCodeEntryExt::V1(ContractCodeEntryV1 { + ext: ExtensionPoint::V0, + cost_inputs, + }); + } else { + return Err(self.err( + ScErrorType::Value, + ScErrorCode::InternalError, + "module cost inputs were not calculated", + &[], + )); + } + } } let hash_obj = self.add_host_object(self.scbytes_from_slice(hash_bytes.as_slice())?)?; @@ -216,10 +217,7 @@ impl Host { self.with_mut_storage(|storage| { let data = ContractCodeEntry { hash: Hash(hash_bytes), - ext: ContractCodeEntryExt::V1(ContractCodeEntryV1 { - ext: ExtensionPoint::V0, - cost_inputs, - }), + ext, code: wasm_bytes_m, }; storage.put( diff --git a/soroban-env-host/src/vm.rs b/soroban-env-host/src/vm.rs index d6b14d99d..f54a2bc68 100644 --- a/soroban-env-host/src/vm.rs +++ b/soroban-env-host/src/vm.rs @@ -11,6 +11,8 @@ mod dispatch; mod fuel_refillable; mod func_info; +mod module_cache; +mod parsed_module; #[cfg(feature = "bench")] pub(crate) use dispatch::dummy0; @@ -19,26 +21,22 @@ pub(crate) use dispatch::protocol_gated_dummy; use crate::{ budget::{get_wasmi_config, AsBudget, Budget}, - err, host::{ error::TryBorrowOrErr, metered_clone::MeteredContainer, metered_hash::{CountingHasher, MeteredHash}, }, - meta::{self, get_ledger_protocol_version}, - xdr::{ - ContractCodeCostInputs, ContractCostType, Hash, Limited, ReadXdr, ScEnvMetaEntry, - ScErrorCode, ScErrorType, - }, + xdr::{ContractCodeCostInputs, ContractCostType, Hash, ScErrorCode, ScErrorType}, ConversionError, Host, HostError, Symbol, SymbolStr, TryIntoVal, Val, WasmiMarshal, - DEFAULT_XDR_RW_LIMITS, }; -use std::{cell::RefCell, collections::BTreeSet, io::Cursor, rc::Rc, time::Instant}; +use std::{cell::RefCell, collections::BTreeSet, rc::Rc, time::Instant}; use fuel_refillable::FuelRefillable; use func_info::HOST_FUNCTIONS; +pub(crate) use module_cache::ModuleCache; +pub(crate) use parsed_module::ParsedModule; -use wasmi::{Engine, Instance, Linker, Memory, Module, Store, Value}; +use wasmi::{Instance, Linker, Memory, Store, Value}; use crate::VmCaller; use wasmi::{Caller, StoreContextMut}; @@ -59,11 +57,8 @@ const MAX_VM_ARGS: usize = 32; /// will fail. pub struct Vm { pub(crate) contract_id: Hash, - // TODO: consider moving store and possibly module to Host so they can be - // recycled across calls. Or possibly beyond, to be recycled across txs. - // https://github.com/stellar/rs-soroban-env/issues/827 - module: Module, - cost_inputs: ContractCodeCostInputs, + #[allow(dead_code)] + module: Rc, store: RefCell>, instance: Instance, pub(crate) memory: Option, @@ -76,134 +71,6 @@ impl std::hash::Hash for Vm { } impl Vm { - fn check_contract_interface_version( - host: &Host, - interface_version: u64, - ) -> Result<(), HostError> { - let want_proto = { - let ledger_proto = host.get_ledger_protocol_version()?; - let env_proto = get_ledger_protocol_version(meta::INTERFACE_VERSION); - if ledger_proto <= env_proto { - // ledger proto should be before or equal to env proto - ledger_proto - } else { - return Err(err!( - host, - (ScErrorType::Context, ScErrorCode::InternalError), - "ledger protocol number is ahead of supported env protocol number", - ledger_proto, - env_proto - )); - } - }; - - // Not used when "next" is enabled - #[cfg(not(feature = "next"))] - let got_pre = meta::get_pre_release_version(interface_version); - - let got_proto = get_ledger_protocol_version(interface_version); - - if got_proto < want_proto { - // Old protocols are finalized, we only support contracts - // with similarly finalized (zero) prerelease numbers. - // - // Note that we only enable this check if the "next" feature isn't enabled - // because a "next" stellar-core can still run a "curr" test using non-finalized - // test wasms. The "next" feature isn't safe for production and is meant to - // simulate the protocol version after the one currently supported in - // stellar-core, so bypassing this check for "next" is safe. - #[cfg(not(feature = "next"))] - if got_pre != 0 { - return Err(err!( - host, - (ScErrorType::WasmVm, ScErrorCode::InvalidInput), - "contract pre-release number for old protocol is nonzero", - got_pre - )); - } - } else if got_proto == want_proto { - // Relax this check as well for the "next" feature to allow for flexibility while testing. - // stellar-core can pass in an older protocol version, in which case the pre-release version - // will not match up with the "next" feature (The "next" pre-release version is always 1). - #[cfg(not(feature = "next"))] - { - // Current protocol might have a nonzero prerelease number; we will - // allow it only if it matches the current prerelease exactly. - let want_pre = meta::get_pre_release_version(meta::INTERFACE_VERSION); - if want_pre != got_pre { - return Err(err!( - host, - (ScErrorType::WasmVm, ScErrorCode::InvalidInput), - "contract pre-release number for current protocol does not match host", - got_pre, - want_pre - )); - } - } - } else { - // Future protocols we don't allow. It might be nice (in the sense - // of "allowing uploads of a future-protocol contract that will go - // live as soon as the network upgrades to it") but there's a risk - // that the "future" protocol semantics baked in to a contract - // differ from the final semantics chosen by the network, so to be - // conservative we avoid even allowing this. - return Err(err!( - host, - (ScErrorType::WasmVm, ScErrorCode::InvalidInput), - "contract protocol number is newer than host", - got_proto - )); - } - Ok(()) - } - - fn check_meta_section(host: &Host, m: &Module) -> Result { - if let Some(env_meta) = Self::module_custom_section(m, meta::ENV_META_V0_SECTION_NAME) { - let mut limits = DEFAULT_XDR_RW_LIMITS; - limits.len = env_meta.len(); - let mut cursor = Limited::new(Cursor::new(env_meta), limits); - if let Some(env_meta_entry) = ScEnvMetaEntry::read_xdr_iter(&mut cursor).next() { - let ScEnvMetaEntry::ScEnvMetaKindInterfaceVersion(v) = - host.map_err(env_meta_entry)?; - Vm::check_contract_interface_version(host, v)?; - Ok(v) - } else { - Err(host.err( - ScErrorType::WasmVm, - ScErrorCode::InvalidInput, - "contract missing environment interface version", - &[], - )) - } - } else { - Err(host.err( - ScErrorType::WasmVm, - ScErrorCode::InvalidInput, - "contract missing metadata section", - &[], - )) - } - } - - fn check_max_args(host: &Host, m: &Module) -> Result<(), HostError> { - for e in m.exports() { - match e.ty() { - wasmi::ExternType::Func(f) => { - if f.params().len() > MAX_VM_ARGS || f.results().len() > MAX_VM_ARGS { - return Err(host.err( - ScErrorType::WasmVm, - ScErrorCode::InvalidInput, - "Too many arguments or results in wasm export", - &[], - )); - } - } - _ => (), - } - } - Ok(()) - } - #[cfg(feature = "testutils")] pub fn get_all_host_functions() -> Vec<(&'static str, &'static str, u32)> { HOST_FUNCTIONS @@ -219,88 +86,32 @@ impl Vm { module_wasm_code: &[u8], code_cost_inputs: Option, ) -> Result, HostError> { - match &code_cost_inputs { - // When we have a set of cost inputs, we charge each of them. This - // is the "cheap" path where we're going to charge a model cost that - // is much closer to the true cost of instantiating the contract, - // modeled as a variety of different linear terms. - Some(inputs) => { - host.charge_budget( - ContractCostType::VmInstantiateInstructions, - Some(inputs.n_instructions as u64), - )?; - host.charge_budget( - ContractCostType::VmInstantiateFunctions, - Some(inputs.n_functions as u64), - )?; - host.charge_budget( - ContractCostType::VmInstantiateGlobals, - Some(inputs.n_globals as u64), - )?; - host.charge_budget( - ContractCostType::VmInstantiateTableEntries, - Some(inputs.n_table_entries as u64), - )?; - host.charge_budget( - ContractCostType::VmInstantiateTypes, - Some(inputs.n_types as u64), - )?; - host.charge_budget( - ContractCostType::VmInstantiateDataSegments, - Some(inputs.n_data_segments as u64), - )?; - host.charge_budget( - ContractCostType::VmInstantiateElemSegments, - Some(inputs.n_elem_segments as u64), - )?; - host.charge_budget( - ContractCostType::VmInstantiateImports, - Some(inputs.n_imports as u64), - )?; - host.charge_budget( - ContractCostType::VmInstantiateExports, - Some(inputs.n_exports as u64), - )?; - host.charge_budget( - ContractCostType::VmInstantiateMemoryPages, - Some(inputs.n_memory_pages as u64), - )?; - } - None => { - // When we don't have a set of cost inputs, either because the - // contract predates storing cost inputs or because we're doing - // an upload-time analysis of an unknown contract, we charge - // based on the byte size and assume the worst-case cost for - // each byte. This is the "expensive" path, but we only have to - // charge it during upload, after which we'll store the cost - // inputs for use in future instantiations. - host.charge_budget( - ContractCostType::VmInstantiation, - Some(module_wasm_code.len() as u64), - )?; - } - } - - let config = get_wasmi_config(host)?; - let engine = Engine::new(&config); - let module = { - let _span0 = tracy_span!("parse module"); - host.map_err(Module::new(&engine, module_wasm_code))? - }; - - Self::check_max_args(host, &module)?; - let interface_version = Self::check_meta_section(host, &module)?; - let contract_proto = get_ledger_protocol_version(interface_version); - - let cost_inputs = match code_cost_inputs { - Some(inputs) => inputs, - None => Self::extract_contract_cost_inputs(host.clone(), module_wasm_code)?, - }; + let _span = tracy_span!("Vm::instantiate"); + + let (parsed_module, mut linker, mut store) = + if let Some(cache) = &*host.try_borrow_module_cache()? { + let parsed_module = cache.get_module(host, &contract_id)?; + let linker = >::new(&cache.engine); + let store = Store::new(&cache.engine, host.clone()); + (parsed_module, linker, store) + } else { + let config = get_wasmi_config(host.as_budget())?; + let engine = wasmi::Engine::new(&config); + let parsed_module = Rc::new(ParsedModule::new( + host, + &engine, + module_wasm_code, + code_cost_inputs, + )?); + let linker = >::new(&engine); + let store = Store::new(&engine, host.clone()); + (parsed_module, linker, store) + }; - let mut store = Store::new(&engine, host.clone()); store.limiter(|host| host); - let module_imports: BTreeSet<(&str, &str)> = module + let module_imports: BTreeSet<(&str, &str)> = parsed_module + .module .imports() .filter(|i| i.ty().func().is_some()) .map(|i| { @@ -310,8 +121,6 @@ impl Vm { }) .collect(); - let mut linker = >::new(&engine); - { // We perform link-time protocol version gating here. // Reasons for doing link-time instead of run-time check: @@ -333,14 +142,14 @@ impl Vm { let ledger_proto = host.with_ledger_info(|li| Ok(li.protocol_version))?; for hf in HOST_FUNCTIONS { if let Some(min_proto) = hf.min_proto { - if contract_proto < min_proto || ledger_proto < min_proto { + if parsed_module.proto_version < min_proto || ledger_proto < min_proto { // We skip linking this hf instead of returning an error // because we have to support old contracts during replay. continue; } } if let Some(max_proto) = hf.max_proto { - if contract_proto > max_proto || ledger_proto > max_proto { + if parsed_module.proto_version > max_proto || ledger_proto > max_proto { // We skip linking this hf instead of returning an error // because we have to support old contracts during replay. continue; @@ -362,7 +171,7 @@ impl Vm { let not_started_instance = { let _span0 = tracy_span!("instantiate module"); - host.map_err(linker.instantiate(&mut store, &module))? + host.map_err(linker.instantiate(&mut store, &parsed_module.module))? }; let instance = host.map_err( @@ -382,8 +191,7 @@ impl Vm { // boundary. Ok(Rc::new(Self { contract_id, - module, - cost_inputs, + module: parsed_module, store: RefCell::new(store), instance, memory, @@ -431,215 +239,6 @@ impl Vm { } } - fn check_const_expr_simple(host: &Host, expr: &wasmparser::ConstExpr) -> Result<(), HostError> { - use wasmparser::Operator::*; - let mut op = expr.get_operators_reader(); - while !op.eof() { - match host.map_err(op.read())? { - I32Const { .. } | I64Const { .. } | RefFunc { .. } | RefNull { .. } | End => (), - _ => { - return Err(host.err( - ScErrorType::WasmVm, - ScErrorCode::InvalidInput, - "unsupported complex wasm constant expression", - &[], - )) - } - } - } - Ok(()) - } - - fn extract_contract_cost_inputs( - host: Host, - wasm: &[u8], - ) -> Result { - use wasmparser::{ElementItems, ElementKind, Parser, Payload::*, TableInit}; - - if !Parser::is_core_wasm(wasm) { - return Err(host.err( - ScErrorType::WasmVm, - ScErrorCode::InvalidInput, - "unsupported non-core wasm module", - &[], - )); - } - - let mut costs = ContractCodeCostInputs { - n_instructions: 0, - n_functions: 0, - n_globals: 0, - n_table_entries: 0, - n_types: 0, - n_data_segments: 0, - n_elem_segments: 0, - n_imports: 0, - n_exports: 0, - n_memory_pages: 0, - }; - - let parser = Parser::new(0); - let mut elements: u32 = 0; - let mut data: u32 = 0; - for section in parser.parse_all(wasm) { - let section = host.map_err(section)?; - match section { - // Ignored sections. - Version { .. } - | DataCountSection { .. } - | CustomSection(_) - | CodeSectionStart { .. } - | End(_) => (), - - // Component-model stuff or other unsupported sections. Error out. - StartSection { .. } - | ModuleSection { .. } - | InstanceSection(_) - | CoreTypeSection(_) - | ComponentSection { .. } - | ComponentInstanceSection(_) - | ComponentAliasSection(_) - | ComponentTypeSection(_) - | ComponentCanonicalSection(_) - | ComponentStartSection { .. } - | ComponentImportSection(_) - | ComponentExportSection(_) - | TagSection(_) - | UnknownSection { .. } => { - return Err(host.err( - ScErrorType::WasmVm, - ScErrorCode::InvalidInput, - "unsupported wasm section", - &[], - )) - } - - MemorySection(s) => { - for mem in s { - let mem = host.map_err(mem)?; - if mem.memory64 { - return Err(host.err( - ScErrorType::WasmVm, - ScErrorCode::InvalidInput, - "unsupported 64-bit memory", - &[], - )); - } - if mem.shared { - return Err(host.err( - ScErrorType::WasmVm, - ScErrorCode::InvalidInput, - "unsupported shared memory", - &[], - )); - } - if mem.initial > 0xffff { - return Err(host.err( - ScErrorType::WasmVm, - ScErrorCode::InvalidInput, - "unsupported memory size", - &[], - )); - } - costs.n_memory_pages = - costs.n_memory_pages.saturating_add(mem.initial as u32); - } - } - - TypeSection(s) => costs.n_types = costs.n_types.saturating_add(s.count()), - ImportSection(s) => costs.n_imports = costs.n_imports.saturating_add(s.count()), - FunctionSection(s) => { - costs.n_functions = costs.n_functions.saturating_add(s.count()) - } - TableSection(s) => { - for table in s { - let table = host.map_err(table)?; - costs.n_table_entries = - costs.n_table_entries.saturating_add(table.ty.initial); - match table.init { - TableInit::RefNull => (), - TableInit::Expr(ref expr) => { - Self::check_const_expr_simple(&host, &expr)?; - } - } - } - } - GlobalSection(s) => { - costs.n_globals = costs.n_globals.saturating_add(s.count()); - for global in s { - let global = host.map_err(global)?; - Self::check_const_expr_simple(&host, &global.init_expr)?; - } - } - ExportSection(s) => costs.n_exports = costs.n_exports.saturating_add(s.count()), - ElementSection(s) => { - costs.n_elem_segments = costs.n_elem_segments.saturating_add(1); - elements = elements.saturating_add(s.count()); - for elem in s { - let elem = host.map_err(elem)?; - match elem.kind { - ElementKind::Declared | ElementKind::Passive => (), - ElementKind::Active { offset_expr, .. } => { - Self::check_const_expr_simple(&host, &offset_expr)? - } - } - match elem.items { - ElementItems::Functions(_) => (), - ElementItems::Expressions(_, exprs) => { - for expr in exprs { - let expr = host.map_err(expr)?; - Self::check_const_expr_simple(&host, &expr)?; - } - } - } - } - } - DataSection(s) => { - costs.n_data_segments = costs.n_data_segments.saturating_add(1); - for d in s { - let d = host.map_err(d)?; - if d.data.len() > u32::MAX as usize { - return Err(host.err( - ScErrorType::WasmVm, - ScErrorCode::InvalidInput, - "data segment too large", - &[], - )); - } - data = data.saturating_add(d.data.len() as u32); - match d.kind { - wasmparser::DataKind::Active { offset_expr, .. } => { - Self::check_const_expr_simple(&host, &offset_expr)? - } - wasmparser::DataKind::Passive => (), - } - } - } - CodeSectionEntry(s) => { - let ops = host.map_err(s.get_operators_reader())?; - for _op in ops { - costs.n_instructions = costs.n_instructions.saturating_add(1); - } - } - } - } - if elements > costs.n_table_entries { - dbg!(elements); - dbg!(costs.n_table_entries); - return Err(host.err( - ScErrorType::WasmVm, - ScErrorCode::InvalidInput, - "too many elements in wasm elem section(s)", - &[], - )); - } - Ok(costs) - } - - pub fn get_contract_code_cost_inputs(&self) -> ContractCodeCostInputs { - self.cost_inputs.clone() - } - pub(crate) fn get_memory(&self, host: &Host) -> Result { match self.memory { Some(mem) => Ok(mem), @@ -782,22 +381,6 @@ impl Vm { self.metered_func_call(host, func_sym, wasm_args.as_slice()) } - fn module_custom_section(m: &Module, name: impl AsRef) -> Option<&[u8]> { - m.custom_sections().iter().find_map(|s| { - if &*s.name == name.as_ref() { - Some(&*s.data) - } else { - None - } - }) - } - - /// Returns the raw bytes content of a named custom section from the WASM - /// module loaded into the [Vm], or `None` if no such custom section exists. - pub fn custom_section(&self, name: impl AsRef) -> Option<&[u8]> { - Self::module_custom_section(&self.module, name) - } - /// Utility function that synthesizes a `VmCaller` configured to point /// to this VM's `Store` and `Instance`, and calls the provided function /// back with it. Mainly used for testing.