diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d1c950ed94e..f3098dd09d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -478,3 +478,14 @@ jobs: files: py-upgradability.json fail_ci_if_error: true flags: pytests,upgradability,linux + + windows_public_libraries_check: + name: "Windows check for building public libraries" + runs-on: "windows-latest" + timeout-minutes: 30 + steps: + - uses: actions/checkout@v4 + - uses: taiki-e/install-action@9b5b983efc779f85e5e5d11539f005e85ccb27ff + with: + tool: just + - run: just check_build_public_libraries diff --git a/Cargo.lock b/Cargo.lock index abe0fedbb0a..5e01e4636ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4597,6 +4597,7 @@ dependencies = [ "chrono", "derive_more", "easy-ext", + "enum-map", "expect-test", "hex", "insta", @@ -4608,7 +4609,6 @@ dependencies = [ "near-rpc-error-macro", "near-stdx", "near-time", - "near-vm-runner", "num-rational 0.3.2", "once_cell", "ordered-float", diff --git a/Justfile b/Justfile index f198c06761d..2dacfc68474 100644 --- a/Justfile +++ b/Justfile @@ -8,6 +8,7 @@ with_macos_excludes := if os() == "macos" { } nightly_flags := "--features nightly,test_features" statelessnet_flags := "--features statelessnet_protocol" +public_libraries := "-p near-primitives -p near-crypto -p near-jsonrpc-primitives -p near-chain-configs -p near-primitives-core" export RUST_BACKTRACE := env("RUST_BACKTRACE", "short") ci_hack_nextest_profile := if env("CI_HACKS", "0") == "1" { "--profile ci" } else { "" } @@ -73,6 +74,11 @@ nextest-integration TYPE *FLAGS: nextest-integration TYPE *FLAGS: @echo "Nextest integration tests are currently disabled on macos!" +[windows] +nextest-integration TYPE *FLAGS: + @echo "Nextest integration tests are currently disabled on windows!" + + doctests: cargo test --doc @@ -165,3 +171,6 @@ update-rpc-errors-schema: build-rpc-errors-schema # check chain/jsonrpc/res/rpc_errors_schema.json check-rpc-errors-schema: build-rpc-errors-schema diff target/rpc_errors_schema.json chain/jsonrpc/res/rpc_errors_schema.json + +check_build_public_libraries: + cargo check {{public_libraries}} diff --git a/chain/chain/src/runtime/mod.rs b/chain/chain/src/runtime/mod.rs index 72f11c070c4..4e166c3f961 100644 --- a/chain/chain/src/runtime/mod.rs +++ b/chain/chain/src/runtime/mod.rs @@ -38,7 +38,7 @@ use near_primitives::types::{ use near_primitives::version::{ProtocolFeature, ProtocolVersion}; use near_primitives::views::{ AccessKeyInfoView, CallResult, ContractCodeView, QueryRequest, QueryResponse, - QueryResponseKind, ViewApplyState, ViewStateResult, + QueryResponseKind, ViewStateResult, }; use near_store::config::StateSnapshotType; use near_store::flat::FlatStorageManager; @@ -50,7 +50,7 @@ use near_store::{ use near_vm_runner::ContractCode; use near_vm_runner::{precompile_contract, ContractRuntimeCache, FilesystemContractRuntimeCache}; use node_runtime::adapter::ViewRuntimeAdapter; -use node_runtime::state_viewer::TrieViewer; +use node_runtime::state_viewer::{TrieViewer, ViewApplyState}; use node_runtime::{ validate_transaction, verify_and_charge_transaction, ApplyState, Runtime, ValidatorAccountsUpdate, @@ -1478,7 +1478,7 @@ impl node_runtime::adapter::ViewRuntimeAdapter for NightshadeRuntime { epoch_height, block_timestamp, current_protocol_version, - cache: Some(Box::new(self.compiled_contract_cache.handle())), + cache: Some(self.compiled_contract_cache.handle()), }; self.trie_viewer.call_function( state_update, diff --git a/core/primitives/Cargo.toml b/core/primitives/Cargo.toml index f36d2230a31..073433ec37c 100644 --- a/core/primitives/Cargo.toml +++ b/core/primitives/Cargo.toml @@ -40,17 +40,17 @@ strum.workspace = true thiserror.workspace = true tracing.workspace = true zstd.workspace = true +enum-map.workspace = true near-time.workspace = true near-crypto.workspace = true near-fmt.workspace = true near-primitives-core.workspace = true near-rpc-error-macro.workspace = true -near-vm-runner.workspace = true near-parameters.workspace = true [features] -sandbox = ["near-vm-runner/sandbox"] +sandbox = [] test_features = [] dump_errors_schema = ["near-rpc-error-macro/dump_errors_schema"] protocol_feature_fix_staking_threshold = [ @@ -58,7 +58,6 @@ protocol_feature_fix_staking_threshold = [ ] protocol_feature_fix_contract_loading_cost = [ "near-primitives-core/protocol_feature_fix_contract_loading_cost", - "near-vm-runner/protocol_feature_fix_contract_loading_cost", ] protocol_feature_reject_blocks_with_outdated_protocol_version = [ "near-primitives-core/protocol_feature_reject_blocks_with_outdated_protocol_version", @@ -71,7 +70,6 @@ nightly = [ "near-fmt/nightly", "near-parameters/nightly", "near-primitives-core/nightly", - "near-vm-runner/nightly", "nightly_protocol", "protocol_feature_fix_contract_loading_cost", "protocol_feature_fix_staking_threshold", @@ -83,7 +81,6 @@ nightly_protocol = [ "near-fmt/nightly_protocol", "near-parameters/nightly_protocol", "near-primitives-core/nightly_protocol", - "near-vm-runner/nightly_protocol", ] statelessnet_protocol = ["near-primitives-core/statelessnet_protocol"] diff --git a/core/primitives/src/lib.rs b/core/primitives/src/lib.rs index 891d52c4067..5987b7edce0 100644 --- a/core/primitives/src/lib.rs +++ b/core/primitives/src/lib.rs @@ -18,6 +18,7 @@ pub mod errors; pub mod merkle; pub mod network; pub mod profile_data_v2; +pub mod profile_data_v3; pub mod rand; pub mod receipt; pub mod reed_solomon; diff --git a/core/primitives/src/profile_data_v3.rs b/core/primitives/src/profile_data_v3.rs new file mode 100644 index 00000000000..270c367d2eb --- /dev/null +++ b/core/primitives/src/profile_data_v3.rs @@ -0,0 +1,457 @@ +use borsh::{BorshDeserialize, BorshSerialize}; +use enum_map::{enum_map, Enum, EnumMap}; +use near_parameters::{ActionCosts, ExtCosts, ExtCostsConfig}; +use near_primitives_core::types::{Compute, Gas}; +use std::fmt; +use strum::IntoEnumIterator; + +/// Profile of gas consumption. +#[derive(Clone, PartialEq, Eq)] +pub struct ProfileDataV3 { + /// Gas spent on sending or executing actions. + pub actions_profile: EnumMap, + /// Non-action gas spent outside the WASM VM while executing a contract. + pub wasm_ext_profile: EnumMap, + /// Gas spent on execution inside the WASM VM. + pub wasm_gas: Gas, +} + +impl Default for ProfileDataV3 { + fn default() -> ProfileDataV3 { + ProfileDataV3::new() + } +} + +impl ProfileDataV3 { + #[inline] + pub fn new() -> Self { + Self { + actions_profile: enum_map! { _ => 0 }, + wasm_ext_profile: enum_map! { _ => 0 }, + wasm_gas: 0, + } + } + + /// Test instance with unique numbers in each field. + pub fn test() -> Self { + let mut profile_data = ProfileDataV3::default(); + for (i, cost) in ExtCosts::iter().enumerate() { + profile_data.add_ext_cost(cost, i as Gas); + } + for (i, cost) in ActionCosts::iter().enumerate() { + profile_data.add_action_cost(cost, i as Gas + 1000); + } + profile_data + } + + #[inline] + pub fn merge(&mut self, other: &ProfileDataV3) { + for ((_, gas), (_, other_gas)) in + self.actions_profile.iter_mut().zip(other.actions_profile.iter()) + { + *gas = gas.saturating_add(*other_gas); + } + for ((_, gas), (_, other_gas)) in + self.wasm_ext_profile.iter_mut().zip(other.wasm_ext_profile.iter()) + { + *gas = gas.saturating_add(*other_gas); + } + self.wasm_gas = self.wasm_gas.saturating_add(other.wasm_gas); + } + + #[inline] + pub fn add_action_cost(&mut self, action: ActionCosts, value: Gas) { + self.actions_profile[action] = self.actions_profile[action].saturating_add(value); + } + + #[inline] + pub fn add_ext_cost(&mut self, ext: ExtCosts, value: Gas) { + self.wasm_ext_profile[ext] = self.wasm_ext_profile[ext].saturating_add(value); + } + + /// WasmInstruction is the only cost we don't explicitly account for. + /// Instead, we compute it at the end of contract call as the difference + /// between total gas burnt and what we've explicitly accounted for in the + /// profile. + /// + /// This is because WasmInstruction is the hottest cost and is implemented + /// with the help on the VM side, so we don't want to have profiling logic + /// there both for simplicity and efficiency reasons. + pub fn compute_wasm_instruction_cost(&mut self, total_gas_burnt: Gas) { + self.wasm_gas = + total_gas_burnt.saturating_sub(self.action_gas()).saturating_sub(self.host_gas()); + } + + pub fn get_action_cost(&self, action: ActionCosts) -> Gas { + self.actions_profile[action] + } + + pub fn get_ext_cost(&self, ext: ExtCosts) -> Gas { + self.wasm_ext_profile[ext] + } + + pub fn get_wasm_cost(&self) -> Gas { + self.wasm_gas + } + + fn host_gas(&self) -> Gas { + self.wasm_ext_profile.as_slice().iter().copied().fold(0, Gas::saturating_add) + } + + pub fn action_gas(&self) -> Gas { + self.actions_profile.as_slice().iter().copied().fold(0, Gas::saturating_add) + } + + /// Returns total compute usage of host calls. + pub fn total_compute_usage(&self, ext_costs_config: &ExtCostsConfig) -> Compute { + let ext_compute_cost = self + .wasm_ext_profile + .iter() + .map(|(key, value)| { + // Technically, gas cost might be zero while the compute cost is non-zero. To + // handle this case, we would need to explicitly count number of calls, not just + // the total gas usage. + // We don't have such costs at the moment, so this case is not implemented. + debug_assert!(key.gas(ext_costs_config) > 0 || key.compute(ext_costs_config) == 0); + + if *value == 0 { + return *value; + } + // If the `value` is non-zero, the gas cost also must be non-zero. + debug_assert!(key.gas(ext_costs_config) != 0); + ((*value as u128).saturating_mul(key.compute(ext_costs_config) as u128) + / (key.gas(ext_costs_config) as u128)) as u64 + }) + .fold(0, Compute::saturating_add); + + // We currently only support compute costs for host calls. In the future we might add + // them for actions as well. + ext_compute_cost.saturating_add(self.action_gas()).saturating_add(self.get_wasm_cost()) + } +} + +impl BorshDeserialize for ProfileDataV3 { + fn deserialize_reader(rd: &mut R) -> std::io::Result { + let actions_array: Vec = BorshDeserialize::deserialize_reader(rd)?; + let ext_array: Vec = BorshDeserialize::deserialize_reader(rd)?; + let wasm_gas: u64 = BorshDeserialize::deserialize_reader(rd)?; + + // Mapping raw arrays to enum maps. + // The enum map could be smaller or larger than the raw array. + // Extra values in the array that are unknown to the current binary will + // be ignored. Missing values are filled with 0. + let actions_profile = enum_map! { + cost => actions_array.get(borsh_action_index(cost)).copied().unwrap_or(0) + }; + let wasm_ext_profile = enum_map! { + cost => ext_array.get(borsh_ext_index(cost)).copied().unwrap_or(0) + }; + + Ok(Self { actions_profile, wasm_ext_profile, wasm_gas }) + } +} + +impl BorshSerialize for ProfileDataV3 { + fn serialize(&self, writer: &mut W) -> Result<(), std::io::Error> { + let mut actions_costs: Vec = vec![0u64; ActionCosts::LENGTH]; + for (cost, gas) in self.actions_profile.iter() { + actions_costs[borsh_action_index(cost)] = *gas; + } + BorshSerialize::serialize(&actions_costs, writer)?; + + let mut ext_costs: Vec = vec![0u64; ExtCosts::LENGTH]; + for (cost, gas) in self.wasm_ext_profile.iter() { + ext_costs[borsh_ext_index(cost)] = *gas; + } + BorshSerialize::serialize(&ext_costs, writer)?; + + let wasm_cost: u64 = self.wasm_gas; + BorshSerialize::serialize(&wasm_cost, writer) + } +} + +/// Fixed index of an action cost for borsh (de)serialization. +/// +/// We use borsh to store profiles on the DB and borsh is quite fragile with +/// changes. This mapping is to decouple the Rust enum from the borsh +/// representation. The enum can be changed freely but here in the mapping we +/// can only append more values at the end. +/// +/// TODO: Consider changing this to a different format (e.g. protobuf) because +/// canonical representation is not required here. +const fn borsh_action_index(action: ActionCosts) -> usize { + // actual indices are defined on the enum variants + action as usize +} + +/// Fixed index of an ext cost for borsh (de)serialization. +/// +/// We use borsh to store profiles on the DB and borsh is quite fragile with +/// changes. This mapping is to decouple the Rust enum from the borsh +/// representation. The enum can be changed freely but here in the mapping we +/// can only append more values at the end. +/// +/// TODO: Consider changing this to a different format (e.g. protobuf) because +/// canonical representation is not required here. +const fn borsh_ext_index(ext: ExtCosts) -> usize { + // actual indices are defined on the enum variants + ext as usize +} + +impl fmt::Debug for ProfileDataV3 { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + use num_rational::Ratio; + let host_gas = self.host_gas(); + let action_gas = self.action_gas(); + + writeln!(f, "------------------------------")?; + writeln!(f, "Action gas: {}", action_gas)?; + writeln!(f, "------ Host functions --------")?; + for cost in ExtCosts::iter() { + let d = self.get_ext_cost(cost); + if d != 0 { + writeln!( + f, + "{} -> {} [{}% host]", + cost, + d, + Ratio::new(d * 100, core::cmp::max(host_gas, 1)).to_integer(), + )?; + } + } + writeln!(f, "------ Actions --------")?; + for cost in ActionCosts::iter() { + let d = self.get_action_cost(cost); + if d != 0 { + writeln!(f, "{} -> {}", cost, d)?; + } + } + writeln!(f, "------------------------------")?; + Ok(()) + } +} + +/// Tests for ProfileDataV3 +#[cfg(test)] +mod test { + use super::*; + + #[test] + #[cfg(not(feature = "nightly"))] + fn test_profile_data_debug() { + let profile_data = ProfileDataV3::test(); + // we don't care about exact formatting, but the numbers should not change unexpectedly + let pretty_debug_str = format!("{profile_data:#?}"); + expect_test::expect![[r#" + ------------------------------ + Action gas: 16120 + ------ Host functions -------- + contract_loading_base -> 1 [0% host] + contract_loading_bytes -> 2 [0% host] + read_memory_base -> 3 [0% host] + read_memory_byte -> 4 [0% host] + write_memory_base -> 5 [0% host] + write_memory_byte -> 6 [0% host] + read_register_base -> 7 [0% host] + read_register_byte -> 8 [0% host] + write_register_base -> 9 [0% host] + write_register_byte -> 10 [0% host] + utf8_decoding_base -> 11 [0% host] + utf8_decoding_byte -> 12 [0% host] + utf16_decoding_base -> 13 [0% host] + utf16_decoding_byte -> 14 [0% host] + sha256_base -> 15 [0% host] + sha256_byte -> 16 [0% host] + keccak256_base -> 17 [0% host] + keccak256_byte -> 18 [0% host] + keccak512_base -> 19 [0% host] + keccak512_byte -> 20 [0% host] + ripemd160_base -> 21 [1% host] + ripemd160_block -> 22 [1% host] + ecrecover_base -> 23 [1% host] + log_base -> 24 [1% host] + log_byte -> 25 [1% host] + storage_write_base -> 26 [1% host] + storage_write_key_byte -> 27 [1% host] + storage_write_value_byte -> 28 [1% host] + storage_write_evicted_byte -> 29 [1% host] + storage_read_base -> 30 [1% host] + storage_read_key_byte -> 31 [1% host] + storage_read_value_byte -> 32 [1% host] + storage_remove_base -> 33 [1% host] + storage_remove_key_byte -> 34 [1% host] + storage_remove_ret_value_byte -> 35 [1% host] + storage_has_key_base -> 36 [1% host] + storage_has_key_byte -> 37 [1% host] + storage_iter_create_prefix_base -> 38 [1% host] + storage_iter_create_prefix_byte -> 39 [1% host] + storage_iter_create_range_base -> 40 [1% host] + storage_iter_create_from_byte -> 41 [1% host] + storage_iter_create_to_byte -> 42 [2% host] + storage_iter_next_base -> 43 [2% host] + storage_iter_next_key_byte -> 44 [2% host] + storage_iter_next_value_byte -> 45 [2% host] + touching_trie_node -> 46 [2% host] + read_cached_trie_node -> 47 [2% host] + promise_and_base -> 48 [2% host] + promise_and_per_promise -> 49 [2% host] + promise_return -> 50 [2% host] + validator_stake_base -> 51 [2% host] + validator_total_stake_base -> 52 [2% host] + alt_bn128_g1_multiexp_base -> 53 [2% host] + alt_bn128_g1_multiexp_element -> 54 [2% host] + alt_bn128_pairing_check_base -> 55 [2% host] + alt_bn128_pairing_check_element -> 56 [2% host] + alt_bn128_g1_sum_base -> 57 [2% host] + alt_bn128_g1_sum_element -> 58 [2% host] + ed25519_verify_base -> 59 [2% host] + ed25519_verify_byte -> 60 [2% host] + yield_create_base -> 61 [2% host] + yield_create_byte -> 62 [2% host] + yield_resume_base -> 63 [3% host] + yield_resume_byte -> 64 [3% host] + ------ Actions -------- + create_account -> 1000 + delete_account -> 1001 + deploy_contract_base -> 1002 + deploy_contract_byte -> 1003 + function_call_base -> 1004 + function_call_byte -> 1005 + transfer -> 1006 + stake -> 1007 + add_full_access_key -> 1008 + add_function_call_key_base -> 1009 + add_function_call_key_byte -> 1010 + delete_key -> 1011 + new_action_receipt -> 1012 + new_data_receipt_base -> 1013 + new_data_receipt_byte -> 1014 + delegate -> 1015 + ------------------------------ + "#]] + .assert_eq(&pretty_debug_str) + } + + #[test] + fn test_profile_data_debug_no_data() { + let profile_data = ProfileDataV3::default(); + // we don't care about exact formatting, but at least it should not panic + println!("{:#?}", &profile_data); + } + + #[test] + fn test_no_panic_on_overflow() { + let mut profile_data = ProfileDataV3::default(); + profile_data.add_action_cost(ActionCosts::add_full_access_key, u64::MAX); + profile_data.add_action_cost(ActionCosts::add_full_access_key, u64::MAX); + + let res = profile_data.get_action_cost(ActionCosts::add_full_access_key); + assert_eq!(res, u64::MAX); + } + + #[test] + fn test_merge() { + let mut profile_data = ProfileDataV3::default(); + profile_data.add_action_cost(ActionCosts::add_full_access_key, 111); + profile_data.add_ext_cost(ExtCosts::storage_read_base, 11); + + let mut profile_data2 = ProfileDataV3::default(); + profile_data2.add_action_cost(ActionCosts::add_full_access_key, 222); + profile_data2.add_ext_cost(ExtCosts::storage_read_base, 22); + + profile_data.merge(&profile_data2); + assert_eq!(profile_data.get_action_cost(ActionCosts::add_full_access_key), 333); + assert_eq!(profile_data.get_ext_cost(ExtCosts::storage_read_base), 33); + } + + #[test] + fn test_total_compute_usage() { + let ext_costs_config = ExtCostsConfig::test_with_undercharging_factor(3); + let mut profile_data = ProfileDataV3::default(); + profile_data.add_ext_cost( + ExtCosts::storage_read_base, + 2 * ExtCosts::storage_read_base.gas(&ext_costs_config), + ); + profile_data.add_ext_cost( + ExtCosts::storage_write_base, + 5 * ExtCosts::storage_write_base.gas(&ext_costs_config), + ); + profile_data.add_action_cost(ActionCosts::function_call_base, 100); + + assert_eq!( + profile_data.total_compute_usage(&ext_costs_config), + 3 * profile_data.host_gas() + profile_data.action_gas() + ); + } + + #[test] + fn test_borsh_ser_deser() { + let mut profile_data = ProfileDataV3::default(); + for (i, cost) in ExtCosts::iter().enumerate() { + profile_data.add_ext_cost(cost, i as Gas); + } + for (i, cost) in ActionCosts::iter().enumerate() { + profile_data.add_action_cost(cost, i as Gas + 1000); + } + let buf = borsh::to_vec(&profile_data).expect("failed serializing a normal profile"); + + let restored: ProfileDataV3 = BorshDeserialize::deserialize(&mut buf.as_slice()) + .expect("failed deserializing a normal profile"); + + assert_eq!(profile_data, restored); + } + + #[test] + fn test_borsh_incomplete_profile() { + let action_profile = vec![50u64, 60]; + let ext_profile = vec![100u64, 200]; + let wasm_cost = 99u64; + let input = manually_encode_profile_v2(action_profile, ext_profile, wasm_cost); + + let profile: ProfileDataV3 = BorshDeserialize::deserialize(&mut input.as_slice()) + .expect("should be able to parse a profile with less entries"); + + assert_eq!(50, profile.get_action_cost(ActionCosts::create_account)); + assert_eq!(60, profile.get_action_cost(ActionCosts::delete_account)); + assert_eq!(0, profile.get_action_cost(ActionCosts::deploy_contract_base)); + + assert_eq!(100, profile.get_ext_cost(ExtCosts::base)); + assert_eq!(200, profile.get_ext_cost(ExtCosts::contract_loading_base)); + assert_eq!(0, profile.get_ext_cost(ExtCosts::contract_loading_bytes)); + } + + #[test] + fn test_borsh_larger_profile_than_current() { + let action_profile = vec![1234u64; ActionCosts::LENGTH + 5]; + let ext_profile = vec![5678u64; ExtCosts::LENGTH + 10]; + let wasm_cost = 90u64; + let input = manually_encode_profile_v2(action_profile, ext_profile, wasm_cost); + + let profile: ProfileDataV3 = BorshDeserialize::deserialize(&mut input.as_slice()).expect( + "should be able to parse a profile with more entries than the current version has", + ); + + for action in ActionCosts::iter() { + assert_eq!(1234, profile.get_action_cost(action), "{action:?}"); + } + + for ext in ExtCosts::iter() { + assert_eq!(5678, profile.get_ext_cost(ext), "{ext:?}"); + } + + assert_eq!(90, profile.wasm_gas); + } + + #[track_caller] + fn manually_encode_profile_v2( + action_profile: Vec, + ext_profile: Vec, + wasm_cost: u64, + ) -> Vec { + let mut input = vec![]; + BorshSerialize::serialize(&action_profile, &mut input).unwrap(); + BorshSerialize::serialize(&ext_profile, &mut input).unwrap(); + BorshSerialize::serialize(&wasm_cost, &mut input).unwrap(); + input + } +} diff --git a/core/primitives/src/shard_layout.rs b/core/primitives/src/shard_layout.rs index f33e267803d..ead63553cf0 100644 --- a/core/primitives/src/shard_layout.rs +++ b/core/primitives/src/shard_layout.rs @@ -491,9 +491,9 @@ impl<'de> serde::de::Visitor<'de> for ShardUIdVisitor { mod tests { use crate::epoch_manager::{AllEpochConfig, EpochConfig, ValidatorSelectionConfig}; use crate::shard_layout::{account_id_to_shard_id, ShardLayout, ShardLayoutV1, ShardUId}; + use near_primitives_core::types::ProtocolVersion; use near_primitives_core::types::{AccountId, ShardId}; use near_primitives_core::version::ProtocolFeature; - use near_vm_runner::logic::ProtocolVersion; use rand::distributions::Alphanumeric; use rand::rngs::StdRng; use rand::{Rng, SeedableRng}; diff --git a/core/primitives/src/transaction.rs b/core/primitives/src/transaction.rs index 6ea92703b99..6f9941f9717 100644 --- a/core/primitives/src/transaction.rs +++ b/core/primitives/src/transaction.rs @@ -1,13 +1,13 @@ use crate::errors::TxExecutionError; use crate::hash::{hash, CryptoHash}; use crate::merkle::MerklePath; +use crate::profile_data_v3::ProfileDataV3; use crate::types::{AccountId, Balance, Gas, Nonce}; use borsh::{BorshDeserialize, BorshSerialize}; use near_crypto::{PublicKey, Signature}; use near_fmt::{AbbrBytes, Slice}; use near_primitives_core::serialize::{from_base64, to_base64}; use near_primitives_core::types::Compute; -use near_vm_runner::ProfileDataV3; use serde::de::Error as DecodeError; use serde::ser::Error as EncodeError; use std::borrow::Borrow; diff --git a/core/primitives/src/types.rs b/core/primitives/src/types.rs index 4957617160b..a70a3e670e1 100644 --- a/core/primitives/src/types.rs +++ b/core/primitives/src/types.rs @@ -745,9 +745,8 @@ pub mod chunk_extra { use crate::types::StateRoot; use borsh::{BorshDeserialize, BorshSerialize}; use near_primitives_core::hash::CryptoHash; - use near_primitives_core::types::{Balance, Gas}; + use near_primitives_core::types::{Balance, Gas, ProtocolVersion}; use near_primitives_core::version::{ProtocolFeature, PROTOCOL_VERSION}; - use near_vm_runner::logic::ProtocolVersion; pub use super::ChunkExtraV1; diff --git a/core/primitives/src/views.rs b/core/primitives/src/views.rs index 10c2f5f0b17..e53086ca60d 100644 --- a/core/primitives/src/views.rs +++ b/core/primitives/src/views.rs @@ -83,29 +83,6 @@ pub struct ContractCodeView { pub hash: CryptoHash, } -/// State for the view call. -#[derive(Debug)] -pub struct ViewApplyState { - /// Currently building block height. - pub block_height: BlockHeight, - /// Prev block hash - pub prev_block_hash: CryptoHash, - /// Currently building block hash - pub block_hash: CryptoHash, - /// To which shard the applied chunk belongs. - pub shard_id: ShardId, - /// Current epoch id - pub epoch_id: EpochId, - /// Current epoch height - pub epoch_height: EpochHeight, - /// The current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC). - pub block_timestamp: u64, - /// Current Protocol version when we apply the state transition - pub current_protocol_version: ProtocolVersion, - /// Cache for compiled contracts. - pub cache: Option>, -} - impl From<&Account> for AccountView { fn from(account: &Account) -> Self { AccountView { @@ -2521,8 +2498,8 @@ impl CongestionInfoView { mod tests { use super::ExecutionMetadataView; use crate::profile_data_v2::ProfileDataV2; + use crate::profile_data_v3::ProfileDataV3; use crate::transaction::ExecutionMetadata; - use near_vm_runner::ProfileDataV3; /// The JSON representation used in RPC responses must not remove or rename /// fields, only adding fields is allowed or we risk breaking clients. diff --git a/integration-tests/src/tests/client/process_blocks.rs b/integration-tests/src/tests/client/process_blocks.rs index bfdc3ca8a8b..1ac99d5b6b3 100644 --- a/integration-tests/src/tests/client/process_blocks.rs +++ b/integration-tests/src/tests/client/process_blocks.rs @@ -3593,12 +3593,12 @@ fn test_long_chain_with_restart_from_snapshot() { mod contract_precompilation_tests { use super::*; use near_primitives::test_utils::MockEpochInfoProvider; - use near_primitives::views::ViewApplyState; use near_store::TrieUpdate; use near_vm_runner::{ get_contract_cache_key, ContractCode, ContractRuntimeCache, FilesystemContractRuntimeCache, }; use node_runtime::state_viewer::TrieViewer; + use node_runtime::state_viewer::ViewApplyState; const EPOCH_LENGTH: u64 = 25; diff --git a/integration-tests/src/tests/runtime/state_viewer.rs b/integration-tests/src/tests/runtime/state_viewer.rs index df4ba18b088..8c71577aa72 100644 --- a/integration-tests/src/tests/runtime/state_viewer.rs +++ b/integration-tests/src/tests/runtime/state_viewer.rs @@ -5,12 +5,11 @@ use borsh::BorshDeserialize; use crate::runtime_utils::{get_runtime_and_trie, get_test_trie_viewer, TEST_SHARD_UID}; use near_primitives::{ account::Account, - hash::hash as sha256, - hash::CryptoHash, + hash::{hash as sha256, CryptoHash}, serialize::to_base64, trie_key::trie_key_parsers, types::{AccountId, StateRoot}, - views::{StateItem, ViewApplyState}, + views::StateItem, }; use near_primitives::{ test_utils::MockEpochInfoProvider, diff --git a/integration-tests/src/user/runtime_user.rs b/integration-tests/src/user/runtime_user.rs index 5d6da18b740..7be65456a57 100644 --- a/integration-tests/src/user/runtime_user.rs +++ b/integration-tests/src/user/runtime_user.rs @@ -18,11 +18,11 @@ use near_primitives::version::{ProtocolFeature, PROTOCOL_VERSION}; use near_primitives::views::{ AccessKeyView, AccountView, BlockView, CallResult, ChunkView, ContractCodeView, ExecutionOutcomeView, ExecutionOutcomeWithIdView, ExecutionStatusView, - FinalExecutionOutcomeView, FinalExecutionStatus, ViewApplyState, ViewStateResult, + FinalExecutionOutcomeView, FinalExecutionStatus, ViewStateResult, }; use near_store::{ShardTries, TrieUpdate}; use node_runtime::state_viewer::TrieViewer; -use node_runtime::{ApplyState, Runtime}; +use node_runtime::{state_viewer::ViewApplyState, ApplyState, Runtime}; use crate::user::{User, POISONED_LOCK_ERR}; use near_primitives::shard_layout::ShardUId; diff --git a/runtime/near-vm-runner/src/profile.rs b/runtime/near-vm-runner/src/profile.rs index e3cdb8dc8d1..270c367d2eb 100644 --- a/runtime/near-vm-runner/src/profile.rs +++ b/runtime/near-vm-runner/src/profile.rs @@ -9,11 +9,11 @@ use strum::IntoEnumIterator; #[derive(Clone, PartialEq, Eq)] pub struct ProfileDataV3 { /// Gas spent on sending or executing actions. - actions_profile: EnumMap, + pub actions_profile: EnumMap, /// Non-action gas spent outside the WASM VM while executing a contract. - wasm_ext_profile: EnumMap, + pub wasm_ext_profile: EnumMap, /// Gas spent on execution inside the WASM VM. - wasm_gas: Gas, + pub wasm_gas: Gas, } impl Default for ProfileDataV3 { diff --git a/runtime/runtime/src/conversions.rs b/runtime/runtime/src/conversions.rs index 303e3205b8c..e06b3229760 100644 --- a/runtime/runtime/src/conversions.rs +++ b/runtime/runtime/src/conversions.rs @@ -56,6 +56,7 @@ mod compilation_error { mod function_call_error { use near_vm_runner::logic::errors::FunctionCallError as From; + impl super::Convert for near_primitives::errors::FunctionCallError { fn convert(outer_err: From) -> Self { match outer_err { @@ -77,3 +78,16 @@ impl Convert for near_vm_runner::logic::TrieNo Self { db_reads: other.db_reads, mem_reads: other.mem_reads } } } + +mod profile_data_v3 { + use near_vm_runner::ProfileDataV3 as From; + impl super::Convert for near_primitives::profile_data_v3::ProfileDataV3 { + fn convert(other: From) -> Self { + Self { + actions_profile: other.actions_profile, + wasm_ext_profile: other.wasm_ext_profile, + wasm_gas: other.wasm_gas, + } + } + } +} diff --git a/runtime/runtime/src/lib.rs b/runtime/runtime/src/lib.rs index 37ac505df31..ee4d99c87fa 100644 --- a/runtime/runtime/src/lib.rs +++ b/runtime/runtime/src/lib.rs @@ -879,7 +879,9 @@ impl Runtime { compute_usage: Some(result.compute_usage), tokens_burnt, executor_id: account_id.clone(), - metadata: ExecutionMetadata::V3(result.profile), + metadata: ExecutionMetadata::V3(Box::new(conversions::Convert::convert( + *result.profile, + ))), }, }) } diff --git a/runtime/runtime/src/state_viewer/mod.rs b/runtime/runtime/src/state_viewer/mod.rs index eec3a45769c..d18c9d15b96 100644 --- a/runtime/runtime/src/state_viewer/mod.rs +++ b/runtime/runtime/src/state_viewer/mod.rs @@ -11,18 +11,43 @@ use near_primitives::receipt::ActionReceipt; use near_primitives::runtime::migration_data::{MigrationData, MigrationFlags}; use near_primitives::transaction::FunctionCallAction; use near_primitives::trie_key::trie_key_parsers; -use near_primitives::types::{AccountId, EpochInfoProvider, Gas}; +use near_primitives::types::{ + AccountId, BlockHeight, EpochHeight, EpochId, EpochInfoProvider, Gas, ShardId, +}; use near_primitives::version::PROTOCOL_VERSION; -use near_primitives::views::{StateItem, ViewApplyState, ViewStateResult}; +use near_primitives::views::{StateItem, ViewStateResult}; use near_primitives_core::config::ViewConfig; use near_store::{get_access_key, get_account, get_code, TrieUpdate}; -use near_vm_runner::logic::ReturnData; -use near_vm_runner::ContractCode; +use near_vm_runner::logic::{ProtocolVersion, ReturnData}; +use near_vm_runner::{ContractCode, ContractRuntimeCache}; use std::{str, sync::Arc, time::Instant}; use tracing::debug; pub mod errors; +/// State for the view call. +#[derive(Debug)] +pub struct ViewApplyState { + /// Currently building block height. + pub block_height: BlockHeight, + /// Prev block hash + pub prev_block_hash: CryptoHash, + /// Currently building block hash + pub block_hash: CryptoHash, + /// To which shard the applied chunk belongs. + pub shard_id: ShardId, + /// Current epoch id + pub epoch_id: EpochId, + /// Current epoch height + pub epoch_height: EpochHeight, + /// The current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC). + pub block_timestamp: u64, + /// Current Protocol version when we apply the state transition + pub current_protocol_version: ProtocolVersion, + /// Cache for compiled contracts. + pub cache: Option>, +} + pub struct TrieViewer { /// Upper bound of the byte size of contract state that is still viewable. None is no limit state_size_limit: Option,