From da792544952c4ee13930387d32f59d480f2098b5 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 25 Jun 2024 22:08:45 +0300 Subject: [PATCH 1/5] refactor: move `method_name` from run to VMContext This is a small one. `VMContext` already contains information specific to a single function call, most notably the input "arguments". So it seems only sensible that it would also include the method to be called, largely encapsulating everything[^1] that's part of the inputs to the function call. (I think `promise_results` should also be a part of that, so maybe not quite *everything* yet, that's the next commit...) --- .../fuzz/fuzz_targets/diffrunner.rs | 5 +- .../fuzz/fuzz_targets/runner.rs | 6 +- runtime/near-vm-runner/fuzz/src/lib.rs | 3 +- runtime/near-vm-runner/src/logic/context.rs | 2 + .../src/logic/tests/vm_logic_builder.rs | 1 + .../src/near_vm_runner/runner.rs | 66 +++++------ runtime/near-vm-runner/src/runner.rs | 4 +- runtime/near-vm-runner/src/tests.rs | 3 +- runtime/near-vm-runner/src/tests/cache.rs | 4 +- runtime/near-vm-runner/src/tests/fuzzers.rs | 8 +- .../near-vm-runner/src/tests/rs_contract.rs | 34 ++---- .../near-vm-runner/src/tests/test_builder.rs | 7 +- .../near-vm-runner/src/tests/ts_contract.rs | 9 +- runtime/near-vm-runner/src/wasmer2_runner.rs | 6 +- runtime/near-vm-runner/src/wasmer_runner.rs | 7 +- runtime/near-vm-runner/src/wasmtime_runner.rs | 110 ++++++++---------- .../src/function_call.rs | 4 +- .../src/gas_metering.rs | 6 +- runtime/runtime-params-estimator/src/lib.rs | 3 +- .../src/vm_estimator.rs | 3 +- runtime/runtime/src/actions.rs | 2 +- 21 files changed, 123 insertions(+), 170 deletions(-) diff --git a/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs b/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs index 36eacd8465a..46129a18c22 100644 --- a/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs +++ b/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs @@ -20,7 +20,8 @@ libfuzzer_sys::fuzz_target!(|module: ArbitraryModule| { fn run_fuzz(code: &ContractCode, vm_kind: VMKind) -> VMOutcome { let mut fake_external = MockedExternal::with_code(code.clone_for_tests()); - let mut context = create_context(vec![]); + let method_name = find_entry_point(code).unwrap_or_else(|| "main".to_string()); + let mut context = create_context(&method_name, vec![]); context.prepaid_gas = 10u64.pow(14); let config_store = RuntimeConfigStore::new(None); let config = config_store.get_config(PROTOCOL_VERSION); @@ -29,9 +30,7 @@ fn run_fuzz(code: &ContractCode, vm_kind: VMKind) -> VMOutcome { wasm_config.limit_config.contract_prepare_version = near_vm_runner::logic::ContractPrepareVersion::V2; - let method_name = find_entry_point(code).unwrap_or_else(|| "main".to_string()); let res = vm_kind.runtime(wasm_config.into()).unwrap().run( - &method_name, &mut fake_external, &context, fees, diff --git a/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs b/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs index 5f58401cae6..feb4b1d2aa3 100644 --- a/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs +++ b/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs @@ -18,16 +18,16 @@ libfuzzer_sys::fuzz_target!(|module: ArbitraryModule| { fn run_fuzz(code: &ContractCode, config: Arc) -> VMOutcome { let mut fake_external = MockedExternal::with_code(code.clone_for_tests()); - let mut context = create_context(vec![]); + let method_name = find_entry_point(code).unwrap_or_else(|| "main".to_string()); + let mut context = create_context(&method_name, vec![]); context.prepaid_gas = 10u64.pow(14); let mut wasm_config = near_parameters::vm::Config::clone(&config.wasm_config); wasm_config.limit_config.wasmer2_stack_limit = i32::MAX; // If we can crash wasmer2 even without the secondary stack limit it's still good to know let vm_kind = config.wasm_config.vm_kind; let fees = Arc::clone(&config.fees); - let method_name = find_entry_point(code).unwrap_or_else(|| "main".to_string()); vm_kind .runtime(wasm_config.into()) .unwrap() - .run(&method_name, &mut fake_external, &context, fees, [].into(), None) + .run(&mut fake_external, &context, fees, [].into(), None) .unwrap_or_else(|err| panic!("fatal error: {err:?}")) } diff --git a/runtime/near-vm-runner/fuzz/src/lib.rs b/runtime/near-vm-runner/fuzz/src/lib.rs index a7eb0a8e92f..3f2f350b959 100644 --- a/runtime/near-vm-runner/fuzz/src/lib.rs +++ b/runtime/near-vm-runner/fuzz/src/lib.rs @@ -30,12 +30,13 @@ pub fn find_entry_point(contract: &ContractCode) -> Option { None } -pub fn create_context(input: Vec) -> VMContext { +pub fn create_context(method: &str, input: Vec) -> VMContext { VMContext { current_account_id: "alice".parse().unwrap(), signer_account_id: "bob".parse().unwrap(), signer_account_pk: vec![0, 1, 2, 3, 4], predecessor_account_id: "carol".parse().unwrap(), + method: method.into(), input, block_height: 10, block_timestamp: 42, diff --git a/runtime/near-vm-runner/src/logic/context.rs b/runtime/near-vm-runner/src/logic/context.rs index 869aa24554f..bc23bde494c 100644 --- a/runtime/near-vm-runner/src/logic/context.rs +++ b/runtime/near-vm-runner/src/logic/context.rs @@ -20,6 +20,8 @@ pub struct VMContext { /// If this execution is the result of direct execution of transaction then it /// is equal to `signer_account_id`. pub predecessor_account_id: AccountId, + /// The name of the method to invoke. + pub method: String, /// The input to the contract call. /// Encoded as base64 string to be able to pass input in borsh binary format. pub input: Vec, diff --git a/runtime/near-vm-runner/src/logic/tests/vm_logic_builder.rs b/runtime/near-vm-runner/src/logic/tests/vm_logic_builder.rs index 32f5bfe24c3..69b3d20b7ed 100644 --- a/runtime/near-vm-runner/src/logic/tests/vm_logic_builder.rs +++ b/runtime/near-vm-runner/src/logic/tests/vm_logic_builder.rs @@ -71,6 +71,7 @@ fn get_context() -> VMContext { signer_account_id: "bob.near".parse().unwrap(), signer_account_pk: vec![0, 1, 2, 3, 4], predecessor_account_id: "carol.near".parse().unwrap(), + method: "VMLogicBuilder::method_not_specified".into(), input: vec![0, 1, 2, 3, 4], block_height: 10, block_timestamp: 42, diff --git a/runtime/near-vm-runner/src/near_vm_runner/runner.rs b/runtime/near-vm-runner/src/near_vm_runner/runner.rs index 9898b798110..8fcdc95b5eb 100644 --- a/runtime/near-vm-runner/src/near_vm_runner/runner.rs +++ b/runtime/near-vm-runner/src/near_vm_runner/runner.rs @@ -214,7 +214,6 @@ impl NearVM { cache: &dyn ContractRuntimeCache, ext: &mut dyn External, context: &VMContext, - method_name: &str, closure: impl FnOnce( ExecutionResultState, &mut dyn External, @@ -307,7 +306,7 @@ impl NearVM { crate::metrics::record_compiled_contract_cache_lookup(is_cache_hit); let mut result_state = ExecutionResultState::new(&context, Arc::clone(&self.config)); - let result = result_state.before_loading_executable(method_name, wasm_bytes); + let result = result_state.before_loading_executable(&context.method, wasm_bytes); if let Err(e) = result { return Ok(VMOutcome::abort(result_state, e)); } @@ -577,7 +576,6 @@ impl<'a> finite_wasm::wasmparser::VisitOperator<'a> for GasCostCfg { impl crate::runner::VM for NearVM { fn run( &self, - method_name: &str, ext: &mut dyn External, context: &VMContext, fees_config: Arc, @@ -585,43 +583,33 @@ impl crate::runner::VM for NearVM { cache: Option<&dyn ContractRuntimeCache>, ) -> Result { let cache = cache.unwrap_or(&NoContractRuntimeCache); - self.with_compiled_and_loaded( - cache, - ext, - context, - method_name, - |result_state, ext, artifact| { - let memory = NearVmMemory::new( - self.config.limit_config.initial_memory_pages, - self.config.limit_config.max_memory_pages, - ) - .expect("Cannot create memory for a contract call"); - // FIXME: this mostly duplicates the `run_module` method. - // Note that we don't clone the actual backing memory, just increase the RC. - let vmmemory = memory.vm(); - let mut logic = - VMLogic::new(ext, context, fees_config, promise_results, result_state, memory); - let import = build_imports( - vmmemory, - &mut logic, - Arc::clone(&self.config), - artifact.engine(), - ); - let entrypoint = match get_entrypoint_index(&*artifact, method_name) { - Ok(index) => index, - Err(e) => { - return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( - logic.result_state, - e, - )) - } - }; - match self.run_method(&artifact, import, entrypoint)? { - Ok(()) => Ok(VMOutcome::ok(logic.result_state)), - Err(err) => Ok(VMOutcome::abort(logic.result_state, err)), + self.with_compiled_and_loaded(cache, ext, context, |result_state, ext, artifact| { + let memory = NearVmMemory::new( + self.config.limit_config.initial_memory_pages, + self.config.limit_config.max_memory_pages, + ) + .expect("Cannot create memory for a contract call"); + // FIXME: this mostly duplicates the `run_module` method. + // Note that we don't clone the actual backing memory, just increase the RC. + let vmmemory = memory.vm(); + let mut logic = + VMLogic::new(ext, context, fees_config, promise_results, result_state, memory); + let import = + build_imports(vmmemory, &mut logic, Arc::clone(&self.config), artifact.engine()); + let entrypoint = match get_entrypoint_index(&*artifact, &context.method) { + Ok(index) => index, + Err(e) => { + return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( + logic.result_state, + e, + )) } - }, - ) + }; + match self.run_method(&artifact, import, entrypoint)? { + Ok(()) => Ok(VMOutcome::ok(logic.result_state)), + Err(err) => Ok(VMOutcome::abort(logic.result_state, err)), + } + }) } fn precompile( diff --git a/runtime/near-vm-runner/src/runner.rs b/runtime/near-vm-runner/src/runner.rs index e28298b11db..e4b7a46b130 100644 --- a/runtime/near-vm-runner/src/runner.rs +++ b/runtime/near-vm-runner/src/runner.rs @@ -48,7 +48,6 @@ pub(crate) type VMResult = Result; compute_usage = tracing::field::Empty, ))] pub fn run( - method_name: &str, ext: &mut (dyn External + Send), context: &VMContext, wasm_config: Arc, @@ -61,7 +60,7 @@ pub fn run( let runtime = vm_kind .runtime(wasm_config) .unwrap_or_else(|| panic!("the {vm_kind:?} runtime has not been enabled at compile time")); - let outcome = runtime.run(method_name, ext, context, fees_config, promise_results, cache); + let outcome = runtime.run(ext, context, fees_config, promise_results, cache); let outcome = match outcome { Ok(o) => o, e @ Err(_) => return e, @@ -89,7 +88,6 @@ pub trait VM { /// implementation. fn run( &self, - method_name: &str, ext: &mut dyn External, context: &VMContext, fees_config: Arc, diff --git a/runtime/near-vm-runner/src/tests.rs b/runtime/near-vm-runner/src/tests.rs index 8747da037d2..7aeabcf02af 100644 --- a/runtime/near-vm-runner/src/tests.rs +++ b/runtime/near-vm-runner/src/tests.rs @@ -52,12 +52,13 @@ pub(crate) fn with_vm_variants( } } -fn create_context(input: Vec) -> VMContext { +fn create_context(method: &str, input: Vec) -> VMContext { VMContext { current_account_id: CURRENT_ACCOUNT_ID.parse().unwrap(), signer_account_id: SIGNER_ACCOUNT_ID.parse().unwrap(), signer_account_pk: Vec::from(&SIGNER_ACCOUNT_PK[..]), predecessor_account_id: PREDECESSOR_ACCOUNT_ID.parse().unwrap(), + method: method.into(), input, block_height: 10, block_timestamp: 42, diff --git a/runtime/near-vm-runner/src/tests/cache.rs b/runtime/near-vm-runner/src/tests/cache.rs index 071ab443f52..561234175e0 100644 --- a/runtime/near-vm-runner/src/tests/cache.rs +++ b/runtime/near-vm-runner/src/tests/cache.rs @@ -122,12 +122,12 @@ fn make_cached_contract_call_vm( MockedExternal::new() }; fake_external.code_hash = code_hash; - let mut context = create_context(vec![]); + let mut context = create_context(method_name, vec![]); let fees = Arc::new(RuntimeFeesConfig::test()); let promise_results = [].into(); context.prepaid_gas = prepaid_gas; let runtime = vm_kind.runtime(config).expect("runtime has not been compiled"); - runtime.run(method_name, &mut fake_external, &context, fees, promise_results, Some(cache)) + runtime.run(&mut fake_external, &context, fees, promise_results, Some(cache)) } #[test] diff --git a/runtime/near-vm-runner/src/tests/fuzzers.rs b/runtime/near-vm-runner/src/tests/fuzzers.rs index 71a2819477b..42b2fca1b40 100644 --- a/runtime/near-vm-runner/src/tests/fuzzers.rs +++ b/runtime/near-vm-runner/src/tests/fuzzers.rs @@ -39,12 +39,13 @@ pub fn find_entry_point(contract: &ContractCode) -> Option { None } -pub fn create_context(input: Vec) -> VMContext { +pub fn create_context(method: &str, input: Vec) -> VMContext { VMContext { current_account_id: "alice".parse().unwrap(), signer_account_id: "bob".parse().unwrap(), signer_account_pk: vec![0, 1, 2, 3, 4], predecessor_account_id: "carol".parse().unwrap(), + method: method.into(), input, block_height: 10, block_timestamp: 42, @@ -108,7 +109,8 @@ impl fmt::Debug for ArbitraryModule { fn run_fuzz(code: &ContractCode, vm_kind: VMKind) -> VMResult { let mut fake_external = MockedExternal::with_code(code.clone_for_tests()); - let mut context = create_context(vec![]); + let method_name = find_entry_point(code).unwrap_or_else(|| "main".to_string()); + let mut context = create_context(&method_name, vec![]); context.prepaid_gas = 10u64.pow(14); let mut config = test_vm_config(); @@ -117,9 +119,7 @@ fn run_fuzz(code: &ContractCode, vm_kind: VMKind) -> VMResult { let fees = Arc::new(RuntimeFeesConfig::test()); let promise_results = [].into(); - let method_name = find_entry_point(code).unwrap_or_else(|| "main".to_string()); let mut res = vm_kind.runtime(config.into()).unwrap().run( - &method_name, &mut fake_external, &context, Arc::clone(&fees), diff --git a/runtime/near-vm-runner/src/tests/rs_contract.rs b/runtime/near-vm-runner/src/tests/rs_contract.rs index 28becc703ce..614c294eb07 100644 --- a/runtime/near-vm-runner/src/tests/rs_contract.rs +++ b/runtime/near-vm-runner/src/tests/rs_contract.rs @@ -55,12 +55,11 @@ pub fn test_read_write() { with_vm_variants(&config, |vm_kind: VMKind| { let code = test_contract(vm_kind); let mut fake_external = MockedExternal::with_code(code); - let context = create_context(encode(&[10u64, 20u64])); + let context = create_context("write_key_value", encode(&[10u64, 20u64])); let promise_results = [].into(); let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); let result = runtime.run( - "write_key_value", &mut fake_external, &context, Arc::clone(&fees), @@ -69,15 +68,9 @@ pub fn test_read_write() { ); assert_run_result(result, 0); - let context = create_context(encode(&[10u64])); - let result = runtime.run( - "read_value", - &mut fake_external, - &context, - Arc::clone(&fees), - promise_results, - None, - ); + let context = create_context("read_value", encode(&[10u64])); + let result = + runtime.run(&mut fake_external, &context, Arc::clone(&fees), promise_results, None); assert_run_result(result, 20); }); } @@ -125,11 +118,11 @@ fn run_test_ext( fake_external.validators = validators.into_iter().map(|(s, b)| (s.parse().unwrap(), b)).collect(); let fees = Arc::new(RuntimeFeesConfig::test()); - let context = create_context(input.to_vec()); + let context = create_context(method, input.to_vec()); let runtime = vm_kind.runtime(config).expect("runtime has not been compiled"); let outcome = runtime - .run(method, &mut fake_external, &context, Arc::clone(&fees), [].into(), None) + .run(&mut fake_external, &context, Arc::clone(&fees), [].into(), None) .unwrap_or_else(|err| panic!("Failed execution: {:?}", err)); assert_eq!(outcome.profile.action_gas(), 0); @@ -230,12 +223,12 @@ pub fn test_out_of_memory() { let code = test_contract(vm_kind); let mut fake_external = MockedExternal::with_code(code); - let context = create_context(Vec::new()); + let context = create_context("out_of_memory", Vec::new()); let fees = Arc::new(RuntimeFeesConfig::free()); let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); let promise_results = [].into(); let result = runtime - .run("out_of_memory", &mut fake_external, &context, fees, promise_results, None) + .run(&mut fake_external, &context, fees, promise_results, None) .expect("execution failed"); assert_eq!( result.aborted, @@ -254,7 +247,7 @@ fn function_call_weight_contract() -> ContractCode { #[test] fn attach_unspent_gas_but_use_all_gas() { - let mut context = create_context(vec![]); + let mut context = create_context("attach_unspent_gas_but_use_all_gas", vec![]); context.prepaid_gas = 100 * 10u64.pow(12); let mut config = test_vm_config(); @@ -268,14 +261,7 @@ fn attach_unspent_gas_but_use_all_gas() { let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); let outcome = runtime - .run( - "attach_unspent_gas_but_use_all_gas", - &mut external, - &context, - fees, - [].into(), - None, - ) + .run(&mut external, &context, fees, [].into(), None) .unwrap_or_else(|err| panic!("Failed execution: {:?}", err)); let err = outcome.aborted.as_ref().unwrap(); diff --git a/runtime/near-vm-runner/src/tests/test_builder.rs b/runtime/near-vm-runner/src/tests/test_builder.rs index 2e099f2358e..82b507cba5e 100644 --- a/runtime/near-vm-runner/src/tests/test_builder.rs +++ b/runtime/near-vm-runner/src/tests/test_builder.rs @@ -16,6 +16,7 @@ pub(crate) fn test_builder() -> TestBuilder { signer_account_pk: vec![0, 1, 2], predecessor_account_id: "carol".parse().unwrap(), input: Vec::new(), + method: "main".into(), block_height: 10, block_timestamp: 42, epoch_height: 1, @@ -37,7 +38,6 @@ pub(crate) fn test_builder() -> TestBuilder { TestBuilder { code: ContractCode::new(Vec::new(), None), context, - method: "main".to_string(), protocol_versions: vec![u32::MAX], skip, opaque_error: false, @@ -49,7 +49,6 @@ pub(crate) struct TestBuilder { code: ContractCode, context: VMContext, protocol_versions: Vec, - method: String, skip: HashSet, opaque_error: bool, opaque_outcome: bool, @@ -74,7 +73,7 @@ impl TestBuilder { } pub(crate) fn method(mut self, method: &str) -> Self { - self.method = method.to_string(); + self.context.method = method.to_string(); self } @@ -224,7 +223,7 @@ impl TestBuilder { }; println!("Running {:?} for protocol version {}", vm_kind, protocol_version); let outcome = runtime - .run(&self.method, &mut fake_external, &context, fees, promise_results, None) + .run(&mut fake_external, &context, fees, promise_results, None) .expect("execution failed"); let mut got = String::new(); diff --git a/runtime/near-vm-runner/src/tests/ts_contract.rs b/runtime/near-vm-runner/src/tests/ts_contract.rs index 6a317c88c20..b4b1297a17c 100644 --- a/runtime/near-vm-runner/src/tests/ts_contract.rs +++ b/runtime/near-vm-runner/src/tests/ts_contract.rs @@ -16,14 +16,13 @@ pub fn test_ts_contract() { with_vm_variants(&config, |vm_kind: VMKind| { let code = ContractCode::new(near_test_contracts::ts_contract().to_vec(), None); let mut fake_external = MockedExternal::with_code(code); - let context = create_context(Vec::new()); + let context = create_context("try_panic", Vec::new()); let fees = Arc::new(RuntimeFeesConfig::test()); // Call method that panics. let promise_results = [].into(); let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); let result = runtime.run( - "try_panic", &mut fake_external, &context, Arc::clone(&fees), @@ -39,10 +38,9 @@ pub fn test_ts_contract() { ); // Call method that writes something into storage. - let context = create_context(b"foo bar".to_vec()); + let context = create_context("try_storage_write", b"foo bar".to_vec()); runtime .run( - "try_storage_write", &mut fake_external, &context, Arc::clone(&fees), @@ -60,10 +58,9 @@ pub fn test_ts_contract() { } // Call method that reads the value from storage using registers. - let context = create_context(b"foo".to_vec()); + let context = create_context("try_storage_read", b"foo".to_vec()); let outcome = runtime .run( - "try_storage_read", &mut fake_external, &context, Arc::clone(&fees), diff --git a/runtime/near-vm-runner/src/wasmer2_runner.rs b/runtime/near-vm-runner/src/wasmer2_runner.rs index 3706b9d7ce0..5fd7a9a7983 100644 --- a/runtime/near-vm-runner/src/wasmer2_runner.rs +++ b/runtime/near-vm-runner/src/wasmer2_runner.rs @@ -566,7 +566,6 @@ impl wasmer_vm::Tunables for &Wasmer2VM { impl crate::runner::VM for Wasmer2VM { fn run( &self, - method_name: &str, ext: &mut dyn External, context: &VMContext, fees_config: Arc, @@ -578,7 +577,8 @@ impl crate::runner::VM for Wasmer2VM { }; let mut result_state = ExecutionResultState::new(&context, Arc::clone(&self.config)); - let result = result_state.before_loading_executable(method_name, code.code().len() as u64); + let result = + result_state.before_loading_executable(&context.method, code.code().len() as u64); if let Err(e) = result { return Ok(VMOutcome::abort(result_state, e)); } @@ -598,7 +598,7 @@ impl crate::runner::VM for Wasmer2VM { if let Err(e) = result { return Ok(VMOutcome::abort(result_state, e)); } - let entrypoint = match get_entrypoint_index(&*artifact, method_name) { + let entrypoint = match get_entrypoint_index(&*artifact, &context.method) { Ok(index) => index, Err(e) => return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol(result_state, e)), }; diff --git a/runtime/near-vm-runner/src/wasmer_runner.rs b/runtime/near-vm-runner/src/wasmer_runner.rs index 7ee8889e066..2b8be172065 100644 --- a/runtime/near-vm-runner/src/wasmer_runner.rs +++ b/runtime/near-vm-runner/src/wasmer_runner.rs @@ -417,7 +417,6 @@ impl Wasmer0VM { impl crate::runner::VM for Wasmer0VM { fn run( &self, - method_name: &str, ext: &mut dyn External, context: &VMContext, fees_config: Arc, @@ -441,7 +440,7 @@ impl crate::runner::VM for Wasmer0VM { let mut execution_state = ExecutionResultState::new(&context, Arc::clone(&self.config)); let result = - execution_state.before_loading_executable(method_name, code.code().len() as u64); + execution_state.before_loading_executable(&context.method, code.code().len() as u64); if let Err(e) = result { return Ok(VMOutcome::abort(execution_state, e)); } @@ -469,7 +468,7 @@ impl crate::runner::VM for Wasmer0VM { if let Err(e) = result { return Ok(VMOutcome::abort(execution_state, e)); } - if let Err(e) = check_method(&module, method_name) { + if let Err(e) = check_method(&module, &context.method) { return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol(execution_state, e)); } @@ -482,7 +481,7 @@ impl crate::runner::VM for Wasmer0VM { let mut logic = VMLogic::new(ext, context, fees_config, promise_results, execution_state, memory); let import_object = build_imports(memory_copy, &self.config, &mut logic); - match run_method(&module, &import_object, method_name)? { + match run_method(&module, &import_object, &context.method)? { Ok(()) => Ok(VMOutcome::ok(logic.result_state)), Err(err) => Ok(VMOutcome::abort(logic.result_state, err)), } diff --git a/runtime/near-vm-runner/src/wasmtime_runner.rs b/runtime/near-vm-runner/src/wasmtime_runner.rs index 9a3ada2e0a1..ed8ec78813d 100644 --- a/runtime/near-vm-runner/src/wasmtime_runner.rs +++ b/runtime/near-vm-runner/src/wasmtime_runner.rs @@ -189,7 +189,6 @@ impl WasmtimeVM { cache: &dyn ContractRuntimeCache, ext: &mut dyn External, context: &VMContext, - method_name: &str, closure: impl FnOnce( ExecutionResultState, &mut dyn External, @@ -253,7 +252,7 @@ impl WasmtimeVM { )?; let mut result_state = ExecutionResultState::new(&context, Arc::clone(&self.config)); - let result = result_state.before_loading_executable(method_name, wasm_bytes); + let result = result_state.before_loading_executable(&context.method, wasm_bytes); if let Err(e) = result { return Ok(VMOutcome::abort(result_state, e)); } @@ -273,7 +272,6 @@ impl WasmtimeVM { impl crate::runner::VM for WasmtimeVM { fn run( &self, - method_name: &str, ext: &mut dyn External, context: &VMContext, fees_config: Arc, @@ -281,35 +279,21 @@ impl crate::runner::VM for WasmtimeVM { cache: Option<&dyn ContractRuntimeCache>, ) -> Result { let cache = cache.unwrap_or(&NoContractRuntimeCache); - self.with_compiled_and_loaded( - cache, - ext, - context, - method_name, - |result_state, ext, module| { - match module.get_export(method_name) { - Some(export) => match export { - Func(func_type) => { - if func_type.params().len() != 0 || func_type.results().len() != 0 { - let err = FunctionCallError::MethodResolveError( - MethodResolveError::MethodInvalidSignature, - ); - return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( - result_state, - err, - )); - } - } - _ => { + self.with_compiled_and_loaded(cache, ext, context, |result_state, ext, module| { + match module.get_export(&context.method) { + Some(export) => match export { + Func(func_type) => { + if func_type.params().len() != 0 || func_type.results().len() != 0 { + let err = FunctionCallError::MethodResolveError( + MethodResolveError::MethodInvalidSignature, + ); return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( result_state, - FunctionCallError::MethodResolveError( - MethodResolveError::MethodNotFound, - ), + err, )); } - }, - None => { + } + _ => { return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( result_state, FunctionCallError::MethodResolveError( @@ -317,46 +301,50 @@ impl crate::runner::VM for WasmtimeVM { ), )); } + }, + None => { + return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( + result_state, + FunctionCallError::MethodResolveError(MethodResolveError::MethodNotFound), + )); } + } - let mut store = Store::new(&self.engine, ()); - let memory = WasmtimeMemory::new( - &mut store, - self.config.limit_config.initial_memory_pages, - self.config.limit_config.max_memory_pages, - ) - .unwrap(); - let memory_copy = memory.0; - let mut logic = - VMLogic::new(ext, context, fees_config, promise_results, result_state, memory); - let mut linker = Linker::new(&(&self.engine)); - link(&mut linker, memory_copy, &store, &self.config, &mut logic); - match linker.instantiate(&mut store, &module) { - Ok(instance) => match instance.get_func(&mut store, method_name) { - Some(func) => match func.typed::<(), ()>(&mut store) { - Ok(run) => match run.call(&mut store, ()) { - Ok(_) => Ok(VMOutcome::ok(logic.result_state)), - Err(err) => { - Ok(VMOutcome::abort(logic.result_state, err.into_vm_error()?)) - } - }, + let mut store = Store::new(&self.engine, ()); + let memory = WasmtimeMemory::new( + &mut store, + self.config.limit_config.initial_memory_pages, + self.config.limit_config.max_memory_pages, + ) + .unwrap(); + let memory_copy = memory.0; + let mut logic = + VMLogic::new(ext, context, fees_config, promise_results, result_state, memory); + let mut linker = Linker::new(&(&self.engine)); + link(&mut linker, memory_copy, &store, &self.config, &mut logic); + match linker.instantiate(&mut store, &module) { + Ok(instance) => match instance.get_func(&mut store, &context.method) { + Some(func) => match func.typed::<(), ()>(&mut store) { + Ok(run) => match run.call(&mut store, ()) { + Ok(_) => Ok(VMOutcome::ok(logic.result_state)), Err(err) => { Ok(VMOutcome::abort(logic.result_state, err.into_vm_error()?)) } }, - None => { - return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( - logic.result_state, - FunctionCallError::MethodResolveError( - MethodResolveError::MethodNotFound, - ), - )); - } + Err(err) => Ok(VMOutcome::abort(logic.result_state, err.into_vm_error()?)), }, - Err(err) => Ok(VMOutcome::abort(logic.result_state, err.into_vm_error()?)), - } - }, - ) + None => { + return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( + logic.result_state, + FunctionCallError::MethodResolveError( + MethodResolveError::MethodNotFound, + ), + )); + } + }, + Err(err) => Ok(VMOutcome::abort(logic.result_state, err.into_vm_error()?)), + } + }) } fn precompile( diff --git a/runtime/runtime-params-estimator/src/function_call.rs b/runtime/runtime-params-estimator/src/function_call.rs index a5b684ae041..57093a92cfe 100644 --- a/runtime/runtime-params-estimator/src/function_call.rs +++ b/runtime/runtime-params-estimator/src/function_call.rs @@ -72,14 +72,13 @@ fn compute_function_call_cost( let runtime = vm_kind.runtime(vm_config).expect("runtime has not been enabled"); let fees = runtime_config.fees.clone(); let mut fake_external = MockedExternal::with_code(contract.clone_for_tests()); - let fake_context = create_context(vec![]); + let fake_context = create_context("hello0", vec![]); let promise_results = Arc::from([]); // Warmup. for _ in 0..warmup_repeats { let result = runtime .run( - "hello0", &mut fake_external, &fake_context, Arc::clone(&fees), @@ -94,7 +93,6 @@ fn compute_function_call_cost( for _ in 0..repeats { let result = runtime .run( - "hello0", &mut fake_external, &fake_context, Arc::clone(&fees), diff --git a/runtime/runtime-params-estimator/src/gas_metering.rs b/runtime/runtime-params-estimator/src/gas_metering.rs index 62670052dee..e189b0bc3fa 100644 --- a/runtime/runtime-params-estimator/src/gas_metering.rs +++ b/runtime/runtime-params-estimator/src/gas_metering.rs @@ -140,14 +140,13 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode let runtime_free_gas = vm_kind.runtime(vm_config_free).expect("runtime has not been enabled"); let fees = runtime_config.fees.clone(); let mut fake_external = MockedExternal::with_code(contract.clone_for_tests()); - let fake_context = create_context(vec![]); + let fake_context = create_context("hello", vec![]); let promise_results = Arc::from([]); // Warmup with gas metering for _ in 0..warmup_repeats { let result = runtime .run( - "hello", &mut fake_external, &fake_context, Arc::clone(&fees), @@ -166,7 +165,6 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode for _ in 0..repeats { let result = runtime .run( - "hello", &mut fake_external, &fake_context, Arc::clone(&fees), @@ -182,7 +180,6 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode for _ in 0..warmup_repeats { let result = runtime_free_gas .run( - "hello", &mut fake_external, &fake_context, Arc::clone(&fees), @@ -198,7 +195,6 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode for _ in 0..repeats { let result = runtime_free_gas .run( - "hello", &mut fake_external, &fake_context, Arc::clone(&fees), diff --git a/runtime/runtime-params-estimator/src/lib.rs b/runtime/runtime-params-estimator/src/lib.rs index 18642023c5d..380d33f7fbf 100644 --- a/runtime/runtime-params-estimator/src/lib.rs +++ b/runtime/runtime-params-estimator/src/lib.rs @@ -893,12 +893,11 @@ fn wasm_instruction(ctx: &mut EstimatorContext) -> GasCost { let cache = MockContractRuntimeCache::default(); let mut run = || { - let context = create_context(vec![]); + let context = create_context("cpu_ram_soak_test", vec![]); let vm_result = vm_kind .runtime(config.clone()) .unwrap() .run( - "cpu_ram_soak_test", &mut fake_external, &context, Arc::clone(&fees), diff --git a/runtime/runtime-params-estimator/src/vm_estimator.rs b/runtime/runtime-params-estimator/src/vm_estimator.rs index 72bd4415ed6..78731828c2e 100644 --- a/runtime/runtime-params-estimator/src/vm_estimator.rs +++ b/runtime/runtime-params-estimator/src/vm_estimator.rs @@ -15,12 +15,13 @@ const SIGNER_ACCOUNT_ID: &str = "bob"; const SIGNER_ACCOUNT_PK: [u8; 3] = [0, 1, 2]; const PREDECESSOR_ACCOUNT_ID: &str = "carol"; -pub(crate) fn create_context(input: Vec) -> VMContext { +pub(crate) fn create_context(method: &str, input: Vec) -> VMContext { VMContext { current_account_id: CURRENT_ACCOUNT_ID.parse().unwrap(), signer_account_id: SIGNER_ACCOUNT_ID.parse().unwrap(), signer_account_pk: Vec::from(&SIGNER_ACCOUNT_PK[..]), predecessor_account_id: PREDECESSOR_ACCOUNT_ID.parse().unwrap(), + method: method.into(), input, block_height: 10, block_timestamp: 42, diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index 87649d2303f..9a6afc60da5 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -76,6 +76,7 @@ pub(crate) fn execute_function_call( signer_account_pk: borsh::to_vec(&action_receipt.signer_public_key) .expect("Failed to serialize"), predecessor_account_id: predecessor_id.clone(), + method: function_call.method_name.clone(), input: function_call.args.clone(), block_height: apply_state.block_height, block_timestamp: apply_state.block_timestamp, @@ -102,7 +103,6 @@ pub(crate) fn execute_function_call( }; let mode_guard = runtime_ext.trie_update.with_trie_cache_mode(mode); let result = near_vm_runner::run( - &function_call.method_name, runtime_ext, &context, Arc::clone(&config.wasm_config), From 824c0bb8eae48a816a97d7e0b87066607e50769e Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Tue, 25 Jun 2024 22:53:24 +0300 Subject: [PATCH 2/5] refactor: move PromiseResults into VMContext Topically this is very similar to the `input`, even down to the detail that this is exposed via similar means to the contract. So it makes a lot of sense that it is treated in a very similar way in code as well. --- .../fuzz/fuzz_targets/diffrunner.rs | 9 ++--- .../fuzz/fuzz_targets/runner.rs | 2 +- runtime/near-vm-runner/fuzz/src/lib.rs | 1 + runtime/near-vm-runner/src/logic/context.rs | 5 ++- runtime/near-vm-runner/src/logic/logic.rs | 8 ++--- .../src/logic/tests/promises.rs | 2 +- .../src/logic/tests/vm_logic_builder.rs | 6 +--- .../src/near_vm_runner/runner.rs | 5 +-- runtime/near-vm-runner/src/runner.rs | 21 ++++-------- runtime/near-vm-runner/src/tests.rs | 1 + runtime/near-vm-runner/src/tests/cache.rs | 3 +- runtime/near-vm-runner/src/tests/fuzzers.rs | 3 +- .../near-vm-runner/src/tests/rs_contract.rs | 22 ++++--------- .../near-vm-runner/src/tests/test_builder.rs | 5 ++- .../near-vm-runner/src/tests/ts_contract.rs | 27 ++------------- runtime/near-vm-runner/src/wasmer2_runner.rs | 5 +-- runtime/near-vm-runner/src/wasmer_runner.rs | 5 +-- runtime/near-vm-runner/src/wasmtime_runner.rs | 5 +-- .../src/function_call.rs | 17 ++-------- .../src/gas_metering.rs | 33 +++---------------- runtime/runtime-params-estimator/src/lib.rs | 9 +---- .../src/vm_estimator.rs | 1 + runtime/runtime/src/actions.rs | 2 +- 23 files changed, 46 insertions(+), 151 deletions(-) diff --git a/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs b/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs index 46129a18c22..1db5871c308 100644 --- a/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs +++ b/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs @@ -30,13 +30,8 @@ fn run_fuzz(code: &ContractCode, vm_kind: VMKind) -> VMOutcome { wasm_config.limit_config.contract_prepare_version = near_vm_runner::logic::ContractPrepareVersion::V2; - let res = vm_kind.runtime(wasm_config.into()).unwrap().run( - &mut fake_external, - &context, - fees, - [].into(), - None, - ); + let res = + vm_kind.runtime(wasm_config.into()).unwrap().run(&mut fake_external, &context, fees, None); // Remove the VMError message details as they can differ between runtimes // TODO: maybe there's actually things we could check for equality here too? diff --git a/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs b/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs index feb4b1d2aa3..4a80d8bd176 100644 --- a/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs +++ b/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs @@ -28,6 +28,6 @@ fn run_fuzz(code: &ContractCode, config: Arc) -> VMOutcome { vm_kind .runtime(wasm_config.into()) .unwrap() - .run(&mut fake_external, &context, fees, [].into(), None) + .run(&mut fake_external, &context, fees, None) .unwrap_or_else(|err| panic!("fatal error: {err:?}")) } diff --git a/runtime/near-vm-runner/fuzz/src/lib.rs b/runtime/near-vm-runner/fuzz/src/lib.rs index 3f2f350b959..2121f8ede3b 100644 --- a/runtime/near-vm-runner/fuzz/src/lib.rs +++ b/runtime/near-vm-runner/fuzz/src/lib.rs @@ -38,6 +38,7 @@ pub fn create_context(method: &str, input: Vec) -> VMContext { predecessor_account_id: "carol".parse().unwrap(), method: method.into(), input, + promise_results: Vec::new().into(), block_height: 10, block_timestamp: 42, epoch_height: 1, diff --git a/runtime/near-vm-runner/src/logic/context.rs b/runtime/near-vm-runner/src/logic/context.rs index bc23bde494c..c2b252dbd40 100644 --- a/runtime/near-vm-runner/src/logic/context.rs +++ b/runtime/near-vm-runner/src/logic/context.rs @@ -1,4 +1,4 @@ -use super::types::PublicKey; +use super::types::{PromiseResult, PublicKey}; use near_primitives_core::config::ViewConfig; use near_primitives_core::types::{ AccountId, Balance, BlockHeight, EpochHeight, Gas, StorageUsage, @@ -25,6 +25,9 @@ pub struct VMContext { /// The input to the contract call. /// Encoded as base64 string to be able to pass input in borsh binary format. pub input: Vec, + /// If this method execution is invoked directly as a callback by one or more contract calls + /// the results of the methods that made the callback are stored in this collection. + pub promise_results: std::sync::Arc<[PromiseResult]>, /// The current block height. pub block_height: BlockHeight, /// The current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC). diff --git a/runtime/near-vm-runner/src/logic/logic.rs b/runtime/near-vm-runner/src/logic/logic.rs index d5d506e119e..a6047cbf560 100644 --- a/runtime/near-vm-runner/src/logic/logic.rs +++ b/runtime/near-vm-runner/src/logic/logic.rs @@ -219,9 +219,6 @@ pub struct VMLogic<'a> { config: Arc, /// Fees charged for various operations that contract may execute. fees_config: Arc, - /// If this method execution is invoked directly as a callback by one or more contract calls the - /// results of the methods that made the callback are stored in this collection. - promise_results: Arc<[PromiseResult]>, /// Pointer to the guest memory. memory: super::vmstate::Memory, @@ -303,7 +300,6 @@ impl<'a> VMLogic<'a> { ext: &'a mut dyn External, context: &'a VMContext, fees_config: Arc, - promise_results: Arc<[PromiseResult]>, result_state: ExecutionResultState, memory: impl MemoryLike + 'static, ) -> Self { @@ -319,7 +315,6 @@ impl<'a> VMLogic<'a> { context, config, fees_config, - promise_results, memory: super::vmstate::Memory::new(memory), current_account_locked_balance, recorded_storage_counter, @@ -2381,7 +2376,7 @@ impl<'a> VMLogic<'a> { } .into()); } - Ok(self.promise_results.len() as _) + Ok(self.context.promise_results.len() as _) } /// If the current function is invoked by a callback we can access the execution results of the @@ -2414,6 +2409,7 @@ impl<'a> VMLogic<'a> { ); } match self + .context .promise_results .get(result_idx as usize) .ok_or(HostError::InvalidPromiseResultIndex { result_idx })? diff --git a/runtime/near-vm-runner/src/logic/tests/promises.rs b/runtime/near-vm-runner/src/logic/tests/promises.rs index 4fd3cebf982..ee8eaca5a13 100644 --- a/runtime/near-vm-runner/src/logic/tests/promises.rs +++ b/runtime/near-vm-runner/src/logic/tests/promises.rs @@ -19,7 +19,7 @@ fn test_promise_results() { ]; let mut logic_builder = VMLogicBuilder::default(); - logic_builder.promise_results = promise_results.into(); + logic_builder.context.promise_results = promise_results.into(); let mut logic = logic_builder.build(); assert_eq!(logic.promise_results_count(), Ok(3), "Total count of registers must be 3"); diff --git a/runtime/near-vm-runner/src/logic/tests/vm_logic_builder.rs b/runtime/near-vm-runner/src/logic/tests/vm_logic_builder.rs index 69b3d20b7ed..23dfd2055c4 100644 --- a/runtime/near-vm-runner/src/logic/tests/vm_logic_builder.rs +++ b/runtime/near-vm-runner/src/logic/tests/vm_logic_builder.rs @@ -1,6 +1,5 @@ use crate::logic::mocks::mock_external::MockedExternal; use crate::logic::mocks::mock_memory::MockedMemory; -use crate::logic::types::PromiseResult; use crate::logic::{Config, ExecutionResultState, MemSlice, VMContext, VMLogic}; use crate::tests::test_vm_config; use near_parameters::RuntimeFeesConfig; @@ -10,7 +9,6 @@ pub(super) struct VMLogicBuilder { pub ext: MockedExternal, pub config: Config, pub fees_config: RuntimeFeesConfig, - pub promise_results: Arc<[PromiseResult]>, pub memory: MockedMemory, pub context: VMContext, } @@ -22,7 +20,6 @@ impl Default for VMLogicBuilder { fees_config: RuntimeFeesConfig::test(), ext: MockedExternal::default(), memory: MockedMemory::default(), - promise_results: [].into(), context: get_context(), } } @@ -43,7 +40,6 @@ impl VMLogicBuilder { &mut self.ext, &self.context, Arc::new(self.fees_config.clone()), - Arc::clone(&self.promise_results), result_state, self.memory.clone(), )) @@ -59,7 +55,6 @@ impl VMLogicBuilder { fees_config: RuntimeFeesConfig::free(), ext: MockedExternal::default(), memory: MockedMemory::default(), - promise_results: [].into(), context: get_context(), } } @@ -73,6 +68,7 @@ fn get_context() -> VMContext { predecessor_account_id: "carol.near".parse().unwrap(), method: "VMLogicBuilder::method_not_specified".into(), input: vec![0, 1, 2, 3, 4], + promise_results: vec![].into(), block_height: 10, block_timestamp: 42, epoch_height: 1, diff --git a/runtime/near-vm-runner/src/near_vm_runner/runner.rs b/runtime/near-vm-runner/src/near_vm_runner/runner.rs index 8fcdc95b5eb..07e24c420b8 100644 --- a/runtime/near-vm-runner/src/near_vm_runner/runner.rs +++ b/runtime/near-vm-runner/src/near_vm_runner/runner.rs @@ -5,7 +5,6 @@ use crate::logic::errors::{ CacheError, CompilationError, FunctionCallError, MethodResolveError, VMRunnerError, WasmTrap, }; use crate::logic::gas_counter::FastGasCounter; -use crate::logic::types::PromiseResult; use crate::logic::{Config, ExecutionResultState, External, VMContext, VMLogic, VMOutcome}; use crate::near_vm_runner::{NearVmCompiler, NearVmEngine}; use crate::runner::VMResult; @@ -579,7 +578,6 @@ impl crate::runner::VM for NearVM { ext: &mut dyn External, context: &VMContext, fees_config: Arc, - promise_results: Arc<[PromiseResult]>, cache: Option<&dyn ContractRuntimeCache>, ) -> Result { let cache = cache.unwrap_or(&NoContractRuntimeCache); @@ -592,8 +590,7 @@ impl crate::runner::VM for NearVM { // FIXME: this mostly duplicates the `run_module` method. // Note that we don't clone the actual backing memory, just increase the RC. let vmmemory = memory.vm(); - let mut logic = - VMLogic::new(ext, context, fees_config, promise_results, result_state, memory); + let mut logic = VMLogic::new(ext, context, fees_config, result_state, memory); let import = build_imports(vmmemory, &mut logic, Arc::clone(&self.config), artifact.engine()); let entrypoint = match get_entrypoint_index(&*artifact, &context.method) { diff --git a/runtime/near-vm-runner/src/runner.rs b/runtime/near-vm-runner/src/runner.rs index e4b7a46b130..d6420b16369 100644 --- a/runtime/near-vm-runner/src/runner.rs +++ b/runtime/near-vm-runner/src/runner.rs @@ -1,6 +1,5 @@ use crate::errors::ContractPrecompilatonResult; use crate::logic::errors::{CacheError, CompilationError, VMRunnerError}; -use crate::logic::types::PromiseResult; use crate::logic::{External, VMContext, VMOutcome}; use crate::{ContractCode, ContractRuntimeCache}; use near_parameters::vm::{Config, VMKind}; @@ -52,7 +51,6 @@ pub fn run( context: &VMContext, wasm_config: Arc, fees_config: Arc, - promise_results: std::sync::Arc<[PromiseResult]>, cache: Option<&dyn ContractRuntimeCache>, ) -> VMResult { let span = tracing::Span::current(); @@ -60,7 +58,7 @@ pub fn run( let runtime = vm_kind .runtime(wasm_config) .unwrap_or_else(|| panic!("the {vm_kind:?} runtime has not been enabled at compile time")); - let outcome = runtime.run(ext, context, fees_config, promise_results, cache); + let outcome = runtime.run(ext, context, fees_config, cache); let outcome = match outcome { Ok(o) => o, e @ Err(_) => return e, @@ -74,24 +72,19 @@ pub fn run( pub trait VM { /// Validate and run the specified contract. /// - /// This is the entry point for executing a NEAR protocol contract. Before - /// the entry point (as specified by the `method_name` argument) of the - /// contract code is executed, the contract will be validated (see - /// [`crate::prepare::prepare_contract`]), instrumented (e.g. for gas - /// accounting), and linked with the externs specified via the `ext` - /// argument. + /// This is the entry point for executing a NEAR protocol contract. Before the entry point (as + /// specified by the `VMContext::method` argument) of the contract code is executed, the + /// contract will be validated (see [`crate::prepare::prepare_contract`]), instrumented (e.g. + /// for gas accounting), and linked with the externs specified via the `ext` argument. /// - /// [`VMContext::input`] will be passed to the contract entrypoint as an - /// argument. + /// [`VMContext::input`] will be passed to the contract entrypoint as an argument. /// - /// The gas cost for contract preparation will be subtracted by the VM - /// implementation. + /// The gas cost for contract preparation will be subtracted by the VM implementation. fn run( &self, ext: &mut dyn External, context: &VMContext, fees_config: Arc, - promise_results: std::sync::Arc<[PromiseResult]>, cache: Option<&dyn ContractRuntimeCache>, ) -> VMResult; diff --git a/runtime/near-vm-runner/src/tests.rs b/runtime/near-vm-runner/src/tests.rs index 7aeabcf02af..a6f5835a718 100644 --- a/runtime/near-vm-runner/src/tests.rs +++ b/runtime/near-vm-runner/src/tests.rs @@ -60,6 +60,7 @@ fn create_context(method: &str, input: Vec) -> VMContext { predecessor_account_id: PREDECESSOR_ACCOUNT_ID.parse().unwrap(), method: method.into(), input, + promise_results: Vec::new().into(), block_height: 10, block_timestamp: 42, epoch_height: 1, diff --git a/runtime/near-vm-runner/src/tests/cache.rs b/runtime/near-vm-runner/src/tests/cache.rs index 561234175e0..6fed48dab1d 100644 --- a/runtime/near-vm-runner/src/tests/cache.rs +++ b/runtime/near-vm-runner/src/tests/cache.rs @@ -124,10 +124,9 @@ fn make_cached_contract_call_vm( fake_external.code_hash = code_hash; let mut context = create_context(method_name, vec![]); let fees = Arc::new(RuntimeFeesConfig::test()); - let promise_results = [].into(); context.prepaid_gas = prepaid_gas; let runtime = vm_kind.runtime(config).expect("runtime has not been compiled"); - runtime.run(&mut fake_external, &context, fees, promise_results, Some(cache)) + runtime.run(&mut fake_external, &context, fees, Some(cache)) } #[test] diff --git a/runtime/near-vm-runner/src/tests/fuzzers.rs b/runtime/near-vm-runner/src/tests/fuzzers.rs index 42b2fca1b40..2d0b8376697 100644 --- a/runtime/near-vm-runner/src/tests/fuzzers.rs +++ b/runtime/near-vm-runner/src/tests/fuzzers.rs @@ -47,6 +47,7 @@ pub fn create_context(method: &str, input: Vec) -> VMContext { predecessor_account_id: "carol".parse().unwrap(), method: method.into(), input, + promise_results: Vec::new().into(), block_height: 10, block_timestamp: 42, epoch_height: 1, @@ -118,12 +119,10 @@ fn run_fuzz(code: &ContractCode, vm_kind: VMKind) -> VMResult { config.limit_config.contract_prepare_version = ContractPrepareVersion::V2; let fees = Arc::new(RuntimeFeesConfig::test()); - let promise_results = [].into(); let mut res = vm_kind.runtime(config.into()).unwrap().run( &mut fake_external, &context, Arc::clone(&fees), - promise_results, None, ); diff --git a/runtime/near-vm-runner/src/tests/rs_contract.rs b/runtime/near-vm-runner/src/tests/rs_contract.rs index 614c294eb07..b1c042a97e3 100644 --- a/runtime/near-vm-runner/src/tests/rs_contract.rs +++ b/runtime/near-vm-runner/src/tests/rs_contract.rs @@ -57,20 +57,12 @@ pub fn test_read_write() { let mut fake_external = MockedExternal::with_code(code); let context = create_context("write_key_value", encode(&[10u64, 20u64])); - let promise_results = [].into(); let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); - let result = runtime.run( - &mut fake_external, - &context, - Arc::clone(&fees), - Arc::clone(&promise_results), - None, - ); + let result = runtime.run(&mut fake_external, &context, Arc::clone(&fees), None); assert_run_result(result, 0); let context = create_context("read_value", encode(&[10u64])); - let result = - runtime.run(&mut fake_external, &context, Arc::clone(&fees), promise_results, None); + let result = runtime.run(&mut fake_external, &context, Arc::clone(&fees), None); assert_run_result(result, 20); }); } @@ -122,7 +114,7 @@ fn run_test_ext( let runtime = vm_kind.runtime(config).expect("runtime has not been compiled"); let outcome = runtime - .run(&mut fake_external, &context, Arc::clone(&fees), [].into(), None) + .run(&mut fake_external, &context, Arc::clone(&fees), None) .unwrap_or_else(|err| panic!("Failed execution: {:?}", err)); assert_eq!(outcome.profile.action_gas(), 0); @@ -226,10 +218,8 @@ pub fn test_out_of_memory() { let context = create_context("out_of_memory", Vec::new()); let fees = Arc::new(RuntimeFeesConfig::free()); let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); - let promise_results = [].into(); - let result = runtime - .run(&mut fake_external, &context, fees, promise_results, None) - .expect("execution failed"); + let result = + runtime.run(&mut fake_external, &context, fees, None).expect("execution failed"); assert_eq!( result.aborted, match vm_kind { @@ -261,7 +251,7 @@ fn attach_unspent_gas_but_use_all_gas() { let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); let outcome = runtime - .run(&mut external, &context, fees, [].into(), None) + .run(&mut external, &context, fees, None) .unwrap_or_else(|err| panic!("Failed execution: {:?}", err)); let err = outcome.aborted.as_ref().unwrap(); diff --git a/runtime/near-vm-runner/src/tests/test_builder.rs b/runtime/near-vm-runner/src/tests/test_builder.rs index 82b507cba5e..277444929d9 100644 --- a/runtime/near-vm-runner/src/tests/test_builder.rs +++ b/runtime/near-vm-runner/src/tests/test_builder.rs @@ -16,6 +16,7 @@ pub(crate) fn test_builder() -> TestBuilder { signer_account_pk: vec![0, 1, 2], predecessor_account_id: "carol".parse().unwrap(), input: Vec::new(), + promise_results: Vec::new().into(), method: "main".into(), block_height: 10, block_timestamp: 42, @@ -216,14 +217,12 @@ impl TestBuilder { let fees = Arc::new(RuntimeFeesConfig::test()); let context = self.context.clone(); - let promise_results = [].into(); - let Some(runtime) = vm_kind.runtime(config) else { panic!("runtime for {:?} has not been compiled", vm_kind); }; println!("Running {:?} for protocol version {}", vm_kind, protocol_version); let outcome = runtime - .run(&mut fake_external, &context, fees, promise_results, None) + .run(&mut fake_external, &context, fees, None) .expect("execution failed"); let mut got = String::new(); diff --git a/runtime/near-vm-runner/src/tests/ts_contract.rs b/runtime/near-vm-runner/src/tests/ts_contract.rs index b4b1297a17c..ffe84863fe0 100644 --- a/runtime/near-vm-runner/src/tests/ts_contract.rs +++ b/runtime/near-vm-runner/src/tests/ts_contract.rs @@ -20,15 +20,8 @@ pub fn test_ts_contract() { let fees = Arc::new(RuntimeFeesConfig::test()); // Call method that panics. - let promise_results = [].into(); let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); - let result = runtime.run( - &mut fake_external, - &context, - Arc::clone(&fees), - Arc::clone(&promise_results), - None, - ); + let result = runtime.run(&mut fake_external, &context, Arc::clone(&fees), None); let outcome = result.expect("execution failed"); assert_eq!( outcome.aborted, @@ -39,15 +32,7 @@ pub fn test_ts_contract() { // Call method that writes something into storage. let context = create_context("try_storage_write", b"foo bar".to_vec()); - runtime - .run( - &mut fake_external, - &context, - Arc::clone(&fees), - Arc::clone(&promise_results), - None, - ) - .expect("bad failure"); + runtime.run(&mut fake_external, &context, Arc::clone(&fees), None).expect("bad failure"); // Verify by looking directly into the storage of the host. { let res = fake_external.storage_get(b"foo", StorageGetMode::Trie); @@ -60,13 +45,7 @@ pub fn test_ts_contract() { // Call method that reads the value from storage using registers. let context = create_context("try_storage_read", b"foo".to_vec()); let outcome = runtime - .run( - &mut fake_external, - &context, - Arc::clone(&fees), - Arc::clone(&promise_results), - None, - ) + .run(&mut fake_external, &context, Arc::clone(&fees), None) .expect("execution failed"); if let ReturnData::Value(value) = outcome.return_data { diff --git a/runtime/near-vm-runner/src/wasmer2_runner.rs b/runtime/near-vm-runner/src/wasmer2_runner.rs index 5fd7a9a7983..24ff628f7a9 100644 --- a/runtime/near-vm-runner/src/wasmer2_runner.rs +++ b/runtime/near-vm-runner/src/wasmer2_runner.rs @@ -4,7 +4,6 @@ use crate::logic::errors::{ CacheError, CompilationError, FunctionCallError, MethodResolveError, VMRunnerError, WasmTrap, }; use crate::logic::gas_counter::FastGasCounter; -use crate::logic::types::PromiseResult; use crate::logic::{ Config, ExecutionResultState, External, MemSlice, MemoryLike, VMContext, VMLogic, VMOutcome, }; @@ -569,7 +568,6 @@ impl crate::runner::VM for Wasmer2VM { ext: &mut dyn External, context: &VMContext, fees_config: Arc, - promise_results: Arc<[PromiseResult]>, cache: Option<&dyn ContractRuntimeCache>, ) -> Result { let Some(code) = ext.get_contract() else { @@ -611,8 +609,7 @@ impl crate::runner::VM for Wasmer2VM { // FIXME: this mostly duplicates the `run_module` method. // Note that we don't clone the actual backing memory, just increase the RC. let vmmemory = memory.vm(); - let mut logic = - VMLogic::new(ext, context, fees_config, promise_results, result_state, memory); + let mut logic = VMLogic::new(ext, context, fees_config, result_state, memory); let import = build_imports(vmmemory, &mut logic, Arc::clone(&self.config), artifact.engine()); match self.run_method(&artifact, import, entrypoint)? { diff --git a/runtime/near-vm-runner/src/wasmer_runner.rs b/runtime/near-vm-runner/src/wasmer_runner.rs index 2b8be172065..38d6e410cb2 100644 --- a/runtime/near-vm-runner/src/wasmer_runner.rs +++ b/runtime/near-vm-runner/src/wasmer_runner.rs @@ -3,7 +3,6 @@ use crate::errors::ContractPrecompilatonResult; use crate::logic::errors::{ CacheError, CompilationError, FunctionCallError, MethodResolveError, VMRunnerError, WasmTrap, }; -use crate::logic::types::PromiseResult; use crate::logic::{ExecutionResultState, External, VMContext, VMLogic, VMLogicError, VMOutcome}; use crate::logic::{MemSlice, MemoryLike}; use crate::prepare; @@ -420,7 +419,6 @@ impl crate::runner::VM for Wasmer0VM { ext: &mut dyn External, context: &VMContext, fees_config: Arc, - promise_results: std::sync::Arc<[PromiseResult]>, cache: Option<&dyn ContractRuntimeCache>, ) -> Result { let Some(code) = ext.get_contract() else { @@ -478,8 +476,7 @@ impl crate::runner::VM for Wasmer0VM { ); // Note that we don't clone the actual backing memory, just increase the RC. let memory_copy = memory.clone(); - let mut logic = - VMLogic::new(ext, context, fees_config, promise_results, execution_state, memory); + let mut logic = VMLogic::new(ext, context, fees_config, execution_state, memory); let import_object = build_imports(memory_copy, &self.config, &mut logic); match run_method(&module, &import_object, &context.method)? { Ok(()) => Ok(VMOutcome::ok(logic.result_state)), diff --git a/runtime/near-vm-runner/src/wasmtime_runner.rs b/runtime/near-vm-runner/src/wasmtime_runner.rs index ed8ec78813d..1070d7082c1 100644 --- a/runtime/near-vm-runner/src/wasmtime_runner.rs +++ b/runtime/near-vm-runner/src/wasmtime_runner.rs @@ -3,7 +3,6 @@ use crate::logic::errors::{ CacheError, CompilationError, FunctionCallError, MethodResolveError, PrepareError, VMLogicError, VMRunnerError, WasmTrap, }; -use crate::logic::types::PromiseResult; use crate::logic::{Config, ExecutionResultState}; use crate::logic::{External, MemSlice, MemoryLike, VMContext, VMLogic, VMOutcome}; use crate::runner::VMResult; @@ -275,7 +274,6 @@ impl crate::runner::VM for WasmtimeVM { ext: &mut dyn External, context: &VMContext, fees_config: Arc, - promise_results: Arc<[PromiseResult]>, cache: Option<&dyn ContractRuntimeCache>, ) -> Result { let cache = cache.unwrap_or(&NoContractRuntimeCache); @@ -318,8 +316,7 @@ impl crate::runner::VM for WasmtimeVM { ) .unwrap(); let memory_copy = memory.0; - let mut logic = - VMLogic::new(ext, context, fees_config, promise_results, result_state, memory); + let mut logic = VMLogic::new(ext, context, fees_config, result_state, memory); let mut linker = Linker::new(&(&self.engine)); link(&mut linker, memory_copy, &store, &self.config, &mut logic); match linker.instantiate(&mut store, &module) { diff --git a/runtime/runtime-params-estimator/src/function_call.rs b/runtime/runtime-params-estimator/src/function_call.rs index 57093a92cfe..88b9acd5ae6 100644 --- a/runtime/runtime-params-estimator/src/function_call.rs +++ b/runtime/runtime-params-estimator/src/function_call.rs @@ -73,18 +73,11 @@ fn compute_function_call_cost( let fees = runtime_config.fees.clone(); let mut fake_external = MockedExternal::with_code(contract.clone_for_tests()); let fake_context = create_context("hello0", vec![]); - let promise_results = Arc::from([]); // Warmup. for _ in 0..warmup_repeats { let result = runtime - .run( - &mut fake_external, - &fake_context, - Arc::clone(&fees), - Arc::clone(&promise_results), - cache, - ) + .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) .expect("fatal error"); assert!(result.aborted.is_none()); } @@ -92,13 +85,7 @@ fn compute_function_call_cost( let start = GasCost::measure(gas_metric); for _ in 0..repeats { let result = runtime - .run( - &mut fake_external, - &fake_context, - Arc::clone(&fees), - Arc::clone(&promise_results), - cache, - ) + .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) .expect("fatal_error"); assert!(result.aborted.is_none()); } diff --git a/runtime/runtime-params-estimator/src/gas_metering.rs b/runtime/runtime-params-estimator/src/gas_metering.rs index e189b0bc3fa..8a95972ec7e 100644 --- a/runtime/runtime-params-estimator/src/gas_metering.rs +++ b/runtime/runtime-params-estimator/src/gas_metering.rs @@ -141,18 +141,11 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode let fees = runtime_config.fees.clone(); let mut fake_external = MockedExternal::with_code(contract.clone_for_tests()); let fake_context = create_context("hello", vec![]); - let promise_results = Arc::from([]); // Warmup with gas metering for _ in 0..warmup_repeats { let result = runtime - .run( - &mut fake_external, - &fake_context, - Arc::clone(&fees), - Arc::clone(&promise_results), - cache, - ) + .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) .expect("fatal_error"); if let Some(err) = &result.aborted { eprintln!("error: {}", err); @@ -164,13 +157,7 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode let start = GasCost::measure(gas_metric); for _ in 0..repeats { let result = runtime - .run( - &mut fake_external, - &fake_context, - Arc::clone(&fees), - Arc::clone(&promise_results), - cache, - ) + .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) .expect("fatal_error"); assert!(result.aborted.is_none()); } @@ -179,13 +166,7 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode // Warmup without gas metering for _ in 0..warmup_repeats { let result = runtime_free_gas - .run( - &mut fake_external, - &fake_context, - Arc::clone(&fees), - Arc::clone(&promise_results), - cache, - ) + .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) .expect("fatal_error"); assert!(result.aborted.is_none()); } @@ -194,13 +175,7 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode let start = GasCost::measure(gas_metric); for _ in 0..repeats { let result = runtime_free_gas - .run( - &mut fake_external, - &fake_context, - Arc::clone(&fees), - Arc::clone(&promise_results), - cache, - ) + .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) .expect("fatal_error"); assert!(result.aborted.is_none()); } diff --git a/runtime/runtime-params-estimator/src/lib.rs b/runtime/runtime-params-estimator/src/lib.rs index 380d33f7fbf..fb322b79bbd 100644 --- a/runtime/runtime-params-estimator/src/lib.rs +++ b/runtime/runtime-params-estimator/src/lib.rs @@ -889,7 +889,6 @@ fn wasm_instruction(ctx: &mut EstimatorContext) -> GasCost { let config_store = RuntimeConfigStore::new(None); let config = config_store.get_config(PROTOCOL_VERSION).wasm_config.clone(); let fees = Arc::new(RuntimeFeesConfig::test()); - let promise_results = [].into(); let cache = MockContractRuntimeCache::default(); let mut run = || { @@ -897,13 +896,7 @@ fn wasm_instruction(ctx: &mut EstimatorContext) -> GasCost { let vm_result = vm_kind .runtime(config.clone()) .unwrap() - .run( - &mut fake_external, - &context, - Arc::clone(&fees), - Arc::clone(&promise_results), - Some(&cache), - ) + .run(&mut fake_external, &context, Arc::clone(&fees), Some(&cache)) .expect("fatal_error"); assert!(vm_result.aborted.is_some()); vm_result diff --git a/runtime/runtime-params-estimator/src/vm_estimator.rs b/runtime/runtime-params-estimator/src/vm_estimator.rs index 78731828c2e..5ef380b666f 100644 --- a/runtime/runtime-params-estimator/src/vm_estimator.rs +++ b/runtime/runtime-params-estimator/src/vm_estimator.rs @@ -23,6 +23,7 @@ pub(crate) fn create_context(method: &str, input: Vec) -> VMContext { predecessor_account_id: PREDECESSOR_ACCOUNT_ID.parse().unwrap(), method: method.into(), input, + promise_results: vec![].into(), block_height: 10, block_timestamp: 42, epoch_height: 0, diff --git a/runtime/runtime/src/actions.rs b/runtime/runtime/src/actions.rs index 9a6afc60da5..aec1eeb5433 100644 --- a/runtime/runtime/src/actions.rs +++ b/runtime/runtime/src/actions.rs @@ -78,6 +78,7 @@ pub(crate) fn execute_function_call( predecessor_account_id: predecessor_id.clone(), method: function_call.method_name.clone(), input: function_call.args.clone(), + promise_results, block_height: apply_state.block_height, block_timestamp: apply_state.block_timestamp, epoch_height: apply_state.epoch_height, @@ -107,7 +108,6 @@ pub(crate) fn execute_function_call( &context, Arc::clone(&config.wasm_config), Arc::clone(&config.fees), - promise_results, apply_state.cache.as_deref(), ); drop(mode_guard); From c772f5ea66b23923eaaeb57a438b343aaf608d52 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Wed, 26 Jun 2024 15:40:01 +0300 Subject: [PATCH 3/5] vm: split preparation and running of a contract This now technically kinda allows us to pipeline these two steps externally. Finally! --- .../fuzz/fuzz_targets/diffrunner.rs | 7 +- .../fuzz/fuzz_targets/runner.rs | 3 +- runtime/near-vm-runner/src/lib.rs | 2 +- runtime/near-vm-runner/src/logic/logic.rs | 2 +- .../src/near_vm_runner/runner.rs | 117 ++++++++----- runtime/near-vm-runner/src/runner.rs | 63 +++++-- runtime/near-vm-runner/src/tests/cache.rs | 2 +- runtime/near-vm-runner/src/tests/fuzzers.rs | 11 +- .../near-vm-runner/src/tests/rs_contract.rs | 25 ++- .../near-vm-runner/src/tests/test_builder.rs | 3 +- .../near-vm-runner/src/tests/ts_contract.rs | 16 +- runtime/near-vm-runner/src/wasmer2_runner.rs | 102 ++++++++---- runtime/near-vm-runner/src/wasmer_runner.rs | 97 +++++++---- runtime/near-vm-runner/src/wasmtime_runner.rs | 157 +++++++++++------- .../src/function_call.rs | 9 +- .../src/gas_metering.rs | 20 ++- runtime/runtime-params-estimator/src/lib.rs | 3 +- 17 files changed, 419 insertions(+), 220 deletions(-) diff --git a/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs b/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs index 1db5871c308..55e6a31e531 100644 --- a/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs +++ b/runtime/near-vm-runner/fuzz/fuzz_targets/diffrunner.rs @@ -30,8 +30,11 @@ fn run_fuzz(code: &ContractCode, vm_kind: VMKind) -> VMOutcome { wasm_config.limit_config.contract_prepare_version = near_vm_runner::logic::ContractPrepareVersion::V2; - let res = - vm_kind.runtime(wasm_config.into()).unwrap().run(&mut fake_external, &context, fees, None); + let res = vm_kind + .runtime(wasm_config.into()) + .unwrap() + .prepare(&fake_external, &context, None) + .run(&mut fake_external, &context, fees); // Remove the VMError message details as they can differ between runtimes // TODO: maybe there's actually things we could check for equality here too? diff --git a/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs b/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs index 4a80d8bd176..54637c797c9 100644 --- a/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs +++ b/runtime/near-vm-runner/fuzz/fuzz_targets/runner.rs @@ -28,6 +28,7 @@ fn run_fuzz(code: &ContractCode, config: Arc) -> VMOutcome { vm_kind .runtime(wasm_config.into()) .unwrap() - .run(&mut fake_external, &context, fees, None) + .prepare(&fake_external, &context, None) + .run(&mut fake_external, &context, fees) .unwrap_or_else(|err| panic!("fatal error: {err:?}")) } diff --git a/runtime/near-vm-runner/src/lib.rs b/runtime/near-vm-runner/src/lib.rs index bc93160ca01..0c035d79d92 100644 --- a/runtime/near-vm-runner/src/lib.rs +++ b/runtime/near-vm-runner/src/lib.rs @@ -36,7 +36,7 @@ pub use code::ContractCode; #[cfg(feature = "metrics")] pub use metrics::{report_metrics, reset_metrics}; pub use profile::ProfileDataV3; -pub use runner::{run, VM}; +pub use runner::{run, VM, PreparedContract}; /// This is public for internal experimentation use only, and should otherwise be considered an /// implementation detail of `near-vm-runner`. diff --git a/runtime/near-vm-runner/src/logic/logic.rs b/runtime/near-vm-runner/src/logic/logic.rs index a6047cbf560..eba5a116c68 100644 --- a/runtime/near-vm-runner/src/logic/logic.rs +++ b/runtime/near-vm-runner/src/logic/logic.rs @@ -35,7 +35,7 @@ fn base64(s: &[u8]) -> String { /// This is a subset of [`VMLogic`] that's strictly necessary to produce `VMOutcome`s. pub struct ExecutionResultState { /// All gas and economic parameters required during contract execution. - config: Arc, + pub(crate) config: Arc, /// Gas tracking for the current contract execution. gas_counter: GasCounter, /// Logs written by the runtime. diff --git a/runtime/near-vm-runner/src/near_vm_runner/runner.rs b/runtime/near-vm-runner/src/near_vm_runner/runner.rs index 07e24c420b8..5af4fece7b1 100644 --- a/runtime/near-vm-runner/src/near_vm_runner/runner.rs +++ b/runtime/near-vm-runner/src/near_vm_runner/runner.rs @@ -209,16 +209,12 @@ impl NearVM { skip_all )] fn with_compiled_and_loaded( - &self, + self: Box, cache: &dyn ContractRuntimeCache, - ext: &mut dyn External, + ext: &dyn External, context: &VMContext, - closure: impl FnOnce( - ExecutionResultState, - &mut dyn External, - &VMArtifact, - ) -> Result, - ) -> VMResult { + closure: impl FnOnce(ExecutionResultState, &VMArtifact, Box) -> VMResult, + ) -> VMResult { // (wasm code size, compilation result) type MemoryCacheType = (u64, Result); let to_any = |v: MemoryCacheType| -> Box { Box::new(v) }; @@ -307,17 +303,20 @@ impl NearVM { let mut result_state = ExecutionResultState::new(&context, Arc::clone(&self.config)); let result = result_state.before_loading_executable(&context.method, wasm_bytes); if let Err(e) = result { - return Ok(VMOutcome::abort(result_state, e)); + return Ok(PreparedContract::Outcome(VMOutcome::abort(result_state, e))); } match artifact_result { Ok(artifact) => { let result = result_state.after_loading_executable(wasm_bytes); if let Err(e) = result { - return Ok(VMOutcome::abort(result_state, e)); + return Ok(PreparedContract::Outcome(VMOutcome::abort(result_state, e))); } - closure(result_state, ext, &artifact) + closure(result_state, &artifact, self) } - Err(e) => Ok(VMOutcome::abort(result_state, FunctionCallError::CompilationError(e))), + Err(e) => Ok(PreparedContract::Outcome(VMOutcome::abort( + result_state, + FunctionCallError::CompilationError(e), + ))), } } @@ -573,40 +572,37 @@ impl<'a> finite_wasm::wasmparser::VisitOperator<'a> for GasCostCfg { } impl crate::runner::VM for NearVM { - fn run( - &self, - ext: &mut dyn External, + fn prepare( + self: Box, + ext: &dyn External, context: &VMContext, - fees_config: Arc, cache: Option<&dyn ContractRuntimeCache>, - ) -> Result { + ) -> Box { let cache = cache.unwrap_or(&NoContractRuntimeCache); - self.with_compiled_and_loaded(cache, ext, context, |result_state, ext, artifact| { - let memory = NearVmMemory::new( - self.config.limit_config.initial_memory_pages, - self.config.limit_config.max_memory_pages, - ) - .expect("Cannot create memory for a contract call"); - // FIXME: this mostly duplicates the `run_module` method. - // Note that we don't clone the actual backing memory, just increase the RC. - let vmmemory = memory.vm(); - let mut logic = VMLogic::new(ext, context, fees_config, result_state, memory); - let import = - build_imports(vmmemory, &mut logic, Arc::clone(&self.config), artifact.engine()); - let entrypoint = match get_entrypoint_index(&*artifact, &context.method) { - Ok(index) => index, - Err(e) => { - return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( - logic.result_state, - e, - )) - } - }; - match self.run_method(&artifact, import, entrypoint)? { - Ok(()) => Ok(VMOutcome::ok(logic.result_state)), - Err(err) => Ok(VMOutcome::abort(logic.result_state, err)), - } - }) + let prepd = + self.with_compiled_and_loaded(cache, ext, context, |result_state, artifact, vm| { + let memory = NearVmMemory::new( + vm.config.limit_config.initial_memory_pages, + vm.config.limit_config.max_memory_pages, + ) + .expect("Cannot create memory for a contract call"); + let entrypoint = match get_entrypoint_index(&*artifact, &context.method) { + Ok(index) => index, + Err(e) => { + return Ok(PreparedContract::Outcome( + VMOutcome::abort_but_nop_outcome_in_old_protocol(result_state, e), + )) + } + }; + Ok(PreparedContract::Ready { + memory, + result_state, + entrypoint, + artifact: Arc::clone(artifact), + vm, + }) + }); + Box::new(prepd) } fn precompile( @@ -623,6 +619,41 @@ impl crate::runner::VM for NearVM { } } +pub(crate) enum PreparedContract { + Outcome(VMOutcome), + Ready { + memory: NearVmMemory, + result_state: ExecutionResultState, + entrypoint: FunctionIndex, + artifact: VMArtifact, + vm: Box, + }, +} + +impl crate::PreparedContract for VMResult { + fn run( + self: Box, + ext: &mut dyn External, + context: &VMContext, + fees_config: Arc, + ) -> VMResult { + let (memory, result_state, entrypoint, artifact, vm) = match (*self)? { + PreparedContract::Outcome(outcome) => return Ok(outcome), + PreparedContract::Ready { memory, result_state, entrypoint, artifact, vm } => { + (memory, result_state, entrypoint, artifact, vm) + } + }; + let config = Arc::clone(&result_state.config); + let vmmemory = memory.vm(); + let mut logic = VMLogic::new(ext, context, fees_config, result_state, memory); + let import = build_imports(vmmemory, &mut logic, config, artifact.engine()); + match vm.run_method(&artifact, import, entrypoint)? { + Ok(()) => Ok(VMOutcome::ok(logic.result_state)), + Err(err) => Ok(VMOutcome::abort(logic.result_state, err)), + } + } +} + pub(crate) struct NearVmImports<'engine, 'vmlogic, 'vmlogic_refs> { pub(crate) memory: VMMemory, config: Arc, diff --git a/runtime/near-vm-runner/src/runner.rs b/runtime/near-vm-runner/src/runner.rs index d6420b16369..ff0f2e8f628 100644 --- a/runtime/near-vm-runner/src/runner.rs +++ b/runtime/near-vm-runner/src/runner.rs @@ -23,6 +23,26 @@ use std::sync::Arc; /// validators, even when a guest error occurs, or else their state will diverge. pub(crate) type VMResult = Result; +#[tracing::instrument(target = "vm", level = "debug", "prepare", skip_all, fields( + code.hash = %ext.code_hash(), + method_name, + vm_kind = ?wasm_config.vm_kind, + burnt_gas = tracing::field::Empty, + compute_usage = tracing::field::Empty, +))] +pub fn prepare( + ext: &(dyn External + Send), + context: &VMContext, + wasm_config: Arc, + cache: Option<&dyn ContractRuntimeCache>, +) -> Box { + let vm_kind = wasm_config.vm_kind; + let runtime = vm_kind + .runtime(wasm_config) + .unwrap_or_else(|| panic!("the {vm_kind:?} runtime has not been enabled at compile time")); + runtime.prepare(ext, context, cache) +} + /// Validate and run the specified contract. /// /// This is the entry point for executing a NEAR protocol contract. Before the @@ -54,11 +74,8 @@ pub fn run( cache: Option<&dyn ContractRuntimeCache>, ) -> VMResult { let span = tracing::Span::current(); - let vm_kind = wasm_config.vm_kind; - let runtime = vm_kind - .runtime(wasm_config) - .unwrap_or_else(|| panic!("the {vm_kind:?} runtime has not been enabled at compile time")); - let outcome = runtime.run(ext, context, fees_config, cache); + let prepared = prepare(ext, context, wasm_config, cache); + let outcome = prepared.run(ext, context, fees_config); let outcome = match outcome { Ok(o) => o, e @ Err(_) => return e, @@ -69,24 +86,38 @@ pub fn run( Ok(outcome) } -pub trait VM { - /// Validate and run the specified contract. +pub trait PreparedContract { + /// Run the prepared contract. /// - /// This is the entry point for executing a NEAR protocol contract. Before the entry point (as - /// specified by the `VMContext::method` argument) of the contract code is executed, the - /// contract will be validated (see [`crate::prepare::prepare_contract`]), instrumented (e.g. - /// for gas accounting), and linked with the externs specified via the `ext` argument. + /// This is the entry point for executing a NEAR protocol contract. The entry point (as + /// specified by the `VMContext::method` argument) of the contract code is executed. /// - /// [`VMContext::input`] will be passed to the contract entrypoint as an argument. - /// - /// The gas cost for contract preparation will be subtracted by the VM implementation. + /// [`VMContext::input`] will be made available to the contract. fn run( - &self, + self: Box, ext: &mut dyn External, context: &VMContext, fees_config: Arc, - cache: Option<&dyn ContractRuntimeCache>, ) -> VMResult; +} + +pub trait VM { + /// Prepare a contract for execution. + /// + /// Work that goes into the preparation is runtime implementation specific, and depending on + /// the runtime may not do anything at all (and instead prepare everything when the contract is + /// `run`.) + /// + /// ## Return + /// + /// This method does not report any errors. If the contract is invalid in any way, the errors + /// will be reported when the returned value is `run`. + fn prepare( + self: Box, + ext: &dyn External, + context: &VMContext, + cache: Option<&dyn ContractRuntimeCache>, + ) -> Box; /// Precompile a WASM contract to a VM specific format and store the result /// into the `cache`. diff --git a/runtime/near-vm-runner/src/tests/cache.rs b/runtime/near-vm-runner/src/tests/cache.rs index 6fed48dab1d..1192ffa5315 100644 --- a/runtime/near-vm-runner/src/tests/cache.rs +++ b/runtime/near-vm-runner/src/tests/cache.rs @@ -126,7 +126,7 @@ fn make_cached_contract_call_vm( let fees = Arc::new(RuntimeFeesConfig::test()); context.prepaid_gas = prepaid_gas; let runtime = vm_kind.runtime(config).expect("runtime has not been compiled"); - runtime.run(&mut fake_external, &context, fees, Some(cache)) + runtime.prepare(&fake_external, &context, Some(cache)).run(&mut fake_external, &context, fees) } #[test] diff --git a/runtime/near-vm-runner/src/tests/fuzzers.rs b/runtime/near-vm-runner/src/tests/fuzzers.rs index 2d0b8376697..95c43e23343 100644 --- a/runtime/near-vm-runner/src/tests/fuzzers.rs +++ b/runtime/near-vm-runner/src/tests/fuzzers.rs @@ -119,12 +119,11 @@ fn run_fuzz(code: &ContractCode, vm_kind: VMKind) -> VMResult { config.limit_config.contract_prepare_version = ContractPrepareVersion::V2; let fees = Arc::new(RuntimeFeesConfig::test()); - let mut res = vm_kind.runtime(config.into()).unwrap().run( - &mut fake_external, - &context, - Arc::clone(&fees), - None, - ); + let mut res = vm_kind + .runtime(config.into()) + .unwrap() + .prepare(&fake_external, &context, None) + .run(&mut fake_external, &context, Arc::clone(&fees)); // Remove the VMError message details as they can differ between runtimes // TODO: maybe there's actually things we could check for equality here too? diff --git a/runtime/near-vm-runner/src/tests/rs_contract.rs b/runtime/near-vm-runner/src/tests/rs_contract.rs index b1c042a97e3..6129c45d950 100644 --- a/runtime/near-vm-runner/src/tests/rs_contract.rs +++ b/runtime/near-vm-runner/src/tests/rs_contract.rs @@ -58,11 +58,20 @@ pub fn test_read_write() { let context = create_context("write_key_value", encode(&[10u64, 20u64])); let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); - let result = runtime.run(&mut fake_external, &context, Arc::clone(&fees), None); + let result = runtime.prepare(&fake_external, &context, None).run( + &mut fake_external, + &context, + Arc::clone(&fees), + ); assert_run_result(result, 0); let context = create_context("read_value", encode(&[10u64])); - let result = runtime.run(&mut fake_external, &context, Arc::clone(&fees), None); + let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); + let result = runtime.prepare(&fake_external, &context, None).run( + &mut fake_external, + &context, + Arc::clone(&fees), + ); assert_run_result(result, 20); }); } @@ -114,7 +123,8 @@ fn run_test_ext( let runtime = vm_kind.runtime(config).expect("runtime has not been compiled"); let outcome = runtime - .run(&mut fake_external, &context, Arc::clone(&fees), None) + .prepare(&fake_external, &context, None) + .run(&mut fake_external, &context, Arc::clone(&fees)) .unwrap_or_else(|err| panic!("Failed execution: {:?}", err)); assert_eq!(outcome.profile.action_gas(), 0); @@ -218,8 +228,10 @@ pub fn test_out_of_memory() { let context = create_context("out_of_memory", Vec::new()); let fees = Arc::new(RuntimeFeesConfig::free()); let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); - let result = - runtime.run(&mut fake_external, &context, fees, None).expect("execution failed"); + let result = runtime + .prepare(&fake_external, &context, None) + .run(&mut fake_external, &context, fees) + .expect("execution failed"); assert_eq!( result.aborted, match vm_kind { @@ -251,7 +263,8 @@ fn attach_unspent_gas_but_use_all_gas() { let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); let outcome = runtime - .run(&mut external, &context, fees, None) + .prepare(&external, &context, None) + .run(&mut external, &context, fees) .unwrap_or_else(|err| panic!("Failed execution: {:?}", err)); let err = outcome.aborted.as_ref().unwrap(); diff --git a/runtime/near-vm-runner/src/tests/test_builder.rs b/runtime/near-vm-runner/src/tests/test_builder.rs index 277444929d9..dbd9312b818 100644 --- a/runtime/near-vm-runner/src/tests/test_builder.rs +++ b/runtime/near-vm-runner/src/tests/test_builder.rs @@ -222,7 +222,8 @@ impl TestBuilder { }; println!("Running {:?} for protocol version {}", vm_kind, protocol_version); let outcome = runtime - .run(&mut fake_external, &context, fees, None) + .prepare(&fake_external, &context, None) + .run(&mut fake_external, &context, fees) .expect("execution failed"); let mut got = String::new(); diff --git a/runtime/near-vm-runner/src/tests/ts_contract.rs b/runtime/near-vm-runner/src/tests/ts_contract.rs index ffe84863fe0..067a74537de 100644 --- a/runtime/near-vm-runner/src/tests/ts_contract.rs +++ b/runtime/near-vm-runner/src/tests/ts_contract.rs @@ -21,7 +21,11 @@ pub fn test_ts_contract() { // Call method that panics. let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); - let result = runtime.run(&mut fake_external, &context, Arc::clone(&fees), None); + let result = runtime.prepare(&fake_external, &context, None).run( + &mut fake_external, + &context, + Arc::clone(&fees), + ); let outcome = result.expect("execution failed"); assert_eq!( outcome.aborted, @@ -32,7 +36,11 @@ pub fn test_ts_contract() { // Call method that writes something into storage. let context = create_context("try_storage_write", b"foo bar".to_vec()); - runtime.run(&mut fake_external, &context, Arc::clone(&fees), None).expect("bad failure"); + let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); + runtime + .prepare(&fake_external, &context, None) + .run(&mut fake_external, &context, Arc::clone(&fees)) + .expect("bad failure"); // Verify by looking directly into the storage of the host. { let res = fake_external.storage_get(b"foo", StorageGetMode::Trie); @@ -44,8 +52,10 @@ pub fn test_ts_contract() { // Call method that reads the value from storage using registers. let context = create_context("try_storage_read", b"foo".to_vec()); + let runtime = vm_kind.runtime(config.clone()).expect("runtime has not been compiled"); let outcome = runtime - .run(&mut fake_external, &context, Arc::clone(&fees), None) + .prepare(&fake_external, &context, None) + .run(&mut fake_external, &context, Arc::clone(&fees)) .expect("execution failed"); if let ReturnData::Value(value) = outcome.return_data { diff --git a/runtime/near-vm-runner/src/wasmer2_runner.rs b/runtime/near-vm-runner/src/wasmer2_runner.rs index 24ff628f7a9..9ea0a3d5528 100644 --- a/runtime/near-vm-runner/src/wasmer2_runner.rs +++ b/runtime/near-vm-runner/src/wasmer2_runner.rs @@ -563,42 +563,58 @@ impl wasmer_vm::Tunables for &Wasmer2VM { } impl crate::runner::VM for Wasmer2VM { - fn run( + fn precompile( &self, - ext: &mut dyn External, + code: &ContractCode, + cache: &dyn ContractRuntimeCache, + ) -> Result< + Result, + crate::logic::errors::CacheError, + > { + Ok(self + .compile_and_cache(code, Some(cache))? + .map(|_| ContractPrecompilatonResult::ContractCompiled)) + } + + fn prepare( + self: Box, + ext: &dyn External, context: &VMContext, - fees_config: Arc, cache: Option<&dyn ContractRuntimeCache>, - ) -> Result { + ) -> Box { + type Result = VMResult; let Some(code) = ext.get_contract() else { - return Err(VMRunnerError::ContractCodeNotPresent); + return Box::new(Result::Err(VMRunnerError::ContractCodeNotPresent)); }; let mut result_state = ExecutionResultState::new(&context, Arc::clone(&self.config)); - let result = result_state.before_loading_executable(&context.method, code.code().len() as u64); if let Err(e) = result { - return Ok(VMOutcome::abort(result_state, e)); + return Box::new(Ok(PreparedContract::Outcome(VMOutcome::abort(result_state, e)))); } - - let artifact = self.compile_and_load(&code, cache)?; - let artifact = match artifact { - Ok(it) => it, - Err(err) => { - return Ok(VMOutcome::abort( + let artifact = match self.compile_and_load(&code, cache) { + Ok(Ok(it)) => it, + Ok(Err(err)) => { + return Box::new(Ok(PreparedContract::Outcome(VMOutcome::abort( result_state, FunctionCallError::CompilationError(err), - )); + )))); + } + Err(err) => { + return Box::new(Result::Err(err)); } }; - let result = result_state.after_loading_executable(code.code().len() as u64); if let Err(e) = result { - return Ok(VMOutcome::abort(result_state, e)); + return Box::new(Ok(PreparedContract::Outcome(VMOutcome::abort(result_state, e)))); } let entrypoint = match get_entrypoint_index(&*artifact, &context.method) { Ok(index) => index, - Err(e) => return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol(result_state, e)), + Err(e) => { + return Box::new(Ok(PreparedContract::Outcome( + VMOutcome::abort_but_nop_outcome_in_old_protocol(result_state, e), + ))) + } }; let memory = Wasmer2Memory::new( @@ -606,30 +622,50 @@ impl crate::runner::VM for Wasmer2VM { self.config.limit_config.max_memory_pages, ) .expect("Cannot create memory for a contract call"); + Box::new(Ok(PreparedContract::Ready { + vm: self, + memory, + result_state, + entrypoint, + artifact, + })) + } +} + +pub(crate) enum PreparedContract { + Outcome(VMOutcome), + Ready { + vm: Box, + memory: Wasmer2Memory, + result_state: ExecutionResultState, + entrypoint: FunctionIndex, + artifact: VMArtifact, + }, +} + +impl crate::PreparedContract for VMResult { + fn run( + self: Box, + ext: &mut dyn External, + context: &VMContext, + fees_config: Arc, + ) -> VMResult { + let (vm, memory, result_state, entrypoint, artifact) = match (*self)? { + PreparedContract::Outcome(outcome) => return Ok(outcome), + PreparedContract::Ready { vm, memory, result_state, entrypoint, artifact } => { + (vm, memory, result_state, entrypoint, artifact) + } + }; // FIXME: this mostly duplicates the `run_module` method. // Note that we don't clone the actual backing memory, just increase the RC. let vmmemory = memory.vm(); let mut logic = VMLogic::new(ext, context, fees_config, result_state, memory); - let import = - build_imports(vmmemory, &mut logic, Arc::clone(&self.config), artifact.engine()); - match self.run_method(&artifact, import, entrypoint)? { + let import = build_imports(vmmemory, &mut logic, Arc::clone(&vm.config), artifact.engine()); + match vm.run_method(&artifact, import, entrypoint)? { Ok(()) => Ok(VMOutcome::ok(logic.result_state)), Err(err) => Ok(VMOutcome::abort(logic.result_state, err)), } } - - fn precompile( - &self, - code: &ContractCode, - cache: &dyn ContractRuntimeCache, - ) -> Result< - Result, - crate::logic::errors::CacheError, - > { - Ok(self - .compile_and_cache(code, Some(cache))? - .map(|_| ContractPrecompilatonResult::ContractCompiled)) - } } pub(crate) struct Wasmer2Imports<'engine, 'vmlogic, 'vmlogic_refs> { diff --git a/runtime/near-vm-runner/src/wasmer_runner.rs b/runtime/near-vm-runner/src/wasmer_runner.rs index 38d6e410cb2..c9721b86931 100644 --- a/runtime/near-vm-runner/src/wasmer_runner.rs +++ b/runtime/near-vm-runner/src/wasmer_runner.rs @@ -414,15 +414,28 @@ impl Wasmer0VM { } impl crate::runner::VM for Wasmer0VM { - fn run( + fn precompile( &self, - ext: &mut dyn External, + code: &ContractCode, + cache: &dyn ContractRuntimeCache, + ) -> Result< + Result, + crate::logic::errors::CacheError, + > { + Ok(self + .compile_and_cache(code, Some(cache))? + .map(|_| ContractPrecompilatonResult::ContractCompiled)) + } + + fn prepare( + self: Box, + ext: &dyn External, context: &VMContext, - fees_config: Arc, cache: Option<&dyn ContractRuntimeCache>, - ) -> Result { + ) -> Box { + type Result = VMResult; let Some(code) = ext.get_contract() else { - return Err(VMRunnerError::ContractCodeNotPresent); + return Box::new(Result::Err(VMRunnerError::ContractCodeNotPresent)); }; if !cfg!(target_arch = "x86") && !cfg!(target_arch = "x86_64") { // TODO(#1940): Remove once NaN is standardized by the VM. @@ -436,17 +449,16 @@ impl crate::runner::VM for Wasmer0VM { panic!("AVX support is required in order to run Wasmer VM Singlepass backend."); } - let mut execution_state = ExecutionResultState::new(&context, Arc::clone(&self.config)); + let mut result_state = ExecutionResultState::new(&context, Arc::clone(&self.config)); let result = - execution_state.before_loading_executable(&context.method, code.code().len() as u64); + result_state.before_loading_executable(&context.method, code.code().len() as u64); if let Err(e) = result { - return Ok(VMOutcome::abort(execution_state, e)); + return Box::new(Ok(PreparedContract::Outcome(VMOutcome::abort(result_state, e)))); } // TODO: consider using get_module() here, once we'll go via deployment path. - let module = self.compile_and_load(&code, cache)?; - let module = match module { - Ok(x) => x, + let module = match self.compile_and_load(&code, cache) { + Ok(Ok(x)) => x, // Note on backwards-compatibility: This error used to be an error // without result, later refactored to NOP outcome. Now this returns // an actual outcome, including gas costs that occurred before this @@ -454,48 +466,67 @@ impl crate::runner::VM for Wasmer0VM { // version do not have gas costs before reaching this code. (Also // see `test_old_fn_loading_behavior_preserved` for a test that // verifies future changes do not counteract this assumption.) - Err(err) => { - return Ok(VMOutcome::abort( - execution_state, + Ok(Err(err)) => { + return Box::new(Ok(PreparedContract::Outcome(VMOutcome::abort( + result_state, FunctionCallError::CompilationError(err), - )) + )))) + } + Err(err) => { + return Box::new(Result::Err(err)) } }; - let result = execution_state.after_loading_executable(code.code().len() as u64); + let result = result_state.after_loading_executable(code.code().len() as u64); if let Err(e) = result { - return Ok(VMOutcome::abort(execution_state, e)); + return Box::new(Ok(PreparedContract::Outcome(VMOutcome::abort(result_state, e)))); } if let Err(e) = check_method(&module, &context.method) { - return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol(execution_state, e)); + return Box::new(Ok(PreparedContract::Outcome( + VMOutcome::abort_but_nop_outcome_in_old_protocol(result_state, e), + ))); } let memory = WasmerMemory::new( self.config.limit_config.initial_memory_pages, self.config.limit_config.max_memory_pages, ); + Box::new(Ok(PreparedContract::Ready { vm: self, memory, result_state, module })) + } +} + +pub(crate) enum PreparedContract { + Outcome(VMOutcome), + Ready { + vm: Box, + memory: WasmerMemory, + result_state: ExecutionResultState, + module: Module, + }, +} + +impl crate::PreparedContract for VMResult { + fn run( + self: Box, + ext: &mut dyn External, + context: &VMContext, + fees_config: Arc, + ) -> Result { + let (vm, memory, result_state, module) = match (*self)? { + PreparedContract::Outcome(outcome) => return Ok(outcome), + PreparedContract::Ready { vm, memory, result_state, module } => { + (vm, memory, result_state, module) + } + }; // Note that we don't clone the actual backing memory, just increase the RC. let memory_copy = memory.clone(); - let mut logic = VMLogic::new(ext, context, fees_config, execution_state, memory); - let import_object = build_imports(memory_copy, &self.config, &mut logic); + let mut logic = VMLogic::new(ext, context, fees_config, result_state, memory); + let import_object = build_imports(memory_copy, &vm.config, &mut logic); match run_method(&module, &import_object, &context.method)? { Ok(()) => Ok(VMOutcome::ok(logic.result_state)), Err(err) => Ok(VMOutcome::abort(logic.result_state, err)), } } - - fn precompile( - &self, - code: &ContractCode, - cache: &dyn ContractRuntimeCache, - ) -> Result< - Result, - crate::logic::errors::CacheError, - > { - Ok(self - .compile_and_cache(code, Some(cache))? - .map(|_| ContractPrecompilatonResult::ContractCompiled)) - } } #[derive(Clone, Copy)] diff --git a/runtime/near-vm-runner/src/wasmtime_runner.rs b/runtime/near-vm-runner/src/wasmtime_runner.rs index 1070d7082c1..bef52299c07 100644 --- a/runtime/near-vm-runner/src/wasmtime_runner.rs +++ b/runtime/near-vm-runner/src/wasmtime_runner.rs @@ -186,14 +186,10 @@ impl WasmtimeVM { fn with_compiled_and_loaded( &self, cache: &dyn ContractRuntimeCache, - ext: &mut dyn External, + ext: &dyn External, context: &VMContext, - closure: impl FnOnce( - ExecutionResultState, - &mut dyn External, - Module, - ) -> Result, - ) -> VMResult { + closure: impl FnOnce(ExecutionResultState, Module) -> VMResult, + ) -> VMResult { let code_hash = ext.code_hash(); type MemoryCacheType = (u64, Result); let to_any = |v: MemoryCacheType| -> Box { Box::new(v) }; @@ -253,31 +249,46 @@ impl WasmtimeVM { let mut result_state = ExecutionResultState::new(&context, Arc::clone(&self.config)); let result = result_state.before_loading_executable(&context.method, wasm_bytes); if let Err(e) = result { - return Ok(VMOutcome::abort(result_state, e)); + return Ok(PreparedContract::Outcome(VMOutcome::abort(result_state, e))); } match module_result { Ok(module) => { let result = result_state.after_loading_executable(wasm_bytes); if let Err(e) = result { - return Ok(VMOutcome::abort(result_state, e)); + return Ok(PreparedContract::Outcome(VMOutcome::abort(result_state, e))); } - closure(result_state, ext, module) + closure(result_state, module) } - Err(e) => Ok(VMOutcome::abort(result_state, FunctionCallError::CompilationError(e))), + Err(e) => Ok(PreparedContract::Outcome(VMOutcome::abort( + result_state, + FunctionCallError::CompilationError(e), + ))), } } } impl crate::runner::VM for WasmtimeVM { - fn run( + fn precompile( &self, - ext: &mut dyn External, + code: &ContractCode, + cache: &dyn ContractRuntimeCache, + ) -> Result< + Result, + crate::logic::errors::CacheError, + > { + Ok(self + .compile_and_cache(code, cache)? + .map(|_| ContractPrecompilatonResult::ContractCompiled)) + } + + fn prepare( + self: Box, + ext: &dyn External, context: &VMContext, - fees_config: Arc, cache: Option<&dyn ContractRuntimeCache>, - ) -> Result { + ) -> Box { let cache = cache.unwrap_or(&NoContractRuntimeCache); - self.with_compiled_and_loaded(cache, ext, context, |result_state, ext, module| { + let prepd = self.with_compiled_and_loaded(cache, ext, context, |result_state, module| { match module.get_export(&context.method) { Some(export) => match export { Func(func_type) => { @@ -285,25 +296,30 @@ impl crate::runner::VM for WasmtimeVM { let err = FunctionCallError::MethodResolveError( MethodResolveError::MethodInvalidSignature, ); - return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( - result_state, - err, + return Ok(PreparedContract::Outcome( + VMOutcome::abort_but_nop_outcome_in_old_protocol(result_state, err), )); } } _ => { - return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( - result_state, - FunctionCallError::MethodResolveError( - MethodResolveError::MethodNotFound, + return Ok(PreparedContract::Outcome( + VMOutcome::abort_but_nop_outcome_in_old_protocol( + result_state, + FunctionCallError::MethodResolveError( + MethodResolveError::MethodNotFound, + ), ), )); } }, None => { - return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( - result_state, - FunctionCallError::MethodResolveError(MethodResolveError::MethodNotFound), + return Ok(PreparedContract::Outcome( + VMOutcome::abort_but_nop_outcome_in_old_protocol( + result_state, + FunctionCallError::MethodResolveError( + MethodResolveError::MethodNotFound, + ), + ), )); } } @@ -315,46 +331,61 @@ impl crate::runner::VM for WasmtimeVM { self.config.limit_config.max_memory_pages, ) .unwrap(); - let memory_copy = memory.0; - let mut logic = VMLogic::new(ext, context, fees_config, result_state, memory); - let mut linker = Linker::new(&(&self.engine)); - link(&mut linker, memory_copy, &store, &self.config, &mut logic); - match linker.instantiate(&mut store, &module) { - Ok(instance) => match instance.get_func(&mut store, &context.method) { - Some(func) => match func.typed::<(), ()>(&mut store) { - Ok(run) => match run.call(&mut store, ()) { - Ok(_) => Ok(VMOutcome::ok(logic.result_state)), - Err(err) => { - Ok(VMOutcome::abort(logic.result_state, err.into_vm_error()?)) - } - }, + Ok(PreparedContract::Ready { store, memory, module, result_state }) + }); + Box::new(prepd) + } +} + +pub(crate) enum PreparedContract { + Outcome(VMOutcome), + Ready { + store: Store<()>, + memory: WasmtimeMemory, + module: Module, + result_state: ExecutionResultState, + }, +} + +impl crate::PreparedContract for VMResult { + fn run( + self: Box, + ext: &mut dyn External, + context: &VMContext, + fees_config: Arc, + ) -> VMResult { + let (mut store, memory, module, result_state) = match (*self)? { + PreparedContract::Outcome(outcome) => return Ok(outcome), + PreparedContract::Ready { store, memory, module, result_state } => { + (store, memory, module, result_state) + } + }; + let memory_copy = memory.0; + let config = Arc::clone(&result_state.config); + let mut logic = VMLogic::new(ext, context, fees_config, result_state, memory); + let engine = store.engine(); + let mut linker = Linker::new(engine); + // TODO: config could be accessed through `logic.result_state`, without this code having to + // figure it out... + link(&mut linker, memory_copy, &store, &config, &mut logic); + match linker.instantiate(&mut store, &module) { + Ok(instance) => match instance.get_func(&mut store, &context.method) { + Some(func) => match func.typed::<(), ()>(&mut store) { + Ok(run) => match run.call(&mut store, ()) { + Ok(_) => Ok(VMOutcome::ok(logic.result_state)), Err(err) => Ok(VMOutcome::abort(logic.result_state, err.into_vm_error()?)), }, - None => { - return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( - logic.result_state, - FunctionCallError::MethodResolveError( - MethodResolveError::MethodNotFound, - ), - )); - } + Err(err) => Ok(VMOutcome::abort(logic.result_state, err.into_vm_error()?)), }, - Err(err) => Ok(VMOutcome::abort(logic.result_state, err.into_vm_error()?)), - } - }) - } - - fn precompile( - &self, - code: &ContractCode, - cache: &dyn ContractRuntimeCache, - ) -> Result< - Result, - crate::logic::errors::CacheError, - > { - Ok(self - .compile_and_cache(code, cache)? - .map(|_| ContractPrecompilatonResult::ContractCompiled)) + None => { + return Ok(VMOutcome::abort_but_nop_outcome_in_old_protocol( + logic.result_state, + FunctionCallError::MethodResolveError(MethodResolveError::MethodNotFound), + )); + } + }, + Err(err) => Ok(VMOutcome::abort(logic.result_state, err.into_vm_error()?)), + } } } diff --git a/runtime/runtime-params-estimator/src/function_call.rs b/runtime/runtime-params-estimator/src/function_call.rs index 88b9acd5ae6..79145da0932 100644 --- a/runtime/runtime-params-estimator/src/function_call.rs +++ b/runtime/runtime-params-estimator/src/function_call.rs @@ -69,23 +69,26 @@ fn compute_function_call_cost( let config_store = RuntimeConfigStore::new(None); let runtime_config = config_store.get_config(protocol_version).as_ref(); let vm_config = runtime_config.wasm_config.clone(); - let runtime = vm_kind.runtime(vm_config).expect("runtime has not been enabled"); let fees = runtime_config.fees.clone(); let mut fake_external = MockedExternal::with_code(contract.clone_for_tests()); let fake_context = create_context("hello0", vec![]); // Warmup. for _ in 0..warmup_repeats { + let runtime = vm_kind.runtime(vm_config.clone()).expect("runtime has not been enabled"); let result = runtime - .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) + .prepare(&fake_external, &fake_context, cache) + .run(&mut fake_external, &fake_context, Arc::clone(&fees)) .expect("fatal error"); assert!(result.aborted.is_none()); } // Run with gas metering. let start = GasCost::measure(gas_metric); for _ in 0..repeats { + let runtime = vm_kind.runtime(vm_config.clone()).expect("runtime has not been enabled"); let result = runtime - .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) + .prepare(&fake_external, &fake_context, cache) + .run(&mut fake_external, &fake_context, Arc::clone(&fees)) .expect("fatal_error"); assert!(result.aborted.is_none()); } diff --git a/runtime/runtime-params-estimator/src/gas_metering.rs b/runtime/runtime-params-estimator/src/gas_metering.rs index 8a95972ec7e..4a710379661 100644 --- a/runtime/runtime-params-estimator/src/gas_metering.rs +++ b/runtime/runtime-params-estimator/src/gas_metering.rs @@ -136,16 +136,16 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode cfg.enable_all_features(); cfg }); - let runtime = vm_kind.runtime(vm_config_gas).expect("runtime has not been enabled"); - let runtime_free_gas = vm_kind.runtime(vm_config_free).expect("runtime has not been enabled"); let fees = runtime_config.fees.clone(); let mut fake_external = MockedExternal::with_code(contract.clone_for_tests()); let fake_context = create_context("hello", vec![]); // Warmup with gas metering for _ in 0..warmup_repeats { + let runtime = vm_kind.runtime(vm_config_gas.clone()).expect("runtime has not been enabled"); let result = runtime - .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) + .prepare(&fake_external, &fake_context, cache) + .run(&mut fake_external, &fake_context, Arc::clone(&fees)) .expect("fatal_error"); if let Some(err) = &result.aborted { eprintln!("error: {}", err); @@ -156,8 +156,10 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode // Run with gas metering. let start = GasCost::measure(gas_metric); for _ in 0..repeats { + let runtime = vm_kind.runtime(vm_config_gas.clone()).expect("runtime has not been enabled"); let result = runtime - .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) + .prepare(&fake_external, &fake_context, cache) + .run(&mut fake_external, &fake_context, Arc::clone(&fees)) .expect("fatal_error"); assert!(result.aborted.is_none()); } @@ -165,8 +167,11 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode // Warmup without gas metering for _ in 0..warmup_repeats { + let runtime_free_gas = + vm_kind.runtime(vm_config_free.clone()).expect("runtime has not been enabled"); let result = runtime_free_gas - .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) + .prepare(&fake_external, &fake_context, cache) + .run(&mut fake_external, &fake_context, Arc::clone(&fees)) .expect("fatal_error"); assert!(result.aborted.is_none()); } @@ -174,8 +179,11 @@ pub(crate) fn compute_gas_metering_cost(config: &Config, contract: &ContractCode // Run without gas metering. let start = GasCost::measure(gas_metric); for _ in 0..repeats { + let runtime_free_gas = + vm_kind.runtime(vm_config_free.clone()).expect("runtime has not been enabled"); let result = runtime_free_gas - .run(&mut fake_external, &fake_context, Arc::clone(&fees), cache) + .prepare(&fake_external, &fake_context, cache) + .run(&mut fake_external, &fake_context, Arc::clone(&fees)) .expect("fatal_error"); assert!(result.aborted.is_none()); } diff --git a/runtime/runtime-params-estimator/src/lib.rs b/runtime/runtime-params-estimator/src/lib.rs index fb322b79bbd..85ed414cabe 100644 --- a/runtime/runtime-params-estimator/src/lib.rs +++ b/runtime/runtime-params-estimator/src/lib.rs @@ -896,7 +896,8 @@ fn wasm_instruction(ctx: &mut EstimatorContext) -> GasCost { let vm_result = vm_kind .runtime(config.clone()) .unwrap() - .run(&mut fake_external, &context, Arc::clone(&fees), Some(&cache)) + .prepare(&fake_external, &context, Some(&cache)) + .run(&mut fake_external, &context, Arc::clone(&fees)) .expect("fatal_error"); assert!(vm_result.aborted.is_some()); vm_result From 5389c70c7ceeedfafcda923c25441643e790480b Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Wed, 26 Jun 2024 23:33:10 +0300 Subject: [PATCH 4/5] clippy fmt --- runtime/near-vm-runner/src/lib.rs | 2 +- runtime/near-vm-runner/src/near_vm_runner/runner.rs | 1 + runtime/near-vm-runner/src/runner.rs | 2 +- runtime/near-vm-runner/src/wasmer2_runner.rs | 1 + runtime/near-vm-runner/src/wasmer_runner.rs | 5 ++--- runtime/near-vm-runner/src/wasmtime_runner.rs | 1 + 6 files changed, 7 insertions(+), 5 deletions(-) diff --git a/runtime/near-vm-runner/src/lib.rs b/runtime/near-vm-runner/src/lib.rs index 0c035d79d92..ca8d19e8b9a 100644 --- a/runtime/near-vm-runner/src/lib.rs +++ b/runtime/near-vm-runner/src/lib.rs @@ -36,7 +36,7 @@ pub use code::ContractCode; #[cfg(feature = "metrics")] pub use metrics::{report_metrics, reset_metrics}; pub use profile::ProfileDataV3; -pub use runner::{run, VM, PreparedContract}; +pub use runner::{run, PreparedContract, VM}; /// This is public for internal experimentation use only, and should otherwise be considered an /// implementation detail of `near-vm-runner`. diff --git a/runtime/near-vm-runner/src/near_vm_runner/runner.rs b/runtime/near-vm-runner/src/near_vm_runner/runner.rs index 5af4fece7b1..b3f553a70dd 100644 --- a/runtime/near-vm-runner/src/near_vm_runner/runner.rs +++ b/runtime/near-vm-runner/src/near_vm_runner/runner.rs @@ -619,6 +619,7 @@ impl crate::runner::VM for NearVM { } } +#[allow(clippy::large_enum_variant)] pub(crate) enum PreparedContract { Outcome(VMOutcome), Ready { diff --git a/runtime/near-vm-runner/src/runner.rs b/runtime/near-vm-runner/src/runner.rs index ff0f2e8f628..3f264658677 100644 --- a/runtime/near-vm-runner/src/runner.rs +++ b/runtime/near-vm-runner/src/runner.rs @@ -86,7 +86,7 @@ pub fn run( Ok(outcome) } -pub trait PreparedContract { +pub trait PreparedContract: Send { /// Run the prepared contract. /// /// This is the entry point for executing a NEAR protocol contract. The entry point (as diff --git a/runtime/near-vm-runner/src/wasmer2_runner.rs b/runtime/near-vm-runner/src/wasmer2_runner.rs index 9ea0a3d5528..e6bb4bacaea 100644 --- a/runtime/near-vm-runner/src/wasmer2_runner.rs +++ b/runtime/near-vm-runner/src/wasmer2_runner.rs @@ -632,6 +632,7 @@ impl crate::runner::VM for Wasmer2VM { } } +#[allow(clippy::large_enum_variant)] pub(crate) enum PreparedContract { Outcome(VMOutcome), Ready { diff --git a/runtime/near-vm-runner/src/wasmer_runner.rs b/runtime/near-vm-runner/src/wasmer_runner.rs index c9721b86931..feafef1ed81 100644 --- a/runtime/near-vm-runner/src/wasmer_runner.rs +++ b/runtime/near-vm-runner/src/wasmer_runner.rs @@ -472,9 +472,7 @@ impl crate::runner::VM for Wasmer0VM { FunctionCallError::CompilationError(err), )))) } - Err(err) => { - return Box::new(Result::Err(err)) - } + Err(err) => return Box::new(Result::Err(err)), }; let result = result_state.after_loading_executable(code.code().len() as u64); @@ -495,6 +493,7 @@ impl crate::runner::VM for Wasmer0VM { } } +#[allow(clippy::large_enum_variant)] pub(crate) enum PreparedContract { Outcome(VMOutcome), Ready { diff --git a/runtime/near-vm-runner/src/wasmtime_runner.rs b/runtime/near-vm-runner/src/wasmtime_runner.rs index bef52299c07..20badf1d1e0 100644 --- a/runtime/near-vm-runner/src/wasmtime_runner.rs +++ b/runtime/near-vm-runner/src/wasmtime_runner.rs @@ -337,6 +337,7 @@ impl crate::runner::VM for WasmtimeVM { } } +#[allow(clippy::large_enum_variant)] pub(crate) enum PreparedContract { Outcome(VMOutcome), Ready { From d9154d923eab894f589905251f96e1f24cea7388 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Mon, 1 Jul 2024 12:07:59 +0300 Subject: [PATCH 5/5] Add struct for a ready contract --- .../src/near_vm_runner/runner.rs | 28 +++++++++--------- runtime/near-vm-runner/src/wasmer2_runner.rs | 28 +++++++++--------- runtime/near-vm-runner/src/wasmer_runner.rs | 29 +++++++++++-------- runtime/near-vm-runner/src/wasmtime_runner.rs | 24 +++++++-------- 4 files changed, 57 insertions(+), 52 deletions(-) diff --git a/runtime/near-vm-runner/src/near_vm_runner/runner.rs b/runtime/near-vm-runner/src/near_vm_runner/runner.rs index b3f553a70dd..aa8738ae292 100644 --- a/runtime/near-vm-runner/src/near_vm_runner/runner.rs +++ b/runtime/near-vm-runner/src/near_vm_runner/runner.rs @@ -594,13 +594,13 @@ impl crate::runner::VM for NearVM { )) } }; - Ok(PreparedContract::Ready { + Ok(PreparedContract::Ready(ReadyContract { memory, result_state, entrypoint, artifact: Arc::clone(artifact), vm, - }) + })) }); Box::new(prepd) } @@ -619,16 +619,18 @@ impl crate::runner::VM for NearVM { } } +struct ReadyContract { + memory: NearVmMemory, + result_state: ExecutionResultState, + entrypoint: FunctionIndex, + artifact: VMArtifact, + vm: Box, +} + #[allow(clippy::large_enum_variant)] -pub(crate) enum PreparedContract { +enum PreparedContract { Outcome(VMOutcome), - Ready { - memory: NearVmMemory, - result_state: ExecutionResultState, - entrypoint: FunctionIndex, - artifact: VMArtifact, - vm: Box, - }, + Ready(ReadyContract), } impl crate::PreparedContract for VMResult { @@ -638,11 +640,9 @@ impl crate::PreparedContract for VMResult { context: &VMContext, fees_config: Arc, ) -> VMResult { - let (memory, result_state, entrypoint, artifact, vm) = match (*self)? { + let ReadyContract { memory, result_state, entrypoint, artifact, vm } = match (*self)? { PreparedContract::Outcome(outcome) => return Ok(outcome), - PreparedContract::Ready { memory, result_state, entrypoint, artifact, vm } => { - (memory, result_state, entrypoint, artifact, vm) - } + PreparedContract::Ready(r) => r, }; let config = Arc::clone(&result_state.config); let vmmemory = memory.vm(); diff --git a/runtime/near-vm-runner/src/wasmer2_runner.rs b/runtime/near-vm-runner/src/wasmer2_runner.rs index e6bb4bacaea..bfef3fb8a6d 100644 --- a/runtime/near-vm-runner/src/wasmer2_runner.rs +++ b/runtime/near-vm-runner/src/wasmer2_runner.rs @@ -622,26 +622,28 @@ impl crate::runner::VM for Wasmer2VM { self.config.limit_config.max_memory_pages, ) .expect("Cannot create memory for a contract call"); - Box::new(Ok(PreparedContract::Ready { + Box::new(Ok(PreparedContract::Ready(ReadyContract { vm: self, memory, result_state, entrypoint, artifact, - })) + }))) } } +struct ReadyContract { + vm: Box, + memory: Wasmer2Memory, + result_state: ExecutionResultState, + entrypoint: FunctionIndex, + artifact: VMArtifact, +} + #[allow(clippy::large_enum_variant)] -pub(crate) enum PreparedContract { +enum PreparedContract { Outcome(VMOutcome), - Ready { - vm: Box, - memory: Wasmer2Memory, - result_state: ExecutionResultState, - entrypoint: FunctionIndex, - artifact: VMArtifact, - }, + Ready(ReadyContract), } impl crate::PreparedContract for VMResult { @@ -651,11 +653,9 @@ impl crate::PreparedContract for VMResult { context: &VMContext, fees_config: Arc, ) -> VMResult { - let (vm, memory, result_state, entrypoint, artifact) = match (*self)? { + let ReadyContract { vm, memory, result_state, entrypoint, artifact } = match (*self)? { PreparedContract::Outcome(outcome) => return Ok(outcome), - PreparedContract::Ready { vm, memory, result_state, entrypoint, artifact } => { - (vm, memory, result_state, entrypoint, artifact) - } + PreparedContract::Ready(r) => r, }; // FIXME: this mostly duplicates the `run_module` method. // Note that we don't clone the actual backing memory, just increase the RC. diff --git a/runtime/near-vm-runner/src/wasmer_runner.rs b/runtime/near-vm-runner/src/wasmer_runner.rs index feafef1ed81..ef666cf6917 100644 --- a/runtime/near-vm-runner/src/wasmer_runner.rs +++ b/runtime/near-vm-runner/src/wasmer_runner.rs @@ -489,19 +489,26 @@ impl crate::runner::VM for Wasmer0VM { self.config.limit_config.initial_memory_pages, self.config.limit_config.max_memory_pages, ); - Box::new(Ok(PreparedContract::Ready { vm: self, memory, result_state, module })) + Box::new(Ok(PreparedContract::Ready(ReadyContract { + vm: self, + memory, + result_state, + module, + }))) } } +struct ReadyContract { + vm: Box, + memory: WasmerMemory, + result_state: ExecutionResultState, + module: Module, +} + #[allow(clippy::large_enum_variant)] -pub(crate) enum PreparedContract { +enum PreparedContract { Outcome(VMOutcome), - Ready { - vm: Box, - memory: WasmerMemory, - result_state: ExecutionResultState, - module: Module, - }, + Ready(ReadyContract), } impl crate::PreparedContract for VMResult { @@ -511,11 +518,9 @@ impl crate::PreparedContract for VMResult { context: &VMContext, fees_config: Arc, ) -> Result { - let (vm, memory, result_state, module) = match (*self)? { + let ReadyContract { vm, memory, result_state, module } = match (*self)? { PreparedContract::Outcome(outcome) => return Ok(outcome), - PreparedContract::Ready { vm, memory, result_state, module } => { - (vm, memory, result_state, module) - } + PreparedContract::Ready(r) => r, }; // Note that we don't clone the actual backing memory, just increase the RC. let memory_copy = memory.clone(); diff --git a/runtime/near-vm-runner/src/wasmtime_runner.rs b/runtime/near-vm-runner/src/wasmtime_runner.rs index 20badf1d1e0..ef79ed89558 100644 --- a/runtime/near-vm-runner/src/wasmtime_runner.rs +++ b/runtime/near-vm-runner/src/wasmtime_runner.rs @@ -331,21 +331,23 @@ impl crate::runner::VM for WasmtimeVM { self.config.limit_config.max_memory_pages, ) .unwrap(); - Ok(PreparedContract::Ready { store, memory, module, result_state }) + Ok(PreparedContract::Ready(ReadyContract { store, memory, module, result_state })) }); Box::new(prepd) } } +struct ReadyContract { + store: Store<()>, + memory: WasmtimeMemory, + module: Module, + result_state: ExecutionResultState, +} + #[allow(clippy::large_enum_variant)] -pub(crate) enum PreparedContract { +enum PreparedContract { Outcome(VMOutcome), - Ready { - store: Store<()>, - memory: WasmtimeMemory, - module: Module, - result_state: ExecutionResultState, - }, + Ready(ReadyContract), } impl crate::PreparedContract for VMResult { @@ -355,11 +357,9 @@ impl crate::PreparedContract for VMResult { context: &VMContext, fees_config: Arc, ) -> VMResult { - let (mut store, memory, module, result_state) = match (*self)? { + let ReadyContract { mut store, memory, module, result_state } = match (*self)? { PreparedContract::Outcome(outcome) => return Ok(outcome), - PreparedContract::Ready { store, memory, module, result_state } => { - (store, memory, module, result_state) - } + PreparedContract::Ready(r) => r, }; let memory_copy = memory.0; let config = Arc::clone(&result_state.config);