diff --git a/Cargo.lock b/Cargo.lock index c5c0b4cdbafc..d771f9b25a70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6511,6 +6511,7 @@ dependencies = [ "hex", "ic-base-types-protobuf-generator", "ic-crypto-sha2", + "ic-deterministic-heap-bytes", "ic-protobuf", "ic-test-utilities-compare-dirs", "phantom_newtype", @@ -9232,6 +9233,7 @@ dependencies = [ name = "ic-error-types" version = "0.2.0" dependencies = [ + "ic-deterministic-heap-bytes", "serde", "strum 0.26.3", "strum_macros 0.26.4", @@ -9283,6 +9285,7 @@ dependencies = [ "ic-crypto-tree-hash", "ic-crypto-utils-canister-threshold-sig", "ic-cycles-account-manager", + "ic-deterministic-heap-bytes", "ic-embedders", "ic-error-types 0.2.0", "ic-interfaces", @@ -14704,6 +14707,7 @@ dependencies = [ "ic-crypto-test-utils-canister-threshold-sigs", "ic-crypto-test-utils-reproducible-rng", "ic-crypto-tree-hash", + "ic-deterministic-heap-bytes", "ic-error-types 0.2.0", "ic-exhaustive-derive", "ic-limits", @@ -18831,6 +18835,7 @@ name = "phantom_newtype" version = "0.9.0" dependencies = [ "candid", + "ic-deterministic-heap-bytes", "num-traits", "serde", "serde_json", diff --git a/packages/ic-deterministic-heap-bytes/BUILD.bazel b/packages/ic-deterministic-heap-bytes/BUILD.bazel index 52560878c7a9..18c828d93349 100644 --- a/packages/ic-deterministic-heap-bytes/BUILD.bazel +++ b/packages/ic-deterministic-heap-bytes/BUILD.bazel @@ -20,9 +20,4 @@ rust_library( rust_test( name = "ic-deterministic-heap-bytes-test", crate = ":ic-deterministic-heap-bytes", - proc_macro_deps = [ - # Keep sorted. - "//packages/ic-deterministic-heap-bytes-derive", - "@crate_index//:paste", - ], ) diff --git a/packages/ic-deterministic-heap-bytes/src/lib.rs b/packages/ic-deterministic-heap-bytes/src/lib.rs index b57297d76546..83bfee9cfced 100644 --- a/packages/ic-deterministic-heap-bytes/src/lib.rs +++ b/packages/ic-deterministic-heap-bytes/src/lib.rs @@ -1,6 +1,5 @@ pub use ic_deterministic_heap_bytes_derive::DeterministicHeapBytes; use paste::paste; -use std::collections::BTreeMap; /// A trait to deterministically report heap memory usage. /// @@ -47,6 +46,8 @@ impl DeterministicHeapBytes for f64 {} impl DeterministicHeapBytes for bool {} impl DeterministicHeapBytes for char {} +impl DeterministicHeapBytes for std::time::Duration {} + impl DeterministicHeapBytes for String { fn deterministic_heap_bytes(&self) -> usize { self.len() @@ -79,7 +80,7 @@ impl DeterministicHeapBytes for Vec { } impl DeterministicHeapBytes - for BTreeMap + for std::collections::BTreeMap { /// Calculates the precise heap size by summing the heap usage of all elements. /// diff --git a/packages/ic-error-types/BUILD.bazel b/packages/ic-error-types/BUILD.bazel index 5e4881adcc0a..5dba14ada257 100644 --- a/packages/ic-error-types/BUILD.bazel +++ b/packages/ic-error-types/BUILD.bazel @@ -4,12 +4,14 @@ package(default_visibility = ["//visibility:public"]) DEPENDENCIES = [ # Keep sorted. + "//packages/ic-deterministic-heap-bytes", "@crate_index//:serde", "@crate_index//:strum", ] MACRO_DEPENDENCIES = [ "@crate_index//:strum_macros", + "//packages/ic-deterministic-heap-bytes-derive", ] rust_library( diff --git a/packages/ic-error-types/Cargo.toml b/packages/ic-error-types/Cargo.toml index 5f74a384deba..5fe4a8201b24 100644 --- a/packages/ic-error-types/Cargo.toml +++ b/packages/ic-error-types/Cargo.toml @@ -13,6 +13,7 @@ authors.workspace = true edition.workspace = true [dependencies] +ic-deterministic-heap-bytes = { path = "../ic-deterministic-heap-bytes" } serde = { workspace = true } strum = { workspace = true } strum_macros = { workspace = true } diff --git a/packages/ic-error-types/src/lib.rs b/packages/ic-error-types/src/lib.rs index 788924458f65..c1c5978f1862 100644 --- a/packages/ic-error-types/src/lib.rs +++ b/packages/ic-error-types/src/lib.rs @@ -1,6 +1,7 @@ //! A crate that groups user-facing and internal error types and codes produced //! by the Internet Computer. +use ic_deterministic_heap_bytes::DeterministicHeapBytes; use serde::{Deserialize, Serialize}; use std::{convert::TryFrom, fmt}; use str_traits::StrEllipsize; @@ -141,7 +142,18 @@ impl From for RejectCode { /// code and the rest is just a sequentially assigned two-digit /// number. #[derive( - Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, EnumIter, Serialize, + Copy, + Clone, + Eq, + DeterministicHeapBytes, + PartialEq, + Ord, + PartialOrd, + Hash, + Debug, + Deserialize, + EnumIter, + Serialize, )] pub enum ErrorCode { // 1xx -- `RejectCode::SysFatal` @@ -217,7 +229,18 @@ const MAX_USER_ERROR_DESCRIPTION_LEN_BYTES: usize = 8 * 1024; /// The error that is sent back to users of IC if something goes /// wrong. It's designed to be copyable and serializable so that we /// can persist it in ingress history. -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)] +#[derive( + Clone, + Eq, + DeterministicHeapBytes, + PartialEq, + Ord, + PartialOrd, + Hash, + Debug, + Deserialize, + Serialize, +)] pub struct UserError { code: ErrorCode, description: String, diff --git a/rs/execution_environment/BUILD.bazel b/rs/execution_environment/BUILD.bazel index 8b628c373ec2..935d33e0fc4f 100644 --- a/rs/execution_environment/BUILD.bazel +++ b/rs/execution_environment/BUILD.bazel @@ -5,6 +5,7 @@ package(default_visibility = ["//visibility:public"]) DEPENDENCIES = [ # Keep sorted. + "//packages/ic-deterministic-heap-bytes", "//packages/ic-error-types", "//rs/canister_sandbox:backend_lib", "//rs/config", @@ -60,7 +61,10 @@ DEPENDENCIES = [ "@crate_index//:wasmparser", ] -MACRO_DEPENDENCIES = [] +MACRO_DEPENDENCIES = [ + # Keep sorted. + "//packages/ic-deterministic-heap-bytes-derive", +] DEV_DEPENDENCIES = [ # Keep sorted. diff --git a/rs/execution_environment/Cargo.toml b/rs/execution_environment/Cargo.toml index ad4195bbf718..3825d35d6dfe 100644 --- a/rs/execution_environment/Cargo.toml +++ b/rs/execution_environment/Cargo.toml @@ -18,6 +18,7 @@ ic-crypto-sha2 = { path = "../crypto/sha2" } ic-crypto-tree-hash = { path = "../crypto/tree_hash" } ic-crypto-utils-canister-threshold-sig = { path = "../crypto/utils/canister_threshold_sig" } ic-cycles-account-manager = { path = "../cycles_account_manager" } +ic-deterministic-heap-bytes = { path = "../../packages/ic-deterministic-heap-bytes" } ic-embedders = { path = "../embedders" } ic-error-types = { path = "../../packages/ic-error-types" } ic-interfaces = { path = "../interfaces" } diff --git a/rs/execution_environment/src/query_handler/query_cache.rs b/rs/execution_environment/src/query_handler/query_cache.rs index 1edd3aaf643e..58fdbdb1bf91 100644 --- a/rs/execution_environment/src/query_handler/query_cache.rs +++ b/rs/execution_environment/src/query_handler/query_cache.rs @@ -1,4 +1,5 @@ use ic_base_types::{CanisterId, NumBytes}; +use ic_deterministic_heap_bytes::DeterministicHeapBytes; use ic_error_types::UserError; use ic_interfaces::execution_environment::SystemApiCallCounters; use ic_metrics::MetricsRegistry; @@ -9,7 +10,7 @@ use ic_types::{ }; use ic_utils_lru_cache::LruCache; use prometheus::{Histogram, IntCounter, IntGauge}; -use std::{collections::BTreeMap, mem::size_of_val, sync::Mutex, time::Duration}; +use std::{collections::BTreeMap, sync::Mutex, time::Duration}; use crate::metrics::duration_histogram; @@ -125,7 +126,7 @@ impl QueryCacheMetrics { /// /// The key is to distinguish query cache entries, i.e. entries with different /// keys are (almost) completely independent from each other. -#[derive(Clone, Eq, PartialEq, Hash)] +#[derive(Clone, Eq, DeterministicHeapBytes, PartialEq, Hash)] pub(crate) struct EntryKey { /// Query source. pub source: UserId, @@ -137,9 +138,10 @@ pub(crate) struct EntryKey { pub method_payload: Vec, } +// The MemoryDiskBytes trait is still required by the LruCache. impl MemoryDiskBytes for EntryKey { fn memory_bytes(&self) -> usize { - size_of_val(self) + self.method_name.len() + self.method_payload.len() + self.deterministic_heap_bytes() } fn disk_bytes(&self) -> usize { @@ -163,7 +165,7 @@ impl From<&Query> for EntryKey { /// /// The cache entry is valid as long as the metadata is unchanged, /// or it can be proven that the query does not depend on the change. -#[derive(PartialEq)] +#[derive(DeterministicHeapBytes, PartialEq)] pub(crate) struct EntryEnv { /// The consensus-determined time when the query is executed. pub batch_time: Time, @@ -196,6 +198,7 @@ impl EntryEnv { //////////////////////////////////////////////////////////////////////// /// Query Cache entry value. +#[derive(DeterministicHeapBytes)] pub(crate) struct EntryValue { /// Query Cache entry environment metadata captured before the query execution. env: EntryEnv, @@ -209,9 +212,10 @@ pub(crate) struct EntryValue { ignore_canister_balances: bool, } +// The MemoryDiskBytes trait is still required by the LruCache. impl MemoryDiskBytes for EntryValue { fn memory_bytes(&self) -> usize { - size_of_val(self) + self.result.memory_bytes() + self.deterministic_heap_bytes() } fn disk_bytes(&self) -> usize { @@ -367,28 +371,24 @@ impl EntryValue { //////////////////////////////////////////////////////////////////////// /// Replica Side Query Cache. +#[derive(DeterministicHeapBytes)] pub(crate) struct QueryCache { // We can't use `RwLock`, as the `LruCache::get()` requires mutable reference // to update the LRU. + // For now, avoid using the derive macro on LruCache. + #[deterministic_heap_bytes(with = + |c: &Mutex>| c.lock().unwrap().memory_bytes())] cache: Mutex>, /// The upper limit on how long the cache entry stays valid in the query cache. max_expiry_time: Duration, /// The upper limit on how long the data certificate stays valid in the query cache. data_certificate_expiry_time: Duration, - /// Query cache metrics (public for tests) + /// Query cache metrics (public for tests). + // Assume the metrics do not use any heap allocations. + #[deterministic_heap_bytes(with = |_| 0)] pub(crate) metrics: QueryCacheMetrics, } -impl MemoryDiskBytes for QueryCache { - fn memory_bytes(&self) -> usize { - size_of_val(self) + self.cache.lock().unwrap().memory_bytes() - } - - fn disk_bytes(&self) -> usize { - 0 - } -} - impl QueryCache { /// Create a new `QueryCache` instance. pub(crate) fn new( diff --git a/rs/execution_environment/src/query_handler/query_cache/tests.rs b/rs/execution_environment/src/query_handler/query_cache/tests.rs index 001e4a0494fc..a2cbf73f3ed7 100644 --- a/rs/execution_environment/src/query_handler/query_cache/tests.rs +++ b/rs/execution_environment/src/query_handler/query_cache/tests.rs @@ -5,6 +5,7 @@ use crate::{ InternalHttpQueryHandler, }; use ic_base_types::CanisterId; +use ic_deterministic_heap_bytes::DeterministicHeapBytes; use ic_error_types::ErrorCode; use ic_interfaces::execution_environment::{SystemApiCallCounters, SystemApiCallId}; use ic_registry_subnet_type::SubnetType; @@ -763,18 +764,18 @@ fn query_cache_frees_memory_after_invalidated_entries() { .build(); let id = test.canister_from_wat(QUERY_CACHE_WAT).unwrap(); - let memory_bytes = query_cache(&test).memory_bytes(); + let heap_bytes = query_cache(&test).deterministic_heap_bytes(); // Initially the cache should be empty, i.e. less than 1MB. - assert!(memory_bytes < BIG_RESPONSE_SIZE); + assert!(heap_bytes < BIG_RESPONSE_SIZE); // The 1MB result will be cached internally. let res = test .non_replicated_query(id, "canister_balance_sized_reply", vec![]) .unwrap(); assert_eq!(BIG_RESPONSE_SIZE, res.memory_bytes()); - let memory_bytes = query_cache(&test).memory_bytes(); + let heap_bytes = query_cache(&test).deterministic_heap_bytes(); // After the first reply, the cache should have more than 1MB of data. - assert!(memory_bytes > BIG_RESPONSE_SIZE); + assert!(heap_bytes > BIG_RESPONSE_SIZE); // Set the canister balance to 42, so the second reply will have just 42 bytes. test.canister_state_mut(id).system_state.remove_cycles( @@ -787,10 +788,10 @@ fn query_cache_frees_memory_after_invalidated_entries() { .non_replicated_query(id, "canister_balance_sized_reply", vec![]) .unwrap(); assert_eq!(SMALL_RESPONSE_SIZE, res.memory_bytes()); - let memory_bytes = query_cache(&test).memory_bytes(); + let heap_bytes = query_cache(&test).deterministic_heap_bytes(); // The second 42 reply should invalidate and replace the first 1MB reply in the cache. - assert!(memory_bytes > SMALL_RESPONSE_SIZE); - assert!(memory_bytes < BIG_RESPONSE_SIZE); + assert!(heap_bytes > SMALL_RESPONSE_SIZE); + assert!(heap_bytes < BIG_RESPONSE_SIZE); } #[test] @@ -801,8 +802,8 @@ fn query_cache_respects_cache_capacity() { let id = test.universal_canister().unwrap(); // Initially the cache should be empty, i.e. less than REPLY_SIZE. - let memory_bytes = query_cache(&test).memory_bytes(); - assert!(memory_bytes < REPLY_SIZE); + let heap_bytes = query_cache(&test).deterministic_heap_bytes(); + assert!(heap_bytes < REPLY_SIZE); // All replies should hit the same cache entry. for _ in 0..ITERATIONS { @@ -810,9 +811,9 @@ fn query_cache_respects_cache_capacity() { let _res = test.non_replicated_query(id, "query", wasm().reply_data(&[1; REPLY_SIZE / 2]).build()); // Now there should be only one reply in the cache. - let memory_bytes = query_cache(&test).memory_bytes(); - assert!(memory_bytes > REPLY_SIZE); - assert!(memory_bytes < QUERY_CACHE_CAPACITY); + let heap_bytes = query_cache(&test).deterministic_heap_bytes(); + assert!(heap_bytes > REPLY_SIZE); + assert!(heap_bytes < QUERY_CACHE_CAPACITY); } // Now the replies should hit another entry. @@ -820,9 +821,9 @@ fn query_cache_respects_cache_capacity() { let _res = test.non_replicated_query(id, "query", wasm().reply_data(&[2; REPLY_SIZE / 2]).build()); // Now there should be two replies in the cache. - let memory_bytes = query_cache(&test).memory_bytes(); - assert!(memory_bytes > REPLY_SIZE * 2); - assert!(memory_bytes < QUERY_CACHE_CAPACITY); + let heap_bytes = query_cache(&test).deterministic_heap_bytes(); + assert!(heap_bytes > REPLY_SIZE * 2); + assert!(heap_bytes < QUERY_CACHE_CAPACITY); } // Now the replies should evict the first entry. @@ -830,9 +831,9 @@ fn query_cache_respects_cache_capacity() { let _res = test.non_replicated_query(id, "query", wasm().reply_data(&[3; REPLY_SIZE / 2]).build()); // There should be still just two replies in the cache. - let memory_bytes = query_cache(&test).memory_bytes(); - assert!(memory_bytes > REPLY_SIZE * 2); - assert!(memory_bytes < QUERY_CACHE_CAPACITY); + let heap_bytes = query_cache(&test).deterministic_heap_bytes(); + assert!(heap_bytes > REPLY_SIZE * 2); + assert!(heap_bytes < QUERY_CACHE_CAPACITY); } } @@ -842,13 +843,13 @@ fn query_cache_works_with_zero_cache_capacity() { let id = test.universal_canister().unwrap(); // Even with zero capacity the cache data structure uses some bytes for the pointers etc. - let initial_memory_bytes = query_cache(&test).memory_bytes(); + let initial_heap_bytes = query_cache(&test).deterministic_heap_bytes(); // Replies should not change the initial (zero) capacity. for _ in 0..ITERATIONS { let _res = test.non_replicated_query(id, "query", wasm().reply_data(&[1]).build()); - let memory_bytes = query_cache(&test).memory_bytes(); - assert_eq!(initial_memory_bytes, memory_bytes); + let heap_bytes = query_cache(&test).deterministic_heap_bytes(); + assert_eq!(initial_heap_bytes, heap_bytes); } } @@ -1563,3 +1564,69 @@ fn query_cache_future_proof_test() { } } } + +#[test] +fn total_bytes_future_proof_guard() { + const HEAP_BYTES: usize = 5; + + // Key with no heap data. + let key = EntryKey { + source: user_test_id(1), + receiver: CanisterId::from_u64(1), + method_name: String::new(), + method_payload: vec![], + }; + assert_eq!(size_of_val(&key), 112); + assert_eq!(key.deterministic_total_bytes(), size_of_val(&key)); + + // Key with some heap data. + let key = EntryKey { + source: user_test_id(1), + receiver: CanisterId::from_u64(1), + method_name: " ".repeat(HEAP_BYTES), + method_payload: vec![42; HEAP_BYTES], + }; + assert_eq!(size_of_val(&key), 112); + assert_eq!( + key.deterministic_total_bytes(), + size_of_val(&key) + HEAP_BYTES * 2 + ); + + // Value with no heap data. + let env = EntryEnv { + batch_time: time::GENESIS, + canisters_versions_balances_stats: vec![], + }; + let value = EntryValue::new( + env, + Result::Ok(WasmResult::Reply(vec![])), + &SystemApiCallCounters::default(), + ); + assert_eq!(size_of_val(&value), 80); + assert_eq!(value.deterministic_total_bytes(), size_of_val(&value)); + + // Value with some heap data. + let env = EntryEnv { + batch_time: time::GENESIS, + canisters_versions_balances_stats: vec![ + ( + CanisterId::from_u64(1), + 0, + 0_u64.into(), + QueryStats::default(), + ); + HEAP_BYTES + ], + }; + let env_vec_size = size_of_val(&*env.canisters_versions_balances_stats); + let value = EntryValue::new( + env, + Result::Ok(WasmResult::Reply(vec![42; HEAP_BYTES])), + &SystemApiCallCounters::default(), + ); + assert_eq!(size_of_val(&value), 80); + assert_eq!( + value.deterministic_total_bytes(), + size_of_val(&value) + env_vec_size + HEAP_BYTES + ); +} diff --git a/rs/phantom_newtype/BUILD.bazel b/rs/phantom_newtype/BUILD.bazel index 55e0bc3afe90..acdabc549ba0 100644 --- a/rs/phantom_newtype/BUILD.bazel +++ b/rs/phantom_newtype/BUILD.bazel @@ -5,8 +5,13 @@ package(default_visibility = ["//visibility:public"]) rust_library( name = "phantom_newtype", srcs = glob(["src/**"]), + proc_macro_deps = [ + # Keep sorted. + "//packages/ic-deterministic-heap-bytes-derive", + ], deps = [ # Keep sorted. + "//packages/ic-deterministic-heap-bytes", "@crate_index//:candid", "@crate_index//:num-traits", "@crate_index//:serde", diff --git a/rs/phantom_newtype/Cargo.toml b/rs/phantom_newtype/Cargo.toml index 915209d6ae13..94ea01c0cb69 100644 --- a/rs/phantom_newtype/Cargo.toml +++ b/rs/phantom_newtype/Cargo.toml @@ -8,6 +8,7 @@ version.workspace = true [dependencies] candid = { workspace = true } +ic-deterministic-heap-bytes = { path = "../../packages/ic-deterministic-heap-bytes" } num-traits = { workspace = true } serde = { workspace = true } slog = { workspace = true } diff --git a/rs/phantom_newtype/src/id.rs b/rs/phantom_newtype/src/id.rs index c314fdcfd39d..926c28946618 100644 --- a/rs/phantom_newtype/src/id.rs +++ b/rs/phantom_newtype/src/id.rs @@ -1,4 +1,5 @@ use crate::displayer::{DisplayProxy, DisplayerOf}; +use ic_deterministic_heap_bytes::DeterministicHeapBytes; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use std::cmp::Ordering; use std::fmt; @@ -303,3 +304,9 @@ impl slog::Value for Id { self.0.serialize(record, key, serializer) } } + +impl DeterministicHeapBytes for Id { + fn deterministic_heap_bytes(&self) -> usize { + self.0.deterministic_heap_bytes() + } +} diff --git a/rs/types/base_types/BUILD.bazel b/rs/types/base_types/BUILD.bazel index e9a94fd8ef10..15c923dbea27 100644 --- a/rs/types/base_types/BUILD.bazel +++ b/rs/types/base_types/BUILD.bazel @@ -21,6 +21,7 @@ rust_library( crate_name = "ic_base_types", proc_macro_deps = [ # Keep sorted. + "//packages/ic-deterministic-heap-bytes-derive", "@crate_index//:strum_macros", ], rustc_flags = select({ @@ -30,6 +31,7 @@ rust_library( version = "0.9.0", deps = [ # Keep sorted. + "//packages/ic-deterministic-heap-bytes", "//rs/crypto/sha2", "//rs/phantom_newtype", "//rs/protobuf", diff --git a/rs/types/base_types/Cargo.toml b/rs/types/base_types/Cargo.toml index bae22a5f0e6d..9149ea4c7d69 100644 --- a/rs/types/base_types/Cargo.toml +++ b/rs/types/base_types/Cargo.toml @@ -14,6 +14,7 @@ candid = { workspace = true } comparable = { version = "0.5.1", features = ["derive"] } hex = { workspace = true } ic-crypto-sha2 = { path = "../../crypto/sha2" } +ic-deterministic-heap-bytes = { path = "../../../packages/ic-deterministic-heap-bytes" } ic-protobuf = { path = "../../protobuf" } phantom_newtype = { path = "../../phantom_newtype" } prost = { workspace = true } diff --git a/rs/types/base_types/src/canister_id.rs b/rs/types/base_types/src/canister_id.rs index 04fc5b6bffdf..567e2d5e2110 100644 --- a/rs/types/base_types/src/canister_id.rs +++ b/rs/types/base_types/src/canister_id.rs @@ -1,12 +1,25 @@ use super::{PrincipalId, PrincipalIdClass, PrincipalIdError, SubnetId}; use candid::types::principal::PrincipalError; use candid::{CandidType, Principal}; +use ic_deterministic_heap_bytes::DeterministicHeapBytes; use ic_protobuf::{proxy::ProxyDecodeError, types::v1 as pb}; use serde::{de::Error, Deserialize, Serialize}; use std::{convert::TryFrom, fmt}; /// A type representing a canister's [`PrincipalId`]. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, CandidType, Serialize)] +#[derive( + Copy, + Clone, + Eq, + DeterministicHeapBytes, + PartialEq, + Ord, + PartialOrd, + Hash, + Debug, + CandidType, + Serialize, +)] pub struct CanisterId(PrincipalId); /// Represents an error that can occur when constructing a [`CanisterId`] from a diff --git a/rs/types/base_types/src/principal_id.rs b/rs/types/base_types/src/principal_id.rs index 8a577dbd2f7c..3cf438154f8d 100644 --- a/rs/types/base_types/src/principal_id.rs +++ b/rs/types/base_types/src/principal_id.rs @@ -4,6 +4,7 @@ use arbitrary::{Arbitrary, Result as ArbitraryResult, Unstructured}; use candid::types::principal::{Principal, PrincipalError}; use candid::types::{Type, TypeId, TypeInner}; use ic_crypto_sha2::Sha224; +use ic_deterministic_heap_bytes::DeterministicHeapBytes; use ic_protobuf::types::v1 as pb; use serde::{Deserialize, Serialize}; use std::{ @@ -24,7 +25,16 @@ use strum_macros::EnumIter; /// want [`PrincipalId`] to implement the Copy trait, we encode them as /// a fixed-size array and a length. #[derive( - Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Deserialize, Serialize, comparable::Comparable, + Copy, + Clone, + Eq, + DeterministicHeapBytes, + PartialEq, + Ord, + PartialOrd, + Deserialize, + Serialize, + comparable::Comparable, )] #[describe_type(String)] #[describe_body(self.to_string())] diff --git a/rs/types/types/BUILD.bazel b/rs/types/types/BUILD.bazel index a646bd8f822e..450973842f25 100644 --- a/rs/types/types/BUILD.bazel +++ b/rs/types/types/BUILD.bazel @@ -9,6 +9,7 @@ rust_library( crate_name = "ic_types", proc_macro_deps = [ # Keep sorted. + "//packages/ic-deterministic-heap-bytes-derive", "//rs/utils/validate_eq_derive", "@crate_index//:strum_macros", ], @@ -19,6 +20,7 @@ rust_library( version = "0.9.0", deps = [ # Keep sorted. + "//packages/ic-deterministic-heap-bytes", "//packages/ic-error-types", "//rs/bitcoin/replica_types", "//rs/crypto/internal/crypto_lib/types", diff --git a/rs/types/types/Cargo.toml b/rs/types/types/Cargo.toml index 12d71300fcd9..6299c6155b72 100644 --- a/rs/types/types/Cargo.toml +++ b/rs/types/types/Cargo.toml @@ -18,6 +18,7 @@ ic-crypto-internal-types = { path = "../../crypto/internal/crypto_lib/types" } ic-crypto-sha2 = { path = "../../crypto/sha2" } ic-crypto-tree-hash = { path = "../../crypto/tree_hash" } ic-error-types = { path = "../../../packages/ic-error-types" } +ic-deterministic-heap-bytes = { path = "../../../packages/ic-deterministic-heap-bytes" } ic-management-canister-types-private = { path = "../management_canister_types" } ic-protobuf = { path = "../../protobuf" } ic-validate-eq = { path = "../../utils/validate_eq" } diff --git a/rs/types/types/src/batch/execution_environment.rs b/rs/types/types/src/batch/execution_environment.rs index a261e966759c..bf96594f7592 100644 --- a/rs/types/types/src/batch/execution_environment.rs +++ b/rs/types/types/src/batch/execution_environment.rs @@ -23,6 +23,7 @@ use crate::{node_id_into_protobuf, node_id_try_from_option, QueryStatsEpoch}; use ic_base_types::{CanisterId, NodeId, NumBytes}; +use ic_deterministic_heap_bytes::DeterministicHeapBytes; use ic_protobuf::registry::subnet::v1 as proto; use ic_protobuf::{ proxy::{try_from_option_field, ProxyDecodeError}, @@ -36,7 +37,7 @@ use prost::{bytes::BufMut, Message}; use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, hash::Hash}; -#[derive(Clone, Eq, PartialEq, Hash, Debug, Default)] +#[derive(Clone, Eq, DeterministicHeapBytes, PartialEq, Hash, Debug, Default)] pub struct QueryStats { pub num_calls: u32, pub num_instructions: u64, // Want u128, but not supported in protobuf diff --git a/rs/types/types/src/funds/cycles.rs b/rs/types/types/src/funds/cycles.rs index 77c2a0110da4..1af6abac6bfc 100644 --- a/rs/types/types/src/funds/cycles.rs +++ b/rs/types/types/src/funds/cycles.rs @@ -1,4 +1,5 @@ use candid::{CandidType, Nat}; +use ic_deterministic_heap_bytes::DeterministicHeapBytes; #[cfg(test)] use ic_exhaustive_derive::ExhaustiveSet; use ic_protobuf::state::canister_state_bits::v1::CyclesAccount as pbCyclesAccount; @@ -19,6 +20,7 @@ use thousands::Separable; Copy, Clone, Eq, + DeterministicHeapBytes, PartialEq, Ord, PartialOrd, diff --git a/rs/types/types/src/ingress.rs b/rs/types/types/src/ingress.rs index 02b88bd81cfc..19a4de31ca9f 100644 --- a/rs/types/types/src/ingress.rs +++ b/rs/types/types/src/ingress.rs @@ -2,6 +2,7 @@ use crate::artifact::IngressMessageId; use crate::{CanisterId, MemoryDiskBytes, PrincipalId, Time, UserId}; +use ic_deterministic_heap_bytes::DeterministicHeapBytes; use ic_error_types::{ErrorCode, UserError}; #[cfg(test)] use ic_exhaustive_derive::ExhaustiveSet; @@ -166,7 +167,18 @@ impl IngressSets { /// This struct describes the different types that executing a Wasm function in /// a canister can produce -#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)] +#[derive( + Clone, + Eq, + DeterministicHeapBytes, + PartialEq, + Ord, + PartialOrd, + Hash, + Debug, + Deserialize, + Serialize, +)] #[cfg_attr(test, derive(ExhaustiveSet))] pub enum WasmResult { /// Raw response, returned in a "happy" case diff --git a/rs/types/types/src/time.rs b/rs/types/types/src/time.rs index b78448526ff0..4a858ab91415 100644 --- a/rs/types/types/src/time.rs +++ b/rs/types/types/src/time.rs @@ -5,6 +5,7 @@ #[cfg(test)] mod tests; +use ic_deterministic_heap_bytes::DeterministicHeapBytes; #[cfg(test)] use ic_exhaustive_derive::ExhaustiveSet; use ic_limits::{MAX_INGRESS_TTL, PERMITTED_DRIFT}; @@ -18,7 +19,19 @@ use thiserror::Error; /// Time since UNIX_EPOCH (in nanoseconds). Just like 'std::time::Instant' or /// 'std::time::SystemTime', [Time] does not implement the [Default] trait. -#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Deserialize, Serialize)] +#[derive( + Copy, + Clone, + Eq, + DeterministicHeapBytes, + PartialEq, + Ord, + PartialOrd, + Hash, + Debug, + Deserialize, + Serialize, +)] #[cfg_attr(test, derive(Arbitrary, ExhaustiveSet))] pub struct Time(u64);