From e270fb1473ed23dd84c6cb081959fa3df046c460 Mon Sep 17 00:00:00 2001 From: Ekleog-NEAR <96595974+Ekleog-NEAR@users.noreply.github.com> Date: Tue, 18 Apr 2023 13:13:19 +0200 Subject: [PATCH] add a protocol version running near-vm (#8912) This closes up the near-vm work. I believe https://github.com/near/nearcore/pull/8886#discussion_r1159879325 is the only remaining point to be checked in former PRs, in addition to all the code added in this PR. Note that there are still things in #8323 that are not in this PR, because either I noticed they actually should not be merged (eg. the wasmparser bump would be a protocol upgrade, so will wait until we get limited replayability), or because they are actually orthogonal to this landing. --- CHANGELOG.md | 3 + core/primitives-core/src/config.rs | 4 +- core/primitives/res/runtime_configs/61.yaml | 2 + .../res/runtime_configs/parameters.snap | 4 +- core/primitives/src/runtime/config_store.rs | 1 + ...runtime__config_store__tests__61.json.snap | 208 ++++++++++++++++++ ..._config_store__tests__testnet_61.json.snap | 208 ++++++++++++++++++ ...es__views__tests__runtime_config_view.snap | 4 +- core/primitives/src/version.rs | 7 + .../src/tests/client/features/wasmer2.rs | 13 +- .../src/tests/client/process_blocks.rs | 4 +- .../src/tests/runtime/sanity_checks.rs | 25 +-- ...__sanity_checks__receipts_gas_profile.snap | 25 +++ ..._checks__receipts_gas_profile_nightly.snap | 25 +++ ...receipts_gas_profile_nondeterministic.snap | 2 +- runtime/near-test-contracts/src/lib.rs | 2 +- .../test-contract-rs/src/lib.rs | 1 + runtime/near-vm-runner/src/tests/cache.rs | 109 +++++++-- .../src/tests/compile_errors.rs | 14 +- runtime/near-vm-runner/src/tests/fuzzers.rs | 45 +++- .../near-vm-runner/src/tests/rs_contract.rs | 2 +- .../src/tests/runtime_errors.rs | 76 ++++--- runtime/near-vm-runner/src/vm_kind.rs | 11 +- runtime/runtime-params-estimator/src/main.rs | 3 +- 24 files changed, 713 insertions(+), 85 deletions(-) create mode 100644 core/primitives/res/runtime_configs/61.yaml create mode 100644 core/primitives/src/runtime/snapshots/near_primitives__runtime__config_store__tests__61.json.snap create mode 100644 core/primitives/src/runtime/snapshots/near_primitives__runtime__config_store__tests__testnet_61.json.snap diff --git a/CHANGELOG.md b/CHANGELOG.md index b9d76de3218..caaef3ce73d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,12 @@ ### Protocol Changes +* Contract preparation and gas charging for wasm execution also switched to using our own code, as per the finite-wasm specification. Contract execution gas costs will change slightly for expected use cases. This opens up opportunities for further changing the execution gas costs (eg. with different costs per opcode) to lower contract execution cost long-term. + ### Non-protocol Changes * Node can sync State from S3. [#8789](https://github.com/near/nearcore/pull/8789) +* The contract runtime switched to using our fork of wasmer, with various improvements. ## 1.33.0 diff --git a/core/primitives-core/src/config.rs b/core/primitives-core/src/config.rs index d8c771987b1..4f9addae925 100644 --- a/core/primitives-core/src/config.rs +++ b/core/primitives-core/src/config.rs @@ -210,8 +210,8 @@ impl VMLimitConfig { // NOTE: Stack height has to be 16K, otherwise Wasmer produces non-deterministic results. // For experimentation try `test_stack_overflow`. - max_stack_height: 16 * 1024, // 16Kib of stack. - contract_prepare_version: ContractPrepareVersion::V1, + max_stack_height: 256 * 1024, // 256kiB of stack. + contract_prepare_version: ContractPrepareVersion::V2, initial_memory_pages: 2u32.pow(10), // 64Mib of memory. max_memory_pages: 2u32.pow(11), // 128Mib of memory. diff --git a/core/primitives/res/runtime_configs/61.yaml b/core/primitives/res/runtime_configs/61.yaml new file mode 100644 index 00000000000..c21d109e49f --- /dev/null +++ b/core/primitives/res/runtime_configs/61.yaml @@ -0,0 +1,2 @@ +max_stack_height: { old: 16384, new: 262144 } +contract_prepare_version: { old: 1, new: 2 } diff --git a/core/primitives/res/runtime_configs/parameters.snap b/core/primitives/res/runtime_configs/parameters.snap index 6b7f612af87..c8fb6a548c8 100644 --- a/core/primitives/res/runtime_configs/parameters.snap +++ b/core/primitives/res/runtime_configs/parameters.snap @@ -138,8 +138,8 @@ wasm_alt_bn128_g1_sum_base 3_000_000_000 wasm_alt_bn128_g1_sum_element 5_000_000_000 max_gas_burnt 300_000_000_000_000 max_gas_burnt_view 300_000_000_000_000 -max_stack_height 16_384 -contract_prepare_version 1 +max_stack_height 262_144 +contract_prepare_version 2 initial_memory_pages 1_024 max_memory_pages 2_048 registers_memory_limit 1_073_741_824 diff --git a/core/primitives/src/runtime/config_store.rs b/core/primitives/src/runtime/config_store.rs index 27c6fe23982..a5d97059ee8 100644 --- a/core/primitives/src/runtime/config_store.rs +++ b/core/primitives/src/runtime/config_store.rs @@ -30,6 +30,7 @@ static CONFIG_DIFFS: &[(ProtocolVersion, &str)] = &[ (57, include_config!("57.yaml")), // Introduce Zero Balance Account and increase account creation cost to 7.7Tgas (59, include_config!("59.yaml")), + (61, include_config!("61.yaml")), ]; /// Testnet parameters for versions <= 29, which (incorrectly) differed from mainnet parameters diff --git a/core/primitives/src/runtime/snapshots/near_primitives__runtime__config_store__tests__61.json.snap b/core/primitives/src/runtime/snapshots/near_primitives__runtime__config_store__tests__61.json.snap new file mode 100644 index 00000000000..60a5bcddd39 --- /dev/null +++ b/core/primitives/src/runtime/snapshots/near_primitives__runtime__config_store__tests__61.json.snap @@ -0,0 +1,208 @@ +--- +source: core/primitives/src/runtime/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/primitives/src/runtime/snapshots/near_primitives__runtime__config_store__tests__testnet_61.json.snap b/core/primitives/src/runtime/snapshots/near_primitives__runtime__config_store__tests__testnet_61.json.snap new file mode 100644 index 00000000000..60a5bcddd39 --- /dev/null +++ b/core/primitives/src/runtime/snapshots/near_primitives__runtime__config_store__tests__testnet_61.json.snap @@ -0,0 +1,208 @@ +--- +source: core/primitives/src/runtime/config_store.rs +expression: config_view +--- +{ + "storage_amount_per_byte": "10000000000000000000", + "transaction_costs": { + "action_receipt_creation_config": { + "send_sir": 108059500000, + "send_not_sir": 108059500000, + "execution": 108059500000 + }, + "data_receipt_creation_config": { + "base_cost": { + "send_sir": 36486732312, + "send_not_sir": 36486732312, + "execution": 36486732312 + }, + "cost_per_byte": { + "send_sir": 17212011, + "send_not_sir": 17212011, + "execution": 17212011 + } + }, + "action_creation_config": { + "create_account_cost": { + "send_sir": 3850000000000, + "send_not_sir": 3850000000000, + "execution": 3850000000000 + }, + "deploy_contract_cost": { + "send_sir": 184765750000, + "send_not_sir": 184765750000, + "execution": 184765750000 + }, + "deploy_contract_cost_per_byte": { + "send_sir": 6812999, + "send_not_sir": 6812999, + "execution": 64572944 + }, + "function_call_cost": { + "send_sir": 2319861500000, + "send_not_sir": 2319861500000, + "execution": 2319861500000 + }, + "function_call_cost_per_byte": { + "send_sir": 2235934, + "send_not_sir": 2235934, + "execution": 2235934 + }, + "transfer_cost": { + "send_sir": 115123062500, + "send_not_sir": 115123062500, + "execution": 115123062500 + }, + "stake_cost": { + "send_sir": 141715687500, + "send_not_sir": 141715687500, + "execution": 102217625000 + }, + "add_key_cost": { + "full_access_cost": { + "send_sir": 101765125000, + "send_not_sir": 101765125000, + "execution": 101765125000 + }, + "function_call_cost": { + "send_sir": 102217625000, + "send_not_sir": 102217625000, + "execution": 102217625000 + }, + "function_call_cost_per_byte": { + "send_sir": 1925331, + "send_not_sir": 1925331, + "execution": 1925331 + } + }, + "delete_key_cost": { + "send_sir": 94946625000, + "send_not_sir": 94946625000, + "execution": 94946625000 + }, + "delete_account_cost": { + "send_sir": 147489000000, + "send_not_sir": 147489000000, + "execution": 147489000000 + }, + "delegate_cost": { + "send_sir": 200000000000, + "send_not_sir": 200000000000, + "execution": 200000000000 + } + }, + "storage_usage_config": { + "num_bytes_account": 100, + "num_extra_bytes_record": 40 + }, + "burnt_gas_reward": [ + 3, + 10 + ], + "pessimistic_gas_price_inflation_ratio": [ + 103, + 100 + ] + }, + "wasm_config": { + "ext_costs": { + "base": 264768111, + "contract_loading_base": 35445963, + "contract_loading_bytes": 216750, + "read_memory_base": 2609863200, + "read_memory_byte": 3801333, + "write_memory_base": 2803794861, + "write_memory_byte": 2723772, + "read_register_base": 2517165186, + "read_register_byte": 98562, + "write_register_base": 2865522486, + "write_register_byte": 3801564, + "utf8_decoding_base": 3111779061, + "utf8_decoding_byte": 291580479, + "utf16_decoding_base": 3543313050, + "utf16_decoding_byte": 163577493, + "sha256_base": 4540970250, + "sha256_byte": 24117351, + "keccak256_base": 5879491275, + "keccak256_byte": 21471105, + "keccak512_base": 5811388236, + "keccak512_byte": 36649701, + "ripemd160_base": 853675086, + "ripemd160_block": 680107584, + "ed25519_verify_base": 210000000000, + "ed25519_verify_byte": 9000000, + "ecrecover_base": 278821988457, + "log_base": 3543313050, + "log_byte": 13198791, + "storage_write_base": 64196736000, + "storage_write_key_byte": 70482867, + "storage_write_value_byte": 31018539, + "storage_write_evicted_byte": 32117307, + "storage_read_base": 56356845750, + "storage_read_key_byte": 30952533, + "storage_read_value_byte": 5611005, + "storage_remove_base": 53473030500, + "storage_remove_key_byte": 38220384, + "storage_remove_ret_value_byte": 11531556, + "storage_has_key_base": 54039896625, + "storage_has_key_byte": 30790845, + "storage_iter_create_prefix_base": 0, + "storage_iter_create_prefix_byte": 0, + "storage_iter_create_range_base": 0, + "storage_iter_create_from_byte": 0, + "storage_iter_create_to_byte": 0, + "storage_iter_next_base": 0, + "storage_iter_next_key_byte": 0, + "storage_iter_next_value_byte": 0, + "touching_trie_node": 16101955926, + "read_cached_trie_node": 2280000000, + "promise_and_base": 1465013400, + "promise_and_per_promise": 5452176, + "promise_return": 560152386, + "validator_stake_base": 911834726400, + "validator_total_stake_base": 911834726400, + "contract_compile_base": 0, + "contract_compile_bytes": 0, + "alt_bn128_g1_multiexp_base": 713000000000, + "alt_bn128_g1_multiexp_element": 320000000000, + "alt_bn128_g1_sum_base": 3000000000, + "alt_bn128_g1_sum_element": 5000000000, + "alt_bn128_pairing_check_base": 9686000000000, + "alt_bn128_pairing_check_element": 5102000000000 + }, + "grow_mem_cost": 1, + "regular_op_cost": 822756, + "limit_config": { + "max_gas_burnt": 300000000000000, + "max_stack_height": 262144, + "contract_prepare_version": 2, + "initial_memory_pages": 1024, + "max_memory_pages": 2048, + "registers_memory_limit": 1073741824, + "max_register_size": 104857600, + "max_number_registers": 100, + "max_number_logs": 100, + "max_total_log_length": 16384, + "max_total_prepaid_gas": 300000000000000, + "max_actions_per_receipt": 100, + "max_number_bytes_method_names": 2000, + "max_length_method_name": 256, + "max_arguments_length": 4194304, + "max_length_returned_data": 4194304, + "max_contract_size": 4194304, + "max_transaction_size": 4194304, + "max_length_storage_key": 2048, + "max_length_storage_value": 4194304, + "max_promises_per_function_call_action": 1024, + "max_number_input_data_dependencies": 128, + "max_functions_number_per_contract": 10000, + "wasmer2_stack_limit": 204800, + "max_locals_per_contract": 1000000, + "account_id_validity_rules_version": 1 + } + }, + "account_creation_config": { + "min_allowed_top_level_account_length": 32, + "registrar_account_id": "registrar" + } +} diff --git a/core/primitives/src/snapshots/near_primitives__views__tests__runtime_config_view.snap b/core/primitives/src/snapshots/near_primitives__views__tests__runtime_config_view.snap index 620a2104a8c..4b2bd548491 100644 --- a/core/primitives/src/snapshots/near_primitives__views__tests__runtime_config_view.snap +++ b/core/primitives/src/snapshots/near_primitives__views__tests__runtime_config_view.snap @@ -174,8 +174,8 @@ expression: "&view" "regular_op_cost": 3856371, "limit_config": { "max_gas_burnt": 200000000000000, - "max_stack_height": 16384, - "contract_prepare_version": 1, + "max_stack_height": 262144, + "contract_prepare_version": 2, "initial_memory_pages": 1024, "max_memory_pages": 2048, "registers_memory_limit": 1073741824, diff --git a/core/primitives/src/version.rs b/core/primitives/src/version.rs index 033fb1b83b0..a56998a460c 100644 --- a/core/primitives/src/version.rs +++ b/core/primitives/src/version.rs @@ -89,6 +89,12 @@ pub enum ProtocolFeature { /// Although wasmer2 is faster, we don't change fees with this protocol /// version -- we can safely do that in a separate step. Wasmer2, + /// This feature switch our WASM engine implementation from wasmer 2.* to + /// near-vm, bringing better performance and reliability. + /// + /// Although near-vm is faster, we don't change fees with this protocol + /// version -- we can safely do that in a separate step. + NearVm, SimpleNightshade, LowerDataReceiptAndEcrecoverBaseCost, /// Lowers the cost of wasm instruction due to switch to wasmer2. @@ -243,6 +249,7 @@ impl ProtocolFeature { | ProtocolFeature::ZeroBalanceAccount | ProtocolFeature::DelegateAction => 59, ProtocolFeature::ComputeCosts => 61, + ProtocolFeature::NearVm => 61, // Nightly features #[cfg(feature = "protocol_feature_fix_staking_threshold")] diff --git a/integration-tests/src/tests/client/features/wasmer2.rs b/integration-tests/src/tests/client/features/wasmer2.rs index 935a4267994..98b2c40f99b 100644 --- a/integration-tests/src/tests/client/features/wasmer2.rs +++ b/integration-tests/src/tests/client/features/wasmer2.rs @@ -10,12 +10,13 @@ use nearcore::config::GenesisExt; // This test fails on aarch because wasmer0 and wasmer2 are not available. #[cfg_attr(all(target_arch = "aarch64", target_vendor = "apple"), ignore)] #[test] -fn test_wasmer2_upgrade() { +fn test_near_vm_upgrade() { let mut capture = near_o11y::testonly::TracingCapture::enable(); let old_protocol_version = - near_primitives::version::ProtocolFeature::Wasmer2.protocol_version() - 1; + near_primitives::version::ProtocolFeature::NearVm.protocol_version() - 1; let new_protocol_version = old_protocol_version + 1; + eprintln!("Testing protocol upgrade between {old_protocol_version} and {new_protocol_version}"); // Prepare TestEnv with a contract at the old protocol version. let mut env = { @@ -81,6 +82,10 @@ fn test_wasmer2_upgrade() { capture.drain() }; - assert!(logs_at_old_version.iter().any(|l| l.contains(&"vm_kind=Wasmer0"))); - assert!(logs_at_new_version.iter().any(|l| l.contains(&"vm_kind=Wasmer2"))); + assert!(logs_at_old_version.iter().any(|l| l.contains(&"vm_kind=Wasmer2"))); + assert!( + logs_at_new_version.iter().any(|l| l.contains(&"vm_kind=NearVm")), + "Expected to find 'vm_kind=NearVm' in logs, occurences of vm_kind are {:?}", + logs_at_new_version.iter().filter(|l| l.contains("vm_kind")).collect::>(), + ); } diff --git a/integration-tests/src/tests/client/process_blocks.rs b/integration-tests/src/tests/client/process_blocks.rs index 9c1c8526092..eff99373727 100644 --- a/integration-tests/src/tests/client/process_blocks.rs +++ b/integration-tests/src/tests/client/process_blocks.rs @@ -2858,11 +2858,11 @@ fn test_execution_metadata() { "cost": "CONTRACT_LOADING_BYTES", "gas_used": "18423750" }, - // We spend two wasm instructions (call & drop). + // We spend two wasm instructions (call & drop), plus 8 ops for initializing the stack. { "cost_category": "WASM_HOST_COST", "cost": "WASM_INSTRUCTION", - "gas_used": (config.wasm_config.regular_op_cost as u64 * 2).to_string() + "gas_used": (config.wasm_config.regular_op_cost as u64 * 10).to_string() } ]); let outcome = &execution_outcome.receipts_outcome[0].outcome; diff --git a/integration-tests/src/tests/runtime/sanity_checks.rs b/integration-tests/src/tests/runtime/sanity_checks.rs index dc8e2f47e3c..71427d0b6fb 100644 --- a/integration-tests/src/tests/runtime/sanity_checks.rs +++ b/integration-tests/src/tests/runtime/sanity_checks.rs @@ -232,22 +232,19 @@ fn test_sanity_used_gas() { .collect::>(); // Executing `used_gas` costs `base_cost`. When executing `used_gas` twice - // within a metered block, the returned values should differ by that amount. + // within a metered block, the returned values should differ by that amount + // plus 2 regular_op_cost, one for the local.set and one for the call. let base_cost = node.client.read().unwrap().runtime_config.wasm_config.ext_costs.gas_cost(ExtCosts::base); - assert_eq!(used_gas[1] - used_gas[0], base_cost); - - // The fees for executing a metered block's WASM code should be paid before - // any of the call instructions within that block are executed. Hence, even - // after arithmetics, the next call of `used_gas` should still return a - // value that differs only by `base_cost`. - assert_eq!(used_gas[2] - used_gas[1], base_cost); - - // Entering a new metered block, all of its instructions are paid upfront. - // Therefore, the difference across blocks must be larger than `base_cost`, - // given that the block contains other instructions besides the call of - // `used_gas`. - assert!(used_gas[3] - used_gas[2] > base_cost); + let regular_op_cost = + u64::from(node.client.read().unwrap().runtime_config.wasm_config.regular_op_cost); + assert_eq!(used_gas[1] - used_gas[0], base_cost + 2 * regular_op_cost); + + // Similarly, we have 7 instructions between the two used_gas calls. + assert_eq!(used_gas[2] - used_gas[1], base_cost + 7 * regular_op_cost); + + // And still the same if there are br_if calls (6 regular ops, as block doesn’t count) + assert_eq!(used_gas[3] - used_gas[2], base_cost + 6 * regular_op_cost); } /// Returns a contract which calls host function `used_gas` multiple times, both diff --git a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile.snap b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile.snap index 1fb9f93c99e..b7622e589dc 100644 --- a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile.snap +++ b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile.snap @@ -382,6 +382,11 @@ expression: receipts_gas_profile cost: "CONTRACT_LOADING_BYTES", gas_used: 0, }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, ], [], [ @@ -395,6 +400,11 @@ expression: receipts_gas_profile cost: "CONTRACT_LOADING_BYTES", gas_used: 0, }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, ], [], [ @@ -408,6 +418,11 @@ expression: receipts_gas_profile cost: "CONTRACT_LOADING_BYTES", gas_used: 0, }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, ], [], [], @@ -425,6 +440,11 @@ expression: receipts_gas_profile cost: "CONTRACT_LOADING_BYTES", gas_used: 0, }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, ], [], [], @@ -441,6 +461,11 @@ expression: receipts_gas_profile cost: "CONTRACT_LOADING_BYTES", gas_used: 0, }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, ], [], [ diff --git a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nightly.snap b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nightly.snap index c9f7315dd83..8cfca701ac1 100644 --- a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nightly.snap +++ b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nightly.snap @@ -382,6 +382,11 @@ expression: receipts_gas_profile cost: "CONTRACT_LOADING_BYTES", gas_used: 0, }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, ], [], [ @@ -395,6 +400,11 @@ expression: receipts_gas_profile cost: "CONTRACT_LOADING_BYTES", gas_used: 0, }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, ], [], [ @@ -408,6 +418,11 @@ expression: receipts_gas_profile cost: "CONTRACT_LOADING_BYTES", gas_used: 0, }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, ], [], [], @@ -425,6 +440,11 @@ expression: receipts_gas_profile cost: "CONTRACT_LOADING_BYTES", gas_used: 0, }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, ], [], [], @@ -441,6 +461,11 @@ expression: receipts_gas_profile cost: "CONTRACT_LOADING_BYTES", gas_used: 0, }, + CostGasUsed { + cost_category: "WASM_HOST_COST", + cost: "WASM_INSTRUCTION", + gas_used: 0, + }, ], [], [ diff --git a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nondeterministic.snap b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nondeterministic.snap index b9616f30a16..ed45e974852 100644 --- a/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nondeterministic.snap +++ b/integration-tests/src/tests/runtime/snapshots/integration_tests__tests__runtime__sanity_checks__receipts_gas_profile_nondeterministic.snap @@ -17,7 +17,7 @@ expression: receipts_gas_profile CostGasUsed { cost_category: "WASM_HOST_COST", cost: "WASM_INSTRUCTION", - gas_used: 1645512, + gas_used: 8227560, }, ], [], diff --git a/runtime/near-test-contracts/src/lib.rs b/runtime/near-test-contracts/src/lib.rs index 70a8275ca27..feaa43f5272 100644 --- a/runtime/near-test-contracts/src/lib.rs +++ b/runtime/near-test-contracts/src/lib.rs @@ -30,7 +30,7 @@ pub fn sized_contract(size: usize) -> Vec { let adjusted_size = size as i64 - (base_size as i64 - size as i64); let payload = "x".repeat(adjusted_size as usize); let code = format!( - r#"(module + r#"(module (memory 1) (func (export "main")) (data (i32.const 0) "{payload}") diff --git a/runtime/near-test-contracts/test-contract-rs/src/lib.rs b/runtime/near-test-contracts/test-contract-rs/src/lib.rs index 2638be47657..b3a1fba14b1 100644 --- a/runtime/near-test-contracts/test-contract-rs/src/lib.rs +++ b/runtime/near-test-contracts/test-contract-rs/src/lib.rs @@ -578,6 +578,7 @@ pub unsafe fn recurse() { /// Rust compiler is getting smarter and starts to optimize my deep recursion. /// We're going to fight it with a more obscure implementations. #[no_mangle] +#[inline(never)] fn internal_recurse(n: u64) -> u64 { if n <= 1 { n diff --git a/runtime/near-vm-runner/src/tests/cache.rs b/runtime/near-vm-runner/src/tests/cache.rs index e89431b0311..1ebe03ed08c 100644 --- a/runtime/near-vm-runner/src/tests/cache.rs +++ b/runtime/near-vm-runner/src/tests/cache.rs @@ -3,6 +3,7 @@ use super::{create_context, with_vm_variants, LATEST_PROTOCOL_VERSION}; use crate::internal::VMKind; +use crate::near_vm_runner::NearVM; use crate::runner::VMResult; use crate::wasmer2_runner::Wasmer2VM; use crate::{prepare, MockCompiledContractCache}; @@ -12,14 +13,14 @@ use near_primitives::hash::CryptoHash; use near_primitives::runtime::fees::RuntimeFeesConfig; use near_primitives::types::{CompiledContract, CompiledContractCache}; use near_stable_hasher::StableHasher; +use near_vm_compiler::{CpuFeature, Target}; +use near_vm_engine::Executable; use near_vm_errors::VMRunnerError; use near_vm_logic::mocks::mock_external::MockedExternal; use near_vm_logic::VMConfig; use std::hash::{Hash, Hasher}; use std::io; use std::sync::atomic::{AtomicBool, Ordering}; -use wasmer_compiler::{CpuFeature, Target}; -use wasmer_engine::Executable; #[test] fn test_caches_compilation_error() { @@ -105,6 +106,7 @@ fn make_cached_contract_call_vm( #[test] fn test_wasmer2_artifact_output_stability() { + use wasmer_engine::Executable; // If this test has failed, you want to adjust the necessary constants so that `cache::vm_hash` // changes (and only then the hashes here). // @@ -112,23 +114,23 @@ fn test_wasmer2_artifact_output_stability() { // fall through the cracks here, but hopefully it should catch most of the fish just fine. let seeds = [2, 3, 5, 7, 11, 13, 17]; let prepared_hashes = [ - 12248437801724644735, - 2647244875869025389, - 892153519407678490, - 8592050243596620350, - 2309330154575012917, - 9323529151210819831, - 11488755771702465226, + 11313378614122864359, + 5865541421624917606, + 11731917380556063495, + 8000182875575317016, + 3130574445877428311, + 11574598916196339098, + 10719493536745069553, ]; let mut got_prepared_hashes = Vec::with_capacity(seeds.len()); let compiled_hashes = [ - 9393269650223240896, - 6124152160101285799, - 8789306975506976814, - 11819823914734034238, - 5479892730668892774, - 8176904529073798417, - 345836015667433529, + 5254981150840481178, + 15529260255496677612, + 407257192602619216, + 10913823971520273759, + 17423008210698923502, + 7011050181604188333, + 15514788595649734538, ]; let mut got_compiled_hashes = Vec::with_capacity(seeds.len()); for seed in seeds { @@ -141,11 +143,84 @@ fn test_wasmer2_artifact_output_stability() { (&contract.code(), &prepared_code).hash(&mut hasher); got_prepared_hashes.push(hasher.finish()); + let mut features = wasmer_compiler::CpuFeature::set(); + features.insert(wasmer_compiler::CpuFeature::AVX); + let triple = "x86_64-unknown-linux-gnu".parse().unwrap(); + let target = wasmer_compiler::Target::new(triple, features); + let vm = Wasmer2VM::new_for_target(config, target); + let artifact = vm.compile_uncached(&contract).unwrap(); + let serialized = artifact.serialize().unwrap(); + let mut hasher = StableHasher::new(); + serialized.hash(&mut hasher); + let this_hash = hasher.finish(); + got_compiled_hashes.push(this_hash); + + std::fs::write(format!("/tmp/artifact{}", this_hash), serialized).unwrap(); + } + // These asserts have failed as a result of some change and the following text describes what + // the implications of the change. + // + // May need a protocol version change, and definitely wants a `WASMER2_CONFIG version update + // too, as below. Maybe something else too. + assert!( + got_prepared_hashes == prepared_hashes, + "contract preparation hashes have changed to {:#?}", + got_prepared_hashes + ); + // In this case you will need to adjust the WASMER2_CONFIG version so that the cached contracts + // are evicted from the contract cache. + assert!( + got_compiled_hashes == compiled_hashes, + "VM output hashes have changed to {:#?}", + got_compiled_hashes + ); + // Once it has been confirmed that these steps have been done, the expected hashes in this test + // can be adjusted. +} + +#[test] +fn test_near_vm_artifact_output_stability() { + // If this test has failed, you want to adjust the necessary constants so that `cache::vm_hash` + // changes (and only then the hashes here). + // + // Note that this test is a best-effort fish net. Some changes that should modify the hash will + // fall through the cracks here, but hopefully it should catch most of the fish just fine. + let seeds = [2, 3, 5, 7, 11, 13, 17]; + let prepared_hashes = [ + 15237011375120738807, + 3750594434467176559, + 2196541628148102482, + 1576495094908614397, + 6394387219699970793, + 18132026143745992229, + 4095228008100475322, + ]; + let mut got_prepared_hashes = Vec::with_capacity(seeds.len()); + let compiled_hashes = [ + 244044469030513689, + 5237896941728381456, + 8878249955373865409, + 14070580588423897597, + 16220196077572326609, + 11663797245987968376, + 17572197684302294358, + ]; + let mut got_compiled_hashes = Vec::with_capacity(seeds.len()); + for seed in seeds { + let contract = ContractCode::new(near_test_contracts::arbitrary_contract(seed), None); + + let config = VMConfig::test(); + let prepared_code = + prepare::prepare_contract(contract.code(), &config, VMKind::NearVm).unwrap(); + let mut hasher = StableHasher::new(); + (&contract.code(), &prepared_code).hash(&mut hasher); + got_prepared_hashes.push(hasher.finish()); + let mut features = CpuFeature::set(); features.insert(CpuFeature::AVX); let triple = "x86_64-unknown-linux-gnu".parse().unwrap(); let target = Target::new(triple, features); - let vm = Wasmer2VM::new_for_target(config, target); + let vm = NearVM::new_for_target(config, target); let artifact = vm.compile_uncached(&contract).unwrap(); let serialized = artifact.serialize().unwrap(); let mut hasher = StableHasher::new(); diff --git a/runtime/near-vm-runner/src/tests/compile_errors.rs b/runtime/near-vm-runner/src/tests/compile_errors.rs index 3447bee9739..a86428bb68a 100644 --- a/runtime/near-vm-runner/src/tests/compile_errors.rs +++ b/runtime/near-vm-runner/src/tests/compile_errors.rs @@ -171,11 +171,11 @@ fn test_limit_contract_functions_number() { VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13048032213 used gas 13048032213 "#]], expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13048032213 used gas 13048032213 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13054614261 used gas 13054614261 "#]], #[cfg(feature = "protocol_feature_fix_contract_loading_cost")] expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13048032213 used gas 13048032213 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13054614261 used gas 13054614261 "#]], ]); @@ -258,7 +258,7 @@ fn test_limit_contract_functions_number() { } #[test] -fn test_limit_locals() { +fn test_limit_locals_bigfunc() { test_builder() .wasm( &near_test_contracts::LargeContract { @@ -293,10 +293,10 @@ fn test_limit_locals() { } .make(), ) - .skip_wasmtime() + .opaque_error() // near-vm returns a proper stack overflow, others return memory access violation .expect(expect![[r#" VMOutcome: balance 4 storage_usage 12 return data None burnt gas 43682463 used gas 43682463 - Err: WebAssembly trap: An `unreachable` opcode was executed. + Err: ... "#]]); } @@ -338,7 +338,7 @@ fn test_limit_locals_global() { .make(), ) .expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 139269213 used gas 139269213 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13001413761 used gas 13001413761 "#]]); } @@ -361,7 +361,7 @@ pub fn test_stablized_host_function() { Err: ... "#]], expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 7143010623 used gas 7143010623 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 7149592671 used gas 7149592671 "#]], ]); } diff --git a/runtime/near-vm-runner/src/tests/fuzzers.rs b/runtime/near-vm-runner/src/tests/fuzzers.rs index 23198c3be06..8afc5e3f87a 100644 --- a/runtime/near-vm-runner/src/tests/fuzzers.rs +++ b/runtime/near-vm-runner/src/tests/fuzzers.rs @@ -170,7 +170,21 @@ fn wasmer2_and_wasmtime_agree() { let wasmer2 = run_fuzz(&code, VMKind::Wasmer2).expect("fatal failure"); let wasmtime = run_fuzz(&code, VMKind::Wasmtime).expect("fatal failure"); assert_eq!(wasmer2, wasmtime); - assert_eq!(wasmer2, wasmtime); + }); +} + +#[test] +fn near_vm_and_wasmtime_agree() { + check!().for_each(|data: &[u8]| { + let module = ArbitraryModule::arbitrary(&mut arbitrary::Unstructured::new(data)); + let module = match module { + Ok(m) => m, + Err(_) => return, + }; + let code = ContractCode::new(module.0.module.to_bytes(), None); + let near_vm = run_fuzz(&code, VMKind::NearVm).expect("fatal failure"); + let wasmtime = run_fuzz(&code, VMKind::Wasmtime).expect("fatal failure"); + assert_eq!(near_vm, wasmtime); }); } @@ -202,3 +216,32 @@ fn wasmer2_is_reproducible() { } }) } + +#[cfg(all(feature = "near_vm", target_arch = "x86_64"))] +#[test] +fn near_vm_is_reproducible() { + use crate::near_vm_runner::NearVM; + use near_primitives::hash::CryptoHash; + use near_vm_engine::Executable; + + bolero::check!().for_each(|data: &[u8]| { + if let Ok(module) = ArbitraryModule::arbitrary(&mut arbitrary::Unstructured::new(data)) { + let code = ContractCode::new(module.0.module.to_bytes(), None); + let config = VMConfig::test(); + let mut first_hash = None; + for _ in 0..3 { + let vm = NearVM::new(config.clone()); + let exec = match vm.compile_uncached(&code) { + Ok(e) => e, + Err(_) => return, + }; + let code = exec.serialize().unwrap(); + let hash = CryptoHash::hash_bytes(&code); + match first_hash { + None => first_hash = Some(hash), + Some(h) => assert_eq!(h, hash), + } + } + } + }) +} diff --git a/runtime/near-vm-runner/src/tests/rs_contract.rs b/runtime/near-vm-runner/src/tests/rs_contract.rs index 52a2abf5b11..6332a87b2e4 100644 --- a/runtime/near-vm-runner/src/tests/rs_contract.rs +++ b/runtime/near-vm-runner/src/tests/rs_contract.rs @@ -151,7 +151,7 @@ def_test_ext!(ext_block_timestamp, "ext_block_timestamp", &42u64.to_le_bytes()); def_test_ext!(ext_storage_usage, "ext_storage_usage", &12u64.to_le_bytes()); // Note, the used_gas is not a global used_gas at the beginning of method, but instead a diff // in used_gas for computing fib(30) in a loop -def_test_ext!(ext_used_gas, "ext_used_gas", &[111, 10, 200, 15, 0, 0, 0, 0]); +def_test_ext!(ext_used_gas, "ext_used_gas", &[72, 146, 120, 16, 0, 0, 0, 0]); def_test_ext!( ext_sha256, "ext_sha256", diff --git a/runtime/near-vm-runner/src/tests/runtime_errors.rs b/runtime/near-vm-runner/src/tests/runtime_errors.rs index 11a1157265e..0723fbdda1a 100644 --- a/runtime/near-vm-runner/src/tests/runtime_errors.rs +++ b/runtime/near-vm-runner/src/tests/runtime_errors.rs @@ -1,5 +1,6 @@ use super::test_builder::test_builder; use expect_test::expect; +#[allow(unused_imports)] // used only when specific features are enabled use near_primitives::version::ProtocolFeature; use std::fmt::Write; @@ -11,7 +12,7 @@ static INFINITE_INITIALIZER_CONTRACT: &str = r#" )"#; #[test] -fn test_infinite_initializer() { +fn test_infinite_initializer_basic() { test_builder() .wat(INFINITE_INITIALIZER_CONTRACT) .gas(10u64.pow(10)) @@ -47,7 +48,7 @@ static SIMPLE_CONTRACT: &str = r#"(module (func (export "main")))"#; #[test] fn test_simple_contract() { test_builder().wat(SIMPLE_CONTRACT).expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 42815463 used gas 42815463 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 49397511 used gas 49397511 "#]]); } @@ -133,7 +134,7 @@ fn test_trap_contract() { .wat(r#"(module (func (export "main") (unreachable)) )"#) .skip_wasmtime() .expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 43854969 used gas 43854969 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 50437017 used gas 50437017 Err: WebAssembly trap: An `unreachable` opcode was executed. "#]]); } @@ -150,7 +151,7 @@ fn test_trap_initializer() { ) .skip_wasmtime() .expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 47322969 used gas 47322969 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 53905017 used gas 53905017 Err: WebAssembly trap: An `unreachable` opcode was executed. "#]]); } @@ -171,7 +172,7 @@ fn test_div_by_zero_contract() { ) .skip_wasmtime() .expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 47406987 used gas 47406987 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 53166279 used gas 53166279 Err: WebAssembly trap: An arithmetic exception, e.g. divided by zero. "#]]); } @@ -192,7 +193,7 @@ fn test_float_to_int_contract() { )) .skip_wasmtime() .expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 47667981 used gas 47667981 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 53427273 used gas 53427273 Err: WebAssembly trap: An arithmetic exception, e.g. divided by zero. "#]]); } @@ -216,7 +217,7 @@ fn test_indirect_call_to_null_contract() { .opaque_error() .skip_wasmtime() .expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 50919231 used gas 50919231 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 56678523 used gas 56678523 Err: ... "#]]) } @@ -242,7 +243,7 @@ fn test_indirect_call_to_wrong_signature_contract() { ) .skip_wasmtime() .expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 55904481 used gas 55904481 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 61663773 used gas 61663773 Err: WebAssembly trap: Call indirect incorrect signature trap. "#]]) } @@ -300,7 +301,7 @@ fn test_guest_panic() { )"#, ) .expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 315775830 used gas 315775830 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 322357878 used gas 322357878 Err: Smart contract panicked: explicit guest panic "#]]); } @@ -326,9 +327,10 @@ fn test_stack_overflow() { test_builder() .wat(r#"(module (func $f (export "main") (call $f)))"#) .skip_wasmtime() + .opaque_error() // near-vm returns stack overflow, others return invalid memory access .expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 13526101017 used gas 13526101017 - Err: WebAssembly trap: An `unreachable` opcode was executed. + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 30376143897 used gas 30376143897 + Err: ... "#]]); } @@ -349,14 +351,15 @@ fn test_stack_instrumentation_protocol_upgrade() { .method("f1") .protocol_features(&[ProtocolFeature::CorrectStackLimit]) .skip_wasmtime() + .opaque_error() // near-vm returns stack overflow, others return invalid memory access .expects(&[ expect![[r#" VMOutcome: balance 4 storage_usage 12 return data None burnt gas 6789985365 used gas 6789985365 - Err: WebAssembly trap: An `unreachable` opcode was executed. + Err: ... "#]], expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 6789985365 used gas 6789985365 - Err: WebAssembly trap: An `unreachable` opcode was executed. + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 31767212013 used gas 31767212013 + Err: ... "#]], ]); @@ -375,14 +378,15 @@ fn test_stack_instrumentation_protocol_upgrade() { .method("f2") .protocol_features(&[ProtocolFeature::CorrectStackLimit]) .skip_wasmtime() + .opaque_error() // near-vm returns stack overflow, others return invalid memory access .expects(&[ expect![[r#" VMOutcome: balance 4 storage_usage 12 return data None burnt gas 6789985365 used gas 6789985365 - Err: WebAssembly trap: An `unreachable` opcode was executed. + Err: ... "#]], expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 2745316869 used gas 2745316869 - Err: WebAssembly trap: An `unreachable` opcode was executed. + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 29698803429 used gas 29698803429 + Err: ... "#]], ]); } @@ -542,7 +546,7 @@ static EXTERNAL_CALL_CONTRACT: &str = r#" #[test] fn test_external_call_ok() { test_builder().wat(EXTERNAL_CALL_CONTRACT).expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 320283336 used gas 320283336 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 326865384 used gas 326865384 "#]]); } @@ -571,7 +575,7 @@ fn test_external_call_indirect() { ) )"# ).expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 328909092 used gas 328909092 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 335491140 used gas 335491140 "#]]); } @@ -589,13 +593,31 @@ fn test_address_overflow() { )"#; test_builder().wat(code).skip_wasmtime().skip_wasmer0().expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 48534981 used gas 48534981 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 54294273 used gas 54294273 Err: WebAssembly trap: Memory out of bounds trap. "#]]); // wasmer0 incorrectly doesn't catch overflow during address calculation - test_builder().wat(code).skip_wasmtime().skip_wasmer2().expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 48534981 used gas 48534981 + test_builder().wat(code).only_wasmer0().expect(expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 55117029 used gas 55117029 + "#]]); +} + +/// Load from address that is within bounds, validating that not all loads do overflow +#[test] +fn test_address_valid() { + let code = r#" +(module + (memory 1) + (func (export "main") + i32.const 10 + i64.load32_u offset=10 align=1 + drop + ) +)"#; + + test_builder().wat(code).expect(expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 54250029 used gas 54250029 "#]]); } @@ -623,12 +645,12 @@ fn test_nan_sign() { )"#; test_builder().wat(code).skip_wasmtime().skip_wasmer0().expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 54988767 used gas 54988767 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 61570815 used gas 61570815 "#]]); // wasmer0 doesn't canonicalize NaNs - test_builder().wat(code).skip_wasmtime().skip_wasmer2().expect(expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 54988767 used gas 54988767 + test_builder().wat(code).only_wasmer0().expect(expect![[r#" + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 60748059 used gas 60748059 Err: WebAssembly trap: An arithmetic exception, e.g. divided by zero. "#]]); } @@ -714,10 +736,10 @@ mod fix_contract_loading_cost_protocol_upgrade { .protocol_features(&[ProtocolFeature::FixContractLoadingCost]) .expects(&[ expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 47406987 used gas 47406987 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 53989035 used gas 53989035 "#]], expect![[r#" - VMOutcome: balance 4 storage_usage 12 return data None burnt gas 47406987 used gas 47406987 + VMOutcome: balance 4 storage_usage 12 return data None burnt gas 53989035 used gas 53989035 "#]], ]); } diff --git a/runtime/near-vm-runner/src/vm_kind.rs b/runtime/near-vm-runner/src/vm_kind.rs index 1f6de45b244..b3f3a6da04e 100644 --- a/runtime/near-vm-runner/src/vm_kind.rs +++ b/runtime/near-vm-runner/src/vm_kind.rs @@ -25,10 +25,10 @@ impl VMKind { // Only wasmtime supports non-x86_64 systems #[cfg(all( not(target_arch = "x86_64"), - any(feature = "force_wasmer0", feature = "force_wasmer2") + any(feature = "force_wasmer0", feature = "force_wasmer2", feature = "force_near_vm") ))] compile_error!( - "Wasmer only supports x86_64, but a force_wasmer* feature was passed to near-vm-runner" + "Wasmer and NearVM only support x86_64, but such a force_* feature was passed to near-vm-runner" ); if cfg!(feature = "force_wasmer0") { @@ -40,9 +40,14 @@ impl VMKind { if cfg!(feature = "force_wasmer2") { return VMKind::Wasmer2; } + if cfg!(feature = "force_near_vm") { + return VMKind::NearVm; + } if cfg!(target_arch = "x86_64") { - if checked_feature!("stable", Wasmer2, protocol_version) { + if checked_feature!("stable", NearVm, protocol_version) { + VMKind::NearVm + } else if checked_feature!("stable", Wasmer2, protocol_version) { VMKind::Wasmer2 } else { VMKind::Wasmer0 diff --git a/runtime/runtime-params-estimator/src/main.rs b/runtime/runtime-params-estimator/src/main.rs index 25356bdf974..1bcb4f37ec6 100644 --- a/runtime/runtime-params-estimator/src/main.rs +++ b/runtime/runtime-params-estimator/src/main.rs @@ -61,7 +61,7 @@ struct CliArgs { #[clap(long, default_value = "time", possible_values = &["icount", "time"])] metric: String, /// Which VM to test. - #[clap(long, possible_values = &["wasmer", "wasmer2", "wasmtime"])] + #[clap(long, possible_values = &["wasmer", "wasmer2", "wasmtime", "near-vm"])] vm_kind: Option, /// Render existing `costs.txt` as `RuntimeConfig`. #[clap(long)] @@ -289,6 +289,7 @@ fn run_estimation(cli_args: CliArgs) -> anyhow::Result> { Some("wasmer") => VMKind::Wasmer0, Some("wasmer2") => VMKind::Wasmer2, Some("wasmtime") => VMKind::Wasmtime, + Some("near-vm") => VMKind::NearVm, None => VMKind::for_protocol_version(PROTOCOL_VERSION), Some(other) => unreachable!("Unknown vm_kind {}", other), };