From 4f9cdfc7e7caf52ee7289076e72166b8242073b0 Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Tue, 19 May 2026 00:38:04 +0400 Subject: [PATCH 01/14] feat(rs): add static storage maps and benchmarks --- .github/workflows/rs-release.yml | 1 + Cargo.lock | 21 + Cargo.toml | 3 + benchmarks/Cargo.toml | 3 + benchmarks/bench_data.json | 212 ++++ benchmarks/build.rs | 1 + benchmarks/idls/storage_stress_program.idl | 43 + benchmarks/src/benchmarks.rs | 268 +++++ benchmarks/src/bin/bench_analyzer.rs | 153 ++- benchmarks/src/clients.rs | 5 + benchmarks/src/entities.rs | 29 + benchmarks/src/lib.rs | 13 +- benchmarks/src/storage_stress_program.rs | 139 +++ benchmarks/storage-stress/Cargo.toml | 14 + benchmarks/storage-stress/build.rs | 9 + benchmarks/storage-stress/src/lib.rs | 1227 ++++++++++++++++++++ docs/storage-static-maps-local-notes.md | 136 +++ rs/Cargo.toml | 6 +- rs/src/build.rs | 655 +++++++++++ rs/src/lib.rs | 11 +- rs/storage/Cargo.toml | 17 + rs/storage/src/lib.rs | 852 ++++++++++++++ 22 files changed, 3788 insertions(+), 30 deletions(-) create mode 100644 benchmarks/idls/storage_stress_program.idl create mode 100644 benchmarks/src/storage_stress_program.rs create mode 100644 benchmarks/storage-stress/Cargo.toml create mode 100644 benchmarks/storage-stress/build.rs create mode 100644 benchmarks/storage-stress/src/lib.rs create mode 100644 docs/storage-static-maps-local-notes.md create mode 100644 rs/src/build.rs create mode 100644 rs/storage/Cargo.toml create mode 100644 rs/storage/src/lib.rs diff --git a/.github/workflows/rs-release.yml b/.github/workflows/rs-release.yml index dd40a2070..7a59d5c46 100644 --- a/.github/workflows/rs-release.yml +++ b/.github/workflows/rs-release.yml @@ -37,6 +37,7 @@ env: sails-client-gen-js sails-macros-core sails-macros + sails-storage sails-rs sails-cli diff --git a/Cargo.lock b/Cargo.lock index 86af27919..369a9a8db 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -740,8 +740,10 @@ dependencies = [ "sails-rs", "serde", "serde_json", + "storage-stress", "tempfile", "tokio", + "wasmparser 0.244.0", ] [[package]] @@ -6595,11 +6597,15 @@ dependencies = [ "sails-idl-meta", "sails-macros", "sails-reflect-hash", + "sails-storage", "sails-type-registry", "spin 0.10.0", + "tempfile", "thiserror 2.0.18", "tokio", "tokio-stream", + "wasm-encoder 0.244.0", + "wasmparser 0.244.0", ] [[package]] @@ -6614,6 +6620,13 @@ dependencies = [ "serde", ] +[[package]] +name = "sails-storage" +version = "1.0.0-beta.5" +dependencies = [ + "gprimitives", +] + [[package]] name = "sails-type-registry" version = "1.0.0-beta.5" @@ -7876,6 +7889,14 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "storage-stress" +version = "1.0.0-beta.5" +dependencies = [ + "sails-rs", + "sails-storage", +] + [[package]] name = "str-buf" version = "3.0.3" diff --git a/Cargo.toml b/Cargo.toml index d9d655b85..e886accb7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,11 +49,13 @@ members = [ "rs/macros/core", "rs/reflect-hash", "rs/sol-gen", + "rs/storage", "rs/type-registry", "rs/type-registry/derive", "benchmarks/alloc-stress", "benchmarks/compute-stress", "benchmarks/counter-bench", + "benchmarks/storage-stress", "benchmarks/ping-pong/app", "benchmarks/ping-pong/client", "benchmarks", @@ -74,6 +76,7 @@ sails-macros-core = { path = "rs/macros/core" } sails-macros = { path = "rs/macros" } sails-reflect-hash-derive = { path = "rs/reflect-hash/derive" } sails-reflect-hash = { path = "rs/reflect-hash" } +sails-storage = { path = "rs/storage" } sails-rs = { path = "rs" } sails-sol-gen = { path = "rs/sol-gen" } sails-type-registry = { path = "rs/type-registry" } diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index defb4aab2..c737e5466 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -24,6 +24,7 @@ sails-rs = { workspace = true, features = ["build"] } alloc-stress = { path = "alloc-stress" } compute-stress = { path = "compute-stress" } counter-bench = { path = "counter-bench" } +storage-stress = { path = "storage-stress" } convert_case.workspace = true sails-client-gen-v2.workspace = true @@ -33,6 +34,7 @@ sails-rs = { workspace = true, features = ["gtest"] } alloc-stress = { path = "alloc-stress" } compute-stress = { path = "compute-stress" } counter-bench = { path = "counter-bench" } +storage-stress = { path = "storage-stress" } ping-pong-bench-app = { path = "ping-pong/app" } ping-pong-stack = { path = "../examples/ping-pong-stack" } redirect-app = { path = "../examples/redirect/app" } @@ -41,3 +43,4 @@ redirect-proxy = { path = "../examples/redirect/proxy" } redirect-proxy-client = { path = "../examples/redirect/proxy-client" } tokio = { workspace = true, features = ["rt", "macros"] } tempfile.workspace = true +wasmparser.workspace = true diff --git a/benchmarks/bench_data.json b/benchmarks/bench_data.json index 42aceb841..0b4961cfb 100644 --- a/benchmarks/bench_data.json +++ b/benchmarks/bench_data.json @@ -28,5 +28,217 @@ "5": 15738223846, "10": 29542694740, "20": 66032420478 + }, + "storage": { + "fixed_allowance_insert_fresh_0": 1080478753, + "fixed_allowance_insert_fresh_1024": 1081708365, + "fixed_allowance_insert_fresh_16": 1080478753, + "fixed_allowance_insert_fresh_256": 1080478753, + "fixed_allowance_prepare_0": 11691762354, + "fixed_allowance_prepare_1024": 20395014497, + "fixed_allowance_prepare_16": 11891343494, + "fixed_allowance_prepare_256": 13803197942, + "fixed_allowance_read_existing_1024": 854891432, + "fixed_allowance_read_existing_16": 854891432, + "fixed_allowance_read_existing_256": 854891432, + "fixed_allowance_read_missing_0": 849118236, + "fixed_allowance_read_missing_1024": 850347848, + "fixed_allowance_read_missing_16": 849118236, + "fixed_allowance_read_missing_256": 849118236, + "fixed_allowance_remove_1024": 1084110439, + "fixed_allowance_remove_16": 1084110439, + "fixed_allowance_remove_256": 1084110439, + "fixed_allowance_update_existing_1024": 970181732, + "fixed_allowance_update_existing_16": 970181732, + "fixed_allowance_update_existing_256": 970181732, + "fixed_balance_insert_fresh_0": 1076533775, + "fixed_balance_insert_fresh_1024": 1077774256, + "fixed_balance_insert_fresh_16": 1076533775, + "fixed_balance_insert_fresh_256": 1077774256, + "fixed_balance_prepare_0": 10118393122, + "fixed_balance_prepare_1024": 14186755208, + "fixed_balance_prepare_16": 10254856390, + "fixed_balance_prepare_256": 11190432070, + "fixed_balance_read_existing_1024": 849854002, + "fixed_balance_read_existing_16": 849854002, + "fixed_balance_read_existing_256": 849854002, + "fixed_balance_read_missing_0": 846741105, + "fixed_balance_read_missing_1024": 847981586, + "fixed_balance_read_missing_16": 846741105, + "fixed_balance_read_missing_256": 847981586, + "fixed_balance_remove_1024": 1079072972, + "fixed_balance_remove_16": 1079072972, + "fixed_balance_remove_256": 1079072972, + "fixed_balance_update_existing_1024": 965144302, + "fixed_balance_update_existing_16": 965144302, + "fixed_balance_update_existing_256": 965144302, + "hashmap_allowance_insert_fresh_0": 1068848248, + "hashmap_allowance_insert_fresh_1024": 1293302336, + "hashmap_allowance_insert_fresh_16": 923683548, + "hashmap_allowance_insert_fresh_256": 1447667698, + "hashmap_allowance_prepare_0": 5867760965, + "hashmap_allowance_prepare_1024": 20303139327, + "hashmap_allowance_prepare_16": 6180003273, + "hashmap_allowance_prepare_256": 9614976891, + "hashmap_allowance_read_existing_1024": 1221407908, + "hashmap_allowance_read_existing_16": 926430017, + "hashmap_allowance_read_existing_256": 1225167720, + "hashmap_allowance_read_missing_0": 1073991570, + "hashmap_allowance_read_missing_1024": 1140349789, + "hashmap_allowance_read_missing_16": 921177688, + "hashmap_allowance_read_missing_256": 1180132590, + "hashmap_allowance_remove_1024": 1300111758, + "hashmap_allowance_remove_16": 926574295, + "hashmap_allowance_remove_256": 1319983389, + "hashmap_allowance_update_existing_1024": 1300063555, + "hashmap_allowance_update_existing_16": 926526092, + "hashmap_allowance_update_existing_256": 1319954955, + "hashmap_balance_insert_fresh_0": 1066973079, + "hashmap_balance_insert_fresh_1024": 1137061805, + "hashmap_balance_insert_fresh_16": 921808379, + "hashmap_balance_insert_fresh_256": 1371274460, + "hashmap_balance_prepare_0": 5867760965, + "hashmap_balance_prepare_1024": 15861084871, + "hashmap_balance_prepare_16": 6127491010, + "hashmap_balance_prepare_256": 8439301099, + "hashmap_balance_read_existing_1024": 1067742829, + "hashmap_balance_read_existing_16": 923211625, + "hashmap_balance_read_existing_256": 1182166527, + "hashmap_balance_read_missing_0": 1073929848, + "hashmap_balance_read_missing_1024": 1139830487, + "hashmap_balance_read_missing_16": 920658386, + "hashmap_balance_read_missing_256": 1179613288, + "hashmap_balance_remove_1024": 1296891036, + "hashmap_balance_remove_16": 923353573, + "hashmap_balance_remove_256": 1336673837, + "hashmap_balance_update_existing_1024": 1142477964, + "hashmap_balance_update_existing_16": 923305863, + "hashmap_balance_update_existing_256": 1336626127, + "raw_static_allowance_insert_fresh_0": 1086612355, + "raw_static_allowance_insert_fresh_1024": 1088913060, + "raw_static_allowance_insert_fresh_16": 1086612355, + "raw_static_allowance_insert_fresh_256": 1086612355, + "raw_static_allowance_prepare_0": 10905039291, + "raw_static_allowance_prepare_1024": 26410108594, + "raw_static_allowance_prepare_16": 11202759839, + "raw_static_allowance_prepare_256": 14612480409, + "raw_static_allowance_read_existing_1024": 856012119, + "raw_static_allowance_read_existing_16": 856012119, + "raw_static_allowance_read_existing_256": 856012119, + "raw_static_allowance_read_missing_0": 853747519, + "raw_static_allowance_read_missing_1024": 856048224, + "raw_static_allowance_read_missing_16": 853747519, + "raw_static_allowance_read_missing_256": 853747519, + "raw_static_allowance_remove_1024": 1085296658, + "raw_static_allowance_remove_16": 1085296658, + "raw_static_allowance_remove_256": 1085296658, + "raw_static_allowance_update_existing_1024": 972008088, + "raw_static_allowance_update_existing_16": 972008088, + "raw_static_allowance_update_existing_256": 972008088, + "raw_static_balance_insert_fresh_0": 1080553713, + "raw_static_balance_insert_fresh_1024": 1082853862, + "raw_static_balance_insert_fresh_16": 1080553713, + "raw_static_balance_insert_fresh_256": 1082853862, + "raw_static_balance_prepare_0": 9331490552, + "raw_static_balance_prepare_1024": 17518663566, + "raw_static_balance_prepare_16": 9532272828, + "raw_static_balance_prepare_256": 11432702398, + "raw_static_balance_read_existing_1024": 850999746, + "raw_static_balance_read_existing_16": 850999746, + "raw_static_balance_read_existing_256": 850999746, + "raw_static_balance_read_missing_0": 849050075, + "raw_static_balance_read_missing_1024": 851350224, + "raw_static_balance_read_missing_16": 849050075, + "raw_static_balance_read_missing_256": 851350224, + "raw_static_balance_remove_1024": 1080284285, + "raw_static_balance_remove_16": 1080284285, + "raw_static_balance_remove_256": 1080284285, + "raw_static_balance_update_existing_1024": 966991863, + "raw_static_balance_update_existing_16": 966991863, + "raw_static_balance_update_existing_256": 966991863, + "sails_fixed_allowance_insert_fresh_0": 1080952389, + "sails_fixed_allowance_insert_fresh_1024": 1083473052, + "sails_fixed_allowance_insert_fresh_16": 1080952389, + "sails_fixed_allowance_insert_fresh_256": 1080952389, + "sails_fixed_allowance_prepare_0": 11316577386, + "sails_fixed_allowance_prepare_1024": 26302715280, + "sails_fixed_allowance_prepare_16": 11528096489, + "sails_fixed_allowance_prepare_256": 15055648414, + "sails_fixed_allowance_read_existing_1024": 855438899, + "sails_fixed_allowance_read_existing_16": 855438899, + "sails_fixed_allowance_read_existing_256": 855438899, + "sails_fixed_allowance_read_missing_0": 849850001, + "sails_fixed_allowance_read_missing_1024": 852388685, + "sails_fixed_allowance_read_missing_16": 849850001, + "sails_fixed_allowance_read_missing_256": 849850001, + "sails_fixed_allowance_remove_1024": 1084636051, + "sails_fixed_allowance_remove_16": 1084636051, + "sails_fixed_allowance_remove_256": 1084636051, + "sails_fixed_allowance_update_existing_1024": 972138658, + "sails_fixed_allowance_update_existing_16": 972138658, + "sails_fixed_allowance_update_existing_256": 972138658, + "sails_fixed_balance_insert_fresh_0": 1077144137, + "sails_fixed_balance_insert_fresh_1024": 1078413479, + "sails_fixed_balance_insert_fresh_16": 1077144137, + "sails_fixed_balance_insert_fresh_256": 1077778808, + "sails_fixed_balance_prepare_0": 9743106061, + "sails_fixed_balance_prepare_1024": 14436536557, + "sails_fixed_balance_prepare_16": 9889335121, + "sails_fixed_balance_prepare_256": 10971397681, + "sails_fixed_balance_read_existing_1024": 850699257, + "sails_fixed_balance_read_existing_16": 850699257, + "sails_fixed_balance_read_existing_256": 850699257, + "sails_fixed_balance_read_missing_0": 847625335, + "sails_fixed_balance_read_missing_1024": 848894677, + "sails_fixed_balance_read_missing_16": 847625335, + "sails_fixed_balance_read_missing_256": 848260006, + "sails_fixed_balance_remove_1024": 1079896409, + "sails_fixed_balance_remove_16": 1079896409, + "sails_fixed_balance_remove_256": 1079896409, + "sails_fixed_balance_update_existing_1024": 965815430, + "sails_fixed_balance_update_existing_16": 965815430, + "sails_fixed_balance_update_existing_256": 965815430, + "sails_static_allowance_insert_fresh_0": 926858230, + "sails_static_allowance_insert_fresh_1024": 930873664, + "sails_static_allowance_insert_fresh_16": 926858230, + "sails_static_allowance_insert_fresh_256": 926858230, + "sails_static_allowance_prepare_0": 9852324426, + "sails_static_allowance_prepare_1024": 25041784414, + "sails_static_allowance_prepare_16": 10136784111, + "sails_static_allowance_prepare_256": 13695221786, + "sails_static_allowance_read_existing_1024": 815651853, + "sails_static_allowance_read_existing_16": 815651853, + "sails_static_allowance_read_existing_256": 815651853, + "sails_static_allowance_read_missing_0": 810119894, + "sails_static_allowance_read_missing_1024": 814467723, + "sails_static_allowance_read_missing_16": 810119894, + "sails_static_allowance_read_missing_256": 810119894, + "sails_static_allowance_remove_1024": 930513600, + "sails_static_allowance_remove_16": 930513600, + "sails_static_allowance_remove_256": 930513600, + "sails_static_allowance_update_existing_1024": 932579326, + "sails_static_allowance_update_existing_16": 932579326, + "sails_static_allowance_update_existing_256": 932579326, + "sails_static_balance_insert_fresh_0": 923049941, + "sails_static_balance_insert_fresh_1024": 924292178, + "sails_static_balance_insert_fresh_16": 923049941, + "sails_static_balance_insert_fresh_256": 924292178, + "sails_static_balance_prepare_0": 8552423960, + "sails_static_balance_prepare_1024": 13568825817, + "sails_static_balance_prepare_16": 8750605215, + "sails_static_balance_prepare_256": 9897209055, + "sails_static_balance_read_existing_1024": 810912211, + "sails_static_balance_read_existing_16": 810912211, + "sails_static_balance_read_existing_256": 810912211, + "sails_static_balance_read_missing_0": 807895228, + "sails_static_balance_read_missing_1024": 809137465, + "sails_static_balance_read_missing_16": 807895228, + "sails_static_balance_read_missing_256": 809137465, + "sails_static_balance_remove_1024": 925773889, + "sails_static_balance_remove_16": 925773889, + "sails_static_balance_remove_256": 925773889, + "sails_static_balance_update_existing_1024": 926264565, + "sails_static_balance_update_existing_16": 926264565, + "sails_static_balance_update_existing_256": 926264565 } } \ No newline at end of file diff --git a/benchmarks/build.rs b/benchmarks/build.rs index 0d5834fd2..c24e6be2b 100644 --- a/benchmarks/build.rs +++ b/benchmarks/build.rs @@ -23,4 +23,5 @@ fn main() { generate_client("alloc_stress_program", &clients_path, &idls_path); generate_client("compute_stress_program", &clients_path, &idls_path); generate_client("counter_bench_program", &clients_path, &idls_path); + generate_client("storage_stress_program", &clients_path, &idls_path); } diff --git a/benchmarks/idls/storage_stress_program.idl b/benchmarks/idls/storage_stress_program.idl new file mode 100644 index 000000000..95dee147d --- /dev/null +++ b/benchmarks/idls/storage_stress_program.idl @@ -0,0 +1,43 @@ + +!@sails: 1.0.0-beta.5 + +service StorageStress@0x66333911850d9b10 { + functions { + Bench(backend: StorageBackend, map: StorageMap, op: StorageOp, seed: u32) -> StorageBenchResult; + Prepare(backend: StorageBackend, map: StorageMap, len: u32) -> StorageBenchResult; + } + types { + enum StorageBackend { + HashMap, + Fixed, + RawStatic, + SailsFixed, + SailsStatic, + } + struct StorageBenchResult { + value: U256, + len: u32, + existed: bool, + } + enum StorageMap { + Balance, + Allowance, + } + enum StorageOp { + InsertFresh, + UpdateExisting, + ReadExisting, + ReadMissing, + Remove, + } + } +} + +program StorageStress { + constructors { + NewForBench(); + } + services { + StorageStress@0x66333911850d9b10, + } +} diff --git a/benchmarks/src/benchmarks.rs b/benchmarks/src/benchmarks.rs index 98e4ce613..646f89bf1 100644 --- a/benchmarks/src/benchmarks.rs +++ b/benchmarks/src/benchmarks.rs @@ -8,6 +8,7 @@ //! - **Compute performance benchmarks** - Tests CPU-intensive operations like Fibonacci calculations (`compute_stress_bench` test) //! - **Cross-program performance** - Tests performance of cross-program communication (`cross_program_bench` test) //! - **Redirect performance** - Tests performance of redirecting calls to another program (`redirect_bench` test) +//! - **Storage benchmarks** - Compares allocator-backed maps with fixed open-addressed maps (`storage_stress_bench` test) //! //! All benchmarks use the `gtest` framework to simulate on-chain execution and measure //! gas consumption. Results are stored to the shared benchmark data file for analysis. @@ -27,6 +28,9 @@ use crate::clients::{ counter_bench_client::{ CounterBench as _, CounterBenchCtors, CounterBenchProgram, counter_bench::*, }, + storage_stress_client::{ + StorageStress as _, StorageStressCtors, StorageStressProgram, storage_stress::*, + }, }; use gtest::{System, constants::DEFAULT_USER_ALICE}; use itertools::{Either, Itertools}; @@ -272,6 +276,54 @@ async fn message_stack_bench() { } } +#[tokio::test] +async fn storage_stress_bench() { + let mut benches: BTreeMap> = Default::default(); + let loads = [0, 16, 256, 1024]; + let wasm_path = "../target/wasm32-gear/release/storage_stress.opt.wasm"; + assert_storage_stress_wasm_static_memory_layout(wasm_path); + + for sample in 0..10 { + for backend in [ + StorageBackend::HashMap, + StorageBackend::Fixed, + StorageBackend::RawStatic, + StorageBackend::SailsFixed, + StorageBackend::SailsStatic, + ] { + for map in [StorageMap::Balance, StorageMap::Allowance] { + for &load in loads.iter() { + let run = storage_stress_test(backend.clone(), map.clone(), load, sample).await; + + for gas in run.prepare { + benches + .entry(storage_prepare_key(&backend, &map, load)) + .or_default() + .push(gas); + } + + for (op, gas) in run.operations { + benches + .entry(storage_bench_key(&backend, &map, &op, load)) + .or_default() + .push(gas); + } + } + } + } + } + + let medians = benches + .into_iter() + .map(|(key, gas_benches)| (key, median(gas_benches))) + .collect(); + + crate::store_bench_data(|bench_data| { + bench_data.replace_storage_benches(medians); + }) + .unwrap(); +} + async fn alloc_stress_test(n: u32) -> (usize, u64) { // Path taken from the .binpath file let wasm_path = "../target/wasm32-gear/release/alloc_stress.opt.wasm"; @@ -319,6 +371,222 @@ async fn message_stack_test(limit: u32) -> u64 { gas } +struct StorageStressRun { + prepare: Vec, + operations: Vec<(StorageOp, u64)>, +} + +async fn storage_stress_test( + backend: StorageBackend, + map: StorageMap, + load: u32, + sample: u32, +) -> StorageStressRun { + let wasm_path = "../target/wasm32-gear/release/storage_stress.opt.wasm"; + let env = create_env(); + let program = deploy_for_bench(&env, wasm_path, |d| StorageStressCtors::new_for_bench(d)).await; + let mut service = program.storage_stress(); + + let ops = storage_ops_for_load(load); + let mut prepare = Vec::with_capacity(ops.len()); + let mut operations = Vec::with_capacity(ops.len()); + + for op in ops { + let message_id = service + .prepare(backend.clone(), map.clone(), load) + .send_one_way() + .unwrap(); + let (payload, gas) = extract_reply_and_gas(env.system(), message_id); + prepare.push(gas); + let prep = + crate::clients::storage_stress_client::storage_stress::io::Prepare::decode_reply( + StorageStressProgram::ROUTE_ID_STORAGE_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(prep.len, load); + + let seed = storage_seed_for_op(&op, load, sample); + let message_id = service + .bench(backend.clone(), map.clone(), op.clone(), seed) + .send_one_way() + .unwrap(); + let (payload, gas) = extract_reply_and_gas(env.system(), message_id); + let result = + crate::clients::storage_stress_client::storage_stress::io::Bench::decode_reply( + StorageStressProgram::ROUTE_ID_STORAGE_STRESS, + payload.as_slice(), + ) + .unwrap(); + + assert_storage_result(&op, load, seed, &result); + operations.push((op, gas)); + } + + StorageStressRun { + prepare, + operations, + } +} + +fn storage_ops_for_load(load: u32) -> Vec { + if load == 0 { + vec![StorageOp::InsertFresh, StorageOp::ReadMissing] + } else { + vec![ + StorageOp::InsertFresh, + StorageOp::UpdateExisting, + StorageOp::ReadExisting, + StorageOp::ReadMissing, + StorageOp::Remove, + ] + } +} + +fn storage_seed_for_op(op: &StorageOp, load: u32, sample: u32) -> u32 { + match op { + StorageOp::InsertFresh | StorageOp::ReadMissing => 10_000 + load + sample, + StorageOp::UpdateExisting | StorageOp::ReadExisting | StorageOp::Remove => { + 1 + (sample % load) + } + } +} + +fn assert_storage_result(op: &StorageOp, load: u32, seed: u32, result: &StorageBenchResult) { + match op { + StorageOp::InsertFresh => { + assert_eq!(result.value, storage_value_for_seed(seed)); + assert_eq!(result.len, load + 1); + assert!(!result.existed); + } + StorageOp::UpdateExisting => { + assert_eq!(result.value, storage_updated_value_for_seed(seed)); + assert_eq!(result.len, load); + assert!(result.existed); + } + StorageOp::ReadExisting => { + assert_eq!(result.value, storage_value_for_seed(seed)); + assert_eq!(result.len, load); + assert!(result.existed); + } + StorageOp::ReadMissing => { + assert_eq!(result.value, U256::zero()); + assert_eq!(result.len, load); + assert!(!result.existed); + } + StorageOp::Remove => { + assert_eq!(result.value, storage_value_for_seed(seed)); + assert_eq!(result.len, load - 1); + assert!(result.existed); + } + } +} + +fn storage_value_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 1) +} + +fn storage_updated_value_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 1_000_001) +} + +fn storage_bench_key( + backend: &StorageBackend, + map: &StorageMap, + op: &StorageOp, + load: u32, +) -> String { + format!( + "{}_{}_{}_{}", + storage_backend_name(backend), + storage_map_name(map), + storage_op_name(op), + load + ) +} + +fn storage_prepare_key(backend: &StorageBackend, map: &StorageMap, load: u32) -> String { + format!( + "{}_{}_prepare_{}", + storage_backend_name(backend), + storage_map_name(map), + load + ) +} + +fn storage_backend_name(backend: &StorageBackend) -> &'static str { + match backend { + StorageBackend::HashMap => "hashmap", + StorageBackend::Fixed => "fixed", + StorageBackend::RawStatic => "raw_static", + StorageBackend::SailsFixed => "sails_fixed", + StorageBackend::SailsStatic => "sails_static", + } +} + +fn storage_map_name(map: &StorageMap) -> &'static str { + match map { + StorageMap::Balance => "balance", + StorageMap::Allowance => "allowance", + } +} + +fn storage_op_name(op: &StorageOp) -> &'static str { + match op { + StorageOp::InsertFresh => "insert_fresh", + StorageOp::UpdateExisting => "update_existing", + StorageOp::ReadExisting => "read_existing", + StorageOp::ReadMissing => "read_missing", + StorageOp::Remove => "remove", + } +} + +fn assert_storage_stress_wasm_static_memory_layout(wasm_path: &str) { + let wasm = std::fs::read(wasm_path).expect("storage-stress optimized WASM exists"); + let mut imported_memory_pages = None; + + 'payloads: for payload in wasmparser::Parser::new(0).parse_all(&wasm) { + let wasmparser::Payload::ImportSection(imports) = + payload.expect("storage-stress optimized WASM parses") + else { + continue; + }; + + for imports in imports { + match imports.expect("storage-stress import parses") { + wasmparser::Imports::Single(_, import) => { + if let wasmparser::TypeRef::Memory(memory) = import.ty { + imported_memory_pages = Some(memory.initial); + break 'payloads; + } + } + wasmparser::Imports::Compact1 { items, .. } => { + for item in items { + let item = item.expect("storage-stress compact import parses"); + if let wasmparser::TypeRef::Memory(memory) = item.ty { + imported_memory_pages = Some(memory.initial); + break 'payloads; + } + } + } + wasmparser::Imports::Compact2 { ty, .. } => { + if let wasmparser::TypeRef::Memory(memory) = ty { + imported_memory_pages = Some(memory.initial); + break 'payloads; + } + } + } + } + } + + let imported_memory_pages = imported_memory_pages.expect("storage-stress imports memory"); + assert!( + imported_memory_pages >= u64::from(::storage_stress::STATIC_MEMORY_END_PAGE), + "storage-stress imported memory has {imported_memory_pages} pages, expected at least {}", + ::storage_stress::STATIC_MEMORY_END_PAGE + ); +} + fn create_env() -> GtestEnv { let system = System::new(); system.mint_to(DEFAULT_USER_ALICE, 1_000_000_000_000_000); diff --git a/benchmarks/src/bin/bench_analyzer.rs b/benchmarks/src/bin/bench_analyzer.rs index 504e80c1a..1588d7582 100644 --- a/benchmarks/src/bin/bench_analyzer.rs +++ b/benchmarks/src/bin/bench_analyzer.rs @@ -1,9 +1,13 @@ use anyhow::{Context, Result, anyhow}; use benchmarks::{ - BenchCategoryComparison, BenchCategoryComparisonReport, BenchData, BenchDataFile, + BenchCategory, BenchCategoryComparison, BenchCategoryComparisonReport, BenchData, BenchDataFile, }; use clap::Parser; -use std::{fs, path::PathBuf}; +use std::{ + collections::{BTreeMap, BTreeSet}, + fs, + path::PathBuf, +}; #[derive(Parser)] #[command(version, about, long_about = None)] @@ -47,34 +51,39 @@ fn analyze_benches( // Flag to track if any benchmarks fail the threshold check. let mut threshold_failed = threshold.map(|_| false); + let current_data: BTreeMap<_, _> = current_data.into_iter().collect(); + let other_data: BTreeMap<_, _> = other_data.into_iter().collect(); + let categories = current_data + .keys() + .chain(other_data.keys()) + .cloned() + .collect::>(); + // Create unfinished report. - let mut report = current_data - .into_iter() - .zip(other_data) - .map( - // Create a comparison entity for each benchmark category. - |((current_category, current_value), (other_category, other_value))| { - assert_eq!(current_category, other_category, "Categories do not match"); - - let comparison = BenchCategoryComparison::new( - current_category, - current_value, - other_value, - threshold, - ); + let mut report = initialize_report(); + for category in categories { + match (current_data.get(&category), other_data.get(&category)) { + (Some(current_value), Some(other_value)) => { + let comparison = + BenchCategoryComparison::new(category, *current_value, *other_value, threshold); if matches!(threshold_failed, Some(false)) && comparison.has_failed_threshold() { let _ = threshold_failed.insert(true); } - comparison - }, - ) - .fold(initialize_report(), |mut report, comparison| { - // Add each comparison to the report. - add_comparison_to_report(&mut report, comparison); - report - }); + add_comparison_to_report(&mut report, comparison); + } + (Some(current_value), None) => { + mark_threshold_failed_on_shape_change(&mut threshold_failed); + add_added_benchmark_to_report(&mut report, category, *current_value); + } + (None, Some(other_value)) => { + mark_threshold_failed_on_shape_change(&mut threshold_failed); + add_removed_benchmark_to_report(&mut report, category, *other_value); + } + (None, None) => unreachable!("category is sourced from one of the maps"), + } + } // Finish the report. add_report_conclusion(&mut report, threshold, threshold_failed); @@ -137,6 +146,24 @@ fn add_comparison_to_report(report: &mut String, comparison: BenchCategoryCompar )); } +fn add_added_benchmark_to_report(report: &mut String, category: BenchCategory, current: u64) { + report.push_str(&format!( + "| {category} | {current} | - | - | - | 🆕 New benchmark |\n", + )); +} + +fn add_removed_benchmark_to_report(report: &mut String, category: BenchCategory, other: u64) { + report.push_str(&format!( + "| {category} | - | {other} | - | - | ⚠️ Missing from current |\n", + )); +} + +fn mark_threshold_failed_on_shape_change(threshold_failed: &mut Option) { + if matches!(threshold_failed, Some(false)) { + let _ = threshold_failed.insert(true); + } +} + fn add_report_conclusion( report: &mut String, threshold: Option, @@ -156,3 +183,81 @@ fn add_report_conclusion( } } } + +#[cfg(test)] +mod tests { + use super::*; + use benchmarks::{BenchDataSerde, ComputeBenchDataSerde, StorageStressDataSerde}; + use std::collections::BTreeMap; + + #[test] + fn report_includes_benchmarks_missing_from_baseline() { + let dir = tempfile::tempdir().unwrap(); + let current_path = dir.path().join("current.json"); + let baseline_path = dir.path().join("baseline.json"); + let output_path = dir.path().join("comparison.md"); + let storage_key = "sails_static_balance_read_existing_1024".to_owned(); + + let current = BenchDataSerde { + compute: ComputeBenchDataSerde { median: 1 }, + storage: StorageStressDataSerde(BTreeMap::from([(storage_key.clone(), 810_912_211)])), + ..Default::default() + }; + let baseline = BenchDataSerde { + compute: ComputeBenchDataSerde { median: 1 }, + ..Default::default() + }; + + fs::write( + ¤t_path, + serde_json::to_string_pretty(¤t).unwrap(), + ) + .unwrap(); + fs::write( + &baseline_path, + serde_json::to_string_pretty(&baseline).unwrap(), + ) + .unwrap(); + + analyze_benches(current_path, baseline_path, Some(output_path.clone()), None).unwrap(); + + let report = fs::read_to_string(output_path).unwrap(); + assert!(report.contains(&format!("storage_{storage_key}"))); + assert!(report.contains("New benchmark")); + } + + #[test] + fn threshold_mode_fails_when_categories_are_missing_from_baseline() { + let dir = tempfile::tempdir().unwrap(); + let current_path = dir.path().join("current.json"); + let baseline_path = dir.path().join("baseline.json"); + let storage_key = "sails_static_balance_read_existing_1024".to_owned(); + + let current = BenchDataSerde { + compute: ComputeBenchDataSerde { median: 1 }, + storage: StorageStressDataSerde(BTreeMap::from([(storage_key, 810_912_211)])), + ..Default::default() + }; + let baseline = BenchDataSerde { + compute: ComputeBenchDataSerde { median: 1 }, + ..Default::default() + }; + + fs::write( + ¤t_path, + serde_json::to_string_pretty(¤t).unwrap(), + ) + .unwrap(); + fs::write( + &baseline_path, + serde_json::to_string_pretty(&baseline).unwrap(), + ) + .unwrap(); + + let error = analyze_benches(current_path, baseline_path, None, Some(1.0)).unwrap_err(); + assert_eq!( + error.to_string(), + "Benchmark contains tests failing the threshold." + ); + } +} diff --git a/benchmarks/src/clients.rs b/benchmarks/src/clients.rs index 0d80a862f..6b46e4112 100644 --- a/benchmarks/src/clients.rs +++ b/benchmarks/src/clients.rs @@ -12,3 +12,8 @@ pub(crate) mod counter_bench_client { #![allow(unused)] include!("counter_bench_program.rs"); } + +pub(crate) mod storage_stress_client { + #![allow(unused)] + include!("storage_stress_program.rs"); +} diff --git a/benchmarks/src/entities.rs b/benchmarks/src/entities.rs index 76bf8328f..02b8a1363 100644 --- a/benchmarks/src/entities.rs +++ b/benchmarks/src/entities.rs @@ -27,6 +27,9 @@ impl BenchData { for (key, value) in data.message_stack.0 { map.insert(BenchCategory::MessageStack(key), value); } + for (key, value) in data.storage.0 { + map.insert(BenchCategory::Storage(key), value); + } Ok(Self(map)) } @@ -65,6 +68,21 @@ impl BenchData { self.0.insert(BenchCategory::MessageStack(limit), value); } + /// Update storage benchmark category value. + pub fn update_storage_bench(&mut self, key: String, value: u64) { + self.0.insert(BenchCategory::Storage(key), value); + } + + /// Replace all storage benchmark values. + pub fn replace_storage_benches(&mut self, values: BTreeMap) { + self.0 + .retain(|key, _| !matches!(key, BenchCategory::Storage(_))); + + for (key, value) in values { + self.update_storage_bench(key, value); + } + } + /// Convert the benchmark data into a JSON string. pub fn into_json_string(self) -> Result { let mut bench_data = BenchDataSerde::default(); @@ -82,6 +100,9 @@ impl BenchData { BenchCategory::MessageStack(limit) => { bench_data.message_stack.0.insert(limit, value); } + BenchCategory::Storage(key) => { + bench_data.storage.0.insert(key, value); + } } } @@ -116,6 +137,8 @@ pub struct BenchDataSerde { pub redirect: RedirectBenchDataSerde, #[serde(default)] pub message_stack: MessageStackDataSerde, + #[serde(default)] + pub storage: StorageStressDataSerde, } /// Compute benchmark data stored in the benchmarks file. @@ -153,6 +176,10 @@ pub struct CounterBenchDataSerde { #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct MessageStackDataSerde(pub BTreeMap); +/// Storage benchmark data stored in the benchmarks file. +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] +pub struct StorageStressDataSerde(pub BTreeMap); + /// Benchmark category that can be read (written) from (to) the benchmarks file. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum BenchCategory { @@ -163,6 +190,7 @@ pub enum BenchCategory { CrossProgram, Redirect, MessageStack(u32), + Storage(String), } impl Display for BenchCategory { @@ -175,6 +203,7 @@ impl Display for BenchCategory { BenchCategory::CrossProgram => write!(f, "cross_program"), BenchCategory::Redirect => write!(f, "redirect"), BenchCategory::MessageStack(limit) => write!(f, "stack_{limit}"), + BenchCategory::Storage(key) => write!(f, "storage_{key}"), } } } diff --git a/benchmarks/src/lib.rs b/benchmarks/src/lib.rs index 281a49d6d..1d72c3c81 100644 --- a/benchmarks/src/lib.rs +++ b/benchmarks/src/lib.rs @@ -17,7 +17,7 @@ use anyhow::{Context, Result}; pub use entities::{ AllocBenchDataSerde, BenchCategory, BenchCategoryComparison, BenchCategoryComparisonReport, BenchData, BenchDataSerde, ComputeBenchDataSerde, CounterBenchDataSerde, - CrossProgramBenchDataSerde, RedirectBenchDataSerde, + CrossProgramBenchDataSerde, RedirectBenchDataSerde, StorageStressDataSerde, }; pub use file::BenchDataFile; use std::{ @@ -59,9 +59,10 @@ mod tests { use super::*; use crate::entities::{ BenchDataSerde, ComputeBenchDataSerde, CounterBenchDataSerde, CrossProgramBenchDataSerde, - RedirectBenchDataSerde, + RedirectBenchDataSerde, StorageStressDataSerde, }; use std::{ + collections::BTreeMap, io::{Read, Seek, SeekFrom, Write}, thread, }; @@ -80,6 +81,10 @@ mod tests { cross_program: CrossProgramBenchDataSerde { median: 42 }, redirect: RedirectBenchDataSerde { median: 4242 }, message_stack: Default::default(), + storage: StorageStressDataSerde(BTreeMap::from([( + "sails_static_balance_prepare_1024".to_owned(), + 777, + )])), }; // Create a temporary file. @@ -142,6 +147,10 @@ mod tests { cross_program: CrossProgramBenchDataSerde { median: 0 }, redirect: RedirectBenchDataSerde { median: 4343 }, message_stack: Default::default(), + storage: StorageStressDataSerde(BTreeMap::from([( + "sails_static_balance_prepare_1024".to_owned(), + 777, + )])), }, ) } diff --git a/benchmarks/src/storage_stress_program.rs b/benchmarks/src/storage_stress_program.rs new file mode 100644 index 000000000..67eb4e383 --- /dev/null +++ b/benchmarks/src/storage_stress_program.rs @@ -0,0 +1,139 @@ +// Code generated by sails-client-gen-v2. DO NOT EDIT. +#[allow(unused_imports)] +use sails_rs::{client::*, collections::*, prelude::*}; +pub struct StorageStressProgram; + +impl StorageStressProgram { + pub const ROUTE_ID_STORAGE_STRESS: u8 = 1; +} + +impl sails_rs::client::Program for StorageStressProgram {} + +pub trait StorageStress { + type Env: sails_rs::client::GearEnv; + fn storage_stress( + &self, + ) -> sails_rs::client::Service; +} + +impl StorageStress + for sails_rs::client::Actor +{ + type Env = E; + fn storage_stress( + &self, + ) -> sails_rs::client::Service { + self.service(StorageStressProgram::ROUTE_ID_STORAGE_STRESS) + } +} +pub trait StorageStressCtors { + type Env: sails_rs::client::GearEnv; + fn new_for_bench( + self, + ) -> sails_rs::client::PendingCtor; +} + +impl StorageStressCtors + for sails_rs::client::Deployment +{ + type Env = E; + fn new_for_bench( + self, + ) -> sails_rs::client::PendingCtor { + self.pending_ctor(()) + } +} + +pub mod io { + use super::*; + sails_rs::io_struct_impl!(NewForBench () -> (), 0); +} + +pub mod storage_stress { + use super::*; + + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub enum StorageBackend { + HashMap, + Fixed, + RawStatic, + SailsFixed, + SailsStatic, + } + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub struct StorageBenchResult { + pub value: U256, + pub len: u32, + pub existed: bool, + } + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub enum StorageMap { + Balance, + Allowance, + } + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub enum StorageOp { + InsertFresh, + UpdateExisting, + ReadExisting, + ReadMissing, + Remove, + } + + pub trait StorageStress { + type Env: sails_rs::client::GearEnv; + fn bench( + &mut self, + backend: StorageBackend, + map: StorageMap, + op: StorageOp, + seed: u32, + ) -> sails_rs::client::PendingCall; + fn prepare( + &mut self, + backend: StorageBackend, + map: StorageMap, + len: u32, + ) -> sails_rs::client::PendingCall; + } + + pub struct StorageStressImpl; + + impl sails_rs::client::Identifiable for StorageStressImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([102, 51, 57, 17, 133, 13, 155, 16]); + } + + impl StorageStress + for sails_rs::client::Service + { + type Env = E; + fn bench( + &mut self, + backend: StorageBackend, + map: StorageMap, + op: StorageOp, + seed: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, map, op, seed)) + } + fn prepare( + &mut self, + backend: StorageBackend, + map: StorageMap, + len: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, map, len)) + } + } + + pub mod io { + use super::*; + sails_rs::io_struct_impl!(Bench (backend: super::StorageBackend, map: super::StorageMap, op: super::StorageOp, seed: u32) -> super::StorageBenchResult, 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(Prepare (backend: super::StorageBackend, map: super::StorageMap, len: u32) -> super::StorageBenchResult, 1, ::INTERFACE_ID); + } +} diff --git a/benchmarks/storage-stress/Cargo.toml b/benchmarks/storage-stress/Cargo.toml new file mode 100644 index 000000000..5b4c7b965 --- /dev/null +++ b/benchmarks/storage-stress/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "storage-stress" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +sails-rs.workspace = true +sails-storage = { workspace = true, features = ["gear"] } + +[build-dependencies] +sails-rs = { workspace = true, features = ["wasm-builder"] } diff --git a/benchmarks/storage-stress/build.rs b/benchmarks/storage-stress/build.rs new file mode 100644 index 000000000..e7b8e4c07 --- /dev/null +++ b/benchmarks/storage-stress/build.rs @@ -0,0 +1,9 @@ +fn main() { + const FIXED_CAPACITY: usize = 2048; + + let layout = sails_rs::build::StaticMemoryLayout::new(1024) + .reserve_table::<32, 32>("sails_static_balances", FIXED_CAPACITY) + .reserve_table::<64, 32>("sails_static_allowances", FIXED_CAPACITY); + + sails_rs::build::build_wasm_with_static_memory(layout); +} diff --git a/benchmarks/storage-stress/src/lib.rs b/benchmarks/storage-stress/src/lib.rs new file mode 100644 index 000000000..346e53e83 --- /dev/null +++ b/benchmarks/storage-stress/src/lib.rs @@ -0,0 +1,1227 @@ +#![no_std] + +use sails_rs::{cell::RefCell, collections::HashMap, prelude::*}; +use sails_storage::gear::{ + FixedAllowanceMap as SailsFixedAllowanceMap, FixedBalanceMap as SailsFixedBalanceMap, + StaticAllowanceTable as SailsStaticAllowanceTable, + StaticBalanceTable as SailsStaticBalanceTable, +}; + +mod static_storage { + include!(concat!(env!("OUT_DIR"), "/sails_static_storage.rs")); +} + +const FIXED_CAPACITY: usize = 2048; + +pub const STATIC_MEMORY_END_PAGE: u32 = static_storage::STATIC_MEMORY_END_PAGE; + +#[sails_type] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StorageBackend { + HashMap, + Fixed, + RawStatic, + SailsFixed, + SailsStatic, +} + +#[sails_type] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StorageMap { + Balance, + Allowance, +} + +#[sails_type] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StorageOp { + InsertFresh, + UpdateExisting, + ReadExisting, + ReadMissing, + Remove, +} + +#[sails_type] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct StorageBenchResult { + pub value: U256, + pub len: u32, + pub existed: bool, +} + +struct StorageStressService<'a> { + state: &'a RefCell, +} + +impl<'a> StorageStressService<'a> { + fn new(state: &'a RefCell) -> Self { + Self { state } + } +} + +#[sails_rs::service] +impl StorageStressService<'_> { + #[export] + pub fn prepare( + &mut self, + backend: StorageBackend, + map: StorageMap, + len: u32, + ) -> StorageBenchResult { + self.state.borrow_mut().prepare(backend, map, len) + } + + #[export] + pub fn bench( + &mut self, + backend: StorageBackend, + map: StorageMap, + op: StorageOp, + seed: u32, + ) -> StorageBenchResult { + self.state.borrow_mut().bench(backend, map, op, seed) + } +} + +pub struct StorageStressProgram { + state: RefCell, +} + +#[sails_rs::program] +impl StorageStressProgram { + pub fn new_for_bench() -> Self { + Self { + state: RefCell::new(StorageStressState::new()), + } + } + + pub fn storage_stress(&self) -> StorageStressService<'_> { + StorageStressService::new(&self.state) + } +} + +struct StorageStressState { + storage: ActiveStorage, +} + +enum ActiveStorage { + Empty, + HashBalances(HashMap), + HashAllowances(HashMap<(ActorId, ActorId), U256>), + FixedBalances(FixedBalanceMap), + FixedAllowances(FixedAllowanceMap), + RawBalances(RawStaticBalanceMap), + RawAllowances(RawStaticAllowanceMap), + SailsFixedBalances(SailsFixedBalanceMap), + SailsFixedAllowances(SailsFixedAllowanceMap), + SailsStaticBalances(SailsStaticBalanceMap), + SailsStaticAllowances(SailsStaticAllowanceMap), +} + +impl StorageStressState { + fn new() -> Self { + Self { + storage: ActiveStorage::Empty, + } + } + + fn prepare( + &mut self, + backend: StorageBackend, + map: StorageMap, + len: u32, + ) -> StorageBenchResult { + assert!((len as usize) < FIXED_CAPACITY); + self.clear(backend, map); + + for seed in 1..=len { + self.insert(backend, map, seed, value_for_seed(seed)); + } + + StorageBenchResult { + value: value_for_seed(len), + len, + existed: false, + } + } + + fn bench( + &mut self, + backend: StorageBackend, + map: StorageMap, + op: StorageOp, + seed: u32, + ) -> StorageBenchResult { + match op { + StorageOp::InsertFresh => { + let value = value_for_seed(seed); + let existed = self.insert(backend, map, seed, value).is_some(); + StorageBenchResult { + value, + len: self.len(backend, map), + existed, + } + } + StorageOp::UpdateExisting => { + let value = updated_value_for_seed(seed); + let existed = self.insert(backend, map, seed, value).is_some(); + StorageBenchResult { + value, + len: self.len(backend, map), + existed, + } + } + StorageOp::ReadExisting | StorageOp::ReadMissing => { + let value = self.get(backend, map, seed).unwrap_or_else(U256::zero); + StorageBenchResult { + value, + len: self.len(backend, map), + existed: !value.is_zero(), + } + } + StorageOp::Remove => { + let value = self.remove(backend, map, seed).unwrap_or_else(U256::zero); + StorageBenchResult { + value, + len: self.len(backend, map), + existed: !value.is_zero(), + } + } + } + } + + fn clear(&mut self, backend: StorageBackend, map: StorageMap) { + self.storage = match (backend, map) { + (StorageBackend::HashMap, StorageMap::Balance) => { + ActiveStorage::HashBalances(HashMap::new()) + } + (StorageBackend::HashMap, StorageMap::Allowance) => { + ActiveStorage::HashAllowances(HashMap::new()) + } + (StorageBackend::Fixed, StorageMap::Balance) => { + ActiveStorage::FixedBalances(FixedBalanceMap::new()) + } + (StorageBackend::Fixed, StorageMap::Allowance) => { + ActiveStorage::FixedAllowances(FixedAllowanceMap::new()) + } + (StorageBackend::RawStatic, StorageMap::Balance) => { + ActiveStorage::RawBalances(RawStaticBalanceMap::new()) + } + (StorageBackend::RawStatic, StorageMap::Allowance) => { + ActiveStorage::RawAllowances(RawStaticAllowanceMap::new()) + } + (StorageBackend::SailsFixed, StorageMap::Balance) => { + ActiveStorage::SailsFixedBalances(SailsFixedBalanceMap::new()) + } + (StorageBackend::SailsFixed, StorageMap::Allowance) => { + ActiveStorage::SailsFixedAllowances(SailsFixedAllowanceMap::new()) + } + (StorageBackend::SailsStatic, StorageMap::Balance) => { + ActiveStorage::SailsStaticBalances(SailsStaticBalanceMap::new()) + } + (StorageBackend::SailsStatic, StorageMap::Allowance) => { + ActiveStorage::SailsStaticAllowances(SailsStaticAllowanceMap::new()) + } + }; + } + + fn len(&self, backend: StorageBackend, map: StorageMap) -> u32 { + match (backend, map, &self.storage) { + (StorageBackend::HashMap, StorageMap::Balance, ActiveStorage::HashBalances(map)) => { + map.len() as u32 + } + ( + StorageBackend::HashMap, + StorageMap::Allowance, + ActiveStorage::HashAllowances(map), + ) => map.len() as u32, + (StorageBackend::Fixed, StorageMap::Balance, ActiveStorage::FixedBalances(map)) => { + map.len() + } + (StorageBackend::Fixed, StorageMap::Allowance, ActiveStorage::FixedAllowances(map)) => { + map.len() + } + (StorageBackend::RawStatic, StorageMap::Balance, ActiveStorage::RawBalances(map)) => { + map.len() + } + ( + StorageBackend::RawStatic, + StorageMap::Allowance, + ActiveStorage::RawAllowances(map), + ) => map.len(), + ( + StorageBackend::SailsFixed, + StorageMap::Balance, + ActiveStorage::SailsFixedBalances(map), + ) => map.len() as u32, + ( + StorageBackend::SailsFixed, + StorageMap::Allowance, + ActiveStorage::SailsFixedAllowances(map), + ) => map.len() as u32, + ( + StorageBackend::SailsStatic, + StorageMap::Balance, + ActiveStorage::SailsStaticBalances(map), + ) => map.len(), + ( + StorageBackend::SailsStatic, + StorageMap::Allowance, + ActiveStorage::SailsStaticAllowances(map), + ) => map.len(), + _ => 0, + } + } + + fn insert( + &mut self, + backend: StorageBackend, + map: StorageMap, + seed: u32, + value: U256, + ) -> Option { + match (backend, map, &mut self.storage) { + (StorageBackend::HashMap, StorageMap::Balance, ActiveStorage::HashBalances(map)) => { + map.insert(actor_for_seed(seed), value) + } + ( + StorageBackend::HashMap, + StorageMap::Allowance, + ActiveStorage::HashAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.insert((owner, spender), value) + } + (StorageBackend::Fixed, StorageMap::Balance, ActiveStorage::FixedBalances(map)) => map + .try_insert(actor_for_seed(seed), value) + .expect("fixed balance map capacity exceeded"), + (StorageBackend::Fixed, StorageMap::Allowance, ActiveStorage::FixedAllowances(map)) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.try_insert(AllowanceKey { owner, spender }, value) + .expect("fixed allowance map capacity exceeded") + } + (StorageBackend::RawStatic, StorageMap::Balance, ActiveStorage::RawBalances(map)) => { + map.try_insert(actor_for_seed(seed), value) + .expect("raw static balance map capacity exceeded") + } + ( + StorageBackend::RawStatic, + StorageMap::Allowance, + ActiveStorage::RawAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.try_insert(AllowanceKey { owner, spender }, value) + .expect("raw static allowance map capacity exceeded") + } + ( + StorageBackend::SailsFixed, + StorageMap::Balance, + ActiveStorage::SailsFixedBalances(map), + ) => map + .insert_actor_u256(actor_for_seed(seed), value) + .expect("sails-storage fixed balance map failed"), + ( + StorageBackend::SailsFixed, + StorageMap::Allowance, + ActiveStorage::SailsFixedAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.insert_allowance_u256(owner, spender, value) + .expect("sails-storage fixed allowance map failed") + } + ( + StorageBackend::SailsStatic, + StorageMap::Balance, + ActiveStorage::SailsStaticBalances(map), + ) => map + .try_insert(actor_for_seed(seed), value) + .expect("sails-storage static balance map failed"), + ( + StorageBackend::SailsStatic, + StorageMap::Allowance, + ActiveStorage::SailsStaticAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.try_insert(owner, spender, value) + .expect("sails-storage static allowance map failed") + } + _ => panic!("storage backend is not prepared"), + } + } + + fn get(&self, backend: StorageBackend, map: StorageMap, seed: u32) -> Option { + match (backend, map, &self.storage) { + (StorageBackend::HashMap, StorageMap::Balance, ActiveStorage::HashBalances(map)) => { + map.get(&actor_for_seed(seed)).copied() + } + ( + StorageBackend::HashMap, + StorageMap::Allowance, + ActiveStorage::HashAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.get(&(owner, spender)).copied() + } + (StorageBackend::Fixed, StorageMap::Balance, ActiveStorage::FixedBalances(map)) => { + map.get(&actor_for_seed(seed)) + } + (StorageBackend::Fixed, StorageMap::Allowance, ActiveStorage::FixedAllowances(map)) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.get(&AllowanceKey { owner, spender }) + } + (StorageBackend::RawStatic, StorageMap::Balance, ActiveStorage::RawBalances(map)) => { + map.get(&actor_for_seed(seed)) + } + ( + StorageBackend::RawStatic, + StorageMap::Allowance, + ActiveStorage::RawAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.get(&AllowanceKey { owner, spender }) + } + ( + StorageBackend::SailsFixed, + StorageMap::Balance, + ActiveStorage::SailsFixedBalances(map), + ) => map + .get_actor_u256(&actor_for_seed(seed)) + .expect("sails-storage fixed balance map failed"), + ( + StorageBackend::SailsFixed, + StorageMap::Allowance, + ActiveStorage::SailsFixedAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.get_allowance_u256(&owner, &spender) + .expect("sails-storage fixed allowance map failed") + } + ( + StorageBackend::SailsStatic, + StorageMap::Balance, + ActiveStorage::SailsStaticBalances(map), + ) => map + .get(&actor_for_seed(seed)) + .expect("sails-storage static balance map failed"), + ( + StorageBackend::SailsStatic, + StorageMap::Allowance, + ActiveStorage::SailsStaticAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.get(&owner, &spender) + .expect("sails-storage static allowance map failed") + } + _ => None, + } + } + + fn remove(&mut self, backend: StorageBackend, map: StorageMap, seed: u32) -> Option { + match (backend, map, &mut self.storage) { + (StorageBackend::HashMap, StorageMap::Balance, ActiveStorage::HashBalances(map)) => { + map.remove(&actor_for_seed(seed)) + } + ( + StorageBackend::HashMap, + StorageMap::Allowance, + ActiveStorage::HashAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.remove(&(owner, spender)) + } + (StorageBackend::Fixed, StorageMap::Balance, ActiveStorage::FixedBalances(map)) => { + map.remove(&actor_for_seed(seed)) + } + (StorageBackend::Fixed, StorageMap::Allowance, ActiveStorage::FixedAllowances(map)) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.remove(&AllowanceKey { owner, spender }) + } + (StorageBackend::RawStatic, StorageMap::Balance, ActiveStorage::RawBalances(map)) => { + map.remove(&actor_for_seed(seed)) + } + ( + StorageBackend::RawStatic, + StorageMap::Allowance, + ActiveStorage::RawAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.remove(&AllowanceKey { owner, spender }) + } + ( + StorageBackend::SailsFixed, + StorageMap::Balance, + ActiveStorage::SailsFixedBalances(map), + ) => map + .remove_actor_u256(&actor_for_seed(seed)) + .expect("sails-storage fixed balance map failed"), + ( + StorageBackend::SailsFixed, + StorageMap::Allowance, + ActiveStorage::SailsFixedAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.remove_allowance_u256(&owner, &spender) + .expect("sails-storage fixed allowance map failed") + } + ( + StorageBackend::SailsStatic, + StorageMap::Balance, + ActiveStorage::SailsStaticBalances(map), + ) => map + .remove(&actor_for_seed(seed)) + .expect("sails-storage static balance map failed"), + ( + StorageBackend::SailsStatic, + StorageMap::Allowance, + ActiveStorage::SailsStaticAllowances(map), + ) => { + let (owner, spender) = allowance_key_for_seed(seed); + map.remove(&owner, &spender) + .expect("sails-storage static allowance map failed") + } + _ => None, + } + } +} + +#[derive(Clone, Copy)] +struct BalanceSlot { + key: Option, + value: Option, + tombstone: bool, +} + +impl BalanceSlot { + const EMPTY: Self = Self { + key: None, + value: None, + tombstone: false, + }; +} + +struct FixedBalanceMap { + slots: [BalanceSlot; CAP], + len: u32, +} + +impl FixedBalanceMap { + const fn new() -> Self { + Self { + slots: [BalanceSlot::EMPTY; CAP], + len: 0, + } + } + + fn len(&self) -> u32 { + self.len + } + + fn get(&self, key: &ActorId) -> Option { + self.find_slot(*key) + .ok() + .and_then(|idx| self.slots[idx].value) + } + + fn try_insert(&mut self, key: ActorId, value: U256) -> Result, FixedMapFull> { + match self.find_slot(key) { + Ok(idx) => Ok(self.slots[idx].value.replace(value)), + Err(idx) if idx < CAP => { + self.slots[idx] = BalanceSlot { + key: Some(key), + value: Some(value), + tombstone: false, + }; + self.len += 1; + Ok(None) + } + Err(_) => Err(FixedMapFull), + } + } + + fn remove(&mut self, key: &ActorId) -> Option { + let idx = self.find_slot(*key).ok()?; + let previous = self.slots[idx].value.take(); + self.slots[idx].key = None; + self.slots[idx].tombstone = true; + self.len -= 1; + previous + } + + fn find_slot(&self, key: ActorId) -> Result { + find_slot(CAP, hash_actor(&key), |idx| { + let slot = &self.slots[idx]; + match slot.key { + Some(existing) if existing == key => Probe::Found, + Some(_) => Probe::Occupied, + None if slot.tombstone => Probe::Tombstone, + None => Probe::Empty, + } + }) + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +struct AllowanceKey { + owner: ActorId, + spender: ActorId, +} + +#[derive(Clone, Copy)] +struct AllowanceSlot { + key: Option, + value: Option, + tombstone: bool, +} + +impl AllowanceSlot { + const EMPTY: Self = Self { + key: None, + value: None, + tombstone: false, + }; +} + +struct FixedAllowanceMap { + slots: [AllowanceSlot; CAP], + len: u32, +} + +impl FixedAllowanceMap { + const fn new() -> Self { + Self { + slots: [AllowanceSlot::EMPTY; CAP], + len: 0, + } + } + + fn len(&self) -> u32 { + self.len + } + + fn get(&self, key: &AllowanceKey) -> Option { + self.find_slot(*key) + .ok() + .and_then(|idx| self.slots[idx].value) + } + + fn try_insert(&mut self, key: AllowanceKey, value: U256) -> Result, FixedMapFull> { + match self.find_slot(key) { + Ok(idx) => Ok(self.slots[idx].value.replace(value)), + Err(idx) if idx < CAP => { + self.slots[idx] = AllowanceSlot { + key: Some(key), + value: Some(value), + tombstone: false, + }; + self.len += 1; + Ok(None) + } + Err(_) => Err(FixedMapFull), + } + } + + fn remove(&mut self, key: &AllowanceKey) -> Option { + let idx = self.find_slot(*key).ok()?; + let previous = self.slots[idx].value.take(); + self.slots[idx].key = None; + self.slots[idx].tombstone = true; + self.len -= 1; + previous + } + + fn find_slot(&self, key: AllowanceKey) -> Result { + find_slot(CAP, hash_allowance(&key), |idx| { + let slot = &self.slots[idx]; + match slot.key { + Some(existing) if existing == key => Probe::Found, + Some(_) => Probe::Occupied, + None if slot.tombstone => Probe::Tombstone, + None => Probe::Empty, + } + }) + } +} + +type RawStaticBalanceMap = RawStaticMap<32, 32, CAP>; +type RawStaticAllowanceMap = RawStaticMap<64, 32, CAP>; + +#[derive(Clone, Copy)] +struct RawStaticSlot { + key: [u8; KEY_SIZE], + value: [u8; VALUE_SIZE], +} + +impl RawStaticSlot { + const EMPTY: Self = Self { + key: [0; KEY_SIZE], + value: [0; VALUE_SIZE], + }; +} + +struct RawStaticMap { + slots: [RawStaticSlot; CAP], + len: u32, +} + +impl + RawStaticMap +{ + const fn new() -> Self { + Self { + slots: [RawStaticSlot::EMPTY; CAP], + len: 0, + } + } + + fn len(&self) -> u32 { + self.len + } + + fn get_raw_with_hash(&self, key: &[u8; KEY_SIZE], hash: usize) -> Option<[u8; VALUE_SIZE]> { + let idx = self.find_slot_with_hash(key, hash).ok()?; + let value = self.slots[idx].value; + (!is_zero(&value)).then_some(value) + } + + fn try_insert_raw( + &mut self, + key: [u8; KEY_SIZE], + value: [u8; VALUE_SIZE], + hash: usize, + ) -> Result, FixedMapFull> { + debug_assert!(!is_zero(&key)); + + if is_zero(&value) { + return Ok(self.remove_with_hash(&key, hash)); + } + + match self.find_slot_with_hash(&key, hash) { + Ok(idx) => { + let previous = self.slots[idx].value; + self.slots[idx].value = value; + if is_zero(&previous) { + self.len += 1; + Ok(None) + } else { + Ok(Some(previous)) + } + } + Err(idx) if idx < CAP => { + self.slots[idx] = RawStaticSlot { key, value }; + self.len += 1; + Ok(None) + } + Err(_) => Err(FixedMapFull), + } + } + + fn remove_with_hash(&mut self, key: &[u8; KEY_SIZE], hash: usize) -> Option<[u8; VALUE_SIZE]> { + let idx = self.find_slot_with_hash(key, hash).ok()?; + let previous = self.slots[idx].value; + if is_zero(&previous) { + None + } else { + self.slots[idx].value = [0; VALUE_SIZE]; + self.len -= 1; + Some(previous) + } + } + + fn find_slot_with_hash(&self, key: &[u8; KEY_SIZE], hash: usize) -> Result { + find_slot(CAP, hash, |idx| { + let slot = &self.slots[idx]; + if is_zero(&slot.key) { + Probe::Empty + } else if slot.key == *key { + Probe::Found + } else if is_zero(&slot.value) { + Probe::Tombstone + } else { + Probe::Occupied + } + }) + } +} + +impl RawStaticBalanceMap { + fn get(&self, key: &ActorId) -> Option { + let raw_key = raw_actor_key(*key); + self.get_raw_with_hash(&raw_key, hash_actor(key)) + .map(u256_from_raw) + } + + fn try_insert(&mut self, key: ActorId, value: U256) -> Result, FixedMapFull> { + let raw_key = raw_actor_key(key); + let raw_value = raw_u256(value); + self.try_insert_raw(raw_key, raw_value, hash_actor(&key)) + .map(|previous| previous.map(u256_from_raw)) + } + + fn remove(&mut self, key: &ActorId) -> Option { + let raw_key = raw_actor_key(*key); + self.remove_with_hash(&raw_key, hash_actor(key)) + .map(u256_from_raw) + } +} + +impl RawStaticAllowanceMap { + fn get(&self, key: &AllowanceKey) -> Option { + let raw_key = raw_allowance_key(*key); + self.get_raw_with_hash(&raw_key, hash_allowance(key)) + .map(u256_from_raw) + } + + fn try_insert(&mut self, key: AllowanceKey, value: U256) -> Result, FixedMapFull> { + let raw_key = raw_allowance_key(key); + let raw_value = raw_u256(value); + self.try_insert_raw(raw_key, raw_value, hash_allowance(&key)) + .map(|previous| previous.map(u256_from_raw)) + } + + fn remove(&mut self, key: &AllowanceKey) -> Option { + let raw_key = raw_allowance_key(*key); + self.remove_with_hash(&raw_key, hash_allowance(key)) + .map(u256_from_raw) + } +} + +struct SailsStaticBalanceMap { + #[cfg(not(target_arch = "wasm32"))] + bytes: [u8; static_storage::SAILS_STATIC_BALANCES_BYTES], + len: u32, +} + +impl SailsStaticBalanceMap { + fn new() -> Self { + let map = Self { + #[cfg(not(target_arch = "wasm32"))] + bytes: [0; static_storage::SAILS_STATIC_BALANCES_BYTES], + len: 0, + }; + #[cfg(target_arch = "wasm32")] + map.table() + .clear() + .expect("sails static balance table can be cleared"); + map + } + + fn len(&self) -> u32 { + self.len + } + + fn get(&self, key: &ActorId) -> Result, sails_storage::TableError> { + self.table().get_actor_u256(key) + } + + fn try_insert( + &mut self, + key: ActorId, + value: U256, + ) -> Result, sails_storage::TableError> { + let previous = self.table_mut().insert_actor_u256(key, value)?; + if previous.is_none() { + self.len += 1; + } + + Ok(previous) + } + + fn remove(&mut self, key: &ActorId) -> Result, sails_storage::TableError> { + let previous = self.table_mut().remove_actor_u256(key)?; + if previous.is_some() { + self.len -= 1; + } + + Ok(previous) + } + + fn table(&self) -> SailsStaticBalanceTable { + #[cfg(target_arch = "wasm32")] + let base = static_storage::SAILS_STATIC_BALANCES_BASE; + #[cfg(not(target_arch = "wasm32"))] + let base = self.bytes.as_ptr() as usize; + + unsafe { + SailsStaticBalanceTable::new(base, static_storage::SAILS_STATIC_BALANCES_SLOTS) + .expect("sails static balance layout is valid") + } + } + + fn table_mut(&mut self) -> SailsStaticBalanceTable { + #[cfg(target_arch = "wasm32")] + let base = static_storage::SAILS_STATIC_BALANCES_BASE; + #[cfg(not(target_arch = "wasm32"))] + let base = self.bytes.as_mut_ptr() as usize; + + unsafe { + SailsStaticBalanceTable::new(base, static_storage::SAILS_STATIC_BALANCES_SLOTS) + .expect("sails static balance layout is valid") + } + } +} + +struct SailsStaticAllowanceMap { + #[cfg(not(target_arch = "wasm32"))] + bytes: [u8; static_storage::SAILS_STATIC_ALLOWANCES_BYTES], + len: u32, +} + +impl SailsStaticAllowanceMap { + fn new() -> Self { + let map = Self { + #[cfg(not(target_arch = "wasm32"))] + bytes: [0; static_storage::SAILS_STATIC_ALLOWANCES_BYTES], + len: 0, + }; + #[cfg(target_arch = "wasm32")] + map.table() + .clear() + .expect("sails static allowance table can be cleared"); + map + } + + fn len(&self) -> u32 { + self.len + } + + fn get( + &self, + owner: &ActorId, + spender: &ActorId, + ) -> Result, sails_storage::TableError> { + self.table().get_allowance_u256(owner, spender) + } + + fn try_insert( + &mut self, + owner: ActorId, + spender: ActorId, + value: U256, + ) -> Result, sails_storage::TableError> { + let previous = self + .table_mut() + .insert_allowance_u256(owner, spender, value)?; + if previous.is_none() { + self.len += 1; + } + + Ok(previous) + } + + fn remove( + &mut self, + owner: &ActorId, + spender: &ActorId, + ) -> Result, sails_storage::TableError> { + let previous = self.table_mut().remove_allowance_u256(owner, spender)?; + if previous.is_some() { + self.len -= 1; + } + + Ok(previous) + } + + fn table(&self) -> SailsStaticAllowanceTable { + #[cfg(target_arch = "wasm32")] + let base = static_storage::SAILS_STATIC_ALLOWANCES_BASE; + #[cfg(not(target_arch = "wasm32"))] + let base = self.bytes.as_ptr() as usize; + + unsafe { + SailsStaticAllowanceTable::new(base, static_storage::SAILS_STATIC_ALLOWANCES_SLOTS) + .expect("sails static allowance layout is valid") + } + } + + fn table_mut(&mut self) -> SailsStaticAllowanceTable { + #[cfg(target_arch = "wasm32")] + let base = static_storage::SAILS_STATIC_ALLOWANCES_BASE; + #[cfg(not(target_arch = "wasm32"))] + let base = self.bytes.as_mut_ptr() as usize; + + unsafe { + SailsStaticAllowanceTable::new(base, static_storage::SAILS_STATIC_ALLOWANCES_SLOTS) + .expect("sails static allowance layout is valid") + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct FixedMapFull; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Probe { + Found, + Occupied, + Tombstone, + Empty, +} + +fn find_slot(cap: usize, hash: usize, probe: impl Fn(usize) -> Probe) -> Result { + debug_assert!(cap > 0); + + let mut first_tombstone = None; + let mut idx = hash % cap; + + for _ in 0..cap { + match probe(idx) { + Probe::Found => return Ok(idx), + Probe::Occupied => {} + Probe::Tombstone => { + if first_tombstone.is_none() { + first_tombstone = Some(idx); + } + } + Probe::Empty => return Err(first_tombstone.unwrap_or(idx)), + } + + idx = (idx + 1) % cap; + } + + Err(first_tombstone.unwrap_or(cap)) +} + +fn actor_for_seed(seed: u32) -> ActorId { + ActorId::from(seed as u64 + 1) +} + +fn allowance_key_for_seed(seed: u32) -> (ActorId, ActorId) { + ( + ActorId::from((seed as u64).wrapping_mul(2).wrapping_add(1)), + ActorId::from((seed as u64).wrapping_mul(2).wrapping_add(2)), + ) +} + +fn value_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 1) +} + +fn updated_value_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 1_000_001) +} + +fn hash_actor(actor: &ActorId) -> usize { + let bytes: &[u8] = actor.as_ref(); + hash_bytes(bytes).wrapping_mul(0x9e37_79b9) +} + +fn hash_bytes(bytes: &[u8]) -> usize { + let mut hash = 0x811c_9dc5u32; + + for byte in bytes { + hash ^= *byte as u32; + hash = hash.wrapping_mul(0x0100_0193); + } + + hash as usize +} + +fn hash_allowance(key: &AllowanceKey) -> usize { + hash_actor(&key.owner).rotate_left(13).wrapping_mul(31) ^ hash_actor(&key.spender) +} + +fn raw_actor_key(actor: ActorId) -> [u8; 32] { + let mut key = [0u8; 32]; + key.copy_from_slice(actor.as_ref()); + key +} + +fn raw_allowance_key(key: AllowanceKey) -> [u8; 64] { + let mut raw = [0u8; 64]; + raw[..32].copy_from_slice(key.owner.as_ref()); + raw[32..].copy_from_slice(key.spender.as_ref()); + raw +} + +fn raw_u256(value: U256) -> [u8; 32] { + let mut bytes = [0u8; 32]; + value.to_little_endian(&mut bytes); + bytes +} + +fn u256_from_raw(bytes: [u8; 32]) -> U256 { + U256::from_little_endian(&bytes) +} + +fn is_zero(bytes: &[u8]) -> bool { + bytes.iter().all(|byte| *byte == 0) +} + +#[cfg(test)] +mod tests { + extern crate std; + + use super::*; + use std::thread; + + #[test] + fn fixed_balance_map_updates_existing_key() { + let mut map = FixedBalanceMap::<8>::new(); + let key = actor_for_seed(1); + + assert_eq!(map.try_insert(key, value_for_seed(1)), Ok(None)); + assert_eq!( + map.try_insert(key, updated_value_for_seed(1)), + Ok(Some(value_for_seed(1))) + ); + assert_eq!(map.get(&key), Some(updated_value_for_seed(1))); + assert_eq!(map.len(), 1); + } + + #[test] + fn fixed_balance_map_reuses_tombstone() { + let mut map = FixedBalanceMap::<1>::new(); + let first = actor_for_seed(1); + let second = actor_for_seed(2); + + assert_eq!(map.try_insert(first, value_for_seed(1)), Ok(None)); + assert_eq!(map.remove(&first), Some(value_for_seed(1))); + assert_eq!(map.try_insert(second, value_for_seed(2)), Ok(None)); + assert_eq!(map.get(&second), Some(value_for_seed(2))); + assert_eq!(map.len(), 1); + } + + #[test] + fn fixed_balance_map_reports_full_table() { + let mut map = FixedBalanceMap::<1>::new(); + + assert_eq!( + map.try_insert(actor_for_seed(1), value_for_seed(1)), + Ok(None) + ); + assert_eq!( + map.try_insert(actor_for_seed(2), value_for_seed(2)), + Err(FixedMapFull) + ); + } + + #[test] + fn fixed_allowance_map_removes_pair_key() { + let mut map = FixedAllowanceMap::<8>::new(); + let (owner, spender) = allowance_key_for_seed(1); + let key = AllowanceKey { owner, spender }; + + assert_eq!(map.try_insert(key, value_for_seed(1)), Ok(None)); + assert_eq!(map.remove(&key), Some(value_for_seed(1))); + assert_eq!(map.get(&key), None); + assert_eq!(map.len(), 0); + } + + #[test] + fn raw_static_balance_map_updates_existing_key() { + let mut map = RawStaticBalanceMap::<8>::new(); + let key = actor_for_seed(1); + + assert_eq!(map.try_insert(key, value_for_seed(1)), Ok(None)); + assert_eq!( + map.try_insert(key, updated_value_for_seed(1)), + Ok(Some(value_for_seed(1))) + ); + assert_eq!(map.get(&key), Some(updated_value_for_seed(1))); + assert_eq!(map.len(), 1); + } + + #[test] + fn raw_static_balance_map_reuses_tombstone() { + let mut map = RawStaticBalanceMap::<1>::new(); + let first = actor_for_seed(1); + let second = actor_for_seed(2); + + assert_eq!(map.try_insert(first, value_for_seed(1)), Ok(None)); + assert_eq!(map.remove(&first), Some(value_for_seed(1))); + assert_eq!(map.try_insert(second, value_for_seed(2)), Ok(None)); + assert_eq!(map.get(&second), Some(value_for_seed(2))); + assert_eq!(map.len(), 1); + } + + #[test] + fn raw_static_balance_map_zero_value_removes_existing_key() { + let mut map = RawStaticBalanceMap::<8>::new(); + let key = actor_for_seed(1); + + assert_eq!(map.try_insert(key, value_for_seed(1)), Ok(None)); + assert_eq!( + map.try_insert(key, U256::zero()), + Ok(Some(value_for_seed(1))) + ); + assert_eq!(map.get(&key), None); + assert_eq!(map.len(), 0); + + assert_eq!(map.try_insert(key, value_for_seed(2)), Ok(None)); + assert_eq!(map.get(&key), Some(value_for_seed(2))); + assert_eq!(map.len(), 1); + } + + #[test] + fn raw_static_balance_map_reports_full_table() { + let mut map = RawStaticBalanceMap::<1>::new(); + + assert_eq!( + map.try_insert(actor_for_seed(1), value_for_seed(1)), + Ok(None) + ); + assert_eq!( + map.try_insert(actor_for_seed(2), value_for_seed(2)), + Err(FixedMapFull) + ); + } + + #[test] + fn raw_static_allowance_map_removes_pair_key() { + let mut map = RawStaticAllowanceMap::<8>::new(); + let (owner, spender) = allowance_key_for_seed(1); + let key = AllowanceKey { owner, spender }; + + assert_eq!(map.try_insert(key, value_for_seed(1)), Ok(None)); + assert_eq!(map.remove(&key), Some(value_for_seed(1))); + assert_eq!(map.get(&key), None); + assert_eq!(map.len(), 0); + } + + #[test] + fn raw_static_state_matches_fixed_for_representative_ops() { + thread::Builder::new() + .stack_size(32 * 1024 * 1024) + .spawn(|| { + for map in [StorageMap::Balance, StorageMap::Allowance] { + for op in [ + StorageOp::InsertFresh, + StorageOp::UpdateExisting, + StorageOp::ReadExisting, + StorageOp::ReadMissing, + StorageOp::Remove, + ] { + let seed = match op { + StorageOp::InsertFresh | StorageOp::ReadMissing => 10_016, + StorageOp::UpdateExisting + | StorageOp::ReadExisting + | StorageOp::Remove => 4, + }; + + let fixed_result = { + let mut state = StorageStressState::new(); + assert_eq!(state.prepare(StorageBackend::Fixed, map, 16).len, 16); + state.bench(StorageBackend::Fixed, map, op, seed) + }; + for backend in [ + StorageBackend::RawStatic, + StorageBackend::SailsFixed, + StorageBackend::SailsStatic, + ] { + let candidate_result = { + let mut state = StorageStressState::new(); + assert_eq!(state.prepare(backend, map, 16).len, 16); + state.bench(backend, map, op, seed) + }; + + assert_eq!(fixed_result, candidate_result); + } + } + } + }) + .unwrap() + .join() + .unwrap(); + } +} diff --git a/docs/storage-static-maps-local-notes.md b/docs/storage-static-maps-local-notes.md new file mode 100644 index 000000000..aeecfc53e --- /dev/null +++ b/docs/storage-static-maps-local-notes.md @@ -0,0 +1,136 @@ +# Sails Storage Static Maps - Local Notes + +This is a local working note for the storage optimization experiment. It is not +published documentation yet. + +## Goal + +Explore whether Sails can expose allocator-light storage primitives that make +canonical contracts cheaper, using the `vara-ft-wat` and `awesome-sails` static +VFT examples as the performance reference. + +## What Was Implemented + +- Added a new `sails-storage` crate under `rs/storage`. +- Added `FixedOpenAddressMap` for bounded, no-allocator map storage. +- Added `StaticOpenAddressTable` for caller-owned static memory regions. +- Added a narrow `sails_rs::build` static-memory build API that emits + `OUT_DIR/sails_static_storage.rs` and patches the Gear imported memory minimum + through the existing `gear_wasm_builder::PreProcessor` hook. +- Added Gear-oriented helpers behind the `gear` feature: + - fixed balance and allowance map aliases + - static balance and allowance table aliases + - `ActorId` and `U256` key/value conversion helpers +- Added `benchmarks/storage-stress`, a benchmark-only program comparing: + - `HashMap` + - local fixed open-addressed map + - local raw static map + - `sails-storage` fixed map + - `sails-storage` static table backed by generated static memory on WASM +- Wired the storage benchmark into the existing benchmark crate, generated IDL + and client, and `bench_data.json`. + +## Important Correction + +The first version had one misleading benchmark label: `SailsStorage` measured the +fixed-map helper aliases, not the static table. This was split into two explicit +backends: + +- `SailsFixed` measures `FixedBalanceMap` / `FixedAllowanceMap`. +- `SailsStatic` measures `StaticBalanceTable` / `StaticAllowanceTable`. + +The benchmark writer now replaces the whole storage section so old storage keys +do not linger after backend renames. + +## Current 1024-Entry Results + +Current storage medians in `benchmarks/bench_data.json` show: + +| Operation | HashMap | SailsFixed | SailsStatic | +| --- | ---: | ---: | ---: | +| balance insert | 1,137,061,805 | 1,078,413,479 | 924,292,178 | +| balance read | 1,067,742,829 | 850,699,257 | 810,912,211 | +| balance update | 1,142,477,964 | 965,815,430 | 926,264,565 | +| allowance insert | 1,293,302,336 | 1,083,473,052 | 930,873,664 | +| allowance read | 1,221,407,908 | 855,438,899 | 815,651,853 | +| allowance update | 1,300,063,555 | 972,138,658 | 932,579,326 | + +Compared with `HashMap`, the bounded forms are about 5-33 percent cheaper for +these generic Sails storage operations. `SailsStatic` is the stronger hot-path +candidate in the refreshed run: at 1024 entries it is about 19-33 percent cheaper +than `HashMap`, and about 5-14 percent cheaper than `SailsFixed`. + +The benchmark data now also records preparation cost under `*_prepare_` +keys, so setup/priming gas is visible separately from the operation gas. That is +important for deciding whether a lean path is worth it for repeated hot-state +access rather than one-off state construction. + +## Interpretation + +`FixedOpenAddressMap` is the safe bounded-map tier: + +- no raw memory layout in contract code +- no static page sizing +- easier unit testing +- useful reference implementation for probing and key/value semantics +- good for small bounded state where 16-28 percent over `HashMap` is enough + +`StaticOpenAddressTable` is the VFT-style performance tier: + +- fixed capacity and explicit storage layout +- intended for hot canonical tables like balances and allowances +- uses explicit static memory sizing from `build.rs` +- best chance to reproduce the WAT-style win when integrated as real lazy-page + storage, not just as another Sails service benchmark backend + +The v1 build API is deliberately narrow: + +```rust +let layout = sails_rs::build::StaticMemoryLayout::new(1024) + .reserve_table::<32, 32>("sails_static_balances", BALANCE_SLOTS) + .reserve_table::<64, 32>("sails_static_allowances", ALLOWANCE_SLOTS); + +sails_rs::build::build_wasm_with_static_memory(layout); +``` + +It keeps `sails_rs::build_wasm()` unchanged for normal programs, emits generated +constants before Gear's recursive no-build path can return, rejects layouts that +start inside the original imported static pages, and reuses `sails-storage` table +sizing instead of duplicating the slot layout in `sails-rs`. + +## Related Awesome-Sails Work + +The `awesome-sails` test app now has a static VFT example that uses the static +table primitive. To keep that repo buildable without a sibling checkout, a local +vendored copy of `sails-storage` was added under `awesome-sails/crates` and +marked `publish = false`. + +That vendored copy is a temporary bridge. The production path should consume a +released `sails-storage` crate from this Sails repo. + +## Verification Run + +The following checks passed after the split: + +- `cargo test -p sails-storage` +- `cargo test -p sails-storage --features gear` +- `cargo test -p storage-stress` +- `cargo test -p benchmarks test_data_not_overwritten` +- `cargo test --release --manifest-path=benchmarks/Cargo.toml storage_stress_bench -- --nocapture` +- `git diff --check` + +For `awesome-sails`, the local verification was: + +- `cargo test -p sails-storage --features gear` +- `cargo test -p awesome-sails-test-app static_vft -- --nocapture` +- `cargo test -p awesome-sails-utils` + +## Next Work + +1. Treat `rs/storage` as the canonical source for the storage primitive. +2. Replace the temporary `awesome-sails` vendored copy with a published or pinned + dependency once `sails-storage` is ready to publish. +3. Build a canonical VFT storage implementation around `StaticOpenAddressTable`, + not `HashMap`. +4. Document capacity planning, migration limits, and layout safety for static + maps before presenting this as production-ready. diff --git a/rs/Cargo.toml b/rs/Cargo.toml index c9f5d2104..91ed7d8f1 100644 --- a/rs/Cargo.toml +++ b/rs/Cargo.toml @@ -34,10 +34,13 @@ sails-idl-gen = { workspace = true, optional = true } sails-idl-meta.workspace = true sails-macros.workspace = true sails-reflect-hash.workspace = true +sails-storage = { workspace = true, optional = true } sails-type-registry = { workspace = true, features = ["derive", "gprimitives"] } spin.workspace = true thiserror.workspace = true tokio-stream = { workspace = true, optional = true } +wasm-encoder = { workspace = true, features = ["wasmparser"], optional = true } +wasmparser = { workspace = true, optional = true } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] gear-core = { workspace = true, optional = true } # Needed for compilation of gstd in native mode @@ -46,6 +49,7 @@ gtest = { workspace = true, optional = true } log = { workspace = true, optional = true } [dev-dependencies] +tempfile.workspace = true tokio = { workspace = true, features = ["rt", "macros"] } [features] @@ -72,4 +76,4 @@ client-builder = ["std", "idl-gen", "idl-embed", "dep:sails-client-gen-v2", "dep idl-embed = ["dep:sails-idl-embed"] mockall = ["std", "dep:mockall"] std = ["futures/std", "sails-idl-gen?/std", "gear-core?/std"] -wasm-builder = ["dep:gwasm-builder"] +wasm-builder = ["dep:gwasm-builder", "dep:sails-storage", "dep:wasm-encoder", "dep:wasmparser"] diff --git a/rs/src/build.rs b/rs/src/build.rs new file mode 100644 index 000000000..bfea48148 --- /dev/null +++ b/rs/src/build.rs @@ -0,0 +1,655 @@ +//! Build-script helpers for Sails programs. + +pub use gwasm_builder::build as build_wasm; + +use gwasm_builder::{PreProcessor, PreProcessorTarget, WasmBuilder}; +use sails_storage::{StaticLayout, StaticOpenAddressTable}; +use std::{ + boxed::Box, + collections::BTreeSet, + env, eprintln, + fmt::Write as _, + fs, + path::{Path, PathBuf}, + process, + string::{String, ToString}, + vec, + vec::Vec, +}; + +const GENERATED_STATIC_STORAGE: &str = "sails_static_storage.rs"; +const WASM_PAGE_SIZE: usize = 64 * 1024; + +/// Maximum static WASM pages accepted by Gear. +pub const MAX_STATIC_MEMORY_PAGES: u32 = 32_768; + +/// Static memory layout reserved by a Sails build script. +#[derive(Clone, Debug)] +pub struct StaticMemoryLayout { + start_page: u32, + tables: Vec, +} + +#[derive(Clone, Debug)] +struct StaticTable { + name: String, + slots: usize, + slot_size: usize, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +struct ResolvedStaticMemoryLayout { + start_page: u32, + end_page: u32, + tables: Vec, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +struct ResolvedStaticTable { + name: String, + base: usize, + slots: usize, + bytes: usize, +} + +/// Errors returned by static-memory build helpers. +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error("static memory table name `{0}` is not a valid snake_case Rust identifier")] + InvalidTableName(String), + #[error("static memory table name `{0}` is reserved by Rust")] + ReservedTableName(String), + #[error("static memory table name `{0}` is duplicated")] + DuplicateTableName(String), + #[error("static memory layout overflows")] + LayoutOverflow, + #[error("static memory layout exceeds Gear limit of {MAX_STATIC_MEMORY_PAGES} WASM pages")] + StaticMemoryLimitExceeded, + #[error("static memory layout is invalid: {0}")] + StorageLayout(sails_storage::TableError), + #[error("OUT_DIR is not set for static memory source generation")] + MissingOutDir(#[from] env::VarError), + #[error("static memory build I/O failed: {0}")] + Io(#[from] std::io::Error), + #[error("WASM parse failed while patching static memory: {0}")] + WasmParse(#[from] wasmparser::BinaryReaderError), + #[error("WASM import cannot be round-tripped while patching static memory: {0}")] + UnsupportedImport(String), + #[error("WASM payload cannot be round-tripped while patching static memory")] + UnsupportedWasmPayload, + #[error("WASM module has no imported memory")] + MemoryImportNotFound, + #[error("WASM module has multiple imported memories")] + MultipleMemoryImports, + #[error("WASM memory uses custom page size {0:?}; Sails static memory requires 64 KiB pages")] + UnsupportedMemoryPageSize(Option), + #[error( + "static memory start page {start_page} overlaps the original imported static pages {original_pages}" + )] + StaticMemoryOverlapsProgram { + start_page: u32, + original_pages: u64, + }, + #[error( + "static memory requires {required_pages} pages, exceeding imported memory maximum {maximum_pages}" + )] + StaticMemoryExceedsMemoryMaximum { + required_pages: u64, + maximum_pages: u64, + }, +} + +/// Builds a WASM binary after reserving static memory pages. +/// +/// This function exits the build script on error, matching +/// [`gwasm_builder::build`]. +pub fn build_wasm_with_static_memory(layout: StaticMemoryLayout) -> Option<(PathBuf, PathBuf)> { + match try_build_wasm_with_static_memory(layout) { + Ok(paths) => paths, + Err(error) => { + eprintln!("error: {error}"); + process::exit(1); + } + } +} + +/// Fallible variant of [`build_wasm_with_static_memory`]. +pub fn try_build_wasm_with_static_memory( + layout: StaticMemoryLayout, +) -> Result, Error> { + let layout = layout.resolve()?; + emit_static_storage(&layout)?; + + Ok(WasmBuilder::new() + .exclude_features(vec!["std"]) + .with_pre_processor(Box::new(StaticMemoryPreProcessor { layout })) + .build()) +} + +impl StaticMemoryLayout { + /// Creates a layout that starts at `start_page`. + pub fn new(start_page: u32) -> Self { + Self { + start_page, + tables: Vec::new(), + } + } + + /// Reserves a named static open-addressed table. + pub fn reserve_table( + mut self, + name: impl Into, + slots: usize, + ) -> Self { + self.tables.push(StaticTable { + name: name.into(), + slots, + slot_size: StaticOpenAddressTable::::slot_size(), + }); + self + } + + fn resolve(self) -> Result { + let start_base = page_offset(self.start_page)?; + let max_bytes = page_offset(MAX_STATIC_MEMORY_PAGES)?; + let bytes = max_bytes + .checked_sub(start_base) + .ok_or(Error::StaticMemoryLimitExceeded)?; + let mut layout = StaticLayout::new(start_base, bytes).map_err(Error::StorageLayout)?; + let mut names = BTreeSet::new(); + let mut tables = Vec::with_capacity(self.tables.len()); + + for table in self.tables { + validate_table_name(&table.name)?; + if !names.insert(table.name.clone()) { + return Err(Error::DuplicateTableName(table.name)); + } + + let bytes = table + .slots + .checked_mul(table.slot_size) + .ok_or(Error::LayoutOverflow)?; + let region = layout.reserve_bytes(bytes).map_err(Error::StorageLayout)?; + tables.push(ResolvedStaticTable { + name: table.name, + base: region.base(), + slots: table.slots, + bytes: region.bytes(), + }); + } + + let end_page = byte_end_to_page(layout.cursor())?; + Ok(ResolvedStaticMemoryLayout { + start_page: self.start_page, + end_page, + tables, + }) + } +} + +struct StaticMemoryPreProcessor { + layout: ResolvedStaticMemoryLayout, +} + +impl PreProcessor for StaticMemoryPreProcessor { + fn name(&self) -> &'static str { + "sails_static_memory" + } + + fn pre_process( + &self, + original: PathBuf, + ) -> gwasm_builder::PreProcessorResult)>> { + let wasm = fs::read(original)?; + let patched = patch_imported_memory(&wasm, self.layout.start_page, self.layout.end_page)?; + Ok(vec![(PreProcessorTarget::Default, patched)]) + } +} + +fn emit_static_storage(layout: &ResolvedStaticMemoryLayout) -> Result<(), Error> { + let out_dir = env::var("OUT_DIR")?; + emit_static_storage_to_dir(layout, out_dir) +} + +fn emit_static_storage_to_dir( + layout: &ResolvedStaticMemoryLayout, + out_dir: impl AsRef, +) -> Result<(), Error> { + let mut source = String::new(); + writeln!( + &mut source, + "// @generated by sails_rs::build::build_wasm_with_static_memory" + ) + .expect("writing to String cannot fail"); + writeln!( + &mut source, + "#[allow(dead_code)]\npub const STATIC_MEMORY_START_PAGE: u32 = {};", + layout.start_page + ) + .expect("writing to String cannot fail"); + writeln!( + &mut source, + "#[allow(dead_code)]\npub const STATIC_MEMORY_END_PAGE: u32 = {};", + layout.end_page + ) + .expect("writing to String cannot fail"); + + for table in &layout.tables { + let name = table.name.to_ascii_uppercase(); + writeln!(&mut source).expect("writing to String cannot fail"); + writeln!( + &mut source, + "#[allow(dead_code)]\npub const {name}_BASE: usize = {};", + table.base + ) + .expect("writing to String cannot fail"); + writeln!( + &mut source, + "#[allow(dead_code)]\npub const {name}_SLOTS: usize = {};", + table.slots + ) + .expect("writing to String cannot fail"); + writeln!( + &mut source, + "#[allow(dead_code)]\npub const {name}_BYTES: usize = {};", + table.bytes + ) + .expect("writing to String cannot fail"); + } + + fs::write(out_dir.as_ref().join(GENERATED_STATIC_STORAGE), source)?; + Ok(()) +} + +fn patch_imported_memory( + wasm: &[u8], + start_page: u32, + required_pages: u32, +) -> Result, Error> { + let parser = wasmparser::Parser::new(0); + let mut module = wasm_encoder::Module::new(); + let mut memory_import_seen = false; + + for payload in parser.parse_all(wasm) { + match payload? { + wasmparser::Payload::Version { .. } => {} + wasmparser::Payload::ImportSection(imports) => { + let mut import_section = wasm_encoder::ImportSection::new(); + + for imports in imports { + match imports? { + wasmparser::Imports::Single(_, import) => append_import( + &mut import_section, + import.module, + import.name, + import.ty, + &mut memory_import_seen, + start_page, + required_pages, + )?, + wasmparser::Imports::Compact1 { module, items } => { + for item in items { + let item = item?; + append_import( + &mut import_section, + module, + item.name, + item.ty, + &mut memory_import_seen, + start_page, + required_pages, + )?; + } + } + wasmparser::Imports::Compact2 { module, ty, names } => { + for name in names { + append_import( + &mut import_section, + module, + name?, + ty, + &mut memory_import_seen, + start_page, + required_pages, + )?; + } + } + } + } + + module.section(&import_section); + } + wasmparser::Payload::End(_) => break, + other => { + if let Some((id, range)) = other.as_section() { + module.section(&wasm_encoder::RawSection { + id, + data: &wasm[range], + }); + } else if !matches!( + other, + wasmparser::Payload::CodeSectionStart { .. } + | wasmparser::Payload::CodeSectionEntry { .. } + ) { + return Err(Error::UnsupportedWasmPayload); + } + } + } + } + + if !memory_import_seen { + return Err(Error::MemoryImportNotFound); + } + + Ok(module.finish()) +} + +fn append_import( + imports: &mut wasm_encoder::ImportSection, + module: &str, + name: &str, + ty: wasmparser::TypeRef, + memory_import_seen: &mut bool, + start_page: u32, + required_pages: u32, +) -> Result<(), Error> { + let entity = match ty { + wasmparser::TypeRef::Memory(memory) => { + if *memory_import_seen { + return Err(Error::MultipleMemoryImports); + } + *memory_import_seen = true; + patched_memory_type(memory, start_page, required_pages)? + } + other => other + .try_into() + .map_err(|error: wasm_encoder::reencode::Error| { + Error::UnsupportedImport(error.to_string()) + })?, + }; + + imports.import(module, name, entity); + Ok(()) +} + +fn patched_memory_type( + memory: wasmparser::MemoryType, + start_page: u32, + required_pages: u32, +) -> Result { + if memory.page_size_log2.is_some_and(|page| page != 16) { + return Err(Error::UnsupportedMemoryPageSize(memory.page_size_log2)); + } + + if u64::from(start_page) < memory.initial { + return Err(Error::StaticMemoryOverlapsProgram { + start_page, + original_pages: memory.initial, + }); + } + + let minimum = u64::from(required_pages).max(memory.initial); + if let Some(maximum) = memory.maximum + && minimum > maximum + { + return Err(Error::StaticMemoryExceedsMemoryMaximum { + required_pages: minimum, + maximum_pages: maximum, + }); + } + + Ok(wasm_encoder::EntityType::Memory(wasm_encoder::MemoryType { + minimum, + maximum: memory.maximum, + memory64: memory.memory64, + shared: memory.shared, + page_size_log2: memory.page_size_log2, + })) +} + +fn page_offset(page: u32) -> Result { + (page as usize) + .checked_mul(WASM_PAGE_SIZE) + .ok_or(Error::LayoutOverflow) +} + +fn byte_end_to_page(byte_end: usize) -> Result { + let pages = byte_end.div_ceil(WASM_PAGE_SIZE); + if pages > MAX_STATIC_MEMORY_PAGES as usize { + return Err(Error::StaticMemoryLimitExceeded); + } + u32::try_from(pages).map_err(|_| Error::StaticMemoryLimitExceeded) +} + +fn validate_table_name(name: &str) -> Result<(), Error> { + let mut chars = name.chars(); + let Some(first) = chars.next() else { + return Err(Error::InvalidTableName(name.to_string())); + }; + + if !(first == '_' || first.is_ascii_lowercase()) { + return Err(Error::InvalidTableName(name.to_string())); + } + + if !chars.all(|ch| ch == '_' || ch.is_ascii_lowercase() || ch.is_ascii_digit()) { + return Err(Error::InvalidTableName(name.to_string())); + } + + if is_rust_keyword(name) { + return Err(Error::ReservedTableName(name.to_string())); + } + + Ok(()) +} + +fn is_rust_keyword(name: &str) -> bool { + matches!( + name, + "as" | "async" + | "await" + | "break" + | "const" + | "continue" + | "crate" + | "dyn" + | "else" + | "enum" + | "extern" + | "false" + | "fn" + | "for" + | "if" + | "impl" + | "in" + | "let" + | "loop" + | "match" + | "mod" + | "move" + | "mut" + | "pub" + | "ref" + | "return" + | "self" + | "Self" + | "static" + | "struct" + | "super" + | "trait" + | "true" + | "type" + | "unsafe" + | "use" + | "where" + | "while" + ) +} + +#[cfg(test)] +mod tests { + use super::*; + use wasm_encoder::{EntityType, ImportSection, MemoryType, Module}; + + fn module_with_memory(minimum: u64, maximum: Option) -> Vec { + let mut imports = ImportSection::new(); + imports.import( + "env", + "memory", + EntityType::Memory(MemoryType { + minimum, + maximum, + memory64: false, + shared: false, + page_size_log2: None, + }), + ); + + let mut module = Module::new(); + module.section(&imports); + module.finish() + } + + fn memory_minimum(wasm: &[u8]) -> u64 { + for payload in wasmparser::Parser::new(0).parse_all(wasm) { + let wasmparser::Payload::ImportSection(imports) = payload.unwrap() else { + continue; + }; + for imports in imports { + match imports.unwrap() { + wasmparser::Imports::Single(_, import) => { + if let wasmparser::TypeRef::Memory(memory) = import.ty { + return memory.initial; + } + } + wasmparser::Imports::Compact1 { items, .. } => { + for item in items { + let item = item.unwrap(); + if let wasmparser::TypeRef::Memory(memory) = item.ty { + return memory.initial; + } + } + } + wasmparser::Imports::Compact2 { ty, .. } => { + if let wasmparser::TypeRef::Memory(memory) = ty { + return memory.initial; + } + } + } + } + } + + panic!("memory import not found") + } + + #[test] + fn resolves_layout_and_generates_constants() { + let layout = StaticMemoryLayout::new(1024) + .reserve_table::<32, 32>("balances", 2) + .reserve_table::<64, 32>("allowances", 1) + .resolve() + .unwrap(); + + assert_eq!(layout.start_page, 1024); + assert_eq!(layout.end_page, 1025); + assert_eq!(layout.tables[0].base, 1024 * WASM_PAGE_SIZE); + assert_eq!(layout.tables[0].bytes, 2 * (1 + 32 + 32)); + assert_eq!(layout.tables[1].bytes, 1 + 64 + 32); + + let dir = tempfile::tempdir().unwrap(); + emit_static_storage_to_dir(&layout, dir.path()).unwrap(); + let generated = fs::read_to_string(dir.path().join(GENERATED_STATIC_STORAGE)).unwrap(); + assert!(generated.contains("pub const BALANCES_BASE: usize = 67108864;")); + assert!(generated.contains("pub const ALLOWANCES_SLOTS: usize = 1;")); + } + + #[test] + fn rejects_invalid_duplicate_and_reserved_names() { + assert!(matches!( + StaticMemoryLayout::new(1) + .reserve_table::<1, 1>("bad-name", 1) + .resolve(), + Err(Error::InvalidTableName(_)) + )); + assert!(matches!( + StaticMemoryLayout::new(1) + .reserve_table::<1, 1>("balances", 1) + .reserve_table::<1, 1>("balances", 1) + .resolve(), + Err(Error::DuplicateTableName(_)) + )); + assert!(matches!( + StaticMemoryLayout::new(1) + .reserve_table::<1, 1>("type", 1) + .resolve(), + Err(Error::ReservedTableName(_)) + )); + } + + #[test] + fn rejects_layout_overflow_and_page_limit() { + assert!(matches!( + StaticMemoryLayout::new(MAX_STATIC_MEMORY_PAGES) + .reserve_table::<1, 1>("balances", 1) + .resolve(), + Err(Error::StorageLayout(_)) + )); + assert!(matches!( + StaticMemoryLayout::new(1) + .reserve_table::<1, 1>("balances", usize::MAX) + .resolve(), + Err(Error::LayoutOverflow) + )); + } + + #[test] + fn patches_memory_import_minimum() { + let wasm = module_with_memory(1, None); + let patched = patch_imported_memory(&wasm, 8, 10).unwrap(); + + assert_eq!(memory_minimum(&patched), 10); + } + + #[test] + fn preserves_higher_existing_memory_minimum() { + let wasm = module_with_memory(12, None); + let patched = patch_imported_memory(&wasm, 12, 10).unwrap(); + + assert_eq!(memory_minimum(&patched), 12); + } + + #[test] + fn rejects_overlap_with_original_static_pages() { + let wasm = module_with_memory(12, None); + let error = patch_imported_memory(&wasm, 8, 20).unwrap_err(); + + assert!(matches!( + error, + Error::StaticMemoryOverlapsProgram { + start_page: 8, + original_pages: 12, + } + )); + } + + #[test] + fn rejects_memory_maximum_below_required_pages() { + let wasm = module_with_memory(1, Some(4)); + let error = patch_imported_memory(&wasm, 2, 8).unwrap_err(); + + assert!(matches!( + error, + Error::StaticMemoryExceedsMemoryMaximum { + required_pages: 8, + maximum_pages: 4, + } + )); + } + + #[test] + fn rejects_missing_memory_import() { + let wasm = Module::new().finish(); + let error = patch_imported_memory(&wasm, 1, 2).unwrap_err(); + + assert!(matches!(error, Error::MemoryImportNotFound)); + } +} diff --git a/rs/src/lib.rs b/rs/src/lib.rs index 0311573fc..8631bf4b6 100644 --- a/rs/src/lib.rs +++ b/rs/src/lib.rs @@ -1,13 +1,16 @@ #![cfg_attr(not(doctest), doc = include_str!("../README.md"))] #![no_std] -#[cfg(all(feature = "std", not(target_arch = "wasm32")))] +#[cfg(any( + all(feature = "std", not(target_arch = "wasm32")), + all(feature = "wasm-builder", not(target_arch = "wasm32")) +))] extern crate std; +#[cfg(all(feature = "wasm-builder", not(target_arch = "wasm32")))] +pub use build::build_wasm; #[cfg(feature = "client-builder")] pub use builder::{ClientBuilder, ClientGenerator, IdlPath, build_client, build_client_as_lib}; -#[cfg(feature = "wasm-builder")] -pub use gwasm_builder::build as build_wasm; pub use hex; #[doc(hidden)] pub use paste; @@ -21,6 +24,8 @@ pub use sails_idl_gen::generate_idl_to_file; pub use sails_idl_meta::{self as meta}; pub use spin; +#[cfg(all(feature = "wasm-builder", not(target_arch = "wasm32")))] +pub mod build; #[cfg(feature = "client-builder")] mod builder; #[cfg(any( diff --git a/rs/storage/Cargo.toml b/rs/storage/Cargo.toml new file mode 100644 index 000000000..ab1d4b3b7 --- /dev/null +++ b/rs/storage/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "sails-storage" +description = "Allocator-light storage primitives for Sails programs" +documentation = "https://docs.rs/sails-storage" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true + +[dependencies] +gprimitives = { workspace = true, optional = true } + +[features] +default = [] +gear = ["dep:gprimitives"] diff --git a/rs/storage/src/lib.rs b/rs/storage/src/lib.rs new file mode 100644 index 000000000..5a446a843 --- /dev/null +++ b/rs/storage/src/lib.rs @@ -0,0 +1,852 @@ +#![no_std] + +use core::{fmt, marker::PhantomData, ptr}; + +/// Errors returned by fixed and static storage tables. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TableError { + /// The table has no reusable slot for a new key. + CapacityOverflow, + /// The provided memory layout overflows or does not fit the requested region. + InvalidLayout, + /// A static-memory slot state byte is not one of the supported values. + InvalidSlotState, +} + +impl fmt::Display for TableError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::CapacityOverflow => f.write_str("capacity overflow"), + Self::InvalidLayout => f.write_str("invalid storage layout"), + Self::InvalidSlotState => f.write_str("invalid slot state"), + } + } +} + +/// The state byte stored before every static-memory slot. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u8)] +pub enum SlotState { + Empty = 0, + Full = 1, + Deleted = 2, +} + +impl SlotState { + fn from_byte(byte: u8) -> Result { + match byte { + 0 => Ok(Self::Empty), + 1 => Ok(Self::Full), + 2 => Ok(Self::Deleted), + _ => Err(TableError::InvalidSlotState), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum SlotMatch { + Found, + Occupied, + Deleted, + Empty, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum Lookup { + Found(usize), + Vacant(usize), + Full, +} + +fn find_slot( + slots: usize, + hash: usize, + mut classify: impl FnMut(usize) -> Result, +) -> Result { + if slots == 0 { + return Ok(Lookup::Full); + } + + let mut first_deleted = None; + let mut index = hash % slots; + + for _ in 0..slots { + match classify(index)? { + SlotMatch::Found => return Ok(Lookup::Found(index)), + SlotMatch::Occupied => {} + SlotMatch::Deleted => { + if first_deleted.is_none() { + first_deleted = Some(index); + } + } + SlotMatch::Empty => return Ok(Lookup::Vacant(first_deleted.unwrap_or(index))), + } + + index += 1; + if index == slots { + index = 0; + } + } + + Ok(first_deleted.map_or(Lookup::Full, Lookup::Vacant)) +} + +fn hash_bytes(bytes: &[u8]) -> usize { + let mut hash = 0x811c_9dc5u32; + + for byte in bytes { + hash ^= u32::from(*byte); + hash = hash.wrapping_mul(0x0100_0193); + } + + hash as usize +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct FixedSlot { + state: SlotState, + key: [u8; KEY_SIZE], + value: [u8; VALUE_SIZE], +} + +impl FixedSlot { + const EMPTY: Self = Self { + state: SlotState::Empty, + key: [0; KEY_SIZE], + value: [0; VALUE_SIZE], + }; +} + +/// A fixed-capacity open-addressed map stored inside the program state. +/// +/// This type is useful for tests, benchmarks, and small bounded state. For +/// lazy-page optimized storage, use [`StaticOpenAddressTable`] over a static +/// memory region. +pub struct FixedOpenAddressMap { + slots: [FixedSlot; CAP], + len: usize, +} + +impl + FixedOpenAddressMap +{ + /// Creates an empty fixed-capacity map. + pub const fn new() -> Self { + Self { + slots: [FixedSlot::EMPTY; CAP], + len: 0, + } + } + + /// Returns the number of visible entries. + pub const fn len(&self) -> usize { + self.len + } + + /// Returns `true` when the map contains no visible entries. + pub const fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Returns the configured capacity. + pub const fn capacity(&self) -> usize { + CAP + } + + /// Returns the visible value for `key`. + pub fn get(&self, key: &[u8; KEY_SIZE]) -> Result, TableError> { + match self.lookup(key)? { + Lookup::Found(index) => Ok(Some(self.slots[index].value)), + Lookup::Vacant(_) | Lookup::Full => Ok(None), + } + } + + /// Inserts or updates `key`, returning the previous visible value. + pub fn insert( + &mut self, + key: [u8; KEY_SIZE], + value: [u8; VALUE_SIZE], + ) -> Result, TableError> { + match self.lookup(&key)? { + Lookup::Found(index) => { + let previous = self.slots[index].value; + self.slots[index] = FixedSlot { + state: SlotState::Full, + key, + value, + }; + Ok(Some(previous)) + } + Lookup::Vacant(index) => { + self.slots[index] = FixedSlot { + state: SlotState::Full, + key, + value, + }; + self.len += 1; + Ok(None) + } + Lookup::Full => Err(TableError::CapacityOverflow), + } + } + + /// Removes the visible value for `key`, preserving the probe chain. + pub fn remove(&mut self, key: &[u8; KEY_SIZE]) -> Result, TableError> { + let Lookup::Found(index) = self.lookup(key)? else { + return Ok(None); + }; + + let previous = self.slots[index].value; + self.slots[index].state = SlotState::Deleted; + self.len -= 1; + Ok(Some(previous)) + } + + fn lookup(&self, key: &[u8; KEY_SIZE]) -> Result { + find_slot(CAP, hash_bytes(key), |index| { + let slot = &self.slots[index]; + Ok(match slot.state { + SlotState::Full if slot.key == *key => SlotMatch::Found, + SlotState::Full => SlotMatch::Occupied, + SlotState::Deleted => SlotMatch::Deleted, + SlotState::Empty => SlotMatch::Empty, + }) + }) + } +} + +impl Default + for FixedOpenAddressMap +{ + fn default() -> Self { + Self::new() + } +} + +/// A byte interval reserved for static storage. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct StaticRegion { + base: usize, + bytes: usize, +} + +impl StaticRegion { + /// Creates a byte region and validates `base + bytes`. + pub fn new(base: usize, bytes: usize) -> Result { + base.checked_add(bytes).ok_or(TableError::InvalidLayout)?; + Ok(Self { base, bytes }) + } + + /// Returns the region base address. + pub const fn base(self) -> usize { + self.base + } + + /// Returns the region byte length. + pub const fn bytes(self) -> usize { + self.bytes + } + + /// Returns the first byte after this region. + pub fn end(self) -> Result { + self.base + .checked_add(self.bytes) + .ok_or(TableError::InvalidLayout) + } +} + +/// A typed static-memory region for an open-addressed table. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct TableRegion { + region: StaticRegion, + slots: usize, +} + +impl TableRegion { + /// Returns the byte region backing the table. + pub const fn region(self) -> StaticRegion { + self.region + } + + /// Returns the table base address. + pub const fn base(self) -> usize { + self.region.base() + } + + /// Returns the configured slot count. + pub const fn slots(self) -> usize { + self.slots + } +} + +/// Sequential static-memory layout builder. +pub struct StaticLayout { + cursor: usize, + end: usize, +} + +impl StaticLayout { + /// Creates a layout over `[base, base + bytes)`. + pub fn new(base: usize, bytes: usize) -> Result { + let end = base.checked_add(bytes).ok_or(TableError::InvalidLayout)?; + Ok(Self { cursor: base, end }) + } + + /// Returns the next free address. + pub const fn cursor(&self) -> usize { + self.cursor + } + + /// Returns the first byte after the layout region. + pub const fn end(&self) -> usize { + self.end + } + + /// Returns the number of bytes not yet reserved. + pub fn remaining(&self) -> usize { + self.end - self.cursor + } + + /// Reserves a raw byte region. + pub fn reserve_bytes(&mut self, bytes: usize) -> Result { + let next = self + .cursor + .checked_add(bytes) + .ok_or(TableError::InvalidLayout)?; + + if next > self.end { + return Err(TableError::InvalidLayout); + } + + let region = StaticRegion { + base: self.cursor, + bytes, + }; + self.cursor = next; + Ok(region) + } + + /// Reserves a typed table region. + pub fn reserve_table( + &mut self, + slots: usize, + ) -> Result, TableError> { + let bytes = StaticOpenAddressTable::::bytes_len(slots)?; + let region = self.reserve_bytes(bytes)?; + Ok(TableRegion { region, slots }) + } +} + +/// A fixed open-addressed table backed by caller-owned static memory. +pub struct StaticOpenAddressTable { + base: usize, + slots: usize, + _marker: PhantomData<*mut u8>, +} + +impl StaticOpenAddressTable { + /// Returns the byte length of one slot. + pub const fn slot_size() -> usize { + 1 + KEY_SIZE + VALUE_SIZE + } + + /// Returns the byte length required for `slots`. + pub fn bytes_len(slots: usize) -> Result { + slots + .checked_mul(Self::slot_size()) + .ok_or(TableError::InvalidLayout) + } + + /// Creates a table over `slots * slot_size()` bytes at `base`. + /// + /// # Safety + /// + /// The caller must ensure the memory interval is valid for reads and writes + /// for the whole lifetime of the table and does not overlap other mutable + /// state. + pub unsafe fn new(base: usize, slots: usize) -> Result { + let bytes = Self::bytes_len(slots)?; + StaticRegion::new(base, bytes)?; + Ok(Self { + base, + slots, + _marker: PhantomData, + }) + } + + /// Creates a table from a typed static-memory region. + /// + /// # Safety + /// + /// The caller must ensure the memory interval is valid for reads and writes + /// for the whole lifetime of the table and does not overlap other mutable + /// state. + pub unsafe fn from_region( + region: TableRegion, + ) -> Result { + let expected = Self::bytes_len(region.slots)?; + if expected > region.region.bytes { + return Err(TableError::InvalidLayout); + } + + unsafe { Self::new(region.region.base, region.slots) } + } + + /// Returns the configured base address. + pub const fn base(&self) -> usize { + self.base + } + + /// Returns the configured slot count. + pub const fn slots(&self) -> usize { + self.slots + } + + /// Returns the total byte length occupied by this table. + pub fn bytes(&self) -> Result { + Self::bytes_len(self.slots) + } + + /// Returns the visible value for `key`. + pub fn get(&self, key: &[u8; KEY_SIZE]) -> Result, TableError> { + match self.lookup(key)? { + Lookup::Found(index) => unsafe { self.read_value(index).map(Some) }, + Lookup::Vacant(_) | Lookup::Full => Ok(None), + } + } + + /// Inserts or updates `key`, returning the previous visible value. + pub fn insert( + &self, + key: &[u8; KEY_SIZE], + value: &[u8; VALUE_SIZE], + ) -> Result, TableError> { + match self.lookup(key)? { + Lookup::Found(index) => { + let previous = unsafe { self.read_value(index)? }; + unsafe { + self.write_key(index, key); + self.write_value(index, value); + self.write_state(index, SlotState::Full); + } + Ok(Some(previous)) + } + Lookup::Vacant(index) => { + unsafe { + self.write_key(index, key); + self.write_value(index, value); + self.write_state(index, SlotState::Full); + } + Ok(None) + } + Lookup::Full => Err(TableError::CapacityOverflow), + } + } + + /// Removes the visible value for `key`, preserving the probe chain. + pub fn remove(&self, key: &[u8; KEY_SIZE]) -> Result, TableError> { + let Lookup::Found(index) = self.lookup(key)? else { + return Ok(None); + }; + + let previous = unsafe { self.read_value(index)? }; + unsafe { + self.write_state(index, SlotState::Deleted); + } + Ok(Some(previous)) + } + + /// Clears every slot to the empty state. + pub fn clear(&self) -> Result<(), TableError> { + let bytes = self.bytes()?; + unsafe { + ptr::write_bytes(self.base as *mut u8, 0, bytes); + } + Ok(()) + } + + fn lookup(&self, key: &[u8; KEY_SIZE]) -> Result { + find_slot(self.slots, hash_bytes(key), |index| { + let state = unsafe { self.read_state(index)? }; + Ok(match state { + SlotState::Full if unsafe { self.key_matches(index, key) } => SlotMatch::Found, + SlotState::Full => SlotMatch::Occupied, + SlotState::Deleted => SlotMatch::Deleted, + SlotState::Empty => SlotMatch::Empty, + }) + }) + } + + unsafe fn read_state(&self, slot: usize) -> Result { + let byte = unsafe { ptr::read(self.state_ptr(slot)) }; + SlotState::from_byte(byte) + } + + unsafe fn key_matches(&self, slot: usize, key: &[u8; KEY_SIZE]) -> bool { + unsafe { core::slice::from_raw_parts(self.key_ptr(slot), KEY_SIZE) == key.as_slice() } + } + + unsafe fn read_value(&self, slot: usize) -> Result<[u8; VALUE_SIZE], TableError> { + let mut bytes = [0u8; VALUE_SIZE]; + unsafe { + ptr::copy_nonoverlapping(self.value_ptr(slot), bytes.as_mut_ptr(), VALUE_SIZE); + } + Ok(bytes) + } + + unsafe fn write_state(&self, slot: usize, state: SlotState) { + unsafe { + ptr::write(self.state_ptr(slot), state as u8); + } + } + + unsafe fn write_key(&self, slot: usize, key: &[u8; KEY_SIZE]) { + unsafe { + ptr::copy_nonoverlapping(key.as_ptr(), self.key_ptr(slot), KEY_SIZE); + } + } + + unsafe fn write_value(&self, slot: usize, value: &[u8; VALUE_SIZE]) { + unsafe { + ptr::copy_nonoverlapping(value.as_ptr(), self.value_ptr(slot), VALUE_SIZE); + } + } + + fn state_ptr(&self, slot: usize) -> *mut u8 { + self.slot_ptr(slot) + } + + fn key_ptr(&self, slot: usize) -> *mut u8 { + unsafe { self.slot_ptr(slot).add(1) } + } + + fn value_ptr(&self, slot: usize) -> *mut u8 { + unsafe { self.slot_ptr(slot).add(1 + KEY_SIZE) } + } + + fn slot_ptr(&self, slot: usize) -> *mut u8 { + (self.base + slot * Self::slot_size()) as *mut u8 + } +} + +#[cfg(feature = "gear")] +pub mod gear { + use super::{FixedOpenAddressMap, StaticOpenAddressTable, TableError}; + use gprimitives::{ActorId, U256}; + + /// Fixed balance map keyed by `ActorId` and storing `U256`. + pub type FixedBalanceMap = FixedOpenAddressMap<32, 32, CAP>; + /// Fixed allowance map keyed by `(owner, spender)` and storing `U256`. + pub type FixedAllowanceMap = FixedOpenAddressMap<64, 32, CAP>; + /// Static balance table keyed by `ActorId` and storing `U256`. + pub type StaticBalanceTable = StaticOpenAddressTable<32, 32>; + /// Static allowance table keyed by `(owner, spender)` and storing `U256`. + pub type StaticAllowanceTable = StaticOpenAddressTable<64, 32>; + + /// Converts an actor id into a raw 32-byte key. + pub fn actor_key(actor: ActorId) -> [u8; 32] { + let mut key = [0u8; 32]; + key.copy_from_slice(actor.as_ref()); + key + } + + /// Converts an allowance pair into a raw 64-byte key. + pub fn allowance_key(owner: ActorId, spender: ActorId) -> [u8; 64] { + let mut key = [0u8; 64]; + key[..32].copy_from_slice(owner.as_ref()); + key[32..].copy_from_slice(spender.as_ref()); + key + } + + /// Converts a `U256` into the little-endian value bytes used by the helpers. + pub fn u256_value(value: U256) -> [u8; 32] { + let mut bytes = [0u8; 32]; + value.to_little_endian(&mut bytes); + bytes + } + + /// Converts little-endian value bytes into a `U256`. + pub fn u256_from_value(bytes: [u8; 32]) -> U256 { + U256::from_little_endian(&bytes) + } + + impl FixedBalanceMap { + /// Returns a balance value by actor id. + pub fn get_actor_u256(&self, key: &ActorId) -> Result, TableError> { + self.get(&actor_key(*key)) + .map(|value| value.map(u256_from_value)) + } + + /// Inserts or updates a balance value by actor id. + pub fn insert_actor_u256( + &mut self, + key: ActorId, + value: U256, + ) -> Result, TableError> { + self.insert(actor_key(key), u256_value(value)) + .map(|previous| previous.map(u256_from_value)) + } + + /// Removes a balance value by actor id. + pub fn remove_actor_u256(&mut self, key: &ActorId) -> Result, TableError> { + self.remove(&actor_key(*key)) + .map(|previous| previous.map(u256_from_value)) + } + } + + impl FixedAllowanceMap { + /// Returns an allowance value by owner and spender. + pub fn get_allowance_u256( + &self, + owner: &ActorId, + spender: &ActorId, + ) -> Result, TableError> { + self.get(&allowance_key(*owner, *spender)) + .map(|value| value.map(u256_from_value)) + } + + /// Inserts or updates an allowance value by owner and spender. + pub fn insert_allowance_u256( + &mut self, + owner: ActorId, + spender: ActorId, + value: U256, + ) -> Result, TableError> { + self.insert(allowance_key(owner, spender), u256_value(value)) + .map(|previous| previous.map(u256_from_value)) + } + + /// Removes an allowance value by owner and spender. + pub fn remove_allowance_u256( + &mut self, + owner: &ActorId, + spender: &ActorId, + ) -> Result, TableError> { + self.remove(&allowance_key(*owner, *spender)) + .map(|previous| previous.map(u256_from_value)) + } + } + + impl StaticBalanceTable { + /// Returns a balance value by actor id. + pub fn get_actor_u256(&self, key: &ActorId) -> Result, TableError> { + self.get(&actor_key(*key)) + .map(|value| value.map(u256_from_value)) + } + + /// Inserts or updates a balance value by actor id. + pub fn insert_actor_u256( + &self, + key: ActorId, + value: U256, + ) -> Result, TableError> { + self.insert(&actor_key(key), &u256_value(value)) + .map(|previous| previous.map(u256_from_value)) + } + + /// Removes a balance value by actor id. + pub fn remove_actor_u256(&self, key: &ActorId) -> Result, TableError> { + self.remove(&actor_key(*key)) + .map(|previous| previous.map(u256_from_value)) + } + } + + impl StaticAllowanceTable { + /// Returns an allowance value by owner and spender. + pub fn get_allowance_u256( + &self, + owner: &ActorId, + spender: &ActorId, + ) -> Result, TableError> { + self.get(&allowance_key(*owner, *spender)) + .map(|value| value.map(u256_from_value)) + } + + /// Inserts or updates an allowance value by owner and spender. + pub fn insert_allowance_u256( + &self, + owner: ActorId, + spender: ActorId, + value: U256, + ) -> Result, TableError> { + self.insert(&allowance_key(owner, spender), &u256_value(value)) + .map(|previous| previous.map(u256_from_value)) + } + + /// Removes an allowance value by owner and spender. + pub fn remove_allowance_u256( + &self, + owner: &ActorId, + spender: &ActorId, + ) -> Result, TableError> { + self.remove(&allowance_key(*owner, *spender)) + .map(|previous| previous.map(u256_from_value)) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + type Fixed = FixedOpenAddressMap<1, 1, 2>; + + fn colliding_keys(slots: usize) -> ([u8; 1], [u8; 1]) { + for left in 0u8..=u8::MAX { + for right in left.wrapping_add(1)..=u8::MAX { + if hash_bytes(&[left]) % slots == hash_bytes(&[right]) % slots { + return ([left], [right]); + } + } + } + + unreachable!("single-byte key space must contain collisions") + } + + #[test] + fn fixed_map_updates_existing_key() { + let mut map = Fixed::new(); + + assert_eq!(map.insert([0], [0]), Ok(None)); + assert_eq!(map.insert([0], [7]), Ok(Some([0]))); + assert_eq!(map.get(&[0]), Ok(Some([7]))); + assert_eq!(map.len(), 1); + } + + #[test] + fn fixed_map_reuses_tombstone() { + let (first, second) = colliding_keys(1); + let mut map = FixedOpenAddressMap::<1, 1, 1>::new(); + + assert_eq!(map.insert(first, [1]), Ok(None)); + assert_eq!(map.remove(&first), Ok(Some([1]))); + assert_eq!(map.insert(second, [2]), Ok(None)); + assert_eq!(map.get(&second), Ok(Some([2]))); + assert_eq!(map.len(), 1); + } + + #[test] + fn fixed_map_reports_full_table() { + let mut map = FixedOpenAddressMap::<1, 1, 1>::new(); + + assert_eq!(map.insert([1], [1]), Ok(None)); + assert_eq!(map.insert([2], [2]), Err(TableError::CapacityOverflow)); + } + + #[test] + fn fixed_map_stores_zero_key_and_zero_value() { + let mut map = Fixed::new(); + + assert_eq!(map.insert([0], [0]), Ok(None)); + assert_eq!(map.get(&[0]), Ok(Some([0]))); + assert_eq!(map.remove(&[0]), Ok(Some([0]))); + assert_eq!(map.get(&[0]), Ok(None)); + } + + #[test] + fn static_table_updates_existing_key() { + let mut memory = [0u8; 8]; + let table = unsafe { + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 2).unwrap() + }; + + assert_eq!(table.insert(&[0], &[0]), Ok(None)); + assert_eq!(table.insert(&[0], &[7]), Ok(Some([0]))); + assert_eq!(table.get(&[0]), Ok(Some([7]))); + } + + #[test] + fn static_table_reuses_tombstone() { + let (first, second) = colliding_keys(1); + let mut memory = [0u8; 3]; + let table = unsafe { + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() + }; + + assert_eq!(table.insert(&first, &[1]), Ok(None)); + assert_eq!(table.remove(&first), Ok(Some([1]))); + assert_eq!(table.insert(&second, &[2]), Ok(None)); + assert_eq!(table.get(&second), Ok(Some([2]))); + } + + #[test] + fn static_table_reports_full_table() { + let mut memory = [0u8; 3]; + let table = unsafe { + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() + }; + + assert_eq!(table.insert(&[1], &[1]), Ok(None)); + assert_eq!(table.insert(&[2], &[2]), Err(TableError::CapacityOverflow)); + } + + #[test] + fn static_table_reports_invalid_slot_state() { + let mut memory = [9u8, 0, 0]; + let table = unsafe { + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() + }; + + assert_eq!(table.get(&[0]), Err(TableError::InvalidSlotState)); + } + + #[test] + fn static_table_clear_resets_deleted_slots() { + let mut memory = [0u8; 3]; + let table = unsafe { + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() + }; + + table.insert(&[1], &[1]).unwrap(); + table.remove(&[1]).unwrap(); + table.clear().unwrap(); + assert_eq!(memory, [0, 0, 0]); + } + + #[test] + fn static_layout_reserves_non_overlapping_regions() { + let mut layout = StaticLayout::new(100, 16).unwrap(); + let table = layout.reserve_table::<1, 1>(2).unwrap(); + let value = layout.reserve_bytes(4).unwrap(); + + assert_eq!(table.base(), 100); + assert_eq!(table.region().bytes(), 6); + assert_eq!(value.base(), 106); + assert_eq!(value.bytes(), 4); + assert_eq!(layout.remaining(), 6); + } + + #[test] + fn static_layout_rejects_overflow_and_out_of_bounds() { + assert_eq!( + StaticLayout::new(usize::MAX, 1).err(), + Some(TableError::InvalidLayout) + ); + + let mut layout = StaticLayout::new(0, 2).unwrap(); + assert_eq!( + layout.reserve_table::<1, 1>(1).err(), + Some(TableError::InvalidLayout) + ); + } + + #[test] + fn fixed_and_static_tables_match_representative_ops() { + let mut fixed = FixedOpenAddressMap::<1, 1, 4>::new(); + let mut memory = [0u8; 12]; + let static_table = unsafe { + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 4).unwrap() + }; + + for (key, value) in [([0], [0]), ([1], [2]), ([2], [3])] { + assert_eq!(fixed.insert(key, value), static_table.insert(&key, &value)); + } + + assert_eq!(fixed.remove(&[1]), static_table.remove(&[1])); + assert_eq!(fixed.insert([3], [4]), static_table.insert(&[3], &[4])); + + for key in [[0], [1], [2], [3]] { + assert_eq!(fixed.get(&key), static_table.get(&key)); + } + } +} From de572e4f38a5827c82131845fe5b50e179624788 Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Wed, 20 May 2026 00:54:38 +0400 Subject: [PATCH 02/14] feat(storage): add experimental VFT account storage --- Cargo.lock | 1415 ++-- Cargo.toml | 2 + benchmarks/Cargo.toml | 12 + benchmarks/bench_data.json | 788 ++- benchmarks/build.rs | 35 +- benchmarks/idls/storage_million_program.idl | 84 + benchmarks/idls/vft_stress_program.idl | 41 + benchmarks/src/benchmarks.rs | 1878 +++++- benchmarks/src/clients.rs | 10 + benchmarks/src/entities.rs | 58 + benchmarks/src/lib.rs | 130 +- benchmarks/src/storage_million_program.rs | 280 + benchmarks/src/vft_stress_program.rs | 149 + benchmarks/storage-million/Cargo.toml | 14 + benchmarks/storage-million/build.rs | 25 + benchmarks/storage-million/src/lib.rs | 2101 ++++++ benchmarks/storage-stress/Cargo.toml | 2 +- benchmarks/vft-stress/Cargo.toml | 18 + benchmarks/vft-stress/build.rs | 9 + benchmarks/vft-stress/src/lib.rs | 1230 ++++ .../storage-primitive-selection/ATTEMPTS.md | 14 + .../storage-primitive-selection/CONTROL.md | 20 + .../storage-primitive-selection/NOTES.md | 101 + .../goals/storage-primitive-selection/PLAN.md | 35 + .../vft-storage-better-solution/ATTEMPTS.md | 14 + .../vft-storage-better-solution/CONTROL.md | 58 + .../goals/vft-storage-better-solution/GOAL.md | 184 + .../vft-storage-better-solution/NOTES.md | 21 + .../goals/vft-storage-better-solution/PLAN.md | 45 + .../vft-storage-better-solution/RESULTS.md | 41 + .../goals/vft-storage-better-solution/SPEC.md | 87 + .../vft-storage-optimization/ATTEMPTS.md | 10 + .../goals/vft-storage-optimization/CONTROL.md | 63 + docs/goals/vft-storage-optimization/GOAL.md | 190 + docs/goals/vft-storage-optimization/NOTES.md | 8 + docs/goals/vft-storage-optimization/PLAN.md | 25 + .../goals/vft-storage-optimization/RESULTS.md | 113 + docs/goals/vft-storage-optimization/SPEC.md | 61 + docs/storage-primitive-selection.md | 217 + docs/storage-static-maps-local-notes.md | 112 +- docs/vft-storage-architecture.md | 124 + examples/aggregator/app/Cargo.toml | 1 + examples/aggregator/app/src/lib.rs | 67 +- examples/aggregator/app/src/msg_tracker.rs | 194 +- examples/aggregator/app/tests/gtest.rs | 29 +- .../aggregator/client/aggregator_client.idl | 24 +- .../client/src/aggregator_client.rs | 78 +- rs/Cargo.toml | 1 + rs/src/build.rs | 593 +- rs/storage/Cargo.toml | 4 +- rs/storage/src/lib.rs | 5784 ++++++++++++++++- scripts/analyze-vft-storage.py | 329 + 52 files changed, 15543 insertions(+), 1385 deletions(-) create mode 100644 benchmarks/idls/storage_million_program.idl create mode 100644 benchmarks/idls/vft_stress_program.idl create mode 100644 benchmarks/src/storage_million_program.rs create mode 100644 benchmarks/src/vft_stress_program.rs create mode 100644 benchmarks/storage-million/Cargo.toml create mode 100644 benchmarks/storage-million/build.rs create mode 100644 benchmarks/storage-million/src/lib.rs create mode 100644 benchmarks/vft-stress/Cargo.toml create mode 100644 benchmarks/vft-stress/build.rs create mode 100644 benchmarks/vft-stress/src/lib.rs create mode 100644 docs/goals/storage-primitive-selection/ATTEMPTS.md create mode 100644 docs/goals/storage-primitive-selection/CONTROL.md create mode 100644 docs/goals/storage-primitive-selection/NOTES.md create mode 100644 docs/goals/storage-primitive-selection/PLAN.md create mode 100644 docs/goals/vft-storage-better-solution/ATTEMPTS.md create mode 100644 docs/goals/vft-storage-better-solution/CONTROL.md create mode 100644 docs/goals/vft-storage-better-solution/GOAL.md create mode 100644 docs/goals/vft-storage-better-solution/NOTES.md create mode 100644 docs/goals/vft-storage-better-solution/PLAN.md create mode 100644 docs/goals/vft-storage-better-solution/RESULTS.md create mode 100644 docs/goals/vft-storage-better-solution/SPEC.md create mode 100644 docs/goals/vft-storage-optimization/ATTEMPTS.md create mode 100644 docs/goals/vft-storage-optimization/CONTROL.md create mode 100644 docs/goals/vft-storage-optimization/GOAL.md create mode 100644 docs/goals/vft-storage-optimization/NOTES.md create mode 100644 docs/goals/vft-storage-optimization/PLAN.md create mode 100644 docs/goals/vft-storage-optimization/RESULTS.md create mode 100644 docs/goals/vft-storage-optimization/SPEC.md create mode 100644 docs/storage-primitive-selection.md create mode 100644 docs/vft-storage-architecture.md create mode 100644 scripts/analyze-vft-storage.py diff --git a/Cargo.lock b/Cargo.lock index 369a9a8db..22d9493c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,19 +15,9 @@ dependencies = [ [[package]] name = "actor-system-error" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8862bc579cb6407ed5ff150781881003f9a09b724fa70ddbde1572a0ff87fd0" dependencies = [ "derive_more 2.1.1", -] - -[[package]] -name = "addr2line" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" -dependencies = [ - "gimli 0.27.3", + "gear-workspace-hack", ] [[package]] @@ -66,6 +56,7 @@ dependencies = [ "redirect-app", "redirect-client", "sails-rs", + "sails-storage", "tokio", ] @@ -152,7 +143,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.14.0", + "indexmap", "proc-macro-error2", "proc-macro2", "quote", @@ -196,15 +187,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstream" version = "1.0.0" @@ -284,15 +266,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "ar_archive_writer" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" -dependencies = [ - "object 0.37.3", -] - [[package]] name = "ark-bls12-377" version = "0.4.0" @@ -684,7 +657,7 @@ version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ - "addr2line 0.25.1", + "addr2line", "cfg-if", "libc", "miniz_oxide", @@ -721,12 +694,15 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" name = "benchmarks" version = "1.0.0-beta.5" dependencies = [ + "aggregator-app", + "aggregator-client", "alloc-stress", "anyhow", "clap", "compute-stress", "convert_case 0.11.0", "counter-bench", + "demo-client", "fs2", "gtest", "itertools 0.14.0", @@ -737,24 +713,18 @@ dependencies = [ "redirect-proxy", "redirect-proxy-client", "sails-client-gen-v2", + "sails-idl-gen", "sails-rs", "serde", "serde_json", + "storage-million", "storage-stress", "tempfile", "tokio", + "vft-stress", "wasmparser 0.244.0", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bindgen" version = "0.70.1" @@ -940,13 +910,12 @@ dependencies = [ [[package]] name = "builtins-common" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13a54b46c994eee08098e021332099c898037316c0776f9f09885a9dc37a3f5c" dependencies = [ "derive_more 2.1.1", "gbuiltin-bls381", "gbuiltin-eth-bridge", "gear-core", + "gear-workspace-hack", "gprimitives", "log", "parity-scale-codec", @@ -1451,24 +1420,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core-processor" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c6661d52b0cc6d2157640dffe0298e42a9babb38d4629198939b70b7a23aaa" -dependencies = [ - "actor-system-error", - "derive_more 2.1.1", - "gear-core", - "gear-core-backend", - "gear-core-errors", - "gear-lazy-pages-common", - "gear-wasm-instrument", - "gsys", - "log", - "parity-scale-codec", -] - [[package]] name = "corosensei" version = "0.3.3" @@ -1489,15 +1440,6 @@ dependencies = [ "sails-rs", ] -[[package]] -name = "cpp_demangle" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" -dependencies = [ - "cfg-if", -] - [[package]] name = "cpufeatures" version = "0.2.17" @@ -1516,15 +1458,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cranelift-entity" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" -dependencies = [ - "serde", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -2327,12 +2260,6 @@ dependencies = [ "rand 0.8.6", ] -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - [[package]] name = "fallible-iterator" version = "0.3.0" @@ -2454,9 +2381,8 @@ dependencies = [ [[package]] name = "frame-benchmarking" -version = "38.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a0f983d69640f90a0ce87f107cff07f6f8f7f5ef9334ffb6f37a9c6e224ca1d" +version = "38.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "frame-support", "frame-support-procedural", @@ -2468,12 +2394,12 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto", - "sp-core", - "sp-io", - "sp-runtime", - "sp-runtime-interface", - "sp-storage 21.0.0", + "sp-application-crypto 38.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-io 38.0.0", + "sp-runtime 39.0.1", + "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "static_assertions", ] @@ -2489,7 +2415,7 @@ dependencies = [ "scale-encode", "scale-info", "scale-type-resolver", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.18", ] @@ -2519,9 +2445,8 @@ dependencies = [ [[package]] name = "frame-support" -version = "38.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7dd8b9f161a8289e3b9fe6c1068519358dbff2270d38097a923d3d1b4459dca" +version = "38.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "aquamarine", "array-bytes", @@ -2538,32 +2463,31 @@ dependencies = [ "paste", "scale-info", "serde", - "serde_json", + "serde-json-wasm", "smallvec", "sp-api", - "sp-arithmetic", - "sp-core", + "sp-arithmetic 26.0.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-crypto-hashing-proc-macro", - "sp-debug-derive 14.0.0", + "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-genesis-builder", "sp-inherents", - "sp-io", + "sp-io 38.0.0", "sp-metadata-ir", - "sp-runtime", + "sp-runtime 39.0.1", "sp-staking", - "sp-state-machine", - "sp-std 14.0.0", - "sp-tracing 17.1.0", - "sp-weights", + "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-tracing 17.0.1", + "sp-weights 31.0.0", "static_assertions", "tt-call", ] [[package]] name = "frame-support-procedural" -version = "30.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da784d943f2a945be923ab081a7c0837355b38045c50945d7ec1a138e2f3c52" +version = "30.0.3" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "Inflector", "cfg-expr", @@ -2576,18 +2500,17 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "syn 2.0.117", ] [[package]] name = "frame-support-procedural-tools" -version = "13.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a088fd6fda5f53ff0c17fc7551ce8bd0ead14ba742228443c8196296a7369b" +version = "13.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "frame-support-procedural-tools-derive", - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -2596,8 +2519,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed971c6435503a099bdac99fe4c5bea08981709e5b5a0a8535a1856f48561191" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "proc-macro2", "quote", @@ -2607,8 +2529,7 @@ dependencies = [ [[package]] name = "frame-system" version = "38.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c7fa02f8c305496d2ae52edaecdb9d165f11afa965e05686d7d7dd1ce93611" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "cfg-if", "docify", @@ -2617,12 +2538,12 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std 14.0.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-io 38.0.0", + "sp-runtime 39.0.1", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-version", - "sp-weights", + "sp-weights 31.0.0", ] [[package]] @@ -2644,16 +2565,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "fs4" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e871a4cfa68bb224863b53149d973df1ac8d1ed2fa1d1bfc37ac1bb65dd37207" -dependencies = [ - "rustix 0.38.44", - "windows-sys 0.52.0", -] - [[package]] name = "funty" version = "2.0.0" @@ -2770,23 +2681,21 @@ dependencies = [ [[package]] name = "galloc" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d95f9357f55c811d9b1f5c7bb1ef3b3f0094dee912a2bdb5155eb46611d8f87" dependencies = [ "gear-dlmalloc", + "gear-workspace-hack", ] [[package]] name = "gbuiltin-bls381" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919a1d0d8397053524bd9785788afb5a4dedc5c50e72650dae6857df2409a3f1" dependencies = [ "ark-bls12-381", "ark-ec", "ark-ff", "ark-scale", "ark-serialize", + "gear-workspace-hack", "parity-scale-codec", "scale-info", ] @@ -2794,9 +2703,8 @@ dependencies = [ [[package]] name = "gbuiltin-eth-bridge" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ea714934fa347452f0b33afa8a9afcdaa4079ea2fc2d7ee46ecdd03681ed72" dependencies = [ + "gear-workspace-hack", "gprimitives", "parity-scale-codec", "scale-info", @@ -2813,11 +2721,11 @@ dependencies = [ "futures", "gear-core", "gear-node-wrapper", - "gear-utils", + "gear-utils 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "gsdk", "hex", "parity-scale-codec", - "sp-core", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.18", "url", ] @@ -2825,12 +2733,11 @@ dependencies = [ [[package]] name = "gcore" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "015093eb24aa69f98f6474a9547a51a1a9230204e55a1778e9c8588cd8ee6047" dependencies = [ "arrayvec 0.7.6", "gear-core-errors", "gear-stack-buffer", + "gear-workspace-hack", "gprimitives", "gsys", "paste", @@ -2840,8 +2747,6 @@ dependencies = [ [[package]] name = "gear-common" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6802013b852310c7a9e1e4e48e6bf56a877c4d746783ab3b93e521c7d70225f3" dependencies = [ "enum-iterator 1.5.0", "fail", @@ -2851,24 +2756,24 @@ dependencies = [ "gear-common-codegen", "gear-core", "gear-wasm-instrument", + "gear-workspace-hack", "gsys", "log", "primitive-types 0.12.2", "scale-decode", "scale-encode", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std 14.0.0", + "sp-arithmetic 26.0.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-io 38.0.0", + "sp-runtime 39.0.1", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", ] [[package]] name = "gear-common-codegen" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ec789cbf723ef6bcb29552cbbe72bd9a1f46a07feaab5a0647a2f42480cf8d" dependencies = [ + "gear-workspace-hack", "quote", "syn 2.0.117", ] @@ -2876,8 +2781,6 @@ dependencies = [ [[package]] name = "gear-core" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7172ef6fd588e780ed2d9f8e99f6cbc2c65a19e4fd61b4686cc5a74c04ab07b" dependencies = [ "blake2", "bytemuck", @@ -2886,6 +2789,7 @@ dependencies = [ "enum-iterator 1.5.0", "gear-core-errors", "gear-wasm-instrument", + "gear-workspace-hack", "gprimitives", "gsys", "hex", @@ -2905,8 +2809,6 @@ dependencies = [ [[package]] name = "gear-core-backend" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4af189ad011d19bf4a42acbfe946b267ef2224b74f9f87778387a2a545495c78" dependencies = [ "actor-system-error", "blake2", @@ -2918,6 +2820,7 @@ dependencies = [ "gear-sandbox", "gear-sandbox-env", "gear-wasm-instrument", + "gear-workspace-hack", "gsys", "log", "parity-scale-codec", @@ -2926,10 +2829,9 @@ dependencies = [ [[package]] name = "gear-core-errors" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a068b7e99afbedd710b959a20c8ac91b2cc77f8a0ce87af687b673bfbf26a809" dependencies = [ "enum-iterator 1.5.0", + "gear-workspace-hack", "parity-scale-codec", "scale-decode", "scale-encode", @@ -2938,6 +2840,23 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "gear-core-processor" +version = "1.10.0" +dependencies = [ + "actor-system-error", + "derive_more 2.1.1", + "gear-core", + "gear-core-backend", + "gear-core-errors", + "gear-lazy-pages-common", + "gear-wasm-instrument", + "gear-workspace-hack", + "gsys", + "log", + "parity-scale-codec", +] + [[package]] name = "gear-dlmalloc" version = "0.2.0" @@ -2954,8 +2873,6 @@ dependencies = [ [[package]] name = "gear-lazy-pages" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e94788c25494ddb5ca7a73b71b61429990caba31dea7e3b55e0dfe33ae3634c" dependencies = [ "cfg-if", "derive_more 2.1.1", @@ -2963,6 +2880,7 @@ dependencies = [ "gear-core", "gear-lazy-pages-common", "gear-sandbox-host", + "gear-workspace-hack", "libc", "log", "mach", @@ -2977,10 +2895,9 @@ dependencies = [ [[package]] name = "gear-lazy-pages-common" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1bd7e4e3d11c037298b2908c6ea1f3061a90bc98dbca188adf147599a88a96" dependencies = [ "gear-core", + "gear-workspace-hack", "num_enum", "parity-scale-codec", ] @@ -2988,12 +2905,11 @@ dependencies = [ [[package]] name = "gear-lazy-pages-native-interface" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affb988d8baa32dced50d696046e9a3470602abf3effe0fe06bb546dd05581d0" dependencies = [ "gear-core", "gear-lazy-pages", "gear-lazy-pages-common", + "gear-workspace-hack", "log", ] @@ -3012,17 +2928,16 @@ dependencies = [ [[package]] name = "gear-sandbox" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ca22d8209f2c3077c0c97063e0804bf4e1c5b60a4a2e6200da664311d1920c" dependencies = [ "gear-sandbox-env", "gear-sandbox-interface", "gear-wasmer-cache", + "gear-workspace-hack", "log", "parity-scale-codec", "pathdiff", - "sp-core", - "sp-std 14.0.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-wasm-interface-common", "wasmer", "wasmer-compiler", @@ -3033,30 +2948,28 @@ dependencies = [ [[package]] name = "gear-sandbox-env" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6570e31af2bd208e6ce48d6a5e86c62815e57465df3f812670f40021e6a6a38b" dependencies = [ + "gear-workspace-hack", "parity-scale-codec", - "sp-debug-derive 14.0.0", - "sp-std 14.0.0", + "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-wasm-interface-common", ] [[package]] name = "gear-sandbox-host" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c08257ac1e9ba954883fd229254fa54abc150cdc0c62380905f22d335c46c44" dependencies = [ "atomic_enum", "defer", "environmental", "gear-sandbox-env", "gear-wasmer-cache", - "gp-allocator", + "gear-workspace-hack", "log", "parity-scale-codec", "region", + "sp-allocator", "sp-wasm-interface-common", "tempfile", "thiserror 2.0.18", @@ -3068,32 +2981,45 @@ dependencies = [ [[package]] name = "gear-sandbox-interface" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9b75e5d27247deff3569a5cfa246e8e8289ea021fd16cc8a80b784eb85ff13" dependencies = [ "gear-sandbox-host", - "gp-runtime-interface", - "gp-wasm-interface", + "gear-workspace-hack", "log", - "parity-scale-codec", + "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-wasm-interface 21.0.1 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", ] [[package]] name = "gear-ss58" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94745919b16a98a5b8d7b7766bcb9127eb8bf013eff098ad828e452744bee6e6" dependencies = [ "blake2", "bs58", + "gear-workspace-hack", "hex", ] [[package]] name = "gear-stack-buffer" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d30f39766a97c23c841c55e868d93e01e146ace585aa36b291b4c46b9d0c15" +dependencies = [ + "gear-workspace-hack", +] + +[[package]] +name = "gear-utils" +version = "1.10.0" +dependencies = [ + "gear-core", + "gear-workspace-hack", + "hex", + "nonempty", + "parity-scale-codec", + "path-clean", + "serde", + "serde_json", + "tracing-subscriber", +] [[package]] name = "gear-utils" @@ -3108,14 +3034,12 @@ dependencies = [ "path-clean", "serde", "serde_json", - "tracing-subscriber 0.3.23", + "tracing-subscriber", ] [[package]] name = "gear-wasm-builder" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bed5dd34648a27e7dc217dc3b00eea4282bd451f4cf76bf627a05c5dbcc84f9" dependencies = [ "anyhow", "cargo_metadata 0.20.0", @@ -3124,6 +3048,7 @@ dependencies = [ "gear-core", "gear-wasm-instrument", "gear-wasm-optimizer", + "gear-workspace-hack", "itertools 0.13.0", "log", "pathdiff", @@ -3135,11 +3060,10 @@ dependencies = [ [[package]] name = "gear-wasm-instrument" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf819c76e5002586f7778e4acd10b775bd797e2baece1c31fa1b95a630eec238" dependencies = [ "derive_more 2.1.1", "enum-iterator 1.5.0", + "gear-workspace-hack", "log", "wasm-encoder 0.230.0", "wasmparser 0.230.0", @@ -3148,12 +3072,11 @@ dependencies = [ [[package]] name = "gear-wasm-optimizer" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed336b47eecdcee1c91dbedb8e6f6f702734f405afae68fdca3f1ddea4c3669d" dependencies = [ "anyhow", "colored", "gear-wasm-instrument", + "gear-workspace-hack", "log", "regex", "rustc_version", @@ -3164,18 +3087,22 @@ dependencies = [ [[package]] name = "gear-wasmer-cache" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1715238463dc4a8436e81bef5faadf28d2ad9f7320ef15e0f752da9568a71c48" dependencies = [ "bytes", "derive_more 2.1.1", - "fs4", + "gear-workspace-hack", "log", "uluru", "wasmer", "wasmer-cache", ] +[[package]] +name = "gear-workspace-hack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ac67118eef8a153d4df06bd865d0a5c3ac51888c238673a203a5c424db0327" + [[package]] name = "genco" version = "0.19.0" @@ -3257,25 +3184,14 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" -dependencies = [ - "fallible-iterator 0.2.0", - "indexmap 1.9.3", - "stable_deref_trait", -] - [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" dependencies = [ - "fallible-iterator 0.3.0", - "indexmap 2.14.0", + "fallible-iterator", + "indexmap", "stable_deref_trait", ] @@ -3302,77 +3218,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "gp-allocator" -version = "4.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93cfb4525b5362966a1971125ff8f9825e6a511403635dccf883273d20c43bdf" -dependencies = [ - "log", - "parity-scale-codec", - "sp-wasm-interface-common", - "thiserror 1.0.69", -] - -[[package]] -name = "gp-runtime-interface" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dfb3786bf8fe51099c67dfe7c9a925fe0bfb78ffe938aa07b598995ee48d0e3" -dependencies = [ - "bytes", - "gp-runtime-interface-proc-macro", - "gp-wasm-interface", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types 0.12.2", - "sp-externalities 0.20.0", - "sp-std 9.0.0", - "sp-storage 14.0.0", - "sp-tracing 10.0.0", - "static_assertions", -] - -[[package]] -name = "gp-runtime-interface-proc-macro" -version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1e4d907f86db64e8b9d9af0b5adfd59e62b4023fd88511e13d62028ed101f2c" -dependencies = [ - "Inflector", - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gp-wasm-interface" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de334e40147f969276463ad04d0ddfe519aebb6254832ea0637a300c6d637c11" -dependencies = [ - "anyhow", - "gp-allocator", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "sp-std 9.0.0", - "sp-wasm-interface-common", - "wasmtime", -] - [[package]] name = "gprimitives" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f79911d3caeb051ae07b0d4b57c196fdc6e105f7be7cc51365416d7248ca5ce" dependencies = [ "alloy-primitives", "alloy-sol-types", "bytemuck", "derive_more 2.1.1", "gear-ss58", + "gear-workspace-hack", "hex", "primitive-types 0.12.2", "scale-decode", @@ -3406,14 +3261,14 @@ dependencies = [ "gear-core-errors", "gsdk-codegen", "hex", - "indexmap 2.14.0", + "indexmap", "jsonrpsee", "log", "parity-scale-codec", "parking_lot", "serde", - "sp-core", - "sp-runtime", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime 39.0.5", "subxt", "thiserror 2.0.18", "url", @@ -3433,8 +3288,6 @@ dependencies = [ [[package]] name = "gstd" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232fec5b812a76c016c11faceef031fbe7608bb4db07051744201a2dabff1072" dependencies = [ "arrayvec 0.7.6", "const_format", @@ -3442,6 +3295,7 @@ dependencies = [ "galloc", "gcore", "gear-core-errors", + "gear-workspace-hack", "gprimitives", "gstd-codegen", "hashbrown 0.14.5", @@ -3455,9 +3309,8 @@ dependencies = [ [[package]] name = "gstd-codegen" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b347d845d9fdc17b47288d082fd7001390d0652acb5d221c29a1b3f16e9a238" dependencies = [ + "gear-workspace-hack", "gprimitives", "proc-macro2", "quote", @@ -3467,30 +3320,28 @@ dependencies = [ [[package]] name = "gsys" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b08a4ef9c0f6aed528b42117cea34f53af2a4a7f080c04b4356eb63adc9302" dependencies = [ "bytemuck", + "gear-workspace-hack", ] [[package]] name = "gtest" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cba01a0d56d5ff4a072ce3dfc142ecba271f097435985fe623a8b69918e785c" dependencies = [ "builtins-common", "cargo_toml", "colored", - "core-processor", "etc", "gear-common", "gear-core", "gear-core-errors", + "gear-core-processor", "gear-lazy-pages", "gear-lazy-pages-common", "gear-lazy-pages-native-interface", - "gear-utils", + "gear-utils 1.10.0", + "gear-workspace-hack", "gprimitives", "gsys", "log", @@ -3499,7 +3350,7 @@ dependencies = [ "rand 0.8.6", "scale-info", "thiserror 2.0.18", - "tracing-subscriber 0.3.23", + "tracing-subscriber", ] [[package]] @@ -3514,7 +3365,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.14.0", + "indexmap", "slab", "tokio", "tokio-util", @@ -3552,12 +3403,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.13.2" @@ -3617,12 +3462,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.5.2" @@ -3996,17 +3835,6 @@ dependencies = [ "quote", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - [[package]] name = "indexmap" version = "2.14.0" @@ -4074,24 +3902,13 @@ dependencies = [ "num-traits", ] -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "is-terminal" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ - "hermit-abi 0.5.2", + "hermit-abi", "libc", "windows-sys 0.61.2", ] @@ -4377,7 +4194,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.8.10", + "regex-syntax", "string_cache", "term", "tiny-keccak", @@ -4391,7 +4208,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.14", + "regex-automata", ] [[package]] @@ -4518,12 +4335,6 @@ dependencies = [ "nalgebra", ] -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -4575,8 +4386,8 @@ dependencies = [ "fnv", "proc-macro2", "quote", - "regex-automata 0.4.14", - "regex-syntax 0.8.10", + "regex-automata", + "regex-syntax", "syn 2.0.117", ] @@ -4686,22 +4497,13 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "matchers" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" -dependencies = [ - "regex-automata 0.1.10", -] - [[package]] name = "matchers" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.4.14", + "regex-automata", ] [[package]] @@ -4720,15 +4522,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "memfd" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" -dependencies = [ - "rustix 1.1.4", -] - [[package]] name = "memmap2" version = "0.6.2" @@ -4756,15 +4549,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -5085,26 +4869,13 @@ dependencies = [ [[package]] name = "numerated" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb9cd51526a9244bccb59b31a21af8fabc6a70a23707ff0e9095abda2a5033a" dependencies = [ "derive_more 2.1.1", + "gear-workspace-hack", "num-traits", "scale-info", ] -[[package]] -name = "object" -version = "0.30.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" -dependencies = [ - "crc32fast", - "hashbrown 0.13.2", - "indexmap 1.9.3", - "memchr", -] - [[package]] name = "object" version = "0.32.2" @@ -5114,7 +4885,7 @@ dependencies = [ "crc32fast", "flate2", "hashbrown 0.14.5", - "indexmap 2.14.0", + "indexmap", "memchr", "ruzstd 0.5.0", ] @@ -5217,7 +4988,7 @@ version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -5230,7 +5001,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" [[package]] -name = "parking" +name = "parity-wasm" +version = "0.45.0" +source = "git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext#bad3e1ec78f655f3eec5a0c9c12a3546c8c9d432" + +[[package]] +name = "parking" version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" @@ -5379,7 +5155,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.14.0", + "indexmap", ] [[package]] @@ -5519,7 +5295,7 @@ checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.5.2", + "hermit-abi", "pin-project-lite", "rustix 1.1.4", "windows-sys 0.61.2", @@ -5628,16 +5404,6 @@ dependencies = [ "uint 0.10.0", ] -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - [[package]] name = "proc-macro-crate" version = "3.5.0" @@ -5736,16 +5502,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "psm" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" -dependencies = [ - "ar_archive_writer", - "cc", -] - [[package]] name = "ptr_meta" version = "0.1.4" @@ -6013,17 +5769,8 @@ checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.14", - "regex-syntax 0.8.10", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -6034,15 +5781,9 @@ checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.10", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.10" @@ -6109,7 +5850,7 @@ dependencies = [ "bytecheck 0.8.2", "bytes", "hashbrown 0.17.0", - "indexmap 2.14.0", + "indexmap", "munge", "ptr_meta 0.3.1", "rancor", @@ -6232,20 +5973,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "rustix" -version = "0.36.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.45.0", -] - [[package]] name = "rustix" version = "0.38.44" @@ -6560,7 +6287,7 @@ dependencies = [ name = "sails-reflect-hash-derive" version = "1.0.0-beta.5" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -6645,7 +6372,7 @@ version = "1.0.0-beta.5" dependencies = [ "insta", "prettyplease", - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -6721,7 +6448,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17020f2d59baabf2ddcdc20a4e567f8210baf089b8a8d4785f5fd5e716f92038" dependencies = [ "darling 0.20.11", - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -6747,7 +6474,7 @@ version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -6931,6 +6658,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-json-wasm" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" +dependencies = [ + "serde", +] + [[package]] name = "serde-untagged" version = "0.1.9" @@ -7312,11 +7048,21 @@ dependencies = [ "sha1", ] +[[package]] +name = "sp-allocator" +version = "29.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "log", + "parity-scale-codec", + "sp-wasm-interface-common", + "thiserror 1.0.69", +] + [[package]] name = "sp-api" version = "34.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbce492e0482134128b7729ea36f5ef1a9f9b4de2d48ff8dde7b5e464e28ce75" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "docify", "hash-db", @@ -7324,27 +7070,26 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api-proc-macro", - "sp-core", - "sp-externalities 0.29.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-metadata-ir", - "sp-runtime", - "sp-runtime-interface", - "sp-state-machine", - "sp-trie", + "sp-runtime 39.0.1", + "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-version", "thiserror 1.0.69", ] [[package]] name = "sp-api-proc-macro" -version = "20.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e3ec5a1a14307e21d2356e73e01573b6c82330a7e30eaceed59a90161b0c2d2" +version = "20.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "Inflector", "blake2", "expander", - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -7359,8 +7104,34 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-io", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-io 38.0.2", +] + +[[package]] +name = "sp-application-crypto" +version = "38.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-io 38.0.0", +] + +[[package]] +name = "sp-arithmetic" +version = "26.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "docify", + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "static_assertions", ] [[package]] @@ -7411,14 +7182,60 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-crypto-hashing", - "sp-debug-derive 14.0.0", - "sp-externalities 0.29.0", - "sp-runtime-interface", - "sp-std 14.0.0", - "sp-storage 21.0.0", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime-interface 28.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "ss58-registry", - "substrate-bip39", + "substrate-bip39 0.6.1", + "thiserror 1.0.69", + "tracing", + "w3f-bls", + "zeroize", +] + +[[package]] +name = "sp-core" +version = "34.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "array-bytes", + "bitflags 1.3.2", + "blake2", + "bounded-collections", + "bs58", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde 0.4.0", + "itertools 0.11.0", + "k256", + "libsecp256k1", + "log", + "merlin", + "parity-bip39", + "parity-scale-codec", + "parking_lot", + "paste", + "primitive-types 0.12.2", + "rand 0.8.6", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "ss58-registry", + "substrate-bip39 0.6.0", "thiserror 1.0.69", "tracing", "w3f-bls", @@ -7439,22 +7256,34 @@ dependencies = [ "twox-hash 1.6.3", ] +[[package]] +name = "sp-crypto-hashing" +version = "0.1.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "blake2b_simd", + "byteorder", + "digest 0.10.7", + "sha2 0.10.9", + "sha3", + "twox-hash 1.6.3", +] + [[package]] name = "sp-crypto-hashing-proc-macro" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b85d0f1f1e44bd8617eb2a48203ee854981229e3e79e6f468c7175d5fd37489b" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "quote", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "syn 2.0.117", ] [[package]] name = "sp-debug-derive" -version = "9.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12dae7cf6c1e825d13ffd4ce16bd9309db7c539929d0302b4443ed451a9f4e5" +checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2", "quote", @@ -7464,8 +7293,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "proc-macro2", "quote", @@ -7474,54 +7302,76 @@ dependencies = [ [[package]] name = "sp-externalities" -version = "0.20.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3313e2c5f2523b06062e541dff9961bde88ad5a28861621dc7b7b47a32bb0f7c" +checksum = "a904407d61cb94228c71b55a9d3708e9d6558991f9e83bd42bd91df37a159d30" dependencies = [ "environmental", "parity-scale-codec", - "sp-std 9.0.0", - "sp-storage 14.0.0", + "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-externalities" version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a904407d61cb94228c71b55a9d3708e9d6558991f9e83bd42bd91df37a159d30" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "environmental", "parity-scale-codec", - "sp-storage 21.0.0", + "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", ] [[package]] name = "sp-genesis-builder" version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a646ed222fd86d5680faa4a8967980eb32f644cae6c8523e1c689a6deda3e8" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "parity-scale-codec", "scale-info", "serde_json", "sp-api", - "sp-runtime", + "sp-runtime 39.0.1", ] [[package]] name = "sp-inherents" version = "34.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afffbddc380d99a90c459ba1554bbbc01d62e892de9f1485af6940b89c4c0d57" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "async-trait", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-runtime", + "sp-runtime 39.0.1", "thiserror 1.0.69", ] +[[package]] +name = "sp-io" +version = "38.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "bytes", + "docify", + "ed25519-dalek", + "libsecp256k1", + "log", + "parity-scale-codec", + "polkavm-derive", + "rustversion", + "secp256k1", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-keystore 0.40.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-tracing 17.0.1", + "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "tracing", + "tracing-core", +] + [[package]] name = "sp-io" version = "38.0.2" @@ -7537,14 +7387,14 @@ dependencies = [ "polkavm-derive", "rustversion", "secp256k1", - "sp-core", - "sp-crypto-hashing", - "sp-externalities 0.29.0", - "sp-keystore", - "sp-runtime-interface", - "sp-state-machine", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-keystore 0.40.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime-interface 28.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-state-machine 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-tracing 17.1.0", - "sp-trie", + "sp-trie 37.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", "tracing-core", ] @@ -7557,21 +7407,41 @@ checksum = "0248b4d784cb4a01472276928977121fa39d977a5bb24793b6b15e64b046df42" dependencies = [ "parity-scale-codec", "parking_lot", - "sp-core", - "sp-externalities 0.29.0", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-keystore" +version = "0.40.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "parity-scale-codec", + "parking_lot", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", ] [[package]] name = "sp-metadata-ir" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a616fa51350b35326682a472ee8e6ba742fdacb18babac38ecd46b3e05ead869" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "frame-metadata 16.0.0", "parity-scale-codec", "scale-info", ] +[[package]] +name = "sp-panic-handler" +version = "13.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + [[package]] name = "sp-panic-handler" version = "13.0.2" @@ -7582,6 +7452,32 @@ dependencies = [ "regex", ] +[[package]] +name = "sp-runtime" +version = "39.0.1" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "docify", + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "num-traits", + "parity-scale-codec", + "paste", + "rand 0.8.6", + "scale-info", + "serde", + "simple-mermaid", + "sp-application-crypto 38.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-arithmetic 26.0.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-io 38.0.0", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-weights 31.0.0", + "tracing", +] + [[package]] name = "sp-runtime" version = "39.0.5" @@ -7600,12 +7496,12 @@ dependencies = [ "scale-info", "serde", "simple-mermaid", - "sp-application-crypto", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-std 14.0.0", - "sp-weights", + "sp-application-crypto 38.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-arithmetic 26.1.0", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-io 38.0.2", + "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-weights 31.1.0", "tracing", ] @@ -7620,12 +7516,31 @@ dependencies = [ "parity-scale-codec", "polkavm-derive", "primitive-types 0.12.2", - "sp-externalities 0.29.0", - "sp-runtime-interface-proc-macro", - "sp-std 14.0.0", - "sp-storage 21.0.0", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime-interface-proc-macro 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-tracing 17.1.0", - "sp-wasm-interface", + "sp-wasm-interface 21.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface" +version = "28.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "polkavm-derive", + "primitive-types 0.12.2", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime-interface-proc-macro 18.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-tracing 17.0.1", + "sp-wasm-interface 21.0.1 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "static_assertions", ] @@ -7637,7 +7552,20 @@ checksum = "0195f32c628fee3ce1dfbbf2e7e52a30ea85f3589da9fe62a8b816d70fc06294" dependencies = [ "Inflector", "expander", - "proc-macro-crate 3.5.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "18.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "Inflector", + "expander", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -7646,15 +7574,14 @@ dependencies = [ [[package]] name = "sp-staking" version = "36.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a73eedb4b85f4cd420d31764827546aa22f82ce1646d0fd258993d051de7a90" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-runtime", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime 39.0.1", ] [[package]] @@ -7669,71 +7596,80 @@ dependencies = [ "parking_lot", "rand 0.8.6", "smallvec", - "sp-core", - "sp-externalities 0.29.0", - "sp-panic-handler", - "sp-trie", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-panic-handler 13.0.2", + "sp-trie 37.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.69", "tracing", "trie-db", ] [[package]] -name = "sp-std" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53458e3c57df53698b3401ec0934bea8e8cfce034816873c0b0abbd83d7bac0d" +name = "sp-state-machine" +version = "0.43.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "parking_lot", + "rand 0.8.6", + "smallvec", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-panic-handler 13.0.0", + "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "thiserror 1.0.69", + "tracing", + "trie-db", +] [[package]] name = "sp-std" -version = "9.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5bbc9339227d1b6a9b7ccd9b2920c818653d40eef1512f1e2e824d72e7a336" +checksum = "12f8ee986414b0a9ad741776762f4083cd3a5128449b982a3919c4df36874834" [[package]] name = "sp-std" version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8ee986414b0a9ad741776762f4083cd3a5128449b982a3919c4df36874834" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" [[package]] name = "sp-storage" -version = "14.0.0" +version = "21.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21245c3a7799ff6d3f1f159b496f9ac72eb32cd6fe68c6f73013155289aa9f1" +checksum = "99c82989b3a4979a7e1ad848aad9f5d0b4388f1f454cc131766526601ab9e8f8" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 9.0.0", - "sp-std 9.0.0", + "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-storage" version = "21.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c82989b3a4979a7e1ad848aad9f5d0b4388f1f454cc131766526601ab9e8f8" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 14.0.0", + "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", ] [[package]] name = "sp-tracing" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357f7591980dd58305956d32f8f6646d0a8ea9ea0e7e868e46f53b68ddf00cec" +version = "17.0.1" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "parity-scale-codec", - "sp-std 8.0.0", "tracing", "tracing-core", - "tracing-subscriber 0.2.25", + "tracing-subscriber", ] [[package]] @@ -7745,7 +7681,7 @@ dependencies = [ "parity-scale-codec", "tracing", "tracing-core", - "tracing-subscriber 0.3.23", + "tracing-subscriber", ] [[package]] @@ -7764,8 +7700,31 @@ dependencies = [ "rand 0.8.6", "scale-info", "schnellru", - "sp-core", - "sp-externalities 0.29.0", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.69", + "tracing", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-trie" +version = "37.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "ahash", + "hash-db", + "lazy_static", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot", + "rand 0.8.6", + "scale-info", + "schnellru", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "thiserror 1.0.69", "tracing", "trie-db", @@ -7775,17 +7734,16 @@ dependencies = [ [[package]] name = "sp-version" version = "37.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d521a405707b5be561367cd3d442ff67588993de24062ce3adefcf8437ee9fe1" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", - "parity-wasm", + "parity-wasm 0.45.0 (registry+https://github.com/rust-lang/crates.io-index)", "scale-info", "serde", "sp-crypto-hashing-proc-macro", - "sp-runtime", - "sp-std 14.0.0", + "sp-runtime 39.0.1", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-version-proc-macro", "thiserror 1.0.69", ] @@ -7793,8 +7751,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aee8f6730641a65fcf0c8f9b1e448af4b3bb083d08058b47528188bccc7b7a7" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -7814,17 +7771,43 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "sp-wasm-interface" +version = "21.0.1" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-allocator", + "sp-wasm-interface-common", +] + [[package]] name = "sp-wasm-interface-common" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03223ee788e1490f6341eb5b76501f83abf931fa8b5d83b5f49a4fecaf83f4ae" +version = "7.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "parity-scale-codec", - "sp-std 9.0.0", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "wasmi 0.13.2", ] +[[package]] +name = "sp-weights" +version = "31.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "bounded-collections", + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic 26.0.0", + "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", +] + [[package]] name = "sp-weights" version = "31.1.0" @@ -7836,8 +7819,8 @@ dependencies = [ "scale-info", "serde", "smallvec", - "sp-arithmetic", - "sp-debug-derive 14.0.0", + "sp-arithmetic 26.1.0", + "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -7889,6 +7872,14 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "storage-million" +version = "1.0.0-beta.5" +dependencies = [ + "sails-rs", + "sails-storage", +] + [[package]] name = "storage-stress" version = "1.0.0-beta.5" @@ -7932,6 +7923,18 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "substrate-bip39" +version = "0.6.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "hmac 0.12.1", + "pbkdf2", + "schnorrkel", + "sha2 0.10.9", + "zeroize", +] + [[package]] name = "substrate-bip39" version = "0.6.1" @@ -7973,7 +7976,7 @@ dependencies = [ "scale-value", "serde", "serde_json", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "subxt-core", "subxt-lightclient", "subxt-macro", @@ -8029,7 +8032,7 @@ dependencies = [ "scale-value", "serde", "serde_json", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "subxt-metadata", "thiserror 2.0.18", "tracing", @@ -8080,7 +8083,7 @@ dependencies = [ "hashbrown 0.14.5", "parity-scale-codec", "scale-info", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.18", ] @@ -8427,7 +8430,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ - "indexmap 2.14.0", + "indexmap", "serde_core", "serde_spanned 1.1.1", "toml_datetime 1.1.1+spec-1.1.0", @@ -8454,24 +8457,13 @@ dependencies = [ "serde_core", ] -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.14.0", - "toml_datetime 0.6.11", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.14.0", + "indexmap", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -8485,7 +8477,7 @@ version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ - "indexmap 2.14.0", + "indexmap", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", @@ -8573,17 +8565,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -8595,55 +8576,23 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-serde" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" -dependencies = [ - "ansi_term", - "chrono", - "lazy_static", - "matchers 0.0.1", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log 0.1.4", - "tracing-serde", -] - [[package]] name = "tracing-subscriber" version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ - "matchers 0.2.0", + "matchers", "nu-ansi-term", "once_cell", - "regex-automata 0.4.14", + "regex-automata", "sharded-slab", "smallvec", "thread_local", "time", "tracing", "tracing-core", - "tracing-log 0.2.0", + "tracing-log", ] [[package]] @@ -8858,6 +8807,14 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vft-stress" +version = "1.0.0-beta.5" +dependencies = [ + "sails-rs", + "sails-storage", +] + [[package]] name = "w3f-bls" version = "0.1.9" @@ -9011,7 +8968,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.14.0", + "indexmap", "wasm-encoder 0.244.0", "wasmparser 0.244.0", ] @@ -9027,7 +8984,7 @@ dependencies = [ "cfg-if", "cmake", "derive_more 2.1.1", - "indexmap 2.14.0", + "indexmap", "js-sys", "more-asserts", "paste", @@ -9132,7 +9089,7 @@ dependencies = [ "enumset", "getrandom 0.2.17", "hex", - "indexmap 2.14.0", + "indexmap", "more-asserts", "rkyv", "sha2 0.10.9", @@ -9155,7 +9112,7 @@ dependencies = [ "dashmap", "enum-iterator 0.7.0", "fnv", - "indexmap 2.14.0", + "indexmap", "libc", "libunwind", "mach2", @@ -9172,10 +9129,9 @@ dependencies = [ [[package]] name = "wasmi" version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" dependencies = [ - "parity-wasm", + "parity-wasm 0.45.0 (git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext)", "wasmi-validation", "wasmi_core 0.2.1", ] @@ -9215,10 +9171,9 @@ dependencies = [ [[package]] name = "wasmi-validation" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" dependencies = [ - "parity-wasm", + "parity-wasm 0.45.0 (git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext)", ] [[package]] @@ -9241,8 +9196,7 @@ checksum = "e80d6b275b1c922021939d561574bf376613493ae2b61c6963b15db0e8813562" [[package]] name = "wasmi_core" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" dependencies = [ "downcast-rs", "libm", @@ -9291,16 +9245,6 @@ dependencies = [ "wasmi_core 0.40.0", ] -[[package]] -name = "wasmparser" -version = "0.102.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" -dependencies = [ - "indexmap 1.9.3", - "url", -] - [[package]] name = "wasmparser" version = "0.221.3" @@ -9326,7 +9270,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" dependencies = [ "bitflags 2.11.1", - "indexmap 2.14.0", + "indexmap", ] [[package]] @@ -9337,7 +9281,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.1", "hashbrown 0.15.5", - "indexmap 2.14.0", + "indexmap", "semver", "serde", ] @@ -9351,138 +9295,6 @@ dependencies = [ "indexmap-nostd", ] -[[package]] -name = "wasmtime" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" -dependencies = [ - "anyhow", - "bincode", - "cfg-if", - "indexmap 1.9.3", - "libc", - "log", - "object 0.30.4", - "once_cell", - "paste", - "psm", - "serde", - "target-lexicon", - "wasmparser 0.102.0", - "wasmtime-environ", - "wasmtime-jit", - "wasmtime-runtime", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-asm-macros" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "wasmtime-environ" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" -dependencies = [ - "anyhow", - "cranelift-entity", - "gimli 0.27.3", - "indexmap 1.9.3", - "log", - "object 0.30.4", - "serde", - "target-lexicon", - "thiserror 1.0.69", - "wasmparser 0.102.0", - "wasmtime-types", -] - -[[package]] -name = "wasmtime-jit" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" -dependencies = [ - "addr2line 0.19.0", - "anyhow", - "bincode", - "cfg-if", - "cpp_demangle", - "gimli 0.27.3", - "log", - "object 0.30.4", - "rustc-demangle", - "serde", - "target-lexicon", - "wasmtime-environ", - "wasmtime-jit-icache-coherence", - "wasmtime-runtime", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-jit-debug" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" -dependencies = [ - "once_cell", -] - -[[package]] -name = "wasmtime-jit-icache-coherence" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-runtime" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" -dependencies = [ - "anyhow", - "cc", - "cfg-if", - "indexmap 1.9.3", - "libc", - "log", - "mach", - "memfd", - "memoffset 0.8.0", - "paste", - "rand 0.8.6", - "rustix 0.36.17", - "wasmtime-asm-macros", - "wasmtime-environ", - "wasmtime-jit-debug", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-types" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" -dependencies = [ - "cranelift-entity", - "serde", - "thiserror 1.0.69", - "wasmparser 0.102.0", -] - [[package]] name = "web-time" version = "1.1.0" @@ -9632,15 +9444,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -9683,21 +9486,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -9720,12 +9508,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -9738,12 +9520,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -9756,12 +9532,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -9780,12 +9550,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -9798,12 +9562,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -9816,12 +9574,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -9834,27 +9586,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.7.15" @@ -9907,7 +9644,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.14.0", + "indexmap", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -9938,7 +9675,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.11.1", - "indexmap 2.14.0", + "indexmap", "log", "serde", "serde_derive", @@ -9957,7 +9694,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.14.0", + "indexmap", "log", "semver", "serde", diff --git a/Cargo.toml b/Cargo.toml index e886accb7..d211e9f55 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,6 +56,8 @@ members = [ "benchmarks/compute-stress", "benchmarks/counter-bench", "benchmarks/storage-stress", + "benchmarks/vft-stress", + "benchmarks/storage-million", "benchmarks/ping-pong/app", "benchmarks/ping-pong/client", "benchmarks", diff --git a/benchmarks/Cargo.toml b/benchmarks/Cargo.toml index c737e5466..00c2f7f02 100644 --- a/benchmarks/Cargo.toml +++ b/benchmarks/Cargo.toml @@ -6,6 +6,10 @@ edition.workspace = true license.workspace = true repository.workspace = true +[features] +default = [] +gas-profile = ["gtest/gas-profile", "vft-stress/wasm-profile"] + [[bin]] name = "bench-analyzer" path = "src/bin/bench_analyzer.rs" @@ -21,20 +25,28 @@ itertools.workspace = true [build-dependencies] sails-rs = { workspace = true, features = ["build"] } +sails-idl-gen = { workspace = true, features = ["std"] } alloc-stress = { path = "alloc-stress" } compute-stress = { path = "compute-stress" } counter-bench = { path = "counter-bench" } storage-stress = { path = "storage-stress" } +vft-stress = { path = "vft-stress" } +storage-million = { path = "storage-million" } convert_case.workspace = true sails-client-gen-v2.workspace = true [dev-dependencies] gtest.workspace = true sails-rs = { workspace = true, features = ["gtest"] } +aggregator-app = { path = "../examples/aggregator/app" } +aggregator-client = { path = "../examples/aggregator/client" } alloc-stress = { path = "alloc-stress" } compute-stress = { path = "compute-stress" } counter-bench = { path = "counter-bench" } +demo-client = { path = "../examples/demo/client" } storage-stress = { path = "storage-stress" } +vft-stress = { path = "vft-stress" } +storage-million = { path = "storage-million" } ping-pong-bench-app = { path = "ping-pong/app" } ping-pong-stack = { path = "../examples/ping-pong-stack" } redirect-app = { path = "../examples/redirect/app" } diff --git a/benchmarks/bench_data.json b/benchmarks/bench_data.json index 0b4961cfb..cb0de8553 100644 --- a/benchmarks/bench_data.json +++ b/benchmarks/bench_data.json @@ -30,215 +30,583 @@ "20": 66032420478 }, "storage": { - "fixed_allowance_insert_fresh_0": 1080478753, - "fixed_allowance_insert_fresh_1024": 1081708365, - "fixed_allowance_insert_fresh_16": 1080478753, - "fixed_allowance_insert_fresh_256": 1080478753, - "fixed_allowance_prepare_0": 11691762354, - "fixed_allowance_prepare_1024": 20395014497, - "fixed_allowance_prepare_16": 11891343494, - "fixed_allowance_prepare_256": 13803197942, - "fixed_allowance_read_existing_1024": 854891432, - "fixed_allowance_read_existing_16": 854891432, - "fixed_allowance_read_existing_256": 854891432, - "fixed_allowance_read_missing_0": 849118236, - "fixed_allowance_read_missing_1024": 850347848, - "fixed_allowance_read_missing_16": 849118236, - "fixed_allowance_read_missing_256": 849118236, - "fixed_allowance_remove_1024": 1084110439, - "fixed_allowance_remove_16": 1084110439, - "fixed_allowance_remove_256": 1084110439, - "fixed_allowance_update_existing_1024": 970181732, - "fixed_allowance_update_existing_16": 970181732, - "fixed_allowance_update_existing_256": 970181732, - "fixed_balance_insert_fresh_0": 1076533775, - "fixed_balance_insert_fresh_1024": 1077774256, - "fixed_balance_insert_fresh_16": 1076533775, - "fixed_balance_insert_fresh_256": 1077774256, - "fixed_balance_prepare_0": 10118393122, - "fixed_balance_prepare_1024": 14186755208, - "fixed_balance_prepare_16": 10254856390, - "fixed_balance_prepare_256": 11190432070, - "fixed_balance_read_existing_1024": 849854002, - "fixed_balance_read_existing_16": 849854002, - "fixed_balance_read_existing_256": 849854002, - "fixed_balance_read_missing_0": 846741105, - "fixed_balance_read_missing_1024": 847981586, - "fixed_balance_read_missing_16": 846741105, - "fixed_balance_read_missing_256": 847981586, - "fixed_balance_remove_1024": 1079072972, - "fixed_balance_remove_16": 1079072972, - "fixed_balance_remove_256": 1079072972, - "fixed_balance_update_existing_1024": 965144302, - "fixed_balance_update_existing_16": 965144302, - "fixed_balance_update_existing_256": 965144302, - "hashmap_allowance_insert_fresh_0": 1068848248, - "hashmap_allowance_insert_fresh_1024": 1293302336, - "hashmap_allowance_insert_fresh_16": 923683548, - "hashmap_allowance_insert_fresh_256": 1447667698, - "hashmap_allowance_prepare_0": 5867760965, - "hashmap_allowance_prepare_1024": 20303139327, - "hashmap_allowance_prepare_16": 6180003273, - "hashmap_allowance_prepare_256": 9614976891, - "hashmap_allowance_read_existing_1024": 1221407908, - "hashmap_allowance_read_existing_16": 926430017, - "hashmap_allowance_read_existing_256": 1225167720, - "hashmap_allowance_read_missing_0": 1073991570, - "hashmap_allowance_read_missing_1024": 1140349789, - "hashmap_allowance_read_missing_16": 921177688, - "hashmap_allowance_read_missing_256": 1180132590, - "hashmap_allowance_remove_1024": 1300111758, - "hashmap_allowance_remove_16": 926574295, - "hashmap_allowance_remove_256": 1319983389, - "hashmap_allowance_update_existing_1024": 1300063555, - "hashmap_allowance_update_existing_16": 926526092, - "hashmap_allowance_update_existing_256": 1319954955, - "hashmap_balance_insert_fresh_0": 1066973079, - "hashmap_balance_insert_fresh_1024": 1137061805, - "hashmap_balance_insert_fresh_16": 921808379, - "hashmap_balance_insert_fresh_256": 1371274460, - "hashmap_balance_prepare_0": 5867760965, - "hashmap_balance_prepare_1024": 15861084871, - "hashmap_balance_prepare_16": 6127491010, - "hashmap_balance_prepare_256": 8439301099, - "hashmap_balance_read_existing_1024": 1067742829, - "hashmap_balance_read_existing_16": 923211625, - "hashmap_balance_read_existing_256": 1182166527, - "hashmap_balance_read_missing_0": 1073929848, - "hashmap_balance_read_missing_1024": 1139830487, - "hashmap_balance_read_missing_16": 920658386, - "hashmap_balance_read_missing_256": 1179613288, - "hashmap_balance_remove_1024": 1296891036, - "hashmap_balance_remove_16": 923353573, - "hashmap_balance_remove_256": 1336673837, - "hashmap_balance_update_existing_1024": 1142477964, - "hashmap_balance_update_existing_16": 923305863, - "hashmap_balance_update_existing_256": 1336626127, - "raw_static_allowance_insert_fresh_0": 1086612355, - "raw_static_allowance_insert_fresh_1024": 1088913060, - "raw_static_allowance_insert_fresh_16": 1086612355, - "raw_static_allowance_insert_fresh_256": 1086612355, - "raw_static_allowance_prepare_0": 10905039291, - "raw_static_allowance_prepare_1024": 26410108594, - "raw_static_allowance_prepare_16": 11202759839, - "raw_static_allowance_prepare_256": 14612480409, - "raw_static_allowance_read_existing_1024": 856012119, - "raw_static_allowance_read_existing_16": 856012119, - "raw_static_allowance_read_existing_256": 856012119, - "raw_static_allowance_read_missing_0": 853747519, - "raw_static_allowance_read_missing_1024": 856048224, - "raw_static_allowance_read_missing_16": 853747519, - "raw_static_allowance_read_missing_256": 853747519, - "raw_static_allowance_remove_1024": 1085296658, - "raw_static_allowance_remove_16": 1085296658, - "raw_static_allowance_remove_256": 1085296658, - "raw_static_allowance_update_existing_1024": 972008088, - "raw_static_allowance_update_existing_16": 972008088, - "raw_static_allowance_update_existing_256": 972008088, - "raw_static_balance_insert_fresh_0": 1080553713, - "raw_static_balance_insert_fresh_1024": 1082853862, - "raw_static_balance_insert_fresh_16": 1080553713, - "raw_static_balance_insert_fresh_256": 1082853862, - "raw_static_balance_prepare_0": 9331490552, - "raw_static_balance_prepare_1024": 17518663566, - "raw_static_balance_prepare_16": 9532272828, - "raw_static_balance_prepare_256": 11432702398, - "raw_static_balance_read_existing_1024": 850999746, - "raw_static_balance_read_existing_16": 850999746, - "raw_static_balance_read_existing_256": 850999746, - "raw_static_balance_read_missing_0": 849050075, - "raw_static_balance_read_missing_1024": 851350224, - "raw_static_balance_read_missing_16": 849050075, - "raw_static_balance_read_missing_256": 851350224, - "raw_static_balance_remove_1024": 1080284285, - "raw_static_balance_remove_16": 1080284285, - "raw_static_balance_remove_256": 1080284285, - "raw_static_balance_update_existing_1024": 966991863, - "raw_static_balance_update_existing_16": 966991863, - "raw_static_balance_update_existing_256": 966991863, - "sails_fixed_allowance_insert_fresh_0": 1080952389, - "sails_fixed_allowance_insert_fresh_1024": 1083473052, - "sails_fixed_allowance_insert_fresh_16": 1080952389, - "sails_fixed_allowance_insert_fresh_256": 1080952389, - "sails_fixed_allowance_prepare_0": 11316577386, - "sails_fixed_allowance_prepare_1024": 26302715280, - "sails_fixed_allowance_prepare_16": 11528096489, - "sails_fixed_allowance_prepare_256": 15055648414, - "sails_fixed_allowance_read_existing_1024": 855438899, - "sails_fixed_allowance_read_existing_16": 855438899, - "sails_fixed_allowance_read_existing_256": 855438899, - "sails_fixed_allowance_read_missing_0": 849850001, - "sails_fixed_allowance_read_missing_1024": 852388685, - "sails_fixed_allowance_read_missing_16": 849850001, - "sails_fixed_allowance_read_missing_256": 849850001, - "sails_fixed_allowance_remove_1024": 1084636051, - "sails_fixed_allowance_remove_16": 1084636051, - "sails_fixed_allowance_remove_256": 1084636051, - "sails_fixed_allowance_update_existing_1024": 972138658, - "sails_fixed_allowance_update_existing_16": 972138658, - "sails_fixed_allowance_update_existing_256": 972138658, - "sails_fixed_balance_insert_fresh_0": 1077144137, - "sails_fixed_balance_insert_fresh_1024": 1078413479, - "sails_fixed_balance_insert_fresh_16": 1077144137, - "sails_fixed_balance_insert_fresh_256": 1077778808, - "sails_fixed_balance_prepare_0": 9743106061, - "sails_fixed_balance_prepare_1024": 14436536557, - "sails_fixed_balance_prepare_16": 9889335121, - "sails_fixed_balance_prepare_256": 10971397681, - "sails_fixed_balance_read_existing_1024": 850699257, - "sails_fixed_balance_read_existing_16": 850699257, - "sails_fixed_balance_read_existing_256": 850699257, - "sails_fixed_balance_read_missing_0": 847625335, - "sails_fixed_balance_read_missing_1024": 848894677, - "sails_fixed_balance_read_missing_16": 847625335, - "sails_fixed_balance_read_missing_256": 848260006, - "sails_fixed_balance_remove_1024": 1079896409, - "sails_fixed_balance_remove_16": 1079896409, - "sails_fixed_balance_remove_256": 1079896409, - "sails_fixed_balance_update_existing_1024": 965815430, - "sails_fixed_balance_update_existing_16": 965815430, - "sails_fixed_balance_update_existing_256": 965815430, - "sails_static_allowance_insert_fresh_0": 926858230, - "sails_static_allowance_insert_fresh_1024": 930873664, - "sails_static_allowance_insert_fresh_16": 926858230, - "sails_static_allowance_insert_fresh_256": 926858230, - "sails_static_allowance_prepare_0": 9852324426, - "sails_static_allowance_prepare_1024": 25041784414, - "sails_static_allowance_prepare_16": 10136784111, - "sails_static_allowance_prepare_256": 13695221786, - "sails_static_allowance_read_existing_1024": 815651853, - "sails_static_allowance_read_existing_16": 815651853, - "sails_static_allowance_read_existing_256": 815651853, - "sails_static_allowance_read_missing_0": 810119894, - "sails_static_allowance_read_missing_1024": 814467723, - "sails_static_allowance_read_missing_16": 810119894, - "sails_static_allowance_read_missing_256": 810119894, - "sails_static_allowance_remove_1024": 930513600, - "sails_static_allowance_remove_16": 930513600, - "sails_static_allowance_remove_256": 930513600, - "sails_static_allowance_update_existing_1024": 932579326, - "sails_static_allowance_update_existing_16": 932579326, - "sails_static_allowance_update_existing_256": 932579326, - "sails_static_balance_insert_fresh_0": 923049941, - "sails_static_balance_insert_fresh_1024": 924292178, - "sails_static_balance_insert_fresh_16": 923049941, - "sails_static_balance_insert_fresh_256": 924292178, - "sails_static_balance_prepare_0": 8552423960, - "sails_static_balance_prepare_1024": 13568825817, - "sails_static_balance_prepare_16": 8750605215, - "sails_static_balance_prepare_256": 9897209055, - "sails_static_balance_read_existing_1024": 810912211, - "sails_static_balance_read_existing_16": 810912211, - "sails_static_balance_read_existing_256": 810912211, - "sails_static_balance_read_missing_0": 807895228, - "sails_static_balance_read_missing_1024": 809137465, - "sails_static_balance_read_missing_16": 807895228, - "sails_static_balance_read_missing_256": 809137465, - "sails_static_balance_remove_1024": 925773889, - "sails_static_balance_remove_16": 925773889, - "sails_static_balance_remove_256": 925773889, - "sails_static_balance_update_existing_1024": 926264565, - "sails_static_balance_update_existing_16": 926264565, - "sails_static_balance_update_existing_256": 926264565 + "fixed_allowance_insert_fresh_0": 1171982927, + "fixed_allowance_insert_fresh_1024": 1173212539, + "fixed_allowance_insert_fresh_16": 1171982927, + "fixed_allowance_insert_fresh_256": 1171982927, + "fixed_allowance_prepare_0": 11788109976, + "fixed_allowance_prepare_1024": 20668957401, + "fixed_allowance_prepare_16": 11985243486, + "fixed_allowance_prepare_256": 13939965294, + "fixed_allowance_read_existing_1024": 946375204, + "fixed_allowance_read_existing_16": 946375204, + "fixed_allowance_read_existing_256": 946375204, + "fixed_allowance_read_missing_0": 940602008, + "fixed_allowance_read_missing_1024": 941831620, + "fixed_allowance_read_missing_16": 940602008, + "fixed_allowance_read_missing_256": 940602008, + "fixed_allowance_remove_1024": 1175594977, + "fixed_allowance_remove_16": 1175594977, + "fixed_allowance_remove_256": 1175594977, + "fixed_allowance_update_existing_1024": 1061685906, + "fixed_allowance_update_existing_16": 1061685906, + "fixed_allowance_update_existing_256": 1061685906, + "fixed_balance_insert_fresh_0": 1168037949, + "fixed_balance_insert_fresh_1024": 1169278430, + "fixed_balance_insert_fresh_16": 1168037949, + "fixed_balance_insert_fresh_256": 1169278430, + "fixed_balance_prepare_0": 10214876701, + "fixed_balance_prepare_1024": 14460834069, + "fixed_balance_prepare_16": 10348892339, + "fixed_balance_prepare_256": 11327335379, + "fixed_balance_read_existing_1024": 941338540, + "fixed_balance_read_existing_16": 941338540, + "fixed_balance_read_existing_256": 941338540, + "fixed_balance_read_missing_0": 938225643, + "fixed_balance_read_missing_1024": 939466124, + "fixed_balance_read_missing_16": 938225643, + "fixed_balance_read_missing_256": 939466124, + "fixed_balance_remove_1024": 1170557547, + "fixed_balance_remove_16": 1170557547, + "fixed_balance_remove_256": 1170557547, + "fixed_balance_update_existing_1024": 1056648476, + "fixed_balance_update_existing_16": 1056648476, + "fixed_balance_update_existing_256": 1056648476, + "hashmap_allowance_insert_fresh_0": 1160390209, + "hashmap_allowance_insert_fresh_1024": 1230472436, + "hashmap_allowance_insert_fresh_16": 1169584372, + "hashmap_allowance_insert_fresh_256": 1499420359, + "hashmap_allowance_prepare_0": 6109595302, + "hashmap_allowance_prepare_1024": 20812178436, + "hashmap_allowance_prepare_16": 6427721257, + "hashmap_allowance_prepare_256": 10068031394, + "hashmap_allowance_read_existing_1024": 1427650911, + "hashmap_allowance_read_existing_16": 1057748289, + "hashmap_allowance_read_existing_256": 1276920390, + "hashmap_allowance_read_missing_0": 1165563973, + "hashmap_allowance_read_missing_1024": 1231910137, + "hashmap_allowance_read_missing_16": 1052520837, + "hashmap_allowance_read_missing_256": 1231910137, + "hashmap_allowance_remove_1024": 1391596296, + "hashmap_allowance_remove_16": 1172424195, + "hashmap_allowance_remove_256": 1391596296, + "hashmap_allowance_update_existing_1024": 1237230326, + "hashmap_allowance_update_existing_16": 1057841026, + "hashmap_allowance_update_existing_256": 1391595688, + "hashmap_balance_insert_fresh_0": 1158513487, + "hashmap_balance_insert_fresh_1024": 1228595714, + "hashmap_balance_insert_fresh_16": 1013342288, + "hashmap_balance_insert_fresh_256": 1537326438, + "hashmap_balance_prepare_0": 6109595302, + "hashmap_balance_prepare_1024": 16313252889, + "hashmap_balance_prepare_16": 6374154379, + "hashmap_balance_prepare_256": 8725663652, + "hashmap_balance_read_existing_1024": 1159278374, + "hashmap_balance_read_existing_16": 1014747170, + "hashmap_balance_read_existing_256": 1273702072, + "hashmap_balance_read_missing_0": 1165502251, + "hashmap_balance_read_missing_1024": 1231390909, + "hashmap_balance_read_missing_16": 1012218808, + "hashmap_balance_read_missing_256": 1271173710, + "hashmap_balance_remove_1024": 1388375574, + "hashmap_balance_remove_16": 1014838111, + "hashmap_balance_remove_256": 1388375574, + "hashmap_balance_update_existing_1024": 1234010097, + "hashmap_balance_update_existing_16": 1014837996, + "hashmap_balance_update_existing_256": 1273792898, + "raw_static_allowance_insert_fresh_0": 1178095535, + "raw_static_allowance_insert_fresh_1024": 1180396240, + "raw_static_allowance_insert_fresh_16": 1178095535, + "raw_static_allowance_insert_fresh_256": 1178095535, + "raw_static_allowance_prepare_0": 11001522870, + "raw_static_allowance_prepare_1024": 26662689599, + "raw_static_allowance_prepare_16": 11296459884, + "raw_static_allowance_prepare_256": 14744009254, + "raw_static_allowance_read_existing_1024": 947495019, + "raw_static_allowance_read_existing_16": 947495019, + "raw_static_allowance_read_existing_256": 947495019, + "raw_static_allowance_read_missing_0": 945230419, + "raw_static_allowance_read_missing_1024": 947531124, + "raw_static_allowance_read_missing_16": 945230419, + "raw_static_allowance_read_missing_256": 945230419, + "raw_static_allowance_remove_1024": 1176781196, + "raw_static_allowance_remove_16": 1176781196, + "raw_static_allowance_remove_256": 1176781196, + "raw_static_allowance_update_existing_1024": 1063491268, + "raw_static_allowance_update_existing_16": 1063491268, + "raw_static_allowance_update_existing_256": 1063491268, + "raw_static_balance_insert_fresh_0": 1172036893, + "raw_static_balance_insert_fresh_1024": 1174337042, + "raw_static_balance_insert_fresh_16": 1172036893, + "raw_static_balance_insert_fresh_256": 1174337042, + "raw_static_balance_prepare_0": 9428110088, + "raw_static_balance_prepare_1024": 17771380528, + "raw_static_balance_prepare_16": 9626108830, + "raw_static_balance_prepare_256": 11564367200, + "raw_static_balance_read_existing_1024": 942482646, + "raw_static_balance_read_existing_16": 942482646, + "raw_static_balance_read_existing_256": 942482646, + "raw_static_balance_read_missing_0": 940532975, + "raw_static_balance_read_missing_1024": 942833124, + "raw_static_balance_read_missing_16": 940532975, + "raw_static_balance_read_missing_256": 942833124, + "raw_static_balance_remove_1024": 1171768823, + "raw_static_balance_remove_16": 1171768823, + "raw_static_balance_remove_256": 1171768823, + "raw_static_balance_update_existing_1024": 1058475043, + "raw_static_balance_update_existing_16": 1058475043, + "raw_static_balance_update_existing_256": 1058475043, + "sails_fixed_allowance_insert_fresh_0": 1173634272, + "sails_fixed_allowance_insert_fresh_1024": 1176157443, + "sails_fixed_allowance_insert_fresh_16": 1173634272, + "sails_fixed_allowance_insert_fresh_256": 1173634272, + "sails_fixed_allowance_prepare_0": 14002108283, + "sails_fixed_allowance_prepare_1024": 30378335021, + "sails_fixed_allowance_prepare_16": 14230031878, + "sails_fixed_allowance_prepare_256": 18084658791, + "sails_fixed_allowance_read_existing_1024": 948122136, + "sails_fixed_allowance_read_existing_16": 948122136, + "sails_fixed_allowance_read_existing_256": 948122136, + "sails_fixed_allowance_read_missing_0": 942531984, + "sails_fixed_allowance_read_missing_1024": 945073176, + "sails_fixed_allowance_read_missing_16": 942531984, + "sails_fixed_allowance_read_missing_256": 942531984, + "sails_fixed_allowance_remove_1024": 1176122034, + "sails_fixed_allowance_remove_16": 1176122034, + "sails_fixed_allowance_remove_256": 1176122034, + "sails_fixed_allowance_update_existing_1024": 1064819565, + "sails_fixed_allowance_update_existing_16": 1064819565, + "sails_fixed_allowance_update_existing_256": 1064819565, + "sails_fixed_balance_insert_fresh_0": 1168733274, + "sails_fixed_balance_insert_fresh_1024": 1170003870, + "sails_fixed_balance_insert_fresh_16": 1168733274, + "sails_fixed_balance_insert_fresh_256": 1169368572, + "sails_fixed_balance_prepare_0": 11574951885, + "sails_fixed_balance_prepare_1024": 16532982283, + "sails_fixed_balance_prepare_16": 11720092723, + "sails_fixed_balance_prepare_256": 12865413763, + "sails_fixed_balance_read_existing_1024": 942291338, + "sails_fixed_balance_read_existing_16": 942291338, + "sails_fixed_balance_read_existing_256": 942291338, + "sails_fixed_balance_read_missing_0": 939216162, + "sails_fixed_balance_read_missing_1024": 940486758, + "sails_fixed_balance_read_missing_16": 939216162, + "sails_fixed_balance_read_missing_256": 939851460, + "sails_fixed_balance_remove_1024": 1171382392, + "sails_fixed_balance_remove_16": 1171382392, + "sails_fixed_balance_remove_256": 1171382392, + "sails_fixed_balance_update_existing_1024": 1057405873, + "sails_fixed_balance_update_existing_16": 1057405873, + "sails_fixed_balance_update_existing_256": 1057405873, + "sails_static_allowance_insert_fresh_0": 1019557170, + "sails_static_allowance_insert_fresh_1024": 1023572604, + "sails_static_allowance_insert_fresh_16": 1019557170, + "sails_static_allowance_insert_fresh_256": 1019557170, + "sails_static_allowance_prepare_0": 9948989986, + "sails_static_allowance_prepare_1024": 26539485639, + "sails_static_allowance_prepare_16": 10250118296, + "sails_static_allowance_prepare_256": 14138167171, + "sails_static_allowance_read_existing_1024": 908332650, + "sails_static_allowance_read_existing_16": 908332650, + "sails_static_allowance_read_existing_256": 908332650, + "sails_static_allowance_read_missing_0": 902800691, + "sails_static_allowance_read_missing_1024": 907148520, + "sails_static_allowance_read_missing_16": 902800691, + "sails_static_allowance_read_missing_256": 902800691, + "sails_static_allowance_remove_1024": 1021998138, + "sails_static_allowance_remove_16": 1021998138, + "sails_static_allowance_remove_256": 1021998138, + "sails_static_allowance_update_existing_1024": 1025277290, + "sails_static_allowance_update_existing_16": 1025277290, + "sails_static_allowance_update_existing_256": 1025277290, + "sails_static_balance_insert_fresh_0": 1014654690, + "sails_static_balance_insert_fresh_1024": 1015896927, + "sails_static_balance_insert_fresh_16": 1014654690, + "sails_static_balance_insert_fresh_256": 1015896927, + "sails_static_balance_prepare_0": 8649094252, + "sails_static_balance_prepare_1024": 13946080190, + "sails_static_balance_prepare_16": 8846437076, + "sails_static_balance_prepare_256": 10060046276, + "sails_static_balance_read_existing_1024": 902500296, + "sails_static_balance_read_existing_16": 902500296, + "sails_static_balance_read_existing_256": 902500296, + "sails_static_balance_read_missing_0": 899483313, + "sails_static_balance_read_missing_1024": 900725550, + "sails_static_balance_read_missing_16": 899483313, + "sails_static_balance_read_missing_256": 900725550, + "sails_static_balance_remove_1024": 1017258427, + "sails_static_balance_remove_16": 1017258427, + "sails_static_balance_remove_256": 1017258427, + "sails_static_balance_update_existing_1024": 1017868338, + "sails_static_balance_update_existing_16": 1017868338, + "sails_static_balance_update_existing_256": 1017868338, + "vft_btree_approve_fresh_1024": 1292821435, + "vft_btree_approve_fresh_128": 1297239159, + "vft_btree_approve_fresh_16": 1141526729, + "vft_btree_approve_fresh_256": 1209541778, + "vft_btree_approve_fresh_64": 1295892091, + "vft_btree_prepare_1024": 67880725446, + "vft_btree_prepare_128": 14059334133, + "vft_btree_prepare_16": 8669943170, + "vft_btree_prepare_256": 21455352153, + "vft_btree_prepare_64": 10694778400, + "vft_btree_transfer_1024": 1548545684, + "vft_btree_transfer_128": 1401324078, + "vft_btree_transfer_16": 1205633477, + "vft_btree_transfer_256": 1299597859, + "vft_btree_transfer_64": 1382889706, + "vft_btree_transfer_fresh_1024": 1482078204, + "vft_btree_transfer_fresh_128": 1380507975, + "vft_btree_transfer_fresh_16": 1184817374, + "vft_btree_transfer_fresh_256": 1253405971, + "vft_btree_transfer_fresh_64": 1362073603, + "vft_btree_transfer_from_1024": 1614507251, + "vft_btree_transfer_from_128": 1435555686, + "vft_btree_transfer_from_16": 1234029170, + "vft_btree_transfer_from_256": 1349295502, + "vft_btree_transfer_from_64": 1413076074, + "vft_hashmap_approve_fresh_1024": 1345221300, + "vft_hashmap_approve_fresh_128": 1195667622, + "vft_hashmap_approve_fresh_16": 1129967874, + "vft_hashmap_approve_fresh_256": 1499586662, + "vft_hashmap_approve_fresh_64": 1284333236, + "vft_hashmap_prepare_1024": 31968798453, + "vft_hashmap_prepare_128": 11359128133, + "vft_hashmap_prepare_16": 8758229127, + "vft_hashmap_prepare_256": 14001810671, + "vft_hashmap_prepare_64": 9747318581, + "vft_hashmap_transfer_1024": 1690737996, + "vft_hashmap_transfer_128": 1077195223, + "vft_hashmap_transfer_16": 1166753846, + "vft_hashmap_transfer_256": 1385925947, + "vft_hashmap_transfer_64": 1321119208, + "vft_hashmap_transfer_fresh_1024": 1515438062, + "vft_hashmap_transfer_fresh_128": 1479415073, + "vft_hashmap_transfer_fresh_16": 1145660411, + "vft_hashmap_transfer_fresh_256": 1365652073, + "vft_hashmap_transfer_fresh_64": 1300025773, + "vft_hashmap_transfer_from_1024": 1733217653, + "vft_hashmap_transfer_from_128": 1448273067, + "vft_hashmap_transfer_from_16": 1189318165, + "vft_hashmap_transfer_from_256": 1602638429, + "vft_hashmap_transfer_from_64": 1343683527, + "vft_sails_fixed_approve_fresh_1024": 1135997912, + "vft_sails_fixed_approve_fresh_128": 1134194358, + "vft_sails_fixed_approve_fresh_16": 1134194358, + "vft_sails_fixed_approve_fresh_256": 1134194358, + "vft_sails_fixed_approve_fresh_64": 1134194358, + "vft_sails_fixed_prepare_1024": 37529615897, + "vft_sails_fixed_prepare_128": 17418317220, + "vft_sails_fixed_prepare_16": 15286235631, + "vft_sails_fixed_prepare_256": 21063587033, + "vft_sails_fixed_prepare_64": 16054401012, + "vft_sails_fixed_transfer_1024": 1376324006, + "vft_sails_fixed_transfer_128": 1376324006, + "vft_sails_fixed_transfer_16": 1376324006, + "vft_sails_fixed_transfer_256": 1376324006, + "vft_sails_fixed_transfer_64": 1376324006, + "vft_sails_fixed_transfer_fresh_1024": 1356224623, + "vft_sails_fixed_transfer_fresh_128": 1356224623, + "vft_sails_fixed_transfer_fresh_16": 1356224623, + "vft_sails_fixed_transfer_fresh_256": 1356224623, + "vft_sails_fixed_transfer_fresh_64": 1356224623, + "vft_sails_fixed_transfer_from_1024": 1570929263, + "vft_sails_fixed_transfer_from_128": 1570929263, + "vft_sails_fixed_transfer_from_16": 1570929263, + "vft_sails_fixed_transfer_from_256": 1570929263, + "vft_sails_fixed_transfer_from_64": 1570929263, + "vft_sails_static_approve_fresh_1024": 982012365, + "vft_sails_static_approve_fresh_128": 980185334, + "vft_sails_static_approve_fresh_16": 980185334, + "vft_sails_static_approve_fresh_256": 980185334, + "vft_sails_static_approve_fresh_64": 980185334, + "vft_sails_static_fast_approve_fresh_1024": 982012365, + "vft_sails_static_fast_approve_fresh_128": 980185334, + "vft_sails_static_fast_approve_fresh_16": 980185334, + "vft_sails_static_fast_approve_fresh_256": 980185334, + "vft_sails_static_fast_approve_fresh_64": 980185334, + "vft_sails_static_fast_prepare_1024": 38180175007, + "vft_sails_static_fast_prepare_128": 17404804748, + "vft_sails_static_fast_prepare_16": 15194606198, + "vft_sails_static_fast_prepare_256": 21154812040, + "vft_sails_static_fast_prepare_64": 15994381100, + "vft_sails_static_fast_transfer_1024": 1156865312, + "vft_sails_static_fast_transfer_128": 1156865312, + "vft_sails_static_fast_transfer_16": 1156865312, + "vft_sails_static_fast_transfer_256": 1156865312, + "vft_sails_static_fast_transfer_64": 1156865312, + "vft_sails_static_fast_transfer_fresh_1024": 1136315359, + "vft_sails_static_fast_transfer_fresh_128": 1136315359, + "vft_sails_static_fast_transfer_fresh_16": 1136315359, + "vft_sails_static_fast_transfer_fresh_256": 1136315359, + "vft_sails_static_fast_transfer_fresh_64": 1136315359, + "vft_sails_static_fast_transfer_from_1024": 1322904505, + "vft_sails_static_fast_transfer_from_128": 1322904505, + "vft_sails_static_fast_transfer_from_16": 1322904505, + "vft_sails_static_fast_transfer_from_256": 1322904505, + "vft_sails_static_fast_transfer_from_64": 1322904505, + "vft_sails_static_prepare_1024": 38180175007, + "vft_sails_static_prepare_128": 17404804748, + "vft_sails_static_prepare_16": 15194606198, + "vft_sails_static_prepare_256": 21154812040, + "vft_sails_static_prepare_64": 15994381100, + "vft_sails_static_transfer_1024": 1158167504, + "vft_sails_static_transfer_128": 1158167504, + "vft_sails_static_transfer_16": 1158167504, + "vft_sails_static_transfer_256": 1158167504, + "vft_sails_static_transfer_64": 1158167504, + "vft_sails_static_transfer_fresh_1024": 1137617551, + "vft_sails_static_transfer_fresh_128": 1137617551, + "vft_sails_static_transfer_fresh_16": 1137617551, + "vft_sails_static_transfer_fresh_256": 1137617551, + "vft_sails_static_transfer_fresh_64": 1137617551, + "vft_sails_static_transfer_from_1024": 1324193005, + "vft_sails_static_transfer_from_128": 1324193005, + "vft_sails_static_transfer_from_16": 1324193005, + "vft_sails_static_transfer_from_256": 1324193005, + "vft_sails_static_transfer_from_64": 1324193005 + }, + "storage_million": { + "control_actor_balance_insert_fresh_1000000": 1288749053, + "control_actor_balance_insert_fresh_batch512_1000000": 100192278327, + "control_actor_balance_prepare_1000000": 195443327062732, + "control_actor_balance_read_existing_1000000": 1065466018, + "control_actor_balance_read_existing_batch512_1000000": 29801388366, + "control_actor_balance_read_missing_1000000": 1022017454, + "control_actor_balance_read_missing_batch512_1000000": 7555586688, + "control_actor_balance_remove_1000000": 1180160656, + "control_actor_balance_remove_batch512_1000000": 44525339598, + "control_actor_balance_update_existing_1000000": 1181438116, + "control_actor_balance_update_existing_batch512_1000000": 89179102542, + "grouped_actor_balance_pages128_insert_fresh_1000000": 1289547500, + "grouped_actor_balance_pages128_insert_fresh_batch512_1000000": 102075561285, + "grouped_actor_balance_pages128_prepare_1000000": 197583093447796, + "grouped_actor_balance_pages128_read_existing_1000000": 1066082488, + "grouped_actor_balance_pages128_read_existing_batch512_1000000": 30117021006, + "grouped_actor_balance_pages128_read_missing_1000000": 1022248334, + "grouped_actor_balance_pages128_read_missing_batch512_1000000": 22543447008, + "grouped_actor_balance_pages128_remove_1000000": 1180946577, + "grouped_actor_balance_pages128_remove_batch512_1000000": 44927731150, + "grouped_actor_balance_pages128_update_existing_1000000": 1182244906, + "grouped_actor_balance_pages128_update_existing_batch512_1000000": 89592179022, + "grouped_actor_balance_pages16_insert_fresh_1000000": 1289551352, + "grouped_actor_balance_pages16_insert_fresh_batch512_1000000": 147354159109, + "grouped_actor_balance_pages16_prepare_1000000": 287369941927267, + "grouped_actor_balance_pages16_read_existing_1000000": 1066082488, + "grouped_actor_balance_pages16_read_existing_batch512_1000000": 42210992510, + "grouped_actor_balance_pages16_read_missing_1000000": 1022248334, + "grouped_actor_balance_pages16_read_missing_batch512_1000000": 20919158310, + "grouped_actor_balance_pages16_remove_1000000": 1180946577, + "grouped_actor_balance_pages16_remove_batch512_1000000": 94216486336, + "grouped_actor_balance_pages16_update_existing_1000000": 1182248758, + "grouped_actor_balance_pages16_update_existing_batch512_1000000": 101568774347, + "grouped_actor_balance_pages2_insert_fresh_1000000": 1290739217, + "grouped_actor_balance_pages2_insert_fresh_batch512_1000000": 121144739489, + "grouped_actor_balance_pages2_prepare_1000000": 237940069558740, + "grouped_actor_balance_pages2_read_existing_1000000": 1066082488, + "grouped_actor_balance_pages2_read_existing_batch512_1000000": 35646830345, + "grouped_actor_balance_pages2_read_missing_1000000": 1023436199, + "grouped_actor_balance_pages2_read_missing_batch512_1000000": 23144014827, + "grouped_actor_balance_pages2_remove_1000000": 1180946577, + "grouped_actor_balance_pages2_remove_batch512_1000000": 93979850301, + "grouped_actor_balance_pages2_update_existing_1000000": 1182248758, + "grouped_actor_balance_pages2_update_existing_batch512_1000000": 94686349774, + "grouped_actor_balance_pages32_insert_fresh_1000000": 1295260313, + "grouped_actor_balance_pages32_insert_fresh_batch512_1000000": 119266316493, + "grouped_actor_balance_pages32_prepare_1000000": 233061174861426, + "grouped_actor_balance_pages32_read_existing_1000000": 1066082488, + "grouped_actor_balance_pages32_read_existing_batch512_1000000": 34930739927, + "grouped_actor_balance_pages32_read_missing_1000000": 1063821421, + "grouped_actor_balance_pages32_read_missing_batch512_1000000": 12927545416, + "grouped_actor_balance_pages32_remove_1000000": 1180946577, + "grouped_actor_balance_pages32_remove_batch512_1000000": 64328452277, + "grouped_actor_balance_pages32_update_existing_1000000": 1182248758, + "grouped_actor_balance_pages32_update_existing_batch512_1000000": 94368087366, + "grouped_actor_balance_pages4_insert_fresh_1000000": 1291531127, + "grouped_actor_balance_pages4_insert_fresh_batch512_1000000": 141401791823, + "grouped_actor_balance_pages4_prepare_1000000": 276148412869382, + "grouped_actor_balance_pages4_read_existing_1000000": 1066082488, + "grouped_actor_balance_pages4_read_existing_batch512_1000000": 40699246072, + "grouped_actor_balance_pages4_read_missing_1000000": 1024228109, + "grouped_actor_balance_pages4_read_missing_batch512_1000000": 23647856888, + "grouped_actor_balance_pages4_remove_1000000": 1180946577, + "grouped_actor_balance_pages4_remove_batch512_1000000": 99310745635, + "grouped_actor_balance_pages4_update_existing_1000000": 1182248758, + "grouped_actor_balance_pages4_update_existing_batch512_1000000": 99977462307, + "grouped_actor_balance_pages64_insert_fresh_1000000": 1289551352, + "grouped_actor_balance_pages64_insert_fresh_batch512_1000000": 100736759910, + "grouped_actor_balance_pages64_prepare_1000000": 197352940452528, + "grouped_actor_balance_pages64_read_existing_1000000": 1066082488, + "grouped_actor_balance_pages64_read_existing_batch512_1000000": 30117021006, + "grouped_actor_balance_pages64_read_missing_1000000": 1022248334, + "grouped_actor_balance_pages64_read_missing_batch512_1000000": 16126240198, + "grouped_actor_balance_pages64_remove_1000000": 1180946577, + "grouped_actor_balance_pages64_remove_batch512_1000000": 44927731150, + "grouped_actor_balance_pages64_update_existing_1000000": 1182248758, + "grouped_actor_balance_pages64_update_existing_batch512_1000000": 89594151246, + "grouped_actor_balance_pages8_insert_fresh_1000000": 1289551352, + "grouped_actor_balance_pages8_insert_fresh_batch512_1000000": 151686621028, + "grouped_actor_balance_pages8_prepare_1000000": 295253186311451, + "grouped_actor_balance_pages8_read_existing_1000000": 1066082488, + "grouped_actor_balance_pages8_read_existing_batch512_1000000": 43245345336, + "grouped_actor_balance_pages8_read_missing_1000000": 1022248334, + "grouped_actor_balance_pages8_read_missing_batch512_1000000": 23362581987, + "grouped_actor_balance_pages8_remove_1000000": 1180946577, + "grouped_actor_balance_pages8_remove_batch512_1000000": 101976193302, + "grouped_actor_balance_pages8_update_existing_1000000": 1182248758, + "grouped_actor_balance_pages8_update_existing_batch512_1000000": 102563344372, + "mixed_actor_balance_insert_fresh_1000000": 1142040026, + "mixed_actor_balance_insert_fresh_batch512_1000000": 82317968817, + "mixed_actor_balance_prepare_1000000": 157752607160917, + "mixed_actor_balance_read_existing_1000000": 1025538974, + "mixed_actor_balance_read_existing_batch512_1000000": 23966842945, + "mixed_actor_balance_read_missing_1000000": 1025756572, + "mixed_actor_balance_read_missing_batch512_1000000": 23908956830, + "mixed_actor_balance_remove_1000000": 1141242565, + "mixed_actor_balance_remove_batch512_1000000": 82501914036, + "mixed_actor_balance_update_existing_1000000": 1141517926, + "mixed_actor_balance_update_existing_batch512_1000000": 82321146244, + "page_local_actor_balance_insert_fresh_1000000": 1138319807, + "page_local_actor_balance_insert_fresh_batch512_1000000": 82449903878, + "page_local_actor_balance_prepare_1000000": 160765422995096, + "page_local_actor_balance_read_existing_1000000": 1025705454, + "page_local_actor_balance_read_existing_batch512_1000000": 24720575182, + "page_local_actor_balance_read_missing_1000000": 1022024275, + "page_local_actor_balance_read_missing_batch512_1000000": 22997160365, + "page_local_actor_balance_remove_1000000": 1140401336, + "page_local_actor_balance_remove_batch512_1000000": 83444866766, + "page_local_actor_balance_update_existing_1000000": 1141684831, + "page_local_actor_balance_update_existing_batch512_1000000": 84102016206, + "static_balance_insert_fresh_1000000": 1141790968, + "static_balance_insert_fresh_batch512_1000000": 80311903073, + "static_balance_prepare_1000000": 154548083108619, + "static_balance_read_existing_1000000": 1026401201, + "static_balance_read_existing_batch512_1000000": 23684125791, + "static_balance_read_missing_1000000": 1025644750, + "static_balance_read_missing_batch512_1000000": 23304153266, + "static_balance_remove_1000000": 1140894359, + "static_balance_remove_batch512_1000000": 80146617396, + "static_balance_update_existing_1000000": 1142343099, + "static_balance_update_existing_batch512_1000000": 83512583430, + "vft_real_control_actor_balance_approve_fresh_bool_1000000": 1180863424, + "vft_real_control_actor_balance_prepare_1000000": 357081845884459, + "vft_real_control_actor_balance_transfer_bool_1000000": 1436031696, + "vft_real_control_actor_balance_transfer_fresh_bool_1000000": 1541280795, + "vft_real_control_actor_balance_transfer_from_bool_1000000": 1612491811, + "vft_real_grouped_actor_balance_pages128_approve_fresh_bool_1000000": 1180863424, + "vft_real_grouped_actor_balance_pages128_prepare_1000000": 357569735122932, + "vft_real_grouped_actor_balance_pages128_transfer_bool_1000000": 1438843932, + "vft_real_grouped_actor_balance_pages128_transfer_fresh_bool_1000000": 1541837328, + "vft_real_grouped_actor_balance_pages128_transfer_from_bool_1000000": 1615304047, + "vft_real_grouped_actor_balance_pages64_approve_fresh_bool_1000000": 1180863424, + "vft_real_grouped_actor_balance_pages64_prepare_1000000": 339906841895334, + "vft_real_grouped_actor_balance_pages64_transfer_bool_1000000": 1438893624, + "vft_real_grouped_actor_balance_pages64_transfer_fresh_bool_1000000": 1541887020, + "vft_real_grouped_actor_balance_pages64_transfer_from_bool_1000000": 1615353739, + "vft_real_inline_owner_account_u256_approve_fresh_bool_1000000": 1342403479, + "vft_real_inline_owner_account_u256_approve_overflow_third_bool_1000000": 1347703980, + "vft_real_inline_owner_account_u256_approve_second_bool_1000000": 1342403479, + "vft_real_inline_owner_account_u256_prepare_1000000": 211288660377756, + "vft_real_inline_owner_account_u256_transfer_bool_1000000": 1460169545, + "vft_real_inline_owner_account_u256_transfer_fresh_bool_1000000": 1457287604, + "vft_real_inline_owner_account_u256_transfer_from_bool_1000000": 1465702081, + "vft_real_inline_owner_account_u256_transfer_from_overflow_bool_1000000": 1636947889, + "vft_real_mixed_actor_balance_approve_fresh_bool_1000000": 1232146698, + "vft_real_mixed_actor_balance_fast_approve_fresh_bool_1000000": 1249950294, + "vft_real_mixed_actor_balance_fast_prepare_1000000": 319332197993586, + "vft_real_mixed_actor_balance_fast_transfer_bool_1000000": 1407257392, + "vft_real_mixed_actor_balance_fast_transfer_fresh_bool_1000000": 1404786928, + "vft_real_mixed_actor_balance_fast_transfer_from_bool_1000000": 1569882831, + "vft_real_mixed_actor_balance_prepare_1000000": 319247447040850, + "vft_real_mixed_actor_balance_transfer_bool_1000000": 1394115220, + "vft_real_mixed_actor_balance_transfer_fresh_bool_1000000": 1391640348, + "vft_real_mixed_actor_balance_transfer_from_bool_1000000": 1558277342, + "vft_real_page_local_actor_balance_approve_fresh_bool_1000000": 1249942992, + "vft_real_page_local_actor_balance_prepare_1000000": 322296197790382, + "vft_real_page_local_actor_balance_transfer_bool_1000000": 1425582812, + "vft_real_page_local_actor_balance_transfer_fresh_bool_1000000": 1418276482, + "vft_real_page_local_actor_balance_transfer_from_bool_1000000": 1602101797, + "vft_real_static_balance_approve_fresh_bool_1000000": 1303371315, + "vft_real_static_balance_approve_overflow_third_bool_1000000": 1303298666, + "vft_real_static_balance_approve_second_bool_1000000": 1305853837, + "vft_real_static_balance_fast_approve_fresh_bool_1000000": 1184163919, + "vft_real_static_balance_fast_prepare_1000000": 321207620333828, + "vft_real_static_balance_fast_transfer_bool_1000000": 1343051976, + "vft_real_static_balance_fast_transfer_fresh_bool_1000000": 1341019015, + "vft_real_static_balance_fast_transfer_from_bool_1000000": 1510812817, + "vft_real_static_balance_fused_approve_fresh_bool_1000000": 1184163919, + "vft_real_static_balance_fused_prepare_1000000": 321207620333828, + "vft_real_static_balance_fused_transfer_bool_1000000": 1344319941, + "vft_real_static_balance_fused_transfer_fresh_bool_1000000": 1342286980, + "vft_real_static_balance_fused_transfer_from_bool_1000000": 1512076735, + "vft_real_static_balance_prepare_1000000": 321556277747506, + "vft_real_static_balance_transfer_bool_1000000": 1478959742, + "vft_real_static_balance_transfer_fresh_bool_1000000": 1474715541, + "vft_real_static_balance_transfer_from_bool_1000000": 1662923723, + "vft_real_static_balance_transfer_from_overflow_bool_1000000": 1665112547, + "vft_real_tag_actor_balance_approve_fresh_bool_1000000": 1244561813, + "vft_real_tag_actor_balance_prepare_1000000": 305601004332701, + "vft_real_tag_actor_balance_transfer_bool_1000000": 1398096988, + "vft_real_tag_actor_balance_transfer_fresh_bool_1000000": 1397842806, + "vft_real_tag_actor_balance_transfer_from_bool_1000000": 1553121278, + "vft_real_tag_u64_actor_balance_approve_fresh_bool_1000000": 1243611942, + "vft_real_tag_u64_actor_balance_prepare_1000000": 308479799767756, + "vft_real_tag_u64_actor_balance_transfer_bool_1000000": 1397302808, + "vft_real_tag_u64_actor_balance_transfer_fresh_bool_1000000": 1397083567, + "vft_real_tag_u64_actor_balance_transfer_from_bool_1000000": 1552199383, + "vft_real_wat_actor_balance_approve_fresh_bool_1000000": 1249942992, + "vft_real_wat_actor_balance_prepare_1000000": 325864961985486, + "vft_real_wat_actor_balance_transfer_bool_1000000": 1411860886, + "vft_real_wat_actor_balance_transfer_fresh_bool_1000000": 1415896964, + "vft_real_wat_actor_balance_transfer_from_bool_1000000": 1576023008, + "wat_actor_balance_insert_fresh_1000000": 1139464070, + "wat_actor_balance_insert_fresh_batch512_1000000": 83453328100, + "wat_actor_balance_prepare_1000000": 162228942621352, + "wat_actor_balance_read_existing_1000000": 1025567398, + "wat_actor_balance_read_existing_batch512_1000000": 24649890510, + "wat_actor_balance_read_missing_1000000": 1023180616, + "wat_actor_balance_read_missing_batch512_1000000": 23427878402, + "wat_actor_balance_remove_1000000": 1141270989, + "wat_actor_balance_remove_batch512_1000000": 83890129102, + "wat_actor_balance_update_existing_1000000": 1141546350, + "wat_actor_balance_update_existing_batch512_1000000": 84031113934 + }, + "examples": { + "aggregator_btree_insert_fresh_0": 1358854820, + "aggregator_btree_insert_fresh_1024": 1749177077, + "aggregator_btree_insert_fresh_16": 1527003262, + "aggregator_btree_insert_fresh_256": 1528612903, + "aggregator_btree_list_statuses_0": 1369471383, + "aggregator_btree_list_statuses_1024": 2305869726, + "aggregator_btree_list_statuses_16": 1531195518, + "aggregator_btree_list_statuses_256": 1754365685, + "aggregator_btree_prepare_0": 3558245828, + "aggregator_btree_prepare_1024": 12509955209, + "aggregator_btree_prepare_16": 3841993157, + "aggregator_btree_prepare_256": 5372511941, + "aggregator_btree_read_existing_1024": 1653321752, + "aggregator_btree_read_existing_16": 1527258604, + "aggregator_btree_read_existing_256": 1527902102, + "aggregator_btree_update_existing_1024": 1767835338, + "aggregator_btree_update_existing_16": 1527420118, + "aggregator_btree_update_existing_256": 1528062548, + "aggregator_sails_fixed_insert_fresh_0": 1371935943, + "aggregator_sails_fixed_insert_fresh_1024": 1371935943, + "aggregator_sails_fixed_insert_fresh_16": 1371935943, + "aggregator_sails_fixed_insert_fresh_256": 1371935943, + "aggregator_sails_fixed_list_statuses_0": 1498212003, + "aggregator_sails_fixed_list_statuses_1024": 24785551573, + "aggregator_sails_fixed_list_statuses_16": 1749383859, + "aggregator_sails_fixed_list_statuses_256": 6087044480, + "aggregator_sails_fixed_prepare_0": 5795782642, + "aggregator_sails_fixed_prepare_1024": 8915101843, + "aggregator_sails_fixed_prepare_16": 5844475347, + "aggregator_sails_fixed_prepare_256": 6575313267, + "aggregator_sails_fixed_read_existing_1024": 1259894063, + "aggregator_sails_fixed_read_existing_16": 1259894063, + "aggregator_sails_fixed_read_existing_256": 1259894063, + "aggregator_sails_fixed_update_existing_1024": 1379764378, + "aggregator_sails_fixed_update_existing_16": 1379764378, + "aggregator_sails_fixed_update_existing_256": 1379764378 } } \ No newline at end of file diff --git a/benchmarks/build.rs b/benchmarks/build.rs index c24e6be2b..d5626e904 100644 --- a/benchmarks/build.rs +++ b/benchmarks/build.rs @@ -15,6 +15,25 @@ fn generate_client( .unwrap(); } +fn generate_idl_and_client( + program_name_str: &str, + clients_path: &std::path::Path, + idls_path: &std::path::Path, +) { + let program_name = program_name_str.to_case(Case::Snake); + let client_path = clients_path.join(&program_name).with_extension("rs"); + let idl_path = idls_path.join(&program_name).with_extension("idl"); + let program_name_pascal = program_name_str + .strip_suffix("_program") + .unwrap_or(program_name_str) + .to_case(Case::Pascal); + + sails_idl_gen::generate_idl_to_file::

(Some(&program_name_pascal), &idl_path).unwrap(); + ClientGenerator::from_idl_path(&idl_path) + .generate_to(client_path) + .unwrap(); +} + fn main() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let clients_path = PathBuf::from(&manifest_dir).join("src"); @@ -23,5 +42,19 @@ fn main() { generate_client("alloc_stress_program", &clients_path, &idls_path); generate_client("compute_stress_program", &clients_path, &idls_path); generate_client("counter_bench_program", &clients_path, &idls_path); - generate_client("storage_stress_program", &clients_path, &idls_path); + generate_idl_and_client::( + "storage_stress_program", + &clients_path, + &idls_path, + ); + generate_idl_and_client::( + "vft_stress_program", + &clients_path, + &idls_path, + ); + generate_idl_and_client::( + "storage_million_program", + &clients_path, + &idls_path, + ); } diff --git a/benchmarks/idls/storage_million_program.idl b/benchmarks/idls/storage_million_program.idl new file mode 100644 index 000000000..4755cabb3 --- /dev/null +++ b/benchmarks/idls/storage_million_program.idl @@ -0,0 +1,84 @@ + +!@sails: 1.0.0-beta.5 + +service StorageMillion@0x4534f13d9216fc6b { + functions { + Bench(backend: MillionStorageBackend, op: MillionStorageOp, seed: u32) -> MillionStorageBenchResult; + BenchMany(backend: MillionStorageBackend, op: MillionStorageOp, start_seed: u32, count: u32) -> MillionStorageBenchResult; + BenchVftApproveBool(backend: MillionVftBackend, owner_seed: u32, spender_seed: u32) -> bool; + BenchVftTransfer(backend: MillionVftBackend, op: MillionVftTransferOp, seed: u32) -> MillionVftTransferResult; + BenchVftTransferBool(backend: MillionVftBackend, op: MillionVftTransferOp, seed: u32) -> bool; + BenchVftTransferFreshBool(backend: MillionVftBackend, seed: u32) -> bool; + BenchVftTransferFromSpenderBool(backend: MillionVftBackend, owner_seed: u32, spender_seed: u32) -> bool; + PrepareChunk(backend: MillionStorageBackend, start: u32, len: u32) -> MillionStorageBenchResult; + PrepareVftChunk(backend: MillionVftBackend, start: u32, len: u32) -> MillionVftTransferResult; + } + types { + enum MillionStorageBackend { + GenericStatic, + WatActorStatic, + MixedActorStatic, + TagActorStatic, + TagU64ActorStatic, + ControlActorStatic, + PageLocalActorStatic, + GroupedActorPages2, + GroupedActorPages4, + GroupedActorPages8, + GroupedActorPages16, + GroupedActorPages32, + GroupedActorPages64, + GroupedActorPages128, + } + struct MillionStorageBenchResult { + value: U256, + len: u32, + existed: bool, + } + enum MillionStorageOp { + InsertFresh, + UpdateExisting, + ReadExisting, + ReadMissing, + Remove, + } + enum MillionVftBackend { + BTree, + HashMap, + GenericStatic, + GenericStaticFused, + GenericStaticFast, + WatActorStatic, + MixedActorStatic, + MixedActorFast, + TagActorStatic, + TagU64ActorStatic, + ControlActorStatic, + PageLocalActorStatic, + GroupedActorPages64, + GroupedActorPages128, + InlineOwnerAccountU256, + } + enum MillionVftTransferOp { + Transfer, + TransferFrom, + } + struct MillionVftTransferResult { + from_balance: U256, + to_balance: U256, + allowance: U256, + balance_len: u32, + allowance_len: u32, + transferred: bool, + } + } +} + +program StorageMillion { + constructors { + NewForBench(); + } + services { + StorageMillion@0x4534f13d9216fc6b, + } +} diff --git a/benchmarks/idls/vft_stress_program.idl b/benchmarks/idls/vft_stress_program.idl new file mode 100644 index 000000000..3d1944ec5 --- /dev/null +++ b/benchmarks/idls/vft_stress_program.idl @@ -0,0 +1,41 @@ + +!@sails: 1.0.0-beta.5 + +service VftStress@0x9d98a323ba179bf4 { + functions { + BenchVftApproveBool(backend: VftStorageBackend, owner_seed: u32, spender_seed: u32) -> bool; + BenchVftTransfer(backend: VftStorageBackend, op: VftTransferOp, seed: u32) -> VftTransferResult; + BenchVftTransferFreshBool(backend: VftStorageBackend, seed: u32) -> bool; + PrepareVft(backend: VftStorageBackend, len: u32) -> VftTransferResult; + } + types { + enum VftStorageBackend { + BTree, + HashMap, + SailsFixed, + SailsStatic, + SailsStaticFast, + } + enum VftTransferOp { + Transfer, + TransferFrom, + } + struct VftTransferResult { + from_balance: U256, + to_balance: U256, + allowance: U256, + balance_len: u32, + allowance_len: u32, + transferred: bool, + } + } +} + +program VftStress { + constructors { + NewForBench(); + } + services { + VftStress@0x9d98a323ba179bf4, + } +} diff --git a/benchmarks/src/benchmarks.rs b/benchmarks/src/benchmarks.rs index 646f89bf1..32dd4c396 100644 --- a/benchmarks/src/benchmarks.rs +++ b/benchmarks/src/benchmarks.rs @@ -8,7 +8,9 @@ //! - **Compute performance benchmarks** - Tests CPU-intensive operations like Fibonacci calculations (`compute_stress_bench` test) //! - **Cross-program performance** - Tests performance of cross-program communication (`cross_program_bench` test) //! - **Redirect performance** - Tests performance of redirecting calls to another program (`redirect_bench` test) +//! - **Example storage benchmarks** - Tests example-shaped state paths (`aggregator_tracker_bench` test) //! - **Storage benchmarks** - Compares allocator-backed maps with fixed open-addressed maps (`storage_stress_bench` test) +//! - **VFT storage benchmarks** - Tests transfer-shaped balance/allowance paths (`vft_storage_transfer_bench` test) //! //! All benchmarks use the `gtest` framework to simulate on-chain execution and measure //! gas consumption. Results are stored to the shared benchmark data file for analysis. @@ -18,6 +20,8 @@ //! cargo test --release --manifest-path=benchmarks/Cargo.toml //! ``` +#[cfg(feature = "gas-profile")] +use crate::clients::vft_stress_client::vft_stress::{VftPhase, VftProfileResult}; use crate::clients::{ alloc_stress_client::{ AllocStress as _, AllocStressCtors, AllocStressProgram, alloc_stress::*, @@ -28,8 +32,26 @@ use crate::clients::{ counter_bench_client::{ CounterBench as _, CounterBenchCtors, CounterBenchProgram, counter_bench::*, }, + storage_million_client::{ + StorageMillion as _, StorageMillionCtors, StorageMillionProgram, storage_million::*, + }, storage_stress_client::{ - StorageStress as _, StorageStressCtors, StorageStressProgram, storage_stress::*, + StorageStress as _, StorageStressCtors, StorageStressProgram, + storage_stress::{ + StorageBackend, StorageBenchResult, StorageMap, StorageOp, StorageStress as _, + }, + }, + vft_stress_client::{ + VftStress as _, VftStressCtors, VftStressProgram, + vft_stress::{VftStorageBackend, VftStress as _, VftTransferOp, VftTransferResult}, + }, +}; +use aggregator_client::{ + AggregatorClient as _, AggregatorClientCtors, AggregatorClientProgram, + TrackerBackend as AggregatorTrackerBackend, + aggregator::{ + Aggregator as _, OpStatus as AggregatorOpStatus, + TrackerBenchResult as AggregatorTrackerBenchResult, TrackerOp as AggregatorTrackerOp, }, }; use gtest::{System, constants::DEFAULT_USER_ALICE}; @@ -40,9 +62,13 @@ use redirect_proxy_client::{ RedirectProxyClient, RedirectProxyClientCtors, RedirectProxyClientProgram, proxy::*, }; use sails_rs::{client::*, prelude::*}; -use std::{collections::BTreeMap, sync::atomic::AtomicU64}; +use std::{collections::BTreeMap, env, sync::atomic::AtomicU64}; static COUNTER_SALT: AtomicU64 = AtomicU64::new(0); +const STORAGE_MILLION_LOAD: u32 = 1_000_000; +const STORAGE_MILLION_SAMPLES: u32 = 1; +const STORAGE_MILLION_BATCH_COUNT: u32 = 512; +const STORAGE_MILLION_PREPARE_CHUNK: u32 = 512; #[tokio::test] async fn alloc_stress_bench() { @@ -276,6 +302,279 @@ async fn message_stack_bench() { } } +#[tokio::test] +async fn aggregator_tracker_bench() { + let mut benches: BTreeMap> = Default::default(); + let loads = [0, 16, 256, 1024]; + + for sample in 0..10 { + for backend in [ + AggregatorTrackerBackend::BTree, + AggregatorTrackerBackend::SailsFixed, + ] { + for &load in loads.iter() { + let run = aggregator_tracker_test(backend.clone(), load, sample).await; + + for gas in run.prepare { + benches + .entry(aggregator_tracker_prepare_key(&backend, load)) + .or_default() + .push(gas); + } + + for (op, gas) in run.operations { + benches + .entry(aggregator_tracker_bench_key(&backend, &op, load)) + .or_default() + .push(gas); + } + } + } + } + + let medians = benches + .into_iter() + .map(|(key, gas_benches)| (key, median(gas_benches))) + .collect(); + + crate::store_bench_data(|bench_data| { + bench_data.replace_example_benches(medians); + }) + .unwrap(); +} + +#[tokio::test] +async fn storage_million_static_bench() { + let mut benches: BTreeMap> = Default::default(); + + for backend in [ + MillionStorageBackend::GenericStatic, + MillionStorageBackend::WatActorStatic, + MillionStorageBackend::MixedActorStatic, + MillionStorageBackend::ControlActorStatic, + MillionStorageBackend::PageLocalActorStatic, + MillionStorageBackend::GroupedActorPages2, + MillionStorageBackend::GroupedActorPages4, + MillionStorageBackend::GroupedActorPages8, + MillionStorageBackend::GroupedActorPages16, + MillionStorageBackend::GroupedActorPages32, + MillionStorageBackend::GroupedActorPages64, + MillionStorageBackend::GroupedActorPages128, + ] { + for sample in 0..STORAGE_MILLION_SAMPLES { + let run = + storage_million_static_test(backend.clone(), STORAGE_MILLION_LOAD, sample).await; + + benches + .entry(storage_million_prepare_key(&backend, STORAGE_MILLION_LOAD)) + .or_default() + .push(run.prepare_total); + + for (op, gas) in run.operations { + benches + .entry(storage_million_bench_key( + &backend, + &op, + STORAGE_MILLION_LOAD, + )) + .or_default() + .push(gas); + } + + for (op, gas) in run.batch_operations { + benches + .entry(storage_million_batch_key( + &backend, + &op, + STORAGE_MILLION_BATCH_COUNT, + STORAGE_MILLION_LOAD, + )) + .or_default() + .push(gas); + } + } + } + + let medians = benches + .into_iter() + .map(|(key, gas_benches)| (key, median(gas_benches))) + .collect(); + + crate::store_bench_data(|bench_data| { + bench_data.replace_storage_million_benches(medians); + }) + .unwrap(); +} + +#[tokio::test] +async fn vft_million_transfer_bench() { + let mut benches: BTreeMap> = Default::default(); + + let backends = filtered_million_vft_backends( + "SAILS_STORAGE_MILLION_VFT_BACKENDS", + &[ + MillionVftBackend::GenericStatic, + MillionVftBackend::GenericStaticFused, + MillionVftBackend::GenericStaticFast, + MillionVftBackend::WatActorStatic, + MillionVftBackend::MixedActorStatic, + MillionVftBackend::MixedActorFast, + MillionVftBackend::TagActorStatic, + MillionVftBackend::TagU64ActorStatic, + MillionVftBackend::ControlActorStatic, + MillionVftBackend::PageLocalActorStatic, + MillionVftBackend::InlineOwnerAccountU256, + ], + ); + + for backend in backends { + for sample in 0..STORAGE_MILLION_SAMPLES { + let run = + storage_million_vft_transfer_test(backend.clone(), STORAGE_MILLION_LOAD, sample) + .await; + + benches + .entry(storage_million_vft_prepare_key( + &backend, + STORAGE_MILLION_LOAD, + )) + .or_default() + .push(run.prepare_total); + + for (op, gas) in run.operations { + benches + .entry(storage_million_vft_key(&backend, &op, STORAGE_MILLION_LOAD)) + .or_default() + .push(gas); + } + } + } + + let medians = benches + .into_iter() + .map(|(key, gas_benches)| (key, median(gas_benches))) + .collect::>(); + + #[cfg(not(feature = "gas-profile"))] + crate::store_bench_data(|bench_data| { + for (key, value) in medians.clone() { + bench_data.update_storage_million_bench(key, value); + } + }) + .unwrap(); + + #[cfg(feature = "gas-profile")] + crate::write_gas_profile_summary(&medians, &render_vft_comparison_markdown(&medians)).unwrap(); +} + +#[tokio::test] +async fn vft_million_real_cost_bench() { + let mut benches: BTreeMap> = Default::default(); + + let backends = filtered_million_vft_backends( + "SAILS_STORAGE_MILLION_VFT_BACKENDS", + &[ + MillionVftBackend::GenericStatic, + MillionVftBackend::GenericStaticFused, + MillionVftBackend::GenericStaticFast, + MillionVftBackend::WatActorStatic, + MillionVftBackend::MixedActorStatic, + MillionVftBackend::MixedActorFast, + MillionVftBackend::TagActorStatic, + MillionVftBackend::TagU64ActorStatic, + MillionVftBackend::PageLocalActorStatic, + MillionVftBackend::ControlActorStatic, + MillionVftBackend::GroupedActorPages64, + MillionVftBackend::GroupedActorPages128, + MillionVftBackend::InlineOwnerAccountU256, + ], + ); + + for backend in backends { + for sample in 0..STORAGE_MILLION_SAMPLES { + let run = + storage_million_vft_real_cost_test(backend.clone(), STORAGE_MILLION_LOAD, sample) + .await; + + benches + .entry(storage_million_vft_real_prepare_key( + &backend, + STORAGE_MILLION_LOAD, + )) + .or_default() + .push(run.prepare_total); + + for (op, gas) in run.operations { + benches + .entry(storage_million_vft_real_key( + &backend, + op, + STORAGE_MILLION_LOAD, + )) + .or_default() + .push(gas); + } + } + } + + let medians = benches + .into_iter() + .map(|(key, gas_benches)| (key, median(gas_benches))) + .collect::>(); + + #[cfg(not(feature = "gas-profile"))] + crate::store_bench_data(|bench_data| { + for (key, value) in medians.clone() { + bench_data.update_storage_million_bench(key, value); + } + }) + .unwrap(); + + #[cfg(feature = "gas-profile")] + crate::write_gas_profile_summary(&medians, &render_vft_comparison_markdown(&medians)).unwrap(); +} + +#[tokio::test] +#[ignore = "BTreeMap/HashMap 1M VFT prepare may exhaust wasm heap; run manually for feasibility evidence"] +async fn vft_million_dynamic_baseline_bench() { + let mut benches: BTreeMap> = Default::default(); + + for backend in [MillionVftBackend::BTree, MillionVftBackend::HashMap] { + for sample in 0..STORAGE_MILLION_SAMPLES { + let run = + storage_million_vft_transfer_test(backend.clone(), STORAGE_MILLION_LOAD, sample) + .await; + + benches + .entry(storage_million_vft_prepare_key( + &backend, + STORAGE_MILLION_LOAD, + )) + .or_default() + .push(run.prepare_total); + + for (op, gas) in run.operations { + benches + .entry(storage_million_vft_key(&backend, &op, STORAGE_MILLION_LOAD)) + .or_default() + .push(gas); + } + } + } + + let medians = benches + .into_iter() + .map(|(key, gas_benches)| (key, median(gas_benches))) + .collect::>(); + + crate::store_bench_data(|bench_data| { + for (key, value) in medians { + bench_data.update_storage_million_bench(key, value); + } + }) + .unwrap(); +} + #[tokio::test] async fn storage_stress_bench() { let mut benches: BTreeMap> = Default::default(); @@ -324,6 +623,77 @@ async fn storage_stress_bench() { .unwrap(); } +#[tokio::test] +async fn vft_storage_transfer_bench() { + let mut benches: BTreeMap> = Default::default(); + #[cfg(feature = "gas-profile")] + let mut profile_benches: BTreeMap> = Default::default(); + let loads = [16, 64, 128, 256, 1024]; + let wasm_path = "../target/wasm32-gear/release/vft_stress.opt.wasm"; + assert_vft_stress_wasm_static_memory_layout(wasm_path); + + for sample in 0..10 { + #[cfg(feature = "gas-profile")] + for (key, gas) in vft_framework_overhead_test(sample).await { + profile_benches.entry(key).or_default().push(gas); + } + + for backend in [ + VftStorageBackend::BTree, + VftStorageBackend::HashMap, + VftStorageBackend::SailsFixed, + VftStorageBackend::SailsStatic, + VftStorageBackend::SailsStaticFast, + ] { + for &load in loads.iter() { + let run = vft_storage_transfer_test(backend.clone(), load, sample).await; + + for gas in run.prepare { + benches + .entry(vft_prepare_key(&backend, load)) + .or_default() + .push(gas); + } + + for (op, gas) in run.operations { + benches + .entry(vft_named_key(&backend, &op, load)) + .or_default() + .push(gas); + } + } + } + } + + let medians = benches + .into_iter() + .map(|(key, gas_benches)| (key, median(gas_benches))) + .collect::>(); + + #[cfg(not(feature = "gas-profile"))] + crate::store_bench_data(|bench_data| { + for (key, value) in medians.clone() { + bench_data.update_storage_bench(key, value); + } + }) + .unwrap(); + + #[cfg(feature = "gas-profile")] + { + let mut profile_medians = medians.clone(); + profile_medians.extend( + profile_benches + .into_iter() + .map(|(key, gas_benches)| (key, median(gas_benches))), + ); + crate::write_gas_profile_summary( + &profile_medians, + &render_vft_comparison_markdown(&profile_medians), + ) + .unwrap(); + } +} + async fn alloc_stress_test(n: u32) -> (usize, u64) { // Path taken from the .binpath file let wasm_path = "../target/wasm32-gear/release/alloc_stress.opt.wasm"; @@ -376,120 +746,1310 @@ struct StorageStressRun { operations: Vec<(StorageOp, u64)>, } -async fn storage_stress_test( - backend: StorageBackend, - map: StorageMap, +struct VftTransferRun { + prepare: Vec, + operations: Vec<(String, u64)>, +} + +struct AggregatorTrackerRun { + prepare: Vec, + operations: Vec<(AggregatorTrackerOp, u64)>, +} + +struct StorageMillionRun { + prepare_total: u64, + operations: Vec<(MillionStorageOp, u64)>, + batch_operations: Vec<(MillionStorageOp, u64)>, +} + +struct MillionVftTransferRun { + prepare_total: u64, + operations: Vec<(MillionVftTransferOp, u64)>, +} + +struct MillionVftRealCostRun { + prepare_total: u64, + operations: Vec<(&'static str, u64)>, +} + +async fn storage_million_static_test( + backend: MillionStorageBackend, load: u32, sample: u32, -) -> StorageStressRun { - let wasm_path = "../target/wasm32-gear/release/storage_stress.opt.wasm"; +) -> StorageMillionRun { + let wasm_path = "../target/wasm32-gear/release/storage_million.opt.wasm"; let env = create_env(); - let program = deploy_for_bench(&env, wasm_path, |d| StorageStressCtors::new_for_bench(d)).await; - let mut service = program.storage_stress(); + let program = + deploy_for_bench(&env, wasm_path, |d| StorageMillionCtors::new_for_bench(d)).await; + let mut service = program.storage_million(); + + let mut start = 0; + let mut prepare_total = 0u64; + while start < load { + let len = (load - start).min(STORAGE_MILLION_PREPARE_CHUNK); + let message_id = service + .prepare_chunk(backend.clone(), start, len) + .send_one_way() + .unwrap(); + let (payload, gas) = extract_reply_and_gas(env.system(), message_id); + prepare_total += gas; + start += len; - let ops = storage_ops_for_load(load); - let mut prepare = Vec::with_capacity(ops.len()); - let mut operations = Vec::with_capacity(ops.len()); + let result = + crate::clients::storage_million_client::storage_million::io::PrepareChunk::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(result.len, start); + } - for op in ops { + let mut current_len = load; + let mut operations = Vec::with_capacity(storage_million_ops().len()); + for op in storage_million_ops() { + let seed = storage_million_seed_for_op(&op, load, sample); let message_id = service - .prepare(backend.clone(), map.clone(), load) + .bench(backend.clone(), op.clone(), seed) .send_one_way() .unwrap(); let (payload, gas) = extract_reply_and_gas(env.system(), message_id); - prepare.push(gas); - let prep = - crate::clients::storage_stress_client::storage_stress::io::Prepare::decode_reply( - StorageStressProgram::ROUTE_ID_STORAGE_STRESS, + let result = + crate::clients::storage_million_client::storage_million::io::Bench::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, payload.as_slice(), ) .unwrap(); - assert_eq!(prep.len, load); - let seed = storage_seed_for_op(&op, load, sample); + assert_storage_million_result(&op, seed, &mut current_len, &result); + operations.push((op, gas)); + } + + let mut batch_operations = Vec::with_capacity(storage_million_ops().len()); + for op in storage_million_ops() { + let start_seed = storage_million_batch_seed_for_op(&op, sample); let message_id = service - .bench(backend.clone(), map.clone(), op.clone(), seed) + .bench_many( + backend.clone(), + op.clone(), + start_seed, + STORAGE_MILLION_BATCH_COUNT, + ) .send_one_way() .unwrap(); let (payload, gas) = extract_reply_and_gas(env.system(), message_id); let result = - crate::clients::storage_stress_client::storage_stress::io::Bench::decode_reply( - StorageStressProgram::ROUTE_ID_STORAGE_STRESS, + crate::clients::storage_million_client::storage_million::io::BenchMany::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, payload.as_slice(), ) .unwrap(); - assert_storage_result(&op, load, seed, &result); - operations.push((op, gas)); + assert_storage_million_batch_result( + &op, + start_seed, + STORAGE_MILLION_BATCH_COUNT, + &mut current_len, + &result, + ); + batch_operations.push((op, gas)); } - StorageStressRun { - prepare, + StorageMillionRun { + prepare_total, operations, + batch_operations, } } -fn storage_ops_for_load(load: u32) -> Vec { - if load == 0 { - vec![StorageOp::InsertFresh, StorageOp::ReadMissing] - } else { - vec![ - StorageOp::InsertFresh, - StorageOp::UpdateExisting, - StorageOp::ReadExisting, - StorageOp::ReadMissing, - StorageOp::Remove, - ] - } -} +async fn storage_million_vft_transfer_test( + backend: MillionVftBackend, + load: u32, + sample: u32, +) -> MillionVftTransferRun { + let wasm_path = "../target/wasm32-gear/release/storage_million.opt.wasm"; + let env = create_env(); + let program = + deploy_for_bench(&env, wasm_path, |d| StorageMillionCtors::new_for_bench(d)).await; + let mut service = program.storage_million(); + + let mut start = 0; + let mut prepare_total = 0u64; + while start < load { + let len = (load - start).min(STORAGE_MILLION_PREPARE_CHUNK); + let message_id = service + .prepare_vft_chunk(backend.clone(), start, len) + .send_one_way() + .unwrap(); + let (payload, gas) = extract_reply_and_gas(env.system(), message_id); + prepare_total += gas; + start += len; -fn storage_seed_for_op(op: &StorageOp, load: u32, sample: u32) -> u32 { - match op { - StorageOp::InsertFresh | StorageOp::ReadMissing => 10_000 + load + sample, - StorageOp::UpdateExisting | StorageOp::ReadExisting | StorageOp::Remove => { - 1 + (sample % load) - } + let result = crate::clients::storage_million_client::storage_million::io::PrepareVftChunk::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(result.balance_len, start); + assert_eq!(result.allowance_len, start); } -} -fn assert_storage_result(op: &StorageOp, load: u32, seed: u32, result: &StorageBenchResult) { - match op { - StorageOp::InsertFresh => { - assert_eq!(result.value, storage_value_for_seed(seed)); - assert_eq!(result.len, load + 1); - assert!(!result.existed); - } - StorageOp::UpdateExisting => { - assert_eq!(result.value, storage_updated_value_for_seed(seed)); - assert_eq!(result.len, load); - assert!(result.existed); - } - StorageOp::ReadExisting => { - assert_eq!(result.value, storage_value_for_seed(seed)); - assert_eq!(result.len, load); - assert!(result.existed); - } - StorageOp::ReadMissing => { - assert_eq!(result.value, U256::zero()); - assert_eq!(result.len, load); - assert!(!result.existed); - } - StorageOp::Remove => { - assert_eq!(result.value, storage_value_for_seed(seed)); - assert_eq!(result.len, load - 1); - assert!(result.existed); - } + let mut operations = Vec::with_capacity(storage_million_vft_ops().len()); + for op in storage_million_vft_ops() { + let seed = storage_million_vft_seed_for_op(&op, load, sample); + let message_id = service + .bench_vft_transfer(backend.clone(), op.clone(), seed) + .send_one_way() + .unwrap(); + let (payload, gas) = extract_reply_and_gas(env.system(), message_id); + let result = crate::clients::storage_million_client::storage_million::io::BenchVftTransfer::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + + assert_storage_million_vft_result(&op, seed, load, &result); + operations.push((op, gas)); } -} -fn storage_value_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 1) + MillionVftTransferRun { + prepare_total, + operations, + } } -fn storage_updated_value_for_seed(seed: u32) -> U256 { +async fn storage_million_vft_real_cost_test( + backend: MillionVftBackend, + load: u32, + sample: u32, +) -> MillionVftRealCostRun { + let wasm_path = "../target/wasm32-gear/release/storage_million.opt.wasm"; + let env = create_env(); + let program = + deploy_for_bench(&env, wasm_path, |d| StorageMillionCtors::new_for_bench(d)).await; + let mut service = program.storage_million(); + + let mut start = 0; + let mut prepare_total = 0u64; + while start < load { + let len = (load - start).min(STORAGE_MILLION_PREPARE_CHUNK); + let message_id = service + .prepare_vft_chunk(backend.clone(), start, len) + .send_one_way() + .unwrap(); + let (payload, gas) = extract_reply_and_gas(env.system(), message_id); + prepare_total += gas; + start += len; + + let result = crate::clients::storage_million_client::storage_million::io::PrepareVftChunk::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(result.balance_len, start); + assert_eq!(result.allowance_len, start); + } + + let mut operations = Vec::with_capacity(7); + + let transfer_seed = 20_000 + sample; + let message_id = service + .bench_vft_transfer_bool( + backend.clone(), + MillionVftTransferOp::Transfer, + transfer_seed, + ) + .send_one_way() + .unwrap(); + let profile_case = (sample == 0).then(|| { + format!( + "{}_sample{sample}", + storage_million_vft_real_key(&backend, "transfer_bool", load) + ) + }); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); + let transferred = crate::clients::storage_million_client::storage_million::io::BenchVftTransferBool::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert!(transferred); + operations.push(("transfer_bool", gas)); + + let fresh_seed = 40_000 + sample; + let message_id = service + .bench_vft_transfer_fresh_bool(backend.clone(), fresh_seed) + .send_one_way() + .unwrap(); + let profile_case = (sample == 0).then(|| { + format!( + "{}_sample{sample}", + storage_million_vft_real_key(&backend, "transfer_fresh_bool", load) + ) + }); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); + let transferred = crate::clients::storage_million_client::storage_million::io::BenchVftTransferFreshBool::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert!(transferred); + operations.push(("transfer_fresh_bool", gas)); + + let transfer_from_seed = 30_000 + sample; + let message_id = service + .bench_vft_transfer_bool( + backend.clone(), + MillionVftTransferOp::TransferFrom, + transfer_from_seed, + ) + .send_one_way() + .unwrap(); + let profile_case = (sample == 0).then(|| { + format!( + "{}_sample{sample}", + storage_million_vft_real_key(&backend, "transfer_from_bool", load) + ) + }); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); + let transferred = crate::clients::storage_million_client::storage_million::io::BenchVftTransferBool::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert!(transferred); + operations.push(("transfer_from_bool", gas)); + + let owner_seed = 50_000 + sample; + let spender_seed = load + 60_000 + sample; + let message_id = service + .bench_vft_approve_bool(backend.clone(), owner_seed, spender_seed) + .send_one_way() + .unwrap(); + let profile_case = (sample == 0).then(|| { + format!( + "{}_sample{sample}", + storage_million_vft_real_key(&backend, "approve_fresh_bool", load) + ) + }); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); + let approved = crate::clients::storage_million_client::storage_million::io::BenchVftApproveBool::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert!(approved); + operations.push(("approve_fresh_bool", gas)); + + let owner_seed = 70_000 + sample; + let spender_seed = load + 170_001 + sample; + let message_id = service + .bench_vft_approve_bool(backend.clone(), owner_seed, spender_seed) + .send_one_way() + .unwrap(); + let profile_case = (sample == 0).then(|| { + format!( + "{}_sample{sample}", + storage_million_vft_real_key(&backend, "approve_second_bool", load) + ) + }); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); + let approved = crate::clients::storage_million_client::storage_million::io::BenchVftApproveBool::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert!(approved); + operations.push(("approve_second_bool", gas)); + + let spender_seed = load + 170_002 + sample; + let message_id = service + .bench_vft_approve_bool(backend.clone(), owner_seed, spender_seed) + .send_one_way() + .unwrap(); + let profile_case = (sample == 0).then(|| { + format!( + "{}_sample{sample}", + storage_million_vft_real_key(&backend, "approve_overflow_third_bool", load) + ) + }); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); + let approved = crate::clients::storage_million_client::storage_million::io::BenchVftApproveBool::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert!(approved); + operations.push(("approve_overflow_third_bool", gas)); + + let owner_seed = 80_000 + sample; + let inline_second_spender_seed = load + 180_001 + sample; + let overflow_spender_seed = load + 180_002 + sample; + let message_id = service + .bench_vft_approve_bool(backend.clone(), owner_seed, inline_second_spender_seed) + .send_one_way() + .unwrap(); + let (payload, _) = extract_reply_and_gas(env.system(), message_id); + let approved = crate::clients::storage_million_client::storage_million::io::BenchVftApproveBool::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert!(approved); + let message_id = service + .bench_vft_approve_bool(backend.clone(), owner_seed, overflow_spender_seed) + .send_one_way() + .unwrap(); + let (payload, _) = extract_reply_and_gas(env.system(), message_id); + let approved = crate::clients::storage_million_client::storage_million::io::BenchVftApproveBool::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert!(approved); + let message_id = service + .bench_vft_transfer_from_spender_bool(backend.clone(), owner_seed, overflow_spender_seed) + .send_one_way() + .unwrap(); + let profile_case = (sample == 0).then(|| { + format!( + "{}_sample{sample}", + storage_million_vft_real_key(&backend, "transfer_from_overflow_bool", load) + ) + }); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); + let transferred = crate::clients::storage_million_client::storage_million::io::BenchVftTransferFromSpenderBool::decode_reply( + StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, + payload.as_slice(), + ) + .unwrap(); + assert!(transferred); + operations.push(("transfer_from_overflow_bool", gas)); + + MillionVftRealCostRun { + prepare_total, + operations, + } +} + +async fn aggregator_tracker_test( + backend: AggregatorTrackerBackend, + load: u32, + sample: u32, +) -> AggregatorTrackerRun { + let env = create_env(); + let code_id = env.system().submit_code(aggregator_app::WASM_BINARY); + let program = deploy_code_for_bench(&env, code_id, |d| { + AggregatorClientCtors::new_with_tracker(d, ActorId::zero(), backend.clone()) + }) + .await; + let mut service = program.aggregator(); + + let ops = aggregator_tracker_ops_for_load(load); + let mut prepare = Vec::with_capacity(ops.len()); + let mut operations = Vec::with_capacity(ops.len()); + + for op in ops { + let message_id = service.prepare_tracker(load).send_one_way().unwrap(); + let (payload, gas) = extract_reply_and_gas(env.system(), message_id); + prepare.push(gas); + let prep = aggregator_client::aggregator::io::PrepareTracker::decode_reply( + AggregatorClientProgram::ROUTE_ID_AGGREGATOR, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(prep.len, load); + + let seed = aggregator_tracker_seed_for_op(&op, load, sample); + let message_id = service + .bench_tracker(op.clone(), seed) + .send_one_way() + .unwrap(); + let (payload, gas) = extract_reply_and_gas(env.system(), message_id); + let result = aggregator_client::aggregator::io::BenchTracker::decode_reply( + AggregatorClientProgram::ROUTE_ID_AGGREGATOR, + payload.as_slice(), + ) + .unwrap(); + + assert_aggregator_tracker_result(&op, load, &result); + operations.push((op, gas)); + } + + AggregatorTrackerRun { + prepare, + operations, + } +} + +fn aggregator_tracker_ops_for_load(load: u32) -> Vec { + if load == 0 { + vec![ + AggregatorTrackerOp::InsertFresh, + AggregatorTrackerOp::ListStatuses, + ] + } else { + vec![ + AggregatorTrackerOp::InsertFresh, + AggregatorTrackerOp::UpdateExisting, + AggregatorTrackerOp::ReadExisting, + AggregatorTrackerOp::ListStatuses, + ] + } +} + +fn aggregator_tracker_seed_for_op(op: &AggregatorTrackerOp, load: u32, sample: u32) -> u32 { + match op { + AggregatorTrackerOp::InsertFresh => 10_000 + load + sample, + AggregatorTrackerOp::UpdateExisting | AggregatorTrackerOp::ReadExisting => { + 1 + (sample % load) + } + AggregatorTrackerOp::ListStatuses => 0, + } +} + +fn assert_aggregator_tracker_result( + op: &AggregatorTrackerOp, + load: u32, + result: &AggregatorTrackerBenchResult, +) { + match op { + AggregatorTrackerOp::InsertFresh => { + assert_eq!(result.len, load + 1); + assert_eq!(result.status, Some(AggregatorOpStatus::Started)); + assert!(!result.existed); + } + AggregatorTrackerOp::UpdateExisting => { + assert_eq!(result.len, load); + assert_eq!(result.status, Some(AggregatorOpStatus::Finalized)); + assert!(result.existed); + } + AggregatorTrackerOp::ReadExisting => { + assert_eq!(result.len, load); + assert_eq!(result.status, Some(AggregatorOpStatus::Started)); + assert!(result.existed); + } + AggregatorTrackerOp::ListStatuses => { + assert_eq!(result.len, load); + if load == 0 { + assert_eq!(result.status, None); + assert!(!result.existed); + } else { + assert_eq!(result.status, Some(AggregatorOpStatus::Started)); + assert!(result.existed); + } + } + } +} + +async fn storage_stress_test( + backend: StorageBackend, + map: StorageMap, + load: u32, + sample: u32, +) -> StorageStressRun { + let wasm_path = "../target/wasm32-gear/release/storage_stress.opt.wasm"; + let env = create_env(); + let program = deploy_for_bench(&env, wasm_path, |d| StorageStressCtors::new_for_bench(d)).await; + let mut service = program.storage_stress(); + + let ops = storage_ops_for_load(load); + let mut prepare = Vec::with_capacity(ops.len()); + let mut operations = Vec::with_capacity(ops.len()); + + for op in ops { + let message_id = service + .prepare(backend.clone(), map.clone(), load) + .send_one_way() + .unwrap(); + let (payload, gas) = extract_reply_and_gas(env.system(), message_id); + prepare.push(gas); + let prep = + crate::clients::storage_stress_client::storage_stress::io::Prepare::decode_reply( + StorageStressProgram::ROUTE_ID_STORAGE_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(prep.len, load); + + let seed = storage_seed_for_op(&op, load, sample); + let message_id = service + .bench(backend.clone(), map.clone(), op.clone(), seed) + .send_one_way() + .unwrap(); + let (payload, gas) = extract_reply_and_gas(env.system(), message_id); + let result = + crate::clients::storage_stress_client::storage_stress::io::Bench::decode_reply( + StorageStressProgram::ROUTE_ID_STORAGE_STRESS, + payload.as_slice(), + ) + .unwrap(); + + assert_storage_result(&op, load, seed, &result); + operations.push((op, gas)); + } + + StorageStressRun { + prepare, + operations, + } +} + +#[cfg(feature = "gas-profile")] +async fn vft_framework_overhead_test(sample: u32) -> Vec<(String, u64)> { + let wasm_path = "../target/wasm32-gear/release/vft_stress.opt.wasm"; + let env = create_env(); + let program = deploy_for_bench(&env, wasm_path, |d| VftStressCtors::new_for_bench(d)).await; + let mut service = program.vft_stress(); + let mut operations = Vec::with_capacity(2); + + let message_id = service.bench_noop().send_one_way().unwrap(); + let profile_case = (sample == 0).then(|| format!("vft_framework_noop_sample{sample}")); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); + let ok = crate::clients::vft_stress_client::vft_stress::io::BenchNoop::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert!(ok); + operations.push(("vft_framework_noop".to_owned(), gas)); + + let message_id = service + .bench_echo_vft_args( + VftStorageBackend::SailsStatic, + VftTransferOp::Transfer, + sample + 1, + ) + .send_one_way() + .unwrap(); + let profile_case = (sample == 0).then(|| format!("vft_framework_echo_args_sample{sample}")); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); + let ok = crate::clients::vft_stress_client::vft_stress::io::BenchEchoVftArgs::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert!(ok); + operations.push(("vft_framework_echo_args".to_owned(), gas)); + + operations +} + +async fn vft_storage_transfer_test( + backend: VftStorageBackend, + load: u32, + sample: u32, +) -> VftTransferRun { + let wasm_path = "../target/wasm32-gear/release/vft_stress.opt.wasm"; + let env = create_env(); + let program = deploy_for_bench(&env, wasm_path, |d| VftStressCtors::new_for_bench(d)).await; + let mut service = program.vft_stress(); + + let ops = vft_transfer_ops(); + let mut prepare = Vec::with_capacity(ops.len() + 2); + let mut operations = Vec::with_capacity(ops.len() + 2); + + for op in ops { + let message_id = service + .prepare_vft(backend.clone(), load) + .send_one_way() + .unwrap(); + let profile_case = + (sample == 0).then(|| format!("{}_sample{sample}", vft_prepare_key(&backend, load))); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); + prepare.push(gas); + let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(prep.balance_len, load); + assert_eq!(prep.allowance_len, load); + + let seed = vft_transfer_seed_for_op(&op, load, sample); + let message_id = service + .bench_vft_transfer(backend.clone(), op.clone(), seed) + .send_one_way() + .unwrap(); + let profile_case = (sample == 0) + .then(|| format!("{}_sample{sample}", vft_transfer_key(&backend, &op, load))); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); + let result = + crate::clients::vft_stress_client::vft_stress::io::BenchVftTransfer::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + + assert_vft_transfer_result(&op, load, seed, &result); + operations.push((vft_transfer_op_name(&op).to_owned(), gas)); + } + + let fresh_prepare_id = service + .prepare_vft(backend.clone(), load) + .send_one_way() + .unwrap(); + let profile_case = + (sample == 0).then(|| format!("{}_sample{sample}", vft_prepare_key(&backend, load))); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), fresh_prepare_id, profile_case.as_deref()); + prepare.push(gas); + let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(prep.balance_len, load); + assert_eq!(prep.allowance_len, load); + + let fresh_seed = 1 + (sample % load); + let fresh_id = service + .bench_vft_transfer_fresh_bool(backend.clone(), fresh_seed) + .send_one_way() + .unwrap(); + let profile_case = (sample == 0).then(|| { + format!( + "{}_sample{sample}", + vft_named_key(&backend, "transfer_fresh", load) + ) + }); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), fresh_id, profile_case.as_deref()); + let transferred = + crate::clients::vft_stress_client::vft_stress::io::BenchVftTransferFreshBool::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert!(transferred); + operations.push(("transfer_fresh".to_owned(), gas)); + + let approve_prepare_id = service + .prepare_vft(backend.clone(), load) + .send_one_way() + .unwrap(); + let profile_case = + (sample == 0).then(|| format!("{}_sample{sample}", vft_prepare_key(&backend, load))); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), approve_prepare_id, profile_case.as_deref()); + prepare.push(gas); + let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(prep.balance_len, load); + assert_eq!(prep.allowance_len, load); + + let owner_seed = 1 + (sample % load); + let spender_seed = 40_000 + sample; + let approve_id = service + .bench_vft_approve_bool(backend.clone(), owner_seed, spender_seed) + .send_one_way() + .unwrap(); + let profile_case = (sample == 0).then(|| { + format!( + "{}_sample{sample}", + vft_named_key(&backend, "approve_fresh", load) + ) + }); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), approve_id, profile_case.as_deref()); + let approved = + crate::clients::vft_stress_client::vft_stress::io::BenchVftApproveBool::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert!(approved); + operations.push(("approve_fresh".to_owned(), gas)); + + #[cfg(feature = "gas-profile")] + if sample == 0 { + for op in vft_transfer_ops() { + let prepare_id = service + .prepare_vft(backend.clone(), load) + .send_one_way() + .unwrap(); + let (payload, _) = extract_reply_and_gas(env.system(), prepare_id); + let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(prep.balance_len, load); + assert_eq!(prep.allowance_len, load); + + let seed = vft_transfer_seed_for_op(&op, load, sample); + let message_id = service + .bench_vft_transfer_profile(backend.clone(), op.clone(), seed) + .send_one_way() + .unwrap(); + let profile_case = format!( + "{}_sample{sample}", + vft_named_key( + &backend, + &format!("{}_profile", vft_transfer_op_name(&op)), + load + ) + ); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, Some(&profile_case)); + let profile = + crate::clients::vft_stress_client::vft_stress::io::BenchVftTransferProfile::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert_vft_transfer_result(&op, load, seed, &profile.result); + write_vft_phase_profile(&profile_case, gas, &profile); + } + + let prepare_id = service + .prepare_vft(backend.clone(), load) + .send_one_way() + .unwrap(); + let (payload, _) = extract_reply_and_gas(env.system(), prepare_id); + let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(prep.balance_len, load); + assert_eq!(prep.allowance_len, load); + + let fresh_seed = 1 + (sample % load); + let profile_case = format!( + "{}_sample{sample}", + vft_named_key(&backend, "transfer_fresh_profile", load) + ); + let message_id = service + .bench_vft_transfer_fresh_profile(backend.clone(), fresh_seed) + .send_one_way() + .unwrap(); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, Some(&profile_case)); + let profile = + crate::clients::vft_stress_client::vft_stress::io::BenchVftTransferFreshProfile::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert_vft_transfer_result(&VftTransferOp::Transfer, load, fresh_seed, &profile.result); + write_vft_phase_profile(&profile_case, gas, &profile); + + let prepare_id = service + .prepare_vft(backend.clone(), load) + .send_one_way() + .unwrap(); + let (payload, _) = extract_reply_and_gas(env.system(), prepare_id); + let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert_eq!(prep.balance_len, load); + assert_eq!(prep.allowance_len, load); + + let owner_seed = 1 + (sample % load); + let spender_seed = 40_000 + sample; + let profile_case = format!( + "{}_sample{sample}", + vft_named_key(&backend, "approve_fresh_profile", load) + ); + let message_id = service + .bench_vft_approve_profile(backend.clone(), owner_seed, spender_seed) + .send_one_way() + .unwrap(); + let (payload, gas) = + extract_reply_and_gas_profiled(env.system(), message_id, Some(&profile_case)); + let profile = + crate::clients::vft_stress_client::vft_stress::io::BenchVftApproveProfile::decode_reply( + VftStressProgram::ROUTE_ID_VFT_STRESS, + payload.as_slice(), + ) + .unwrap(); + assert!(profile.result.transferred); + write_vft_phase_profile(&profile_case, gas, &profile); + } + + VftTransferRun { + prepare, + operations, + } +} + +fn storage_ops_for_load(load: u32) -> Vec { + if load == 0 { + vec![StorageOp::InsertFresh, StorageOp::ReadMissing] + } else { + vec![ + StorageOp::InsertFresh, + StorageOp::UpdateExisting, + StorageOp::ReadExisting, + StorageOp::ReadMissing, + StorageOp::Remove, + ] + } +} + +fn vft_transfer_ops() -> [VftTransferOp; 2] { + [VftTransferOp::Transfer, VftTransferOp::TransferFrom] +} + +fn storage_seed_for_op(op: &StorageOp, load: u32, sample: u32) -> u32 { + match op { + StorageOp::InsertFresh | StorageOp::ReadMissing => 10_000 + load + sample, + StorageOp::UpdateExisting | StorageOp::ReadExisting | StorageOp::Remove => { + 1 + (sample % load) + } + } +} + +fn vft_transfer_seed_for_op(_op: &VftTransferOp, load: u32, sample: u32) -> u32 { + 1 + (sample % load) +} + +fn assert_storage_result(op: &StorageOp, load: u32, seed: u32, result: &StorageBenchResult) { + match op { + StorageOp::InsertFresh => { + assert_eq!(result.value, storage_value_for_seed(seed)); + assert_eq!(result.len, load + 1); + assert!(!result.existed); + } + StorageOp::UpdateExisting => { + assert_eq!(result.value, storage_updated_value_for_seed(seed)); + assert_eq!(result.len, load); + assert!(result.existed); + } + StorageOp::ReadExisting => { + assert_eq!(result.value, storage_value_for_seed(seed)); + assert_eq!(result.len, load); + assert!(result.existed); + } + StorageOp::ReadMissing => { + assert_eq!(result.value, U256::zero()); + assert_eq!(result.len, load); + assert!(!result.existed); + } + StorageOp::Remove => { + assert_eq!(result.value, storage_value_for_seed(seed)); + assert_eq!(result.len, load - 1); + assert!(result.existed); + } + } +} + +fn assert_vft_transfer_result( + op: &VftTransferOp, + load: u32, + seed: u32, + result: &VftTransferResult, +) { + let amount = vft_transfer_amount(seed); + assert!(result.transferred); + assert_eq!(result.from_balance, vft_balance_for_seed(seed) - amount); + assert_eq!(result.to_balance, amount); + assert_eq!(result.balance_len, load + 1); + + match op { + VftTransferOp::Transfer => { + assert_eq!(result.allowance, U256::zero()); + assert_eq!(result.allowance_len, load); + } + VftTransferOp::TransferFrom => { + assert_eq!(result.allowance, vft_allowance_for_seed(seed) - amount); + assert_eq!(result.allowance_len, load); + } + } +} + +fn storage_value_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 1) +} + +fn storage_updated_value_for_seed(seed: u32) -> U256 { U256::from(seed as u64 + 1_000_001) } +fn vft_balance_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 1_000_000) +} + +fn vft_allowance_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 10_000) +} + +fn vft_transfer_amount(seed: u32) -> U256 { + U256::from((seed % 7) as u64 + 1) +} + +fn storage_million_ops() -> [MillionStorageOp; 5] { + [ + MillionStorageOp::ReadExisting, + MillionStorageOp::UpdateExisting, + MillionStorageOp::ReadMissing, + MillionStorageOp::InsertFresh, + MillionStorageOp::Remove, + ] +} + +fn storage_million_vft_ops() -> [MillionVftTransferOp; 2] { + [ + MillionVftTransferOp::Transfer, + MillionVftTransferOp::TransferFrom, + ] +} + +fn storage_million_seed_for_op(op: &MillionStorageOp, load: u32, sample: u32) -> u32 { + match op { + MillionStorageOp::InsertFresh | MillionStorageOp::ReadMissing => 10_000_000 + load + sample, + MillionStorageOp::UpdateExisting + | MillionStorageOp::ReadExisting + | MillionStorageOp::Remove => 1 + (sample % load), + } +} + +fn storage_million_vft_seed_for_op(op: &MillionVftTransferOp, _load: u32, sample: u32) -> u32 { + match op { + MillionVftTransferOp::Transfer => 20_000 + sample, + MillionVftTransferOp::TransferFrom => 30_000 + sample, + } +} + +fn storage_million_batch_seed_for_op(op: &MillionStorageOp, sample: u32) -> u32 { + let sample_offset = sample * 10_000; + match op { + MillionStorageOp::ReadExisting => 20_000 + sample_offset, + MillionStorageOp::UpdateExisting => 30_000 + sample_offset, + MillionStorageOp::ReadMissing => 30_000_000 + sample_offset, + MillionStorageOp::InsertFresh => 40_000_000 + sample_offset, + MillionStorageOp::Remove => 40_000 + sample_offset, + } +} + +fn assert_storage_million_result( + op: &MillionStorageOp, + seed: u32, + current_len: &mut u32, + result: &MillionStorageBenchResult, +) { + match op { + MillionStorageOp::InsertFresh => { + *current_len += 1; + assert_eq!(result.value, storage_value_for_seed(seed)); + assert_eq!(result.len, *current_len); + assert!(!result.existed); + } + MillionStorageOp::UpdateExisting => { + assert_eq!(result.value, storage_updated_value_for_seed(seed)); + assert_eq!(result.len, *current_len); + assert!(result.existed); + } + MillionStorageOp::ReadExisting => { + assert_eq!(result.value, storage_value_for_seed(seed)); + assert_eq!(result.len, *current_len); + assert!(result.existed); + } + MillionStorageOp::ReadMissing => { + assert_eq!(result.value, U256::zero()); + assert_eq!(result.len, *current_len); + assert!(!result.existed); + } + MillionStorageOp::Remove => { + *current_len -= 1; + assert_eq!(result.value, storage_updated_value_for_seed(seed)); + assert_eq!(result.len, *current_len); + assert!(result.existed); + } + } +} + +fn assert_storage_million_vft_result( + op: &MillionVftTransferOp, + seed: u32, + load: u32, + result: &MillionVftTransferResult, +) { + let amount = vft_transfer_amount(seed); + let to_seed = seed + 1; + + assert!(result.transferred); + assert_eq!(result.from_balance, vft_balance_for_seed(seed) - amount); + assert_eq!(result.to_balance, vft_balance_for_seed(to_seed) + amount); + assert_eq!(result.balance_len, load); + assert_eq!(result.allowance_len, load); + + match op { + MillionVftTransferOp::Transfer => assert_eq!(result.allowance, U256::zero()), + MillionVftTransferOp::TransferFrom => { + assert_eq!(result.allowance, vft_allowance_for_seed(seed) - amount) + } + } +} + +fn assert_storage_million_batch_result( + op: &MillionStorageOp, + start_seed: u32, + count: u32, + current_len: &mut u32, + result: &MillionStorageBenchResult, +) { + let last_seed = start_seed + count - 1; + match op { + MillionStorageOp::InsertFresh => { + *current_len += count; + assert_eq!(result.value, storage_value_for_seed(last_seed)); + assert_eq!(result.len, *current_len); + assert!(!result.existed); + } + MillionStorageOp::UpdateExisting => { + assert_eq!(result.value, storage_updated_value_for_seed(last_seed)); + assert_eq!(result.len, *current_len); + assert!(result.existed); + } + MillionStorageOp::ReadExisting => { + assert_eq!(result.value, storage_value_for_seed(last_seed)); + assert_eq!(result.len, *current_len); + assert!(result.existed); + } + MillionStorageOp::ReadMissing => { + assert_eq!(result.value, U256::zero()); + assert_eq!(result.len, *current_len); + assert!(!result.existed); + } + MillionStorageOp::Remove => { + *current_len -= count; + assert_eq!(result.value, storage_value_for_seed(last_seed)); + assert_eq!(result.len, *current_len); + assert!(result.existed); + } + } +} + +fn storage_million_bench_key( + backend: &MillionStorageBackend, + op: &MillionStorageOp, + load: u32, +) -> String { + format!( + "{}_{}_{}", + storage_million_backend_name(backend), + storage_million_op_name(op), + load + ) +} + +fn storage_million_batch_key( + backend: &MillionStorageBackend, + op: &MillionStorageOp, + count: u32, + load: u32, +) -> String { + format!( + "{}_{}_batch{count}_{load}", + storage_million_backend_name(backend), + storage_million_op_name(op) + ) +} + +fn storage_million_vft_key( + backend: &MillionVftBackend, + op: &MillionVftTransferOp, + load: u32, +) -> String { + format!( + "vft_{}_{}_{}", + storage_million_vft_backend_name(backend), + storage_million_vft_op_name(op), + load + ) +} + +fn storage_million_vft_real_key( + backend: &MillionVftBackend, + op: &'static str, + load: u32, +) -> String { + format!( + "vft_real_{}_{}_{}", + storage_million_vft_backend_name(backend), + op, + load + ) +} + +fn storage_million_prepare_key(backend: &MillionStorageBackend, load: u32) -> String { + format!("{}_prepare_{load}", storage_million_backend_name(backend)) +} + +fn storage_million_vft_prepare_key(backend: &MillionVftBackend, load: u32) -> String { + format!( + "vft_{}_prepare_{load}", + storage_million_vft_backend_name(backend) + ) +} + +fn storage_million_vft_real_prepare_key(backend: &MillionVftBackend, load: u32) -> String { + format!( + "vft_real_{}_prepare_{load}", + storage_million_vft_backend_name(backend) + ) +} + +fn storage_million_backend_name(backend: &MillionStorageBackend) -> &'static str { + match backend { + MillionStorageBackend::GenericStatic => "static_balance", + MillionStorageBackend::WatActorStatic => "wat_actor_balance", + MillionStorageBackend::MixedActorStatic => "mixed_actor_balance", + MillionStorageBackend::TagActorStatic => "tag_actor_balance", + MillionStorageBackend::TagU64ActorStatic => "tag_u64_actor_balance", + MillionStorageBackend::ControlActorStatic => "control_actor_balance", + MillionStorageBackend::PageLocalActorStatic => "page_local_actor_balance", + MillionStorageBackend::GroupedActorPages2 => "grouped_actor_balance_pages2", + MillionStorageBackend::GroupedActorPages4 => "grouped_actor_balance_pages4", + MillionStorageBackend::GroupedActorPages8 => "grouped_actor_balance_pages8", + MillionStorageBackend::GroupedActorPages16 => "grouped_actor_balance_pages16", + MillionStorageBackend::GroupedActorPages32 => "grouped_actor_balance_pages32", + MillionStorageBackend::GroupedActorPages64 => "grouped_actor_balance_pages64", + MillionStorageBackend::GroupedActorPages128 => "grouped_actor_balance_pages128", + } +} + +fn storage_million_vft_backend_name(backend: &MillionVftBackend) -> &'static str { + match backend { + MillionVftBackend::BTree => "btree", + MillionVftBackend::HashMap => "hashmap", + MillionVftBackend::GenericStatic => "static_balance", + MillionVftBackend::GenericStaticFused => "static_balance_fused", + MillionVftBackend::GenericStaticFast => "static_balance_fast", + MillionVftBackend::WatActorStatic => "wat_actor_balance", + MillionVftBackend::MixedActorStatic => "mixed_actor_balance", + MillionVftBackend::MixedActorFast => "mixed_actor_balance_fast", + MillionVftBackend::TagActorStatic => "tag_actor_balance", + MillionVftBackend::TagU64ActorStatic => "tag_u64_actor_balance", + MillionVftBackend::ControlActorStatic => "control_actor_balance", + MillionVftBackend::PageLocalActorStatic => "page_local_actor_balance", + MillionVftBackend::GroupedActorPages64 => "grouped_actor_balance_pages64", + MillionVftBackend::GroupedActorPages128 => "grouped_actor_balance_pages128", + MillionVftBackend::InlineOwnerAccountU256 => "inline_owner_account_u256", + } +} + +fn filtered_million_vft_backends( + env_key: &str, + defaults: &[MillionVftBackend], +) -> Vec { + let Ok(value) = env::var(env_key) else { + return defaults.to_vec(); + }; + if value.trim().is_empty() { + return defaults.to_vec(); + } + + value + .split(',') + .map(str::trim) + .filter(|name| !name.is_empty()) + .map(|name| parse_million_vft_backend(env_key, name)) + .collect() +} + +fn parse_million_vft_backend(env_key: &str, name: &str) -> MillionVftBackend { + match name { + "btree" => MillionVftBackend::BTree, + "hashmap" => MillionVftBackend::HashMap, + "static_balance" | "generic_static" => MillionVftBackend::GenericStatic, + "static_balance_fused" | "generic_static_fused" => MillionVftBackend::GenericStaticFused, + "static_balance_fast" | "generic_static_fast" => MillionVftBackend::GenericStaticFast, + "wat_actor_balance" | "wat_actor_static" => MillionVftBackend::WatActorStatic, + "mixed_actor_balance" | "mixed_actor_static" => MillionVftBackend::MixedActorStatic, + "mixed_actor_balance_fast" | "mixed_actor_fast" => MillionVftBackend::MixedActorFast, + "tag_actor_balance" | "tag_actor_static" => MillionVftBackend::TagActorStatic, + "tag_u64_actor_balance" | "tag_u64_actor_static" => MillionVftBackend::TagU64ActorStatic, + "control_actor_balance" | "control_actor_static" => MillionVftBackend::ControlActorStatic, + "page_local_actor_balance" | "page_local_actor_static" => { + MillionVftBackend::PageLocalActorStatic + } + "grouped_actor_balance_pages64" | "grouped_actor_pages64" => { + MillionVftBackend::GroupedActorPages64 + } + "grouped_actor_balance_pages128" | "grouped_actor_pages128" => { + MillionVftBackend::GroupedActorPages128 + } + "inline_owner_account_u256" + | "inline_owner_account" + | "inline_allowance_u256" + | "inline_allowance_balance" + | "inline_allowance" => MillionVftBackend::InlineOwnerAccountU256, + other => std::panic!("unknown {env_key} backend `{other}`"), + } +} + +fn storage_million_vft_op_name(op: &MillionVftTransferOp) -> &'static str { + match op { + MillionVftTransferOp::Transfer => "transfer", + MillionVftTransferOp::TransferFrom => "transfer_from", + } +} + +fn storage_million_op_name(op: &MillionStorageOp) -> &'static str { + match op { + MillionStorageOp::InsertFresh => "insert_fresh", + MillionStorageOp::UpdateExisting => "update_existing", + MillionStorageOp::ReadExisting => "read_existing", + MillionStorageOp::ReadMissing => "read_missing", + MillionStorageOp::Remove => "remove", + } +} + +fn aggregator_tracker_bench_key( + backend: &AggregatorTrackerBackend, + op: &AggregatorTrackerOp, + load: u32, +) -> String { + format!( + "aggregator_{}_{}_{}", + aggregator_tracker_backend_name(backend), + aggregator_tracker_op_name(op), + load + ) +} + +fn aggregator_tracker_prepare_key(backend: &AggregatorTrackerBackend, load: u32) -> String { + format!( + "aggregator_{}_prepare_{}", + aggregator_tracker_backend_name(backend), + load + ) +} + +fn aggregator_tracker_backend_name(backend: &AggregatorTrackerBackend) -> &'static str { + match backend { + AggregatorTrackerBackend::BTree => "btree", + AggregatorTrackerBackend::SailsFixed => "sails_fixed", + } +} + +fn aggregator_tracker_op_name(op: &AggregatorTrackerOp) -> &'static str { + match op { + AggregatorTrackerOp::InsertFresh => "insert_fresh", + AggregatorTrackerOp::UpdateExisting => "update_existing", + AggregatorTrackerOp::ReadExisting => "read_existing", + AggregatorTrackerOp::ListStatuses => "list_statuses", + } +} + fn storage_bench_key( backend: &StorageBackend, map: &StorageMap, @@ -514,6 +2074,18 @@ fn storage_prepare_key(backend: &StorageBackend, map: &StorageMap, load: u32) -> ) } +fn vft_transfer_key(backend: &VftStorageBackend, op: &VftTransferOp, load: u32) -> String { + vft_named_key(backend, vft_transfer_op_name(op), load) +} + +fn vft_prepare_key(backend: &VftStorageBackend, load: u32) -> String { + format!("vft_{}_prepare_{load}", vft_backend_name(backend)) +} + +fn vft_named_key(backend: &VftStorageBackend, op: &str, load: u32) -> String { + format!("vft_{}_{}_{}", vft_backend_name(backend), op, load) +} + fn storage_backend_name(backend: &StorageBackend) -> &'static str { match backend { StorageBackend::HashMap => "hashmap", @@ -524,6 +2096,23 @@ fn storage_backend_name(backend: &StorageBackend) -> &'static str { } } +fn vft_backend_name(backend: &VftStorageBackend) -> &'static str { + match backend { + VftStorageBackend::BTree => "btree", + VftStorageBackend::HashMap => "hashmap", + VftStorageBackend::SailsFixed => "sails_fixed", + VftStorageBackend::SailsStatic => "sails_static", + VftStorageBackend::SailsStaticFast => "sails_static_fast", + } +} + +fn vft_transfer_op_name(op: &VftTransferOp) -> &'static str { + match op { + VftTransferOp::Transfer => "transfer", + VftTransferOp::TransferFrom => "transfer_from", + } +} + fn storage_map_name(map: &StorageMap) -> &'static str { match map { StorageMap::Balance => "balance", @@ -587,6 +2176,52 @@ fn assert_storage_stress_wasm_static_memory_layout(wasm_path: &str) { ); } +fn assert_vft_stress_wasm_static_memory_layout(wasm_path: &str) { + let wasm = std::fs::read(wasm_path).expect("vft-stress optimized WASM exists"); + let mut imported_memory_pages = None; + + 'payloads: for payload in wasmparser::Parser::new(0).parse_all(&wasm) { + let wasmparser::Payload::ImportSection(imports) = + payload.expect("vft-stress optimized WASM parses") + else { + continue; + }; + + for imports in imports { + match imports.expect("vft-stress import parses") { + wasmparser::Imports::Single(_, import) => { + if let wasmparser::TypeRef::Memory(memory) = import.ty { + imported_memory_pages = Some(memory.initial); + break 'payloads; + } + } + wasmparser::Imports::Compact1 { items, .. } => { + for item in items { + let item = item.expect("vft-stress compact import parses"); + if let wasmparser::TypeRef::Memory(memory) = item.ty { + imported_memory_pages = Some(memory.initial); + break 'payloads; + } + } + } + wasmparser::Imports::Compact2 { ty, .. } => { + if let wasmparser::TypeRef::Memory(memory) = ty { + imported_memory_pages = Some(memory.initial); + break 'payloads; + } + } + } + } + } + + let imported_memory_pages = imported_memory_pages.expect("vft-stress imports memory"); + assert!( + imported_memory_pages >= u64::from(::vft_stress::STATIC_MEMORY_END_PAGE), + "vft-stress imported memory has {imported_memory_pages} pages, expected at least {}", + ::vft_stress::STATIC_MEMORY_END_PAGE + ); +} + fn create_env() -> GtestEnv { let system = System::new(); system.mint_to(DEFAULT_USER_ALICE, 1_000_000_000_000_000); @@ -632,8 +2267,19 @@ where } fn extract_reply_and_gas(system: &System, message_id: MessageId) -> (Vec, u64) { + extract_reply_and_gas_profiled(system, message_id, None) +} + +fn extract_reply_and_gas_profiled( + system: &System, + message_id: MessageId, + #[allow(unused_variables)] benchmark: Option<&str>, +) -> (Vec, u64) { let block_res = system.run_next_block(); - assert!(block_res.succeed.contains(&message_id)); + assert!( + block_res.succeed.contains(&message_id), + "message {message_id:?} did not succeed: {block_res:?}" + ); let payload = block_res .log() .iter() @@ -645,9 +2291,83 @@ fn extract_reply_and_gas(system: &System, message_id: MessageId) -> (Vec, u6 .expect("reply found"); let gas = *block_res.gas_burned.get(&message_id).expect("gas recorded"); + + #[cfg(feature = "gas-profile")] + if let Some(benchmark) = benchmark { + let profile = block_res + .gas_profiles + .get(&message_id) + .expect("gas profile should be recorded"); + let buckets = profile + .buckets() + .iter() + .map(|(key, amount)| { + ( + key.category.as_str().to_owned(), + key.label.to_owned(), + *amount, + ) + }) + .collect::>(); + crate::write_gas_profile_artifact(benchmark, gas, buckets) + .expect("failed to persist gas profile artifact"); + } + (payload, gas) } +#[cfg(feature = "gas-profile")] +fn write_vft_phase_profile(benchmark: &str, gas: u64, profile: &VftProfileResult) { + let buckets = profile + .phases + .iter() + .map(|phase| { + ( + "wasm_phase".to_owned(), + vft_phase_name(&phase.phase).to_owned(), + phase.gas, + ) + }) + .collect::>(); + crate::write_gas_profile_artifact(&format!("{benchmark}_wasm_phases"), gas, buckets) + .expect("failed to persist wasm phase profile artifact"); +} + +#[cfg(feature = "gas-profile")] +fn vft_phase_name(phase: &VftPhase) -> &'static str { + match phase { + VftPhase::ProbeOverhead => "probe_overhead", + VftPhase::NoopBody => "noop_body", + VftPhase::EchoBody => "echo_body", + VftPhase::KeyDerive => "key_derive", + VftPhase::AllowanceGet => "allowance_get", + VftPhase::AllowancePut => "allowance_put", + VftPhase::BalanceGetFrom => "balance_get_from", + VftPhase::BalanceGetTo => "balance_get_to", + VftPhase::BalancePutFrom => "balance_put_from", + VftPhase::BalancePutTo => "balance_put_to", + VftPhase::BalanceTransfer => "balance_transfer", + VftPhase::BalanceTransferFrom => "balance_transfer_from", + VftPhase::ResultBuild => "result_build", + } +} + +#[cfg(feature = "gas-profile")] +fn render_vft_comparison_markdown(medians: &BTreeMap) -> String { + let mut lines = vec![ + "# VFT Gas Profile Summary".to_owned(), + "".to_owned(), + "| Key | Median gas |".to_owned(), + "| --- | ---: |".to_owned(), + ]; + + for (key, value) in medians { + lines.push(format!("| `{key}` | {value} |")); + } + + lines.join("\n") +} + fn median(mut values: Vec) -> u64 { values.sort_unstable(); diff --git a/benchmarks/src/clients.rs b/benchmarks/src/clients.rs index 6b46e4112..59c221c80 100644 --- a/benchmarks/src/clients.rs +++ b/benchmarks/src/clients.rs @@ -17,3 +17,13 @@ pub(crate) mod storage_stress_client { #![allow(unused)] include!("storage_stress_program.rs"); } + +pub(crate) mod vft_stress_client { + #![allow(unused)] + include!("vft_stress_program.rs"); +} + +pub(crate) mod storage_million_client { + #![allow(unused)] + include!("storage_million_program.rs"); +} diff --git a/benchmarks/src/entities.rs b/benchmarks/src/entities.rs index 02b8a1363..f77d3f5d1 100644 --- a/benchmarks/src/entities.rs +++ b/benchmarks/src/entities.rs @@ -30,6 +30,12 @@ impl BenchData { for (key, value) in data.storage.0 { map.insert(BenchCategory::Storage(key), value); } + for (key, value) in data.storage_million.0 { + map.insert(BenchCategory::StorageMillion(key), value); + } + for (key, value) in data.examples.0 { + map.insert(BenchCategory::Example(key), value); + } Ok(Self(map)) } @@ -83,6 +89,36 @@ impl BenchData { } } + /// Update million-entry storage benchmark category value. + pub fn update_storage_million_bench(&mut self, key: String, value: u64) { + self.0.insert(BenchCategory::StorageMillion(key), value); + } + + /// Replace all million-entry storage benchmark values. + pub fn replace_storage_million_benches(&mut self, values: BTreeMap) { + self.0 + .retain(|key, _| !matches!(key, BenchCategory::StorageMillion(_))); + + for (key, value) in values { + self.update_storage_million_bench(key, value); + } + } + + /// Update example benchmark category value. + pub fn update_example_bench(&mut self, key: String, value: u64) { + self.0.insert(BenchCategory::Example(key), value); + } + + /// Replace all example benchmark values. + pub fn replace_example_benches(&mut self, values: BTreeMap) { + self.0 + .retain(|key, _| !matches!(key, BenchCategory::Example(_))); + + for (key, value) in values { + self.update_example_bench(key, value); + } + } + /// Convert the benchmark data into a JSON string. pub fn into_json_string(self) -> Result { let mut bench_data = BenchDataSerde::default(); @@ -103,6 +139,12 @@ impl BenchData { BenchCategory::Storage(key) => { bench_data.storage.0.insert(key, value); } + BenchCategory::StorageMillion(key) => { + bench_data.storage_million.0.insert(key, value); + } + BenchCategory::Example(key) => { + bench_data.examples.0.insert(key, value); + } } } @@ -139,6 +181,10 @@ pub struct BenchDataSerde { pub message_stack: MessageStackDataSerde, #[serde(default)] pub storage: StorageStressDataSerde, + #[serde(default)] + pub storage_million: StorageMillionDataSerde, + #[serde(default)] + pub examples: ExampleBenchDataSerde, } /// Compute benchmark data stored in the benchmarks file. @@ -180,6 +226,14 @@ pub struct MessageStackDataSerde(pub BTreeMap); #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct StorageStressDataSerde(pub BTreeMap); +/// Million-entry storage benchmark data stored in the benchmarks file. +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] +pub struct StorageMillionDataSerde(pub BTreeMap); + +/// Example benchmark data stored in the benchmarks file. +#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] +pub struct ExampleBenchDataSerde(pub BTreeMap); + /// Benchmark category that can be read (written) from (to) the benchmarks file. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum BenchCategory { @@ -191,6 +245,8 @@ pub enum BenchCategory { Redirect, MessageStack(u32), Storage(String), + StorageMillion(String), + Example(String), } impl Display for BenchCategory { @@ -204,6 +260,8 @@ impl Display for BenchCategory { BenchCategory::Redirect => write!(f, "redirect"), BenchCategory::MessageStack(limit) => write!(f, "stack_{limit}"), BenchCategory::Storage(key) => write!(f, "storage_{key}"), + BenchCategory::StorageMillion(key) => write!(f, "storage_million_{key}"), + BenchCategory::Example(key) => write!(f, "example_{key}"), } } } diff --git a/benchmarks/src/lib.rs b/benchmarks/src/lib.rs index 1d72c3c81..3c7fad58b 100644 --- a/benchmarks/src/lib.rs +++ b/benchmarks/src/lib.rs @@ -17,11 +17,14 @@ use anyhow::{Context, Result}; pub use entities::{ AllocBenchDataSerde, BenchCategory, BenchCategoryComparison, BenchCategoryComparisonReport, BenchData, BenchDataSerde, ComputeBenchDataSerde, CounterBenchDataSerde, - CrossProgramBenchDataSerde, RedirectBenchDataSerde, StorageStressDataSerde, + CrossProgramBenchDataSerde, ExampleBenchDataSerde, RedirectBenchDataSerde, + StorageMillionDataSerde, StorageStressDataSerde, }; pub use file::BenchDataFile; +#[cfg(feature = "gas-profile")] +use serde::Serialize; use std::{ - env, + env, fs, path::{Path, PathBuf}, }; @@ -54,12 +57,117 @@ fn store_bench_data_to_file(path: impl AsRef, f: impl FnOnce(&mut BenchDat .context("Failed to unlock bench data file after writing") } +#[cfg(feature = "gas-profile")] +#[derive(Serialize)] +struct GasProfileBucketSerde { + category: String, + label: String, + amount: u64, +} + +#[cfg(feature = "gas-profile")] +#[derive(Serialize)] +struct GasProfileArtifactSerde { + benchmark: String, + total_gas: u64, + buckets: Vec, +} + +#[cfg(feature = "gas-profile")] +fn gas_profile_root() -> Option { + env::var_os("SAILS_GAS_PROFILE_DIR").map(PathBuf::from) +} + +#[cfg(feature = "gas-profile")] +pub fn write_gas_profile_artifact( + benchmark: &str, + total_gas: u64, + buckets: Vec<(String, String, u64)>, +) -> Result<()> { + let Some(root) = gas_profile_root() else { + return Ok(()); + }; + + fs::create_dir_all(root.join("profiles")) + .context("Failed to create gas profile artifact directory")?; + + let artifact = GasProfileArtifactSerde { + benchmark: benchmark.to_owned(), + total_gas, + buckets: buckets + .iter() + .map(|(category, label, amount)| GasProfileBucketSerde { + category: category.clone(), + label: label.clone(), + amount: *amount, + }) + .collect(), + }; + + let json_path = root.join("profiles").join(format!("{benchmark}.json")); + let folded_path = root.join("profiles").join(format!("{benchmark}.folded")); + + fs::write( + &json_path, + serde_json::to_vec_pretty(&artifact).context("Failed to encode gas profile artifact")?, + ) + .with_context(|| { + format!( + "Failed to write gas profile artifact to {}", + json_path.display() + ) + })?; + + let folded = buckets + .into_iter() + .map(|(category, label, amount)| format!("{benchmark};{category};{label} {amount}")) + .collect::>() + .join("\n"); + fs::write(&folded_path, format!("{folded}\n")).with_context(|| { + format!( + "Failed to write folded gas profile artifact to {}", + folded_path.display() + ) + })?; + + Ok(()) +} + +#[cfg(feature = "gas-profile")] +pub fn write_gas_profile_summary( + summary: &std::collections::BTreeMap, + comparison_markdown: &str, +) -> Result<()> { + let Some(root) = gas_profile_root() else { + return Ok(()); + }; + + fs::create_dir_all(&root).context("Failed to create gas profile output directory")?; + + let summary_path = root.join("summary.json"); + fs::write( + &summary_path, + serde_json::to_vec_pretty(summary).context("Failed to encode gas profile summary")?, + ) + .with_context(|| format!("Failed to write summary to {}", summary_path.display()))?; + + let comparison_path = root.join("comparison.md"); + fs::write(&comparison_path, comparison_markdown).with_context(|| { + format!( + "Failed to write gas profile comparison markdown to {}", + comparison_path.display() + ) + })?; + + Ok(()) +} + #[cfg(test)] mod tests { use super::*; use crate::entities::{ BenchDataSerde, ComputeBenchDataSerde, CounterBenchDataSerde, CrossProgramBenchDataSerde, - RedirectBenchDataSerde, StorageStressDataSerde, + ExampleBenchDataSerde, RedirectBenchDataSerde, StorageStressDataSerde, }; use std::{ collections::BTreeMap, @@ -85,6 +193,14 @@ mod tests { "sails_static_balance_prepare_1024".to_owned(), 777, )])), + storage_million: StorageMillionDataSerde(BTreeMap::from([( + "static_balance_prepare_1000000".to_owned(), + 999, + )])), + examples: ExampleBenchDataSerde(BTreeMap::from([( + "aggregator_btree_prepare_1024".to_owned(), + 888, + )])), }; // Create a temporary file. @@ -151,6 +267,14 @@ mod tests { "sails_static_balance_prepare_1024".to_owned(), 777, )])), + storage_million: StorageMillionDataSerde(BTreeMap::from([( + "static_balance_prepare_1000000".to_owned(), + 999, + )])), + examples: ExampleBenchDataSerde(BTreeMap::from([( + "aggregator_btree_prepare_1024".to_owned(), + 888, + )])), }, ) } diff --git a/benchmarks/src/storage_million_program.rs b/benchmarks/src/storage_million_program.rs new file mode 100644 index 000000000..6ed6b4fd7 --- /dev/null +++ b/benchmarks/src/storage_million_program.rs @@ -0,0 +1,280 @@ +// Code generated by sails-client-gen-v2. DO NOT EDIT. +#[allow(unused_imports)] +use sails_rs::{client::*, collections::*, prelude::*}; +pub struct StorageMillionProgram; + +impl StorageMillionProgram { + pub const ROUTE_ID_STORAGE_MILLION: u8 = 1; +} + +impl sails_rs::client::Program for StorageMillionProgram {} + +pub trait StorageMillion { + type Env: sails_rs::client::GearEnv; + fn storage_million( + &self, + ) -> sails_rs::client::Service; +} + +impl StorageMillion + for sails_rs::client::Actor +{ + type Env = E; + fn storage_million( + &self, + ) -> sails_rs::client::Service { + self.service(StorageMillionProgram::ROUTE_ID_STORAGE_MILLION) + } +} +pub trait StorageMillionCtors { + type Env: sails_rs::client::GearEnv; + fn new_for_bench( + self, + ) -> sails_rs::client::PendingCtor; +} + +impl StorageMillionCtors + for sails_rs::client::Deployment +{ + type Env = E; + fn new_for_bench( + self, + ) -> sails_rs::client::PendingCtor { + self.pending_ctor(()) + } +} + +pub mod io { + use super::*; + sails_rs::io_struct_impl!(NewForBench () -> (), 0); +} + +pub mod storage_million { + use super::*; + + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub enum MillionStorageBackend { + GenericStatic, + WatActorStatic, + MixedActorStatic, + TagActorStatic, + TagU64ActorStatic, + ControlActorStatic, + PageLocalActorStatic, + GroupedActorPages2, + GroupedActorPages4, + GroupedActorPages8, + GroupedActorPages16, + GroupedActorPages32, + GroupedActorPages64, + GroupedActorPages128, + } + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub struct MillionStorageBenchResult { + pub value: U256, + pub len: u32, + pub existed: bool, + } + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub enum MillionStorageOp { + InsertFresh, + UpdateExisting, + ReadExisting, + ReadMissing, + Remove, + } + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub enum MillionVftBackend { + BTree, + HashMap, + GenericStatic, + GenericStaticFused, + GenericStaticFast, + WatActorStatic, + MixedActorStatic, + MixedActorFast, + TagActorStatic, + TagU64ActorStatic, + ControlActorStatic, + PageLocalActorStatic, + GroupedActorPages64, + GroupedActorPages128, + InlineOwnerAccountU256, + } + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub enum MillionVftTransferOp { + Transfer, + TransferFrom, + } + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub struct MillionVftTransferResult { + pub from_balance: U256, + pub to_balance: U256, + pub allowance: U256, + pub balance_len: u32, + pub allowance_len: u32, + pub transferred: bool, + } + + pub trait StorageMillion { + type Env: sails_rs::client::GearEnv; + fn bench( + &mut self, + backend: MillionStorageBackend, + op: MillionStorageOp, + seed: u32, + ) -> sails_rs::client::PendingCall; + fn bench_many( + &mut self, + backend: MillionStorageBackend, + op: MillionStorageOp, + start_seed: u32, + count: u32, + ) -> sails_rs::client::PendingCall; + fn bench_vft_approve_bool( + &mut self, + backend: MillionVftBackend, + owner_seed: u32, + spender_seed: u32, + ) -> sails_rs::client::PendingCall; + fn bench_vft_transfer( + &mut self, + backend: MillionVftBackend, + op: MillionVftTransferOp, + seed: u32, + ) -> sails_rs::client::PendingCall; + fn bench_vft_transfer_bool( + &mut self, + backend: MillionVftBackend, + op: MillionVftTransferOp, + seed: u32, + ) -> sails_rs::client::PendingCall; + fn bench_vft_transfer_fresh_bool( + &mut self, + backend: MillionVftBackend, + seed: u32, + ) -> sails_rs::client::PendingCall; + fn bench_vft_transfer_from_spender_bool( + &mut self, + backend: MillionVftBackend, + owner_seed: u32, + spender_seed: u32, + ) -> sails_rs::client::PendingCall; + fn prepare_chunk( + &mut self, + backend: MillionStorageBackend, + start: u32, + len: u32, + ) -> sails_rs::client::PendingCall; + fn prepare_vft_chunk( + &mut self, + backend: MillionVftBackend, + start: u32, + len: u32, + ) -> sails_rs::client::PendingCall; + } + + pub struct StorageMillionImpl; + + impl sails_rs::client::Identifiable for StorageMillionImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([69, 52, 241, 61, 146, 22, 252, 107]); + } + + impl StorageMillion + for sails_rs::client::Service + { + type Env = E; + fn bench( + &mut self, + backend: MillionStorageBackend, + op: MillionStorageOp, + seed: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, op, seed)) + } + fn bench_many( + &mut self, + backend: MillionStorageBackend, + op: MillionStorageOp, + start_seed: u32, + count: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, op, start_seed, count)) + } + fn bench_vft_approve_bool( + &mut self, + backend: MillionVftBackend, + owner_seed: u32, + spender_seed: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, owner_seed, spender_seed)) + } + fn bench_vft_transfer( + &mut self, + backend: MillionVftBackend, + op: MillionVftTransferOp, + seed: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, op, seed)) + } + fn bench_vft_transfer_bool( + &mut self, + backend: MillionVftBackend, + op: MillionVftTransferOp, + seed: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, op, seed)) + } + fn bench_vft_transfer_fresh_bool( + &mut self, + backend: MillionVftBackend, + seed: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, seed)) + } + fn bench_vft_transfer_from_spender_bool( + &mut self, + backend: MillionVftBackend, + owner_seed: u32, + spender_seed: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, owner_seed, spender_seed)) + } + fn prepare_chunk( + &mut self, + backend: MillionStorageBackend, + start: u32, + len: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, start, len)) + } + fn prepare_vft_chunk( + &mut self, + backend: MillionVftBackend, + start: u32, + len: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, start, len)) + } + } + + pub mod io { + use super::*; + sails_rs::io_struct_impl!(Bench (backend: super::MillionStorageBackend, op: super::MillionStorageOp, seed: u32) -> super::MillionStorageBenchResult, 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(BenchMany (backend: super::MillionStorageBackend, op: super::MillionStorageOp, start_seed: u32, count: u32) -> super::MillionStorageBenchResult, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(BenchVftApproveBool (backend: super::MillionVftBackend, owner_seed: u32, spender_seed: u32) -> bool, 2, ::INTERFACE_ID); + sails_rs::io_struct_impl!(BenchVftTransfer (backend: super::MillionVftBackend, op: super::MillionVftTransferOp, seed: u32) -> super::MillionVftTransferResult, 3, ::INTERFACE_ID); + sails_rs::io_struct_impl!(BenchVftTransferBool (backend: super::MillionVftBackend, op: super::MillionVftTransferOp, seed: u32) -> bool, 4, ::INTERFACE_ID); + sails_rs::io_struct_impl!(BenchVftTransferFreshBool (backend: super::MillionVftBackend, seed: u32) -> bool, 5, ::INTERFACE_ID); + sails_rs::io_struct_impl!(BenchVftTransferFromSpenderBool (backend: super::MillionVftBackend, owner_seed: u32, spender_seed: u32) -> bool, 6, ::INTERFACE_ID); + sails_rs::io_struct_impl!(PrepareChunk (backend: super::MillionStorageBackend, start: u32, len: u32) -> super::MillionStorageBenchResult, 7, ::INTERFACE_ID); + sails_rs::io_struct_impl!(PrepareVftChunk (backend: super::MillionVftBackend, start: u32, len: u32) -> super::MillionVftTransferResult, 8, ::INTERFACE_ID); + } +} diff --git a/benchmarks/src/vft_stress_program.rs b/benchmarks/src/vft_stress_program.rs new file mode 100644 index 000000000..7a7ed259f --- /dev/null +++ b/benchmarks/src/vft_stress_program.rs @@ -0,0 +1,149 @@ +// Code generated by sails-client-gen-v2. DO NOT EDIT. +#[allow(unused_imports)] +use sails_rs::{client::*, collections::*, prelude::*}; +pub struct VftStressProgram; + +impl VftStressProgram { + pub const ROUTE_ID_VFT_STRESS: u8 = 1; +} + +impl sails_rs::client::Program for VftStressProgram {} + +pub trait VftStress { + type Env: sails_rs::client::GearEnv; + fn vft_stress(&self) -> sails_rs::client::Service; +} + +impl VftStress for sails_rs::client::Actor { + type Env = E; + fn vft_stress(&self) -> sails_rs::client::Service { + self.service(VftStressProgram::ROUTE_ID_VFT_STRESS) + } +} +pub trait VftStressCtors { + type Env: sails_rs::client::GearEnv; + fn new_for_bench( + self, + ) -> sails_rs::client::PendingCtor; +} + +impl VftStressCtors + for sails_rs::client::Deployment +{ + type Env = E; + fn new_for_bench( + self, + ) -> sails_rs::client::PendingCtor { + self.pending_ctor(()) + } +} + +pub mod io { + use super::*; + sails_rs::io_struct_impl!(NewForBench () -> (), 0); +} + +pub mod vft_stress { + use super::*; + + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub enum VftStorageBackend { + BTree, + HashMap, + SailsFixed, + SailsStatic, + SailsStaticFast, + } + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub enum VftTransferOp { + Transfer, + TransferFrom, + } + #[sails_rs::sails_type(crate = sails_rs)] + #[derive(PartialEq, Clone, Debug)] + pub struct VftTransferResult { + pub from_balance: U256, + pub to_balance: U256, + pub allowance: U256, + pub balance_len: u32, + pub allowance_len: u32, + pub transferred: bool, + } + + pub trait VftStress { + type Env: sails_rs::client::GearEnv; + fn bench_vft_approve_bool( + &mut self, + backend: VftStorageBackend, + owner_seed: u32, + spender_seed: u32, + ) -> sails_rs::client::PendingCall; + fn bench_vft_transfer( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + ) -> sails_rs::client::PendingCall; + fn bench_vft_transfer_fresh_bool( + &mut self, + backend: VftStorageBackend, + seed: u32, + ) -> sails_rs::client::PendingCall; + fn prepare_vft( + &mut self, + backend: VftStorageBackend, + len: u32, + ) -> sails_rs::client::PendingCall; + } + + pub struct VftStressImpl; + + impl sails_rs::client::Identifiable for VftStressImpl { + const INTERFACE_ID: sails_rs::InterfaceId = + sails_rs::InterfaceId::from_bytes_8([157, 152, 163, 35, 186, 23, 155, 244]); + } + + impl VftStress for sails_rs::client::Service { + type Env = E; + fn bench_vft_approve_bool( + &mut self, + backend: VftStorageBackend, + owner_seed: u32, + spender_seed: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, owner_seed, spender_seed)) + } + fn bench_vft_transfer( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, op, seed)) + } + fn bench_vft_transfer_fresh_bool( + &mut self, + backend: VftStorageBackend, + seed: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, seed)) + } + fn prepare_vft( + &mut self, + backend: VftStorageBackend, + len: u32, + ) -> sails_rs::client::PendingCall { + self.pending_call((backend, len)) + } + } + + pub mod io { + use super::*; + sails_rs::io_struct_impl!(BenchVftApproveBool (backend: super::VftStorageBackend, owner_seed: u32, spender_seed: u32) -> bool, 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(BenchVftTransfer (backend: super::VftStorageBackend, op: super::VftTransferOp, seed: u32) -> super::VftTransferResult, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(BenchVftTransferFreshBool (backend: super::VftStorageBackend, seed: u32) -> bool, 2, ::INTERFACE_ID); + sails_rs::io_struct_impl!(PrepareVft (backend: super::VftStorageBackend, len: u32) -> super::VftTransferResult, 3, ::INTERFACE_ID); + } +} diff --git a/benchmarks/storage-million/Cargo.toml b/benchmarks/storage-million/Cargo.toml new file mode 100644 index 000000000..5d8a5b3b8 --- /dev/null +++ b/benchmarks/storage-million/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "storage-million" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[dependencies] +sails-rs.workspace = true +sails-storage = { workspace = true, features = ["experimental-vft-account"] } + +[build-dependencies] +sails-rs = { workspace = true, features = ["wasm-builder", "experimental-vft-account"] } diff --git a/benchmarks/storage-million/build.rs b/benchmarks/storage-million/build.rs new file mode 100644 index 000000000..606e06e81 --- /dev/null +++ b/benchmarks/storage-million/build.rs @@ -0,0 +1,25 @@ +fn main() { + const MILLION_BALANCE_LOG2_SLOTS: u8 = 21; + const MILLION_BALANCE_SLOTS: usize = 1usize << MILLION_BALANCE_LOG2_SLOTS; + const PAGE_LOCAL_BALANCE_LOG2_TILES: u8 = 13; + + let layout = sails_rs::build::StaticMemoryLayout::new(1024) + .reserve_table::<32, 32>("million_balances", MILLION_BALANCE_SLOTS) + .reserve_table::<64, 32>("million_allowances", MILLION_BALANCE_SLOTS) + .reserve_actor_u256_map::("wat_actor_balances") + .reserve_actor_u256_map::("mixed_actor_balances") + .reserve_allowance_u256_map::("wat_allowances") + .reserve_control_actor_u256_map::("control_actor_balances") + .reserve_page_local_actor_u256_map::( + "page_local_actor_balances", + ) + .reserve_grouped_control_actor_u256_map::<12, 1>("grouped_actor_balances_pages2") + .reserve_grouped_control_actor_u256_map::<11, 2>("grouped_actor_balances_pages4") + .reserve_grouped_control_actor_u256_map::<10, 3>("grouped_actor_balances_pages8") + .reserve_grouped_control_actor_u256_map::<9, 4>("grouped_actor_balances_pages16") + .reserve_grouped_control_actor_u256_map::<8, 5>("grouped_actor_balances_pages32") + .reserve_grouped_control_actor_u256_map::<7, 6>("grouped_actor_balances_pages64") + .reserve_grouped_control_actor_u256_map::<6, 7>("grouped_actor_balances_pages128"); + + sails_rs::build::build_wasm_with_static_memory(layout); +} diff --git a/benchmarks/storage-million/src/lib.rs b/benchmarks/storage-million/src/lib.rs new file mode 100644 index 000000000..1edf42c6a --- /dev/null +++ b/benchmarks/storage-million/src/lib.rs @@ -0,0 +1,2101 @@ +#![no_std] + +use sails_rs::{ + cell::RefCell, + collections::{BTreeMap, HashMap}, + prelude::*, +}; +use sails_storage::gear::{ + StaticActorIdU256Map as SailsStaticActorIdU256Map, + StaticActorTagU64Map as SailsStaticActorTagU64Map, + StaticActorTagU256Map as SailsStaticActorTagU256Map, + StaticAllowanceTable as SailsStaticAllowanceTable, + StaticAllowanceU256Map as SailsStaticAllowanceU256Map, + StaticBalanceTable as SailsStaticBalanceTable, + StaticControlActorIdU256Map as SailsStaticControlActorIdU256Map, + StaticGroupedControlActorIdU256Map as SailsStaticGroupedControlActorIdU256Map, + StaticMixedActorIdU256Map as SailsStaticMixedActorIdU256Map, + StaticPageLocalActorIdU256Map as SailsStaticPageLocalActorIdU256Map, + StaticVftAccountStorage as SailsStaticVftAccountStorage, +}; + +mod static_storage { + include!(concat!(env!("OUT_DIR"), "/sails_static_storage.rs")); +} + +const GENERIC_BALANCE_CAPACITY: u32 = static_storage::MILLION_BALANCES_SLOTS as u32; +const GENERIC_ALLOWANCE_CAPACITY: u32 = static_storage::MILLION_ALLOWANCES_SLOTS as u32; +const WAT_ACTOR_BALANCE_CAPACITY: u32 = static_storage::WAT_ACTOR_BALANCES_SLOTS as u32; +const MIXED_ACTOR_BALANCE_CAPACITY: u32 = static_storage::MIXED_ACTOR_BALANCES_SLOTS as u32; +const TAG_ACTOR_BALANCE_CAPACITY: u32 = static_storage::MILLION_BALANCES_SLOTS as u32; +const TAG_U64_ACTOR_BALANCE_CAPACITY: u32 = (static_storage::MILLION_BALANCES_SLOTS * 4) as u32; +const WAT_ALLOWANCE_CAPACITY: u32 = static_storage::WAT_ALLOWANCES_SLOTS as u32; +const CONTROL_ACTOR_BALANCE_CAPACITY: u32 = static_storage::CONTROL_ACTOR_BALANCES_SLOTS as u32; +const PAGE_LOCAL_ACTOR_BALANCE_CAPACITY: u32 = + static_storage::PAGE_LOCAL_ACTOR_BALANCES_SLOTS as u32; + +pub const STATIC_MEMORY_END_PAGE: u32 = static_storage::STATIC_MEMORY_END_PAGE; + +type WatActorBalanceTable = + SailsStaticActorIdU256Map<{ static_storage::WAT_ACTOR_BALANCES_LOG2_SLOTS }>; +type MixedActorBalanceTable = + SailsStaticMixedActorIdU256Map<{ static_storage::MIXED_ACTOR_BALANCES_LOG2_SLOTS }>; +type TagActorBalanceTable = SailsStaticActorTagU256Map; +type TagU64ActorTable = SailsStaticActorTagU64Map; +type WatAllowanceTable = SailsStaticAllowanceU256Map<{ static_storage::WAT_ALLOWANCES_LOG2_SLOTS }>; +type ControlActorBalanceTable = + SailsStaticControlActorIdU256Map<{ static_storage::CONTROL_ACTOR_BALANCES_LOG2_SLOTS }>; +type PageLocalActorBalanceTable = + SailsStaticPageLocalActorIdU256Map<{ static_storage::PAGE_LOCAL_ACTOR_BALANCES_LOG2_TILES }>; +type GroupedActorPages2Table = SailsStaticGroupedControlActorIdU256Map< + { static_storage::GROUPED_ACTOR_BALANCES_PAGES2_LOG2_GROUPS }, + { static_storage::GROUPED_ACTOR_BALANCES_PAGES2_LOG2_GROUP_PAGES }, +>; +type GroupedActorPages4Table = SailsStaticGroupedControlActorIdU256Map< + { static_storage::GROUPED_ACTOR_BALANCES_PAGES4_LOG2_GROUPS }, + { static_storage::GROUPED_ACTOR_BALANCES_PAGES4_LOG2_GROUP_PAGES }, +>; +type GroupedActorPages8Table = SailsStaticGroupedControlActorIdU256Map< + { static_storage::GROUPED_ACTOR_BALANCES_PAGES8_LOG2_GROUPS }, + { static_storage::GROUPED_ACTOR_BALANCES_PAGES8_LOG2_GROUP_PAGES }, +>; +type GroupedActorPages16Table = SailsStaticGroupedControlActorIdU256Map< + { static_storage::GROUPED_ACTOR_BALANCES_PAGES16_LOG2_GROUPS }, + { static_storage::GROUPED_ACTOR_BALANCES_PAGES16_LOG2_GROUP_PAGES }, +>; +type GroupedActorPages32Table = SailsStaticGroupedControlActorIdU256Map< + { static_storage::GROUPED_ACTOR_BALANCES_PAGES32_LOG2_GROUPS }, + { static_storage::GROUPED_ACTOR_BALANCES_PAGES32_LOG2_GROUP_PAGES }, +>; +type GroupedActorPages64Table = SailsStaticGroupedControlActorIdU256Map< + { static_storage::GROUPED_ACTOR_BALANCES_PAGES64_LOG2_GROUPS }, + { static_storage::GROUPED_ACTOR_BALANCES_PAGES64_LOG2_GROUP_PAGES }, +>; +type GroupedActorPages128Table = SailsStaticGroupedControlActorIdU256Map< + { static_storage::GROUPED_ACTOR_BALANCES_PAGES128_LOG2_GROUPS }, + { static_storage::GROUPED_ACTOR_BALANCES_PAGES128_LOG2_GROUP_PAGES }, +>; +type InlineOwnerAccountTable = SailsStaticVftAccountStorage< + { static_storage::WAT_ACTOR_BALANCES_LOG2_SLOTS }, + { static_storage::WAT_ALLOWANCES_LOG2_SLOTS }, +>; + +const STORAGE_BACKEND_COUNT: usize = 14; + +#[sails_type] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MillionStorageBackend { + GenericStatic, + WatActorStatic, + MixedActorStatic, + TagActorStatic, + TagU64ActorStatic, + ControlActorStatic, + PageLocalActorStatic, + GroupedActorPages2, + GroupedActorPages4, + GroupedActorPages8, + GroupedActorPages16, + GroupedActorPages32, + GroupedActorPages64, + GroupedActorPages128, +} + +#[sails_type] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MillionVftBackend { + BTree, + HashMap, + GenericStatic, + GenericStaticFused, + GenericStaticFast, + WatActorStatic, + MixedActorStatic, + MixedActorFast, + TagActorStatic, + TagU64ActorStatic, + ControlActorStatic, + PageLocalActorStatic, + GroupedActorPages64, + GroupedActorPages128, + InlineOwnerAccountU256, +} + +#[sails_type] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MillionStorageOp { + InsertFresh, + UpdateExisting, + ReadExisting, + ReadMissing, + Remove, +} + +#[sails_type] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum MillionVftTransferOp { + Transfer, + TransferFrom, +} + +#[sails_type] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MillionStorageBenchResult { + pub value: U256, + pub len: u32, + pub existed: bool, +} + +#[sails_type] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct MillionVftTransferResult { + pub from_balance: U256, + pub to_balance: U256, + pub allowance: U256, + pub balance_len: u32, + pub allowance_len: u32, + pub transferred: bool, +} + +struct AppliedOp { + value: U256, + existed: bool, +} + +struct StorageMillionService<'a> { + state: &'a RefCell, +} + +impl<'a> StorageMillionService<'a> { + fn new(state: &'a RefCell) -> Self { + Self { state } + } +} + +#[sails_rs::service] +impl StorageMillionService<'_> { + #[export] + pub fn prepare_chunk( + &mut self, + backend: MillionStorageBackend, + start: u32, + len: u32, + ) -> MillionStorageBenchResult { + self.state.borrow_mut().prepare_chunk(backend, start, len) + } + + #[export] + pub fn bench( + &mut self, + backend: MillionStorageBackend, + op: MillionStorageOp, + seed: u32, + ) -> MillionStorageBenchResult { + self.state.borrow_mut().bench(backend, op, seed) + } + + #[export] + pub fn bench_many( + &mut self, + backend: MillionStorageBackend, + op: MillionStorageOp, + start_seed: u32, + count: u32, + ) -> MillionStorageBenchResult { + self.state + .borrow_mut() + .bench_many(backend, op, start_seed, count) + } + + #[export] + pub fn prepare_vft_chunk( + &mut self, + backend: MillionVftBackend, + start: u32, + len: u32, + ) -> MillionVftTransferResult { + self.state + .borrow_mut() + .prepare_vft_chunk(backend, start, len) + } + + #[export] + pub fn bench_vft_transfer( + &mut self, + backend: MillionVftBackend, + op: MillionVftTransferOp, + seed: u32, + ) -> MillionVftTransferResult { + self.state + .borrow_mut() + .bench_vft_transfer(backend, op, seed) + } + + #[export] + pub fn bench_vft_transfer_bool( + &mut self, + backend: MillionVftBackend, + op: MillionVftTransferOp, + seed: u32, + ) -> bool { + self.state + .borrow_mut() + .bench_vft_transfer_bool(backend, op, seed) + } + + #[export] + pub fn bench_vft_transfer_fresh_bool(&mut self, backend: MillionVftBackend, seed: u32) -> bool { + self.state + .borrow_mut() + .bench_vft_transfer_fresh_bool(backend, seed) + } + + #[export] + pub fn bench_vft_transfer_from_spender_bool( + &mut self, + backend: MillionVftBackend, + owner_seed: u32, + spender_seed: u32, + ) -> bool { + self.state + .borrow_mut() + .bench_vft_transfer_from_spender_bool(backend, owner_seed, spender_seed) + } + + #[export] + pub fn bench_vft_approve_bool( + &mut self, + backend: MillionVftBackend, + owner_seed: u32, + spender_seed: u32, + ) -> bool { + self.state + .borrow_mut() + .bench_vft_approve_bool(backend, owner_seed, spender_seed) + } +} + +pub struct StorageMillionProgram { + state: RefCell, +} + +#[sails_rs::program] +impl StorageMillionProgram { + pub fn new_for_bench() -> Self { + Self { + state: RefCell::new(StorageMillionState::new()), + } + } + + pub fn storage_million(&self) -> StorageMillionService<'_> { + StorageMillionService::new(&self.state) + } +} + +struct StorageMillionState { + lens: [u32; STORAGE_BACKEND_COUNT], + allowance_lens: [u32; STORAGE_BACKEND_COUNT], + dynamic_vft: DynamicMillionVftStorage, +} + +enum DynamicMillionVftStorage { + Empty, + BTree { + balances: BTreeMap, + allowances: BTreeMap<(ActorId, ActorId), U256>, + }, + HashMap { + balances: HashMap, + allowances: HashMap<(ActorId, ActorId), U256>, + }, +} + +impl StorageMillionState { + fn new() -> Self { + Self { + lens: [0; STORAGE_BACKEND_COUNT], + allowance_lens: [0; STORAGE_BACKEND_COUNT], + dynamic_vft: DynamicMillionVftStorage::Empty, + } + } + + fn prepare_chunk( + &mut self, + backend: MillionStorageBackend, + start: u32, + len: u32, + ) -> MillionStorageBenchResult { + let end = start.checked_add(len).expect("million prepare overflow"); + assert!(end <= backend_capacity(backend)); + + for seed in start + 1..=end { + self.insert(backend, seed, value_for_seed(seed)); + } + + MillionStorageBenchResult { + value: value_for_seed(end), + len: self.len(backend), + existed: false, + } + } + + fn bench( + &mut self, + backend: MillionStorageBackend, + op: MillionStorageOp, + seed: u32, + ) -> MillionStorageBenchResult { + let applied = self.apply_op(backend, op, seed); + MillionStorageBenchResult { + value: applied.value, + len: self.len(backend), + existed: applied.existed, + } + } + + fn bench_many( + &mut self, + backend: MillionStorageBackend, + op: MillionStorageOp, + start_seed: u32, + count: u32, + ) -> MillionStorageBenchResult { + assert!(count > 0); + + let mut applied = AppliedOp { + value: U256::zero(), + existed: false, + }; + for offset in 0..count { + let seed = start_seed + .checked_add(offset) + .expect("million batch seed overflow"); + applied = self.apply_op(backend, op, seed); + } + + MillionStorageBenchResult { + value: applied.value, + len: self.len(backend), + existed: applied.existed, + } + } + + fn prepare_vft_chunk( + &mut self, + backend: MillionVftBackend, + start: u32, + len: u32, + ) -> MillionVftTransferResult { + let end = start + .checked_add(len) + .expect("million VFT prepare overflow"); + if let Some(static_backend) = backend.static_backend() { + assert!(end <= backend_capacity(static_backend)); + assert!(end <= allowance_capacity(static_backend)); + } + if start == 0 { + self.dynamic_vft = match backend { + MillionVftBackend::BTree => DynamicMillionVftStorage::BTree { + balances: BTreeMap::new(), + allowances: BTreeMap::new(), + }, + MillionVftBackend::HashMap => DynamicMillionVftStorage::HashMap { + balances: HashMap::new(), + allowances: HashMap::new(), + }, + _ => DynamicMillionVftStorage::Empty, + }; + } + + for seed in start + 1..=end { + self.vft_insert_balance(backend, seed, vft_balance_for_seed(seed)); + self.insert_allowance( + backend, + vft_actor_for_seed(seed), + vft_spender_for_seed(seed), + vft_allowance_for_seed(seed), + ); + } + + MillionVftTransferResult { + from_balance: vft_balance_for_seed(end), + to_balance: U256::zero(), + allowance: U256::zero(), + balance_len: self.vft_balance_len(backend), + allowance_len: self.vft_allowance_len(backend), + transferred: false, + } + } + + fn bench_vft_transfer( + &mut self, + backend: MillionVftBackend, + op: MillionVftTransferOp, + seed: u32, + ) -> MillionVftTransferResult { + let len = self.vft_balance_len(backend); + assert!(len > 1); + let to_seed = if seed == len { 1 } else { seed + 1 }; + let amount = vft_transfer_amount(seed); + let spender = vft_spender_for_seed(seed); + + if backend == MillionVftBackend::WatActorStatic { + let owner = vft_actor_for_seed(seed); + let recipient = vft_actor_for_seed(to_seed); + let allowance_key = matches!(op, MillionVftTransferOp::TransferFrom).then_some(spender); + + if allowance_key.is_some() { + let Some(transfer) = wat_actor_balance_table() + .transfer_actor_u256_from( + &wat_allowance_table(), + owner, + spender, + recipient, + amount, + ) + .expect("wat actor million transfer-from failed") + else { + return self.vft_result(backend, seed, to_seed, allowance_key, false); + }; + + return MillionVftTransferResult { + from_balance: transfer.from_balance, + to_balance: transfer.to_balance, + allowance: transfer.allowance, + balance_len: self.vft_balance_len(backend), + allowance_len: self.vft_allowance_len(backend), + transferred: true, + }; + } + + let Some(transfer) = wat_actor_balance_table() + .transfer_actor_u256(owner, recipient, amount) + .expect("wat actor million balance transfer failed") + else { + return self.vft_result(backend, seed, to_seed, allowance_key, false); + }; + + return MillionVftTransferResult { + from_balance: transfer.from_balance, + to_balance: transfer.to_balance, + allowance: U256::zero(), + balance_len: self.vft_balance_len(backend), + allowance_len: self.vft_allowance_len(backend), + transferred: true, + }; + } + if backend == MillionVftBackend::MixedActorStatic { + let owner = vft_actor_for_seed(seed); + let recipient = vft_actor_for_seed(to_seed); + let allowance_key = matches!(op, MillionVftTransferOp::TransferFrom).then_some(spender); + + if allowance_key.is_some() { + let Some(transfer) = mixed_actor_balance_table() + .transfer_actor_u256_from( + &wat_allowance_table(), + owner, + spender, + recipient, + amount, + ) + .expect("mixed actor million transfer-from failed") + else { + return self.vft_result(backend, seed, to_seed, allowance_key, false); + }; + + return MillionVftTransferResult { + from_balance: transfer.from_balance, + to_balance: transfer.to_balance, + allowance: transfer.allowance, + balance_len: self.vft_balance_len(backend), + allowance_len: self.vft_allowance_len(backend), + transferred: true, + }; + } + + let Some(transfer) = mixed_actor_balance_table() + .transfer_actor_u256(owner, recipient, amount) + .expect("mixed actor million balance transfer failed") + else { + return self.vft_result(backend, seed, to_seed, allowance_key, false); + }; + + return MillionVftTransferResult { + from_balance: transfer.from_balance, + to_balance: transfer.to_balance, + allowance: U256::zero(), + balance_len: self.vft_balance_len(backend), + allowance_len: self.vft_allowance_len(backend), + transferred: true, + }; + } + if backend == MillionVftBackend::MixedActorFast { + let owner = vft_actor_for_seed(seed); + let recipient = vft_actor_for_seed(to_seed); + let allowance_key = matches!(op, MillionVftTransferOp::TransferFrom).then_some(spender); + + if allowance_key.is_some() { + let Some(transfer) = (unsafe { + mixed_actor_balance_table() + .transfer_actor_u256_from_nonzero_distinct_unchecked( + &wat_allowance_table(), + owner, + spender, + recipient, + amount, + ) + .expect("mixed actor fast million transfer-from failed") + }) else { + return self.vft_result(backend, seed, to_seed, allowance_key, false); + }; + + return MillionVftTransferResult { + from_balance: transfer.from_balance, + to_balance: transfer.to_balance, + allowance: transfer.allowance, + balance_len: self.vft_balance_len(backend), + allowance_len: self.vft_allowance_len(backend), + transferred: true, + }; + } + + let Some(transfer) = (unsafe { + mixed_actor_balance_table() + .transfer_actor_u256_nonzero_distinct_unchecked(owner, recipient, amount) + .expect("mixed actor fast million balance transfer failed") + }) else { + return self.vft_result(backend, seed, to_seed, allowance_key, false); + }; + + return MillionVftTransferResult { + from_balance: transfer.from_balance, + to_balance: transfer.to_balance, + allowance: U256::zero(), + balance_len: self.vft_balance_len(backend), + allowance_len: self.vft_allowance_len(backend), + transferred: true, + }; + } + if matches!(op, MillionVftTransferOp::TransferFrom) { + let allowance = self + .get_allowance(backend, vft_actor_for_seed(seed), spender) + .unwrap_or_else(U256::zero); + if allowance < amount { + return self.vft_result(backend, seed, to_seed, Some(spender), false); + } + self.insert_allowance( + backend, + vft_actor_for_seed(seed), + spender, + allowance - amount, + ); + } + + let from_balance = self + .vft_get_balance(backend, seed) + .unwrap_or_else(U256::zero); + if from_balance < amount { + let allowance_key = matches!(op, MillionVftTransferOp::TransferFrom).then_some(spender); + return self.vft_result(backend, seed, to_seed, allowance_key, false); + } + + let to_balance = self + .vft_get_balance(backend, to_seed) + .unwrap_or_else(U256::zero); + self.vft_insert_balance(backend, seed, from_balance - amount); + self.vft_insert_balance(backend, to_seed, to_balance + amount); + + let allowance_key = matches!(op, MillionVftTransferOp::TransferFrom).then_some(spender); + self.vft_result(backend, seed, to_seed, allowance_key, true) + } + + fn bench_vft_transfer_bool( + &mut self, + backend: MillionVftBackend, + op: MillionVftTransferOp, + seed: u32, + ) -> bool { + let len = self.vft_balance_len(backend); + assert!(len > 1); + let to_seed = if seed == len { 1 } else { seed + 1 }; + self.apply_vft_transfer_bool(backend, op, seed, to_seed) + } + + fn bench_vft_transfer_fresh_bool(&mut self, backend: MillionVftBackend, seed: u32) -> bool { + let len = self.vft_balance_len(backend); + assert!(len > 1); + let to_seed = len + .checked_add(seed) + .and_then(|seed| seed.checked_add(1)) + .expect("fresh VFT recipient seed overflow"); + self.apply_vft_transfer_bool(backend, MillionVftTransferOp::Transfer, seed, to_seed) + } + + fn bench_vft_transfer_from_spender_bool( + &mut self, + backend: MillionVftBackend, + owner_seed: u32, + spender_seed: u32, + ) -> bool { + let len = self.vft_balance_len(backend); + assert!(len > 1); + let to_seed = if owner_seed == len { + 1 + } else { + owner_seed + 1 + }; + self.apply_vft_transfer_from_spender_bool(backend, owner_seed, spender_seed, to_seed) + } + + fn bench_vft_approve_bool( + &mut self, + backend: MillionVftBackend, + owner_seed: u32, + spender_seed: u32, + ) -> bool { + let owner = vft_actor_for_seed(owner_seed); + let spender = vft_spender_for_seed(spender_seed); + if owner == spender { + return false; + } + + if backend == MillionVftBackend::TagActorStatic { + return unsafe { + tag_allowance_table() + .insert_allowance_u256_low64_bool_unchecked( + owner, + spender, + vft_allowance_for_seed(spender_seed), + ) + .expect("tag million allowance bool insert failed") + }; + } + if backend == MillionVftBackend::TagU64ActorStatic { + return unsafe { + tag_u64_allowance_table().insert_tag_value_bool_capacity_unchecked( + vft_allowance_tag_for_seeds(owner_seed, spender_seed), + vft_allowance_for_seed_u64(spender_seed), + ) + }; + } + + self.insert_allowance( + backend, + owner, + spender, + vft_allowance_for_seed(spender_seed), + ); + true + } + + fn apply_vft_transfer_bool( + &mut self, + backend: MillionVftBackend, + op: MillionVftTransferOp, + seed: u32, + to_seed: u32, + ) -> bool { + let amount = vft_transfer_amount(seed); + let spender = vft_spender_for_seed(seed); + + if matches!( + backend, + MillionVftBackend::GenericStaticFused | MillionVftBackend::GenericStaticFast + ) { + let owner = vft_actor_for_seed(seed); + let recipient = vft_actor_for_seed(to_seed); + let inserted_recipient = to_seed > self.vft_balance_len(backend); + let transferred = match op { + MillionVftTransferOp::Transfer + if backend == MillionVftBackend::GenericStaticFast => + { + unsafe { + generic_balance_table() + .transfer_actor_u256_nonzero_distinct_unchecked( + owner, recipient, amount, + ) + .expect("generic million balance transfer failed") + .is_some() + } + } + MillionVftTransferOp::Transfer => generic_balance_table() + .transfer_actor_u256(owner, recipient, amount) + .expect("generic million balance transfer failed") + .is_some(), + MillionVftTransferOp::TransferFrom + if backend == MillionVftBackend::GenericStaticFast => + { + unsafe { + generic_balance_table() + .transfer_actor_u256_from_nonzero_distinct_unchecked( + &generic_allowance_table(), + owner, + spender, + recipient, + amount, + ) + .expect("generic million transfer-from failed") + .is_some() + } + } + MillionVftTransferOp::TransferFrom => generic_balance_table() + .transfer_actor_u256_from( + &generic_allowance_table(), + owner, + spender, + recipient, + amount, + ) + .expect("generic million transfer-from failed") + .is_some() + }; + if transferred && inserted_recipient { + *self.len_mut(MillionStorageBackend::GenericStatic) += 1; + } + return transferred; + } + + if backend == MillionVftBackend::WatActorStatic { + let owner = vft_actor_for_seed(seed); + let recipient = vft_actor_for_seed(to_seed); + return match op { + MillionVftTransferOp::Transfer => wat_actor_balance_table() + .transfer_actor_u256(owner, recipient, amount) + .expect("wat actor million balance transfer failed") + .is_some(), + MillionVftTransferOp::TransferFrom => wat_actor_balance_table() + .transfer_actor_u256_from( + &wat_allowance_table(), + owner, + spender, + recipient, + amount, + ) + .expect("wat actor million transfer-from failed") + .is_some(), + }; + } + if backend == MillionVftBackend::MixedActorStatic { + let owner = vft_actor_for_seed(seed); + let recipient = vft_actor_for_seed(to_seed); + return match op { + MillionVftTransferOp::Transfer => mixed_actor_balance_table() + .transfer_actor_u256(owner, recipient, amount) + .expect("mixed actor million balance transfer failed") + .is_some(), + MillionVftTransferOp::TransferFrom => mixed_actor_balance_table() + .transfer_actor_u256_from( + &wat_allowance_table(), + owner, + spender, + recipient, + amount, + ) + .expect("mixed actor million transfer-from failed") + .is_some(), + }; + } + + if backend == MillionVftBackend::MixedActorFast { + let owner = vft_actor_for_seed(seed); + let recipient = vft_actor_for_seed(to_seed); + return match op { + MillionVftTransferOp::Transfer => unsafe { + mixed_actor_balance_table() + .transfer_actor_u256_bool_nonzero_distinct_unchecked( + owner, recipient, amount, + ) + .expect("mixed actor fast million balance transfer failed") + }, + MillionVftTransferOp::TransferFrom => unsafe { + mixed_actor_balance_table() + .transfer_actor_u256_from_bool_nonzero_distinct_unchecked( + &wat_allowance_table(), + owner, + spender, + recipient, + amount, + ) + .expect("mixed actor fast million transfer-from failed") + }, + }; + } + if backend == MillionVftBackend::TagActorStatic { + let owner = vft_actor_for_seed(seed); + let recipient = vft_actor_for_seed(to_seed); + return match op { + MillionVftTransferOp::Transfer => unsafe { + tag_actor_balance_table() + .transfer_actor_u256_low64_bool_unchecked(owner, recipient, amount) + .expect("tag actor million balance transfer failed") + }, + MillionVftTransferOp::TransferFrom => unsafe { + tag_actor_balance_table() + .transfer_actor_u256_from_tag_low64_bool_unchecked( + &tag_allowance_table(), + owner, + spender, + recipient, + amount, + ) + .expect("tag actor million transfer-from failed") + }, + }; + } + if backend == MillionVftBackend::TagU64ActorStatic { + let owner = vft_actor_tag_for_seed(seed); + let recipient = vft_actor_tag_for_seed(to_seed); + let amount = vft_transfer_amount_u64(seed); + return match op { + MillionVftTransferOp::Transfer => unsafe { + tag_u64_balance_table() + .transfer_tags_bool_capacity_unchecked(owner, recipient, amount) + }, + MillionVftTransferOp::TransferFrom => unsafe { + tag_u64_balance_table().transfer_from_tags_bool_capacity_unchecked( + &tag_u64_allowance_table(), + vft_allowance_tag_for_seeds(seed, seed), + owner, + recipient, + amount, + ) + }, + }; + } + if backend == MillionVftBackend::InlineOwnerAccountU256 { + return match op { + MillionVftTransferOp::Transfer => { + inline_owner_account_table().transfer( + vft_actor_for_seed(seed), + vft_actor_for_seed(to_seed), + amount, + ) + .expect("inline owner account transfer failed") + }, + MillionVftTransferOp::TransferFrom => self + .apply_vft_transfer_from_spender_bool(backend, seed, seed, to_seed), + }; + } + + if matches!(op, MillionVftTransferOp::TransferFrom) { + let allowance = self + .get_allowance(backend, vft_actor_for_seed(seed), spender) + .unwrap_or_else(U256::zero); + if allowance < amount { + return false; + } + self.insert_allowance( + backend, + vft_actor_for_seed(seed), + spender, + allowance - amount, + ); + } + + let from_balance = self + .vft_get_balance(backend, seed) + .unwrap_or_else(U256::zero); + if from_balance < amount { + return false; + } + + let to_balance = self + .vft_get_balance(backend, to_seed) + .unwrap_or_else(U256::zero); + let (to_balance, overflow) = to_balance.overflowing_add(amount); + if overflow { + return false; + } + + self.vft_insert_balance(backend, seed, from_balance - amount); + self.vft_insert_balance(backend, to_seed, to_balance); + true + } + + fn apply_vft_transfer_from_spender_bool( + &mut self, + backend: MillionVftBackend, + owner_seed: u32, + spender_seed: u32, + to_seed: u32, + ) -> bool { + let amount = vft_transfer_amount(owner_seed); + if backend == MillionVftBackend::TagActorStatic { + return unsafe { + tag_actor_balance_table() + .transfer_actor_u256_from_tag_low64_bool_unchecked( + &tag_allowance_table(), + vft_actor_for_seed(owner_seed), + vft_spender_for_seed(spender_seed), + vft_actor_for_seed(to_seed), + amount, + ) + .expect("tag actor spender transfer-from failed") + }; + } + if backend == MillionVftBackend::TagU64ActorStatic { + return unsafe { + tag_u64_balance_table().transfer_from_tags_bool_capacity_unchecked( + &tag_u64_allowance_table(), + vft_allowance_tag_for_seeds(owner_seed, spender_seed), + vft_actor_tag_for_seed(owner_seed), + vft_actor_tag_for_seed(to_seed), + vft_transfer_amount_u64(owner_seed), + ) + }; + } + if backend == MillionVftBackend::InlineOwnerAccountU256 { + return inline_owner_account_table() + .transfer_from( + vft_spender_for_seed(spender_seed), + vft_actor_for_seed(owner_seed), + vft_actor_for_seed(to_seed), + amount, + ) + .expect("inline owner account transfer-from failed"); + } + + let owner = vft_actor_for_seed(owner_seed); + let spender = vft_spender_for_seed(spender_seed); + let allowance = self + .get_allowance(backend, owner, spender) + .unwrap_or_else(U256::zero); + if allowance < amount { + return false; + } + + let from_balance = self + .vft_get_balance(backend, owner_seed) + .unwrap_or_else(U256::zero); + if from_balance < amount { + return false; + } + + let to_balance = self + .vft_get_balance(backend, to_seed) + .unwrap_or_else(U256::zero); + let (to_balance, overflow) = to_balance.overflowing_add(amount); + if overflow { + return false; + } + + self.insert_allowance(backend, owner, spender, allowance - amount); + self.vft_insert_balance(backend, owner_seed, from_balance - amount); + self.vft_insert_balance(backend, to_seed, to_balance); + true + } + + fn apply_op( + &mut self, + backend: MillionStorageBackend, + op: MillionStorageOp, + seed: u32, + ) -> AppliedOp { + match op { + MillionStorageOp::InsertFresh => { + let value = value_for_seed(seed); + let existed = self.insert(backend, seed, value).is_some(); + AppliedOp { value, existed } + } + MillionStorageOp::UpdateExisting => { + let value = updated_value_for_seed(seed); + let existed = self.insert(backend, seed, value).is_some(); + AppliedOp { value, existed } + } + MillionStorageOp::ReadExisting | MillionStorageOp::ReadMissing => { + let value = self.get(backend, seed); + AppliedOp { + existed: value.is_some(), + value: value.unwrap_or_else(U256::zero), + } + } + MillionStorageOp::Remove => { + let value = self.remove(backend, seed); + AppliedOp { + existed: value.is_some(), + value: value.unwrap_or_else(U256::zero), + } + } + } + } + + fn len(&self, backend: MillionStorageBackend) -> u32 { + self.lens[backend_index(backend)] + } + + fn len_mut(&mut self, backend: MillionStorageBackend) -> &mut u32 { + &mut self.lens[backend_index(backend)] + } + + fn allowance_len(&self, backend: MillionStorageBackend) -> u32 { + self.allowance_lens[backend_index(backend)] + } + + fn allowance_len_mut(&mut self, backend: MillionStorageBackend) -> &mut u32 { + &mut self.allowance_lens[backend_index(backend)] + } + + fn insert_actor_value( + &mut self, + backend: MillionStorageBackend, + actor: ActorId, + value: U256, + ) -> Option { + let previous = match backend { + MillionStorageBackend::GenericStatic => generic_balance_table() + .insert_actor_u256(actor, value) + .expect("generic million balance table insert failed"), + MillionStorageBackend::WatActorStatic => wat_actor_balance_table() + .insert_actor_u256(actor, value) + .expect("wat actor million balance table insert failed"), + MillionStorageBackend::MixedActorStatic => mixed_actor_balance_table() + .insert_actor_u256(actor, value) + .expect("mixed actor million balance table insert failed"), + MillionStorageBackend::TagActorStatic => tag_actor_balance_table() + .insert_actor_u256(actor, value) + .expect("tag actor million balance table insert failed"), + MillionStorageBackend::TagU64ActorStatic => tag_u64_balance_table() + .insert_actor_u256(actor, value) + .expect("tag u64 actor million balance table insert failed"), + MillionStorageBackend::ControlActorStatic => control_actor_balance_table() + .insert_actor_u256(actor, value) + .expect("control actor million balance table insert failed"), + MillionStorageBackend::PageLocalActorStatic => page_local_actor_balance_table() + .insert_actor_u256(actor, value) + .expect("page-local actor million balance table insert failed"), + MillionStorageBackend::GroupedActorPages2 => grouped_actor_pages2_table() + .insert_actor_u256(actor, value) + .expect("grouped actor pages2 million balance table insert failed"), + MillionStorageBackend::GroupedActorPages4 => grouped_actor_pages4_table() + .insert_actor_u256(actor, value) + .expect("grouped actor pages4 million balance table insert failed"), + MillionStorageBackend::GroupedActorPages8 => grouped_actor_pages8_table() + .insert_actor_u256(actor, value) + .expect("grouped actor pages8 million balance table insert failed"), + MillionStorageBackend::GroupedActorPages16 => grouped_actor_pages16_table() + .insert_actor_u256(actor, value) + .expect("grouped actor pages16 million balance table insert failed"), + MillionStorageBackend::GroupedActorPages32 => grouped_actor_pages32_table() + .insert_actor_u256(actor, value) + .expect("grouped actor pages32 million balance table insert failed"), + MillionStorageBackend::GroupedActorPages64 => grouped_actor_pages64_table() + .insert_actor_u256(actor, value) + .expect("grouped actor pages64 million balance table insert failed"), + MillionStorageBackend::GroupedActorPages128 => grouped_actor_pages128_table() + .insert_actor_u256(actor, value) + .expect("grouped actor pages128 million balance table insert failed"), + }; + if previous.is_none() && !value.is_zero() { + *self.len_mut(backend) += 1; + } + previous + } + + fn get_actor_value(&self, backend: MillionStorageBackend, actor: ActorId) -> Option { + match backend { + MillionStorageBackend::GenericStatic => generic_balance_table() + .get_actor_u256(&actor) + .expect("generic million balance table get failed"), + MillionStorageBackend::WatActorStatic => wat_actor_balance_table() + .get_actor_u256(&actor) + .expect("wat actor million balance table get failed"), + MillionStorageBackend::MixedActorStatic => mixed_actor_balance_table() + .get_actor_u256(&actor) + .expect("mixed actor million balance table get failed"), + MillionStorageBackend::TagActorStatic => tag_actor_balance_table() + .get_actor_u256(&actor) + .expect("tag actor million balance table get failed"), + MillionStorageBackend::TagU64ActorStatic => tag_u64_balance_table() + .get_actor_u256(&actor) + .expect("tag u64 actor million balance table get failed"), + MillionStorageBackend::ControlActorStatic => control_actor_balance_table() + .get_actor_u256(&actor) + .expect("control actor million balance table get failed"), + MillionStorageBackend::PageLocalActorStatic => page_local_actor_balance_table() + .get_actor_u256(&actor) + .expect("page-local actor million balance table get failed"), + MillionStorageBackend::GroupedActorPages2 => grouped_actor_pages2_table() + .get_actor_u256(&actor) + .expect("grouped actor pages2 million balance table get failed"), + MillionStorageBackend::GroupedActorPages4 => grouped_actor_pages4_table() + .get_actor_u256(&actor) + .expect("grouped actor pages4 million balance table get failed"), + MillionStorageBackend::GroupedActorPages8 => grouped_actor_pages8_table() + .get_actor_u256(&actor) + .expect("grouped actor pages8 million balance table get failed"), + MillionStorageBackend::GroupedActorPages16 => grouped_actor_pages16_table() + .get_actor_u256(&actor) + .expect("grouped actor pages16 million balance table get failed"), + MillionStorageBackend::GroupedActorPages32 => grouped_actor_pages32_table() + .get_actor_u256(&actor) + .expect("grouped actor pages32 million balance table get failed"), + MillionStorageBackend::GroupedActorPages64 => grouped_actor_pages64_table() + .get_actor_u256(&actor) + .expect("grouped actor pages64 million balance table get failed"), + MillionStorageBackend::GroupedActorPages128 => grouped_actor_pages128_table() + .get_actor_u256(&actor) + .expect("grouped actor pages128 million balance table get failed"), + } + } + + fn insert(&mut self, backend: MillionStorageBackend, seed: u32, value: U256) -> Option { + let actor = actor_for_seed(seed); + self.insert_actor_value(backend, actor, value) + } + + fn get(&self, backend: MillionStorageBackend, seed: u32) -> Option { + let actor = actor_for_seed(seed); + self.get_actor_value(backend, actor) + } + + fn remove(&mut self, backend: MillionStorageBackend, seed: u32) -> Option { + let actor = actor_for_seed(seed); + let previous = match backend { + MillionStorageBackend::GenericStatic => generic_balance_table() + .remove_actor_u256(&actor) + .expect("generic million balance table remove failed"), + MillionStorageBackend::WatActorStatic => wat_actor_balance_table() + .remove_actor_u256(&actor) + .expect("wat actor million balance table remove failed"), + MillionStorageBackend::MixedActorStatic => mixed_actor_balance_table() + .remove_actor_u256(&actor) + .expect("mixed actor million balance table remove failed"), + MillionStorageBackend::TagActorStatic => tag_actor_balance_table() + .remove_actor_u256(&actor) + .expect("tag actor million balance table remove failed"), + MillionStorageBackend::TagU64ActorStatic => tag_u64_balance_table() + .remove_actor_u256(&actor) + .expect("tag u64 actor million balance table remove failed"), + MillionStorageBackend::ControlActorStatic => control_actor_balance_table() + .remove_actor_u256(&actor) + .expect("control actor million balance table remove failed"), + MillionStorageBackend::PageLocalActorStatic => page_local_actor_balance_table() + .remove_actor_u256(&actor) + .expect("page-local actor million balance table remove failed"), + MillionStorageBackend::GroupedActorPages2 => grouped_actor_pages2_table() + .remove_actor_u256(&actor) + .expect("grouped actor pages2 million balance table remove failed"), + MillionStorageBackend::GroupedActorPages4 => grouped_actor_pages4_table() + .remove_actor_u256(&actor) + .expect("grouped actor pages4 million balance table remove failed"), + MillionStorageBackend::GroupedActorPages8 => grouped_actor_pages8_table() + .remove_actor_u256(&actor) + .expect("grouped actor pages8 million balance table remove failed"), + MillionStorageBackend::GroupedActorPages16 => grouped_actor_pages16_table() + .remove_actor_u256(&actor) + .expect("grouped actor pages16 million balance table remove failed"), + MillionStorageBackend::GroupedActorPages32 => grouped_actor_pages32_table() + .remove_actor_u256(&actor) + .expect("grouped actor pages32 million balance table remove failed"), + MillionStorageBackend::GroupedActorPages64 => grouped_actor_pages64_table() + .remove_actor_u256(&actor) + .expect("grouped actor pages64 million balance table remove failed"), + MillionStorageBackend::GroupedActorPages128 => grouped_actor_pages128_table() + .remove_actor_u256(&actor) + .expect("grouped actor pages128 million balance table remove failed"), + }; + if previous.is_some() { + *self.len_mut(backend) -= 1; + } + previous + } + + fn vft_balance_len(&self, backend: MillionVftBackend) -> u32 { + match (backend, &self.dynamic_vft) { + (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { balances, .. }) => { + balances.len() as u32 + } + (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { balances, .. }) => { + balances.len() as u32 + } + _ => backend + .static_backend() + .map_or(0, |backend| self.len(backend)), + } + } + + fn vft_allowance_len(&self, backend: MillionVftBackend) -> u32 { + match (backend, &self.dynamic_vft) { + (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { allowances, .. }) => { + allowances.len() as u32 + } + (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { allowances, .. }) => { + allowances.len() as u32 + } + _ => backend + .static_backend() + .map_or(0, |backend| self.allowance_len(backend)), + } + } + + fn vft_get_balance(&self, backend: MillionVftBackend, seed: u32) -> Option { + let actor = vft_actor_for_seed(seed); + match (backend, &self.dynamic_vft) { + (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { balances, .. }) => { + balances.get(&actor).copied() + } + (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { balances, .. }) => { + balances.get(&actor).copied() + } + (MillionVftBackend::InlineOwnerAccountU256, _) => { + inline_owner_account_table() + .accounts() + .get_balance(vft_actor_for_seed(seed)) + .expect("inline owner account balance get failed") + } + _ => backend + .static_backend() + .and_then(|backend| self.get_actor_value(backend, actor)), + } + } + + fn vft_insert_balance( + &mut self, + backend: MillionVftBackend, + seed: u32, + value: U256, + ) -> Option { + let actor = vft_actor_for_seed(seed); + match (backend, &mut self.dynamic_vft) { + (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { balances, .. }) => { + balances.insert(actor, value) + } + (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { balances, .. }) => { + balances.insert(actor, value) + } + (MillionVftBackend::InlineOwnerAccountU256, _) => { + let previous = inline_owner_account_table() + .accounts() + .insert_balance(vft_actor_for_seed(seed), value) + .expect("inline owner account balance insert failed"); + if previous.is_none() && !value.is_zero() { + *self.len_mut(MillionStorageBackend::GroupedActorPages128) += 1; + } + previous + } + _ => { + let static_backend = backend + .static_backend() + .expect("static VFT backend expected"); + self.insert_actor_value(static_backend, actor, value) + } + } + } + + fn insert_allowance( + &mut self, + backend: MillionVftBackend, + owner: ActorId, + spender: ActorId, + value: U256, + ) -> Option { + match (backend, &mut self.dynamic_vft) { + (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { allowances, .. }) => { + return allowances.insert((owner, spender), value); + } + (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { allowances, .. }) => { + return allowances.insert((owner, spender), value); + } + _ => {} + } + + if backend == MillionVftBackend::InlineOwnerAccountU256 { + let storage = inline_owner_account_table(); + let previous = storage + .allowance(owner, spender) + .expect("inline owner account allowance get failed"); + storage + .approve(owner, spender, value) + .expect("inline owner account allowance insert failed"); + if previous.is_zero() && !value.is_zero() { + *self.allowance_len_mut(MillionStorageBackend::GroupedActorPages128) += 1; + } + return (!previous.is_zero()).then_some(previous); + } + + let static_backend = backend + .static_backend() + .expect("static VFT backend expected"); + let previous = match static_backend { + MillionStorageBackend::GenericStatic => generic_allowance_table() + .insert_allowance_u256(owner, spender, value) + .expect("generic million allowance table insert failed"), + MillionStorageBackend::TagActorStatic => tag_allowance_table() + .insert_allowance_u256(owner, spender, value) + .expect("tag million allowance table insert failed"), + MillionStorageBackend::TagU64ActorStatic => { + tag_u64_allowance_table() + .insert_allowance_u256_bool(owner, spender, value) + .expect("tag u64 million allowance table insert failed"); + None + } + _ => wat_allowance_table() + .insert_allowance_u256(owner, spender, value) + .expect("wat million allowance table insert failed"), + }; + if previous.is_none() && !value.is_zero() { + *self.allowance_len_mut(static_backend) += 1; + } + previous + } + + fn get_allowance( + &self, + backend: MillionVftBackend, + owner: ActorId, + spender: ActorId, + ) -> Option { + match (backend, &self.dynamic_vft) { + (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { allowances, .. }) => { + return allowances.get(&(owner, spender)).copied(); + } + (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { allowances, .. }) => { + return allowances.get(&(owner, spender)).copied(); + } + _ => {} + } + + if backend == MillionVftBackend::InlineOwnerAccountU256 { + let allowance = inline_owner_account_table() + .allowance(owner, spender) + .expect("inline owner account allowance get failed"); + return (!allowance.is_zero()).then_some(allowance); + } + + match backend + .static_backend() + .expect("static VFT backend expected") + { + MillionStorageBackend::GenericStatic => generic_allowance_table() + .get_allowance_u256(&owner, &spender) + .expect("generic million allowance table get failed"), + MillionStorageBackend::TagActorStatic => tag_allowance_table() + .get_allowance_u256(&owner, &spender) + .expect("tag million allowance table get failed"), + MillionStorageBackend::TagU64ActorStatic => tag_u64_allowance_table() + .get_allowance_u256(&owner, &spender) + .expect("tag u64 million allowance table get failed"), + _ => wat_allowance_table() + .get_allowance_u256(&owner, &spender) + .expect("wat million allowance table get failed"), + } + } + + fn vft_result( + &self, + backend: MillionVftBackend, + from_seed: u32, + to_seed: u32, + spender: Option, + transferred: bool, + ) -> MillionVftTransferResult { + let owner = vft_actor_for_seed(from_seed); + MillionVftTransferResult { + from_balance: self + .vft_get_balance(backend, from_seed) + .unwrap_or_else(U256::zero), + to_balance: self + .vft_get_balance(backend, to_seed) + .unwrap_or_else(U256::zero), + allowance: spender + .and_then(|spender| self.get_allowance(backend, owner, spender)) + .unwrap_or_else(U256::zero), + balance_len: self.vft_balance_len(backend), + allowance_len: self.vft_allowance_len(backend), + transferred, + } + } +} + +impl MillionVftBackend { + fn static_backend(self) -> Option { + match self { + Self::BTree | Self::HashMap => None, + Self::GenericStatic | Self::GenericStaticFused | Self::GenericStaticFast => { + Some(MillionStorageBackend::GenericStatic) + } + Self::WatActorStatic => Some(MillionStorageBackend::WatActorStatic), + Self::MixedActorStatic | Self::MixedActorFast => { + Some(MillionStorageBackend::MixedActorStatic) + } + Self::TagActorStatic => Some(MillionStorageBackend::TagActorStatic), + Self::TagU64ActorStatic => Some(MillionStorageBackend::TagU64ActorStatic), + Self::ControlActorStatic => Some(MillionStorageBackend::ControlActorStatic), + Self::PageLocalActorStatic => Some(MillionStorageBackend::PageLocalActorStatic), + Self::GroupedActorPages64 => Some(MillionStorageBackend::GroupedActorPages64), + Self::GroupedActorPages128 | Self::InlineOwnerAccountU256 => { + Some(MillionStorageBackend::GroupedActorPages128) + } + } + } +} + +fn backend_index(backend: MillionStorageBackend) -> usize { + match backend { + MillionStorageBackend::GenericStatic => 0, + MillionStorageBackend::WatActorStatic => 1, + MillionStorageBackend::MixedActorStatic => 2, + MillionStorageBackend::TagActorStatic => 3, + MillionStorageBackend::TagU64ActorStatic => 4, + MillionStorageBackend::ControlActorStatic => 5, + MillionStorageBackend::PageLocalActorStatic => 6, + MillionStorageBackend::GroupedActorPages2 => 7, + MillionStorageBackend::GroupedActorPages4 => 8, + MillionStorageBackend::GroupedActorPages8 => 9, + MillionStorageBackend::GroupedActorPages16 => 10, + MillionStorageBackend::GroupedActorPages32 => 11, + MillionStorageBackend::GroupedActorPages64 => 12, + MillionStorageBackend::GroupedActorPages128 => 13, + } +} + +fn backend_capacity(backend: MillionStorageBackend) -> u32 { + match backend { + MillionStorageBackend::GenericStatic => GENERIC_BALANCE_CAPACITY, + MillionStorageBackend::WatActorStatic => WAT_ACTOR_BALANCE_CAPACITY, + MillionStorageBackend::MixedActorStatic => MIXED_ACTOR_BALANCE_CAPACITY, + MillionStorageBackend::TagActorStatic => TAG_ACTOR_BALANCE_CAPACITY, + MillionStorageBackend::TagU64ActorStatic => TAG_U64_ACTOR_BALANCE_CAPACITY, + MillionStorageBackend::ControlActorStatic => CONTROL_ACTOR_BALANCE_CAPACITY, + MillionStorageBackend::PageLocalActorStatic => PAGE_LOCAL_ACTOR_BALANCE_CAPACITY, + MillionStorageBackend::GroupedActorPages2 => { + static_storage::GROUPED_ACTOR_BALANCES_PAGES2_SLOTS as u32 + } + MillionStorageBackend::GroupedActorPages4 => { + static_storage::GROUPED_ACTOR_BALANCES_PAGES4_SLOTS as u32 + } + MillionStorageBackend::GroupedActorPages8 => { + static_storage::GROUPED_ACTOR_BALANCES_PAGES8_SLOTS as u32 + } + MillionStorageBackend::GroupedActorPages16 => { + static_storage::GROUPED_ACTOR_BALANCES_PAGES16_SLOTS as u32 + } + MillionStorageBackend::GroupedActorPages32 => { + static_storage::GROUPED_ACTOR_BALANCES_PAGES32_SLOTS as u32 + } + MillionStorageBackend::GroupedActorPages64 => { + static_storage::GROUPED_ACTOR_BALANCES_PAGES64_SLOTS as u32 + } + MillionStorageBackend::GroupedActorPages128 => { + static_storage::GROUPED_ACTOR_BALANCES_PAGES128_SLOTS as u32 + } + } +} + +fn allowance_capacity(backend: MillionStorageBackend) -> u32 { + match backend { + MillionStorageBackend::GenericStatic => GENERIC_ALLOWANCE_CAPACITY, + _ => WAT_ALLOWANCE_CAPACITY, + } +} + +#[cfg(any())] +struct InlineOwnerAccountU256Table { + base: usize, + slots: usize, + mask: usize, +} + +#[cfg(any())] +impl InlineOwnerAccountU256Table { + const OCCUPIED: u8 = 1; + const OWNER_STATE_OFFSET: usize = 0; + const SPENDER0_STATE_OFFSET: usize = 1; + const SPENDER1_STATE_OFFSET: usize = 2; + const OWNER_OFFSET: usize = 8; + const BALANCE_OFFSET: usize = 40; + const SPENDER0_OFFSET: usize = 72; + const ALLOWANCE0_OFFSET: usize = 104; + const SPENDER1_OFFSET: usize = 136; + const ALLOWANCE1_OFFSET: usize = 168; + const SLOT_SIZE: usize = 200; + + #[allow(dead_code)] + unsafe fn new(base: usize, slots: usize) -> Self { + debug_assert!(slots.is_power_of_two()); + Self { + base, + slots, + mask: slots - 1, + } + } + + fn get_balance(&self, owner: ActorId) -> Option { + let slot = unsafe { self.lookup(owner)? }; + Some(unsafe { self.read_u256(slot, Self::BALANCE_OFFSET) }) + } + + unsafe fn insert_balance(&self, owner: ActorId, value: U256) -> Option { + let slot = unsafe { self.lookup_or_insert(owner) }; + let previous = unsafe { self.read_u256(slot, Self::BALANCE_OFFSET) }; + unsafe { + self.write_u256(slot, Self::BALANCE_OFFSET, value); + } + (!previous.is_zero()).then_some(previous) + } + + fn get_inline_allowance(&self, owner: ActorId, spender: ActorId) -> Option { + let slot = unsafe { self.lookup(owner)? }; + unsafe { self.inline_allowance_offset(slot, spender) } + .map(|offset| unsafe { self.read_u256(slot, offset) }) + } + + unsafe fn insert_inline_allowance( + &self, + owner: ActorId, + spender: ActorId, + value: U256, + ) -> Option> { + let slot = unsafe { self.lookup_or_insert(owner) }; + unsafe { + if self.read_state(slot, Self::SPENDER0_STATE_OFFSET) == Self::OCCUPIED + && self.actor_matches(slot, Self::SPENDER0_OFFSET, spender) + { + let previous = self.read_u256(slot, Self::ALLOWANCE0_OFFSET); + self.write_u256(slot, Self::ALLOWANCE0_OFFSET, value); + return Some(Some(previous)); + } + if self.read_state(slot, Self::SPENDER1_STATE_OFFSET) == Self::OCCUPIED + && self.actor_matches(slot, Self::SPENDER1_OFFSET, spender) + { + let previous = self.read_u256(slot, Self::ALLOWANCE1_OFFSET); + self.write_u256(slot, Self::ALLOWANCE1_OFFSET, value); + return Some(Some(previous)); + } + if self.read_state(slot, Self::SPENDER0_STATE_OFFSET) != Self::OCCUPIED { + self.write_state(slot, Self::SPENDER0_STATE_OFFSET, Self::OCCUPIED); + self.write_actor(slot, Self::SPENDER0_OFFSET, spender); + self.write_u256(slot, Self::ALLOWANCE0_OFFSET, value); + return Some(None); + } + if self.read_state(slot, Self::SPENDER1_STATE_OFFSET) != Self::OCCUPIED { + self.write_state(slot, Self::SPENDER1_STATE_OFFSET, Self::OCCUPIED); + self.write_actor(slot, Self::SPENDER1_OFFSET, spender); + self.write_u256(slot, Self::ALLOWANCE1_OFFSET, value); + return Some(None); + } + } + None + } + + unsafe fn transfer(&self, from: ActorId, to: ActorId, amount: U256) -> bool { + let Some(from_slot) = (unsafe { self.lookup(from) }) else { + return false; + }; + let from_balance = unsafe { self.read_u256(from_slot, Self::BALANCE_OFFSET) }; + if from_balance < amount { + return false; + } + + let to_slot = unsafe { self.lookup_or_insert(to) }; + let to_balance = unsafe { self.read_u256(to_slot, Self::BALANCE_OFFSET) }; + let (to_balance, overflow) = to_balance.overflowing_add(amount); + if overflow { + return false; + } + unsafe { + self.write_u256(from_slot, Self::BALANCE_OFFSET, from_balance - amount); + self.write_u256(to_slot, Self::BALANCE_OFFSET, to_balance); + } + true + } + + unsafe fn transfer_from( + &self, + overflow_allowances: &WatAllowanceTable, + owner: ActorId, + spender: ActorId, + to: ActorId, + amount: U256, + ) -> bool { + let Some(owner_slot) = (unsafe { self.lookup(owner) }) else { + return false; + }; + + let inline_allowance_offset = unsafe { self.inline_allowance_offset(owner_slot, spender) }; + let allowance = if let Some(offset) = inline_allowance_offset { + unsafe { self.read_u256(owner_slot, offset) } + } else { + overflow_allowances + .get_allowance_u256(&owner, &spender) + .expect("inline owner overflow allowance get failed") + .unwrap_or_else(U256::zero) + }; + + if allowance < amount { + return false; + } + let owner_balance = unsafe { self.read_u256(owner_slot, Self::BALANCE_OFFSET) }; + if owner_balance < amount { + return false; + } + + let to_slot = unsafe { self.lookup_or_insert(to) }; + let to_balance = unsafe { self.read_u256(to_slot, Self::BALANCE_OFFSET) }; + let (to_balance, overflow) = to_balance.overflowing_add(amount); + if overflow { + return false; + } + unsafe { + if let Some(offset) = inline_allowance_offset { + self.write_u256(owner_slot, offset, allowance - amount); + } else { + overflow_allowances + .insert_allowance_u256(owner, spender, allowance - amount) + .expect("inline owner overflow allowance update failed"); + } + self.write_u256(owner_slot, Self::BALANCE_OFFSET, owner_balance - amount); + self.write_u256(to_slot, Self::BALANCE_OFFSET, to_balance); + } + true + } + + unsafe fn inline_allowance_offset(&self, slot: usize, spender: ActorId) -> Option { + if unsafe { + self.read_state(slot, Self::SPENDER0_STATE_OFFSET) == Self::OCCUPIED + && self.actor_matches(slot, Self::SPENDER0_OFFSET, spender) + } { + return Some(Self::ALLOWANCE0_OFFSET); + } + if unsafe { + self.read_state(slot, Self::SPENDER1_STATE_OFFSET) == Self::OCCUPIED + && self.actor_matches(slot, Self::SPENDER1_OFFSET, spender) + } { + return Some(Self::ALLOWANCE1_OFFSET); + } + None + } + + unsafe fn lookup(&self, owner: ActorId) -> Option { + let mut index = account_index(owner, self.mask); + for _ in 0..self.slots { + let occupied = + unsafe { self.read_state(index, Self::OWNER_STATE_OFFSET) } == Self::OCCUPIED; + if !occupied { + return None; + } + if unsafe { self.actor_matches(index, Self::OWNER_OFFSET, owner) } { + return Some(index); + } + index = (index + 1) & self.mask; + } + None + } + + unsafe fn lookup_or_insert(&self, owner: ActorId) -> usize { + let mut index = account_index(owner, self.mask); + for _ in 0..self.slots { + let occupied = + unsafe { self.read_state(index, Self::OWNER_STATE_OFFSET) } == Self::OCCUPIED; + if occupied && unsafe { self.actor_matches(index, Self::OWNER_OFFSET, owner) } { + return index; + } + if !occupied { + unsafe { + self.write_state(index, Self::OWNER_STATE_OFFSET, Self::OCCUPIED); + self.write_actor(index, Self::OWNER_OFFSET, owner); + } + return index; + } + index = (index + 1) & self.mask; + } + panic!("inline owner account table is full") + } + + unsafe fn actor_matches(&self, slot: usize, offset: usize, actor: ActorId) -> bool { + unsafe { + core::slice::from_raw_parts(self.slot_ptr(slot).add(offset), 32) == actor.as_ref() + } + } + + unsafe fn read_state(&self, slot: usize, offset: usize) -> u8 { + unsafe { ptr::read(self.slot_ptr(slot).add(offset)) } + } + + unsafe fn read_u256(&self, slot: usize, offset: usize) -> U256 { + let mut bytes = [0u8; 32]; + unsafe { + ptr::copy_nonoverlapping(self.slot_ptr(slot).add(offset), bytes.as_mut_ptr(), 32); + } + U256::from_little_endian(&bytes) + } + + unsafe fn write_state(&self, slot: usize, offset: usize, state: u8) { + unsafe { + ptr::write(self.slot_ptr(slot).add(offset), state); + } + } + + unsafe fn write_actor(&self, slot: usize, offset: usize, actor: ActorId) { + unsafe { + ptr::copy_nonoverlapping(actor.as_ref().as_ptr(), self.slot_ptr(slot).add(offset), 32); + } + } + + unsafe fn write_u256(&self, slot: usize, offset: usize, value: U256) { + let mut bytes = [0u8; 32]; + value.to_little_endian(&mut bytes); + unsafe { + ptr::copy_nonoverlapping(bytes.as_ptr(), self.slot_ptr(slot).add(offset), 32); + } + } + + fn slot_ptr(&self, slot: usize) -> *mut u8 { + (self.base + slot * Self::SLOT_SIZE) as *mut u8 + } +} + +#[cfg(target_arch = "wasm32")] +fn generic_balance_table() -> SailsStaticBalanceTable { + unsafe { + SailsStaticBalanceTable::new( + static_storage::MILLION_BALANCES_BASE, + static_storage::MILLION_BALANCES_SLOTS, + ) + .expect("million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn generic_balance_table() -> SailsStaticBalanceTable { + unimplemented!("million static balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn generic_allowance_table() -> SailsStaticAllowanceTable { + unsafe { + SailsStaticAllowanceTable::new( + static_storage::MILLION_ALLOWANCES_BASE, + static_storage::MILLION_ALLOWANCES_SLOTS, + ) + .expect("million allowance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn generic_allowance_table() -> SailsStaticAllowanceTable { + unimplemented!("million static allowance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn wat_actor_balance_table() -> WatActorBalanceTable { + unsafe { + WatActorBalanceTable::new(static_storage::WAT_ACTOR_BALANCES_BASE) + .expect("wat actor million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn wat_actor_balance_table() -> WatActorBalanceTable { + unimplemented!("wat actor million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn mixed_actor_balance_table() -> MixedActorBalanceTable { + unsafe { + MixedActorBalanceTable::new(static_storage::MIXED_ACTOR_BALANCES_BASE) + .expect("mixed actor million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn mixed_actor_balance_table() -> MixedActorBalanceTable { + unimplemented!("mixed actor million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn tag_actor_balance_table() -> TagActorBalanceTable { + unsafe { + TagActorBalanceTable::new( + static_storage::GROUPED_ACTOR_BALANCES_PAGES128_BASE, + static_storage::MILLION_BALANCES_SLOTS, + ) + .expect("tag actor million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn tag_actor_balance_table() -> TagActorBalanceTable { + unimplemented!("tag actor million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn tag_u64_balance_table() -> TagU64ActorTable { + unsafe { + TagU64ActorTable::new( + static_storage::GROUPED_ACTOR_BALANCES_PAGES128_BASE, + static_storage::MILLION_BALANCES_SLOTS * 4, + ) + .expect("tag u64 actor million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn tag_u64_balance_table() -> TagU64ActorTable { + unimplemented!("tag u64 actor million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn tag_allowance_table() -> TagActorBalanceTable { + unsafe { + TagActorBalanceTable::new( + static_storage::CONTROL_ACTOR_BALANCES_SLOTS_BASE, + static_storage::MILLION_ALLOWANCES_SLOTS, + ) + .expect("tag allowance million table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn tag_allowance_table() -> TagActorBalanceTable { + unimplemented!("tag allowance million table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn tag_u64_allowance_table() -> TagU64ActorTable { + unsafe { + TagU64ActorTable::new( + static_storage::CONTROL_ACTOR_BALANCES_SLOTS_BASE, + static_storage::MILLION_ALLOWANCES_SLOTS * 4, + ) + .expect("tag u64 allowance million table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn tag_u64_allowance_table() -> TagU64ActorTable { + unimplemented!("tag u64 allowance million table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn inline_owner_account_table() -> InlineOwnerAccountTable { + unsafe { + InlineOwnerAccountTable::new( + static_storage::GROUPED_ACTOR_BALANCES_PAGES16_BASE, + static_storage::WAT_ALLOWANCES_BASE, + ) + .expect("inline owner account storage layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn inline_owner_account_table() -> InlineOwnerAccountTable { + unimplemented!("inline owner account million table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn wat_allowance_table() -> WatAllowanceTable { + unsafe { + WatAllowanceTable::new(static_storage::WAT_ALLOWANCES_BASE) + .expect("wat million allowance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn wat_allowance_table() -> WatAllowanceTable { + unimplemented!("wat million allowance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn control_actor_balance_table() -> ControlActorBalanceTable { + unsafe { + ControlActorBalanceTable::new( + static_storage::CONTROL_ACTOR_BALANCES_CONTROL_BASE, + static_storage::CONTROL_ACTOR_BALANCES_SLOTS_BASE, + ) + .expect("control actor million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn control_actor_balance_table() -> ControlActorBalanceTable { + unimplemented!("control actor million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn page_local_actor_balance_table() -> PageLocalActorBalanceTable { + unsafe { + PageLocalActorBalanceTable::new(static_storage::PAGE_LOCAL_ACTOR_BALANCES_BASE) + .expect("page-local actor million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn page_local_actor_balance_table() -> PageLocalActorBalanceTable { + unimplemented!("page-local actor million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn grouped_actor_pages2_table() -> GroupedActorPages2Table { + unsafe { + GroupedActorPages2Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES2_BASE) + .expect("grouped actor pages2 million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn grouped_actor_pages2_table() -> GroupedActorPages2Table { + unimplemented!("grouped actor pages2 million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn grouped_actor_pages4_table() -> GroupedActorPages4Table { + unsafe { + GroupedActorPages4Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES4_BASE) + .expect("grouped actor pages4 million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn grouped_actor_pages4_table() -> GroupedActorPages4Table { + unimplemented!("grouped actor pages4 million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn grouped_actor_pages8_table() -> GroupedActorPages8Table { + unsafe { + GroupedActorPages8Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES8_BASE) + .expect("grouped actor pages8 million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn grouped_actor_pages8_table() -> GroupedActorPages8Table { + unimplemented!("grouped actor pages8 million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn grouped_actor_pages16_table() -> GroupedActorPages16Table { + unsafe { + GroupedActorPages16Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES16_BASE) + .expect("grouped actor pages16 million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn grouped_actor_pages16_table() -> GroupedActorPages16Table { + unimplemented!("grouped actor pages16 million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn grouped_actor_pages32_table() -> GroupedActorPages32Table { + unsafe { + GroupedActorPages32Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES32_BASE) + .expect("grouped actor pages32 million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn grouped_actor_pages32_table() -> GroupedActorPages32Table { + unimplemented!("grouped actor pages32 million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn grouped_actor_pages64_table() -> GroupedActorPages64Table { + unsafe { + GroupedActorPages64Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES64_BASE) + .expect("grouped actor pages64 million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn grouped_actor_pages64_table() -> GroupedActorPages64Table { + unimplemented!("grouped actor pages64 million balance table is available only on wasm32") +} + +#[cfg(target_arch = "wasm32")] +fn grouped_actor_pages128_table() -> GroupedActorPages128Table { + unsafe { + GroupedActorPages128Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES128_BASE) + .expect("grouped actor pages128 million balance table layout is valid") + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn grouped_actor_pages128_table() -> GroupedActorPages128Table { + unimplemented!("grouped actor pages128 million balance table is available only on wasm32") +} + +fn actor_for_seed(seed: u32) -> ActorId { + ActorId::from(seed as u64 + 1) +} + +fn vft_actor_for_seed(seed: u32) -> ActorId { + actor_from_u64(vft_random_u64(seed, 0xA11C_E001_D15C_A11C)) +} + +fn vft_actor_tag_for_seed(seed: u32) -> u64 { + vft_random_u64(seed, 0xA11C_E001_D15C_A11C).max(1) +} + +fn vft_spender_for_seed(seed: u32) -> ActorId { + actor_from_u64(vft_random_u64(seed, 0x5EED_5EED_A110_CAFE)) +} + +fn vft_spender_tag_for_seed(seed: u32) -> u64 { + vft_random_u64(seed, 0x5EED_5EED_A110_CAFE).max(1) +} + +#[cfg(any())] +fn account_index(actor: ActorId, mask: usize) -> usize { + let key = actor.as_ref(); + let hot_lane = unsafe { ptr::read_unaligned(key.as_ptr().add(12).cast::()) }; + let hash = if hot_lane != 0 { + hot_lane + } else { + unsafe { + ptr::read_unaligned(key.as_ptr().cast::()) + ^ ptr::read_unaligned(key.as_ptr().add(8).cast::()) + ^ ptr::read_unaligned(key.as_ptr().add(16).cast::()) + ^ ptr::read_unaligned(key.as_ptr().add(24).cast::()) + } + }; + hash as usize & mask +} + +fn vft_allowance_tag_for_seeds(owner_seed: u32, spender_seed: u32) -> u64 { + vft_actor_tag_for_seed(owner_seed) + .wrapping_mul(0x9E37_79B9_7F4A_7C15) + .wrapping_add(vft_spender_tag_for_seed(spender_seed).wrapping_mul(0xBF58_476D_1CE4_E5B9)) + .max(1) +} + +fn actor_from_u64(id: u64) -> ActorId { + ActorId::from(id.max(1)) +} + +fn vft_random_u64(seed: u32, domain: u64) -> u64 { + let state = u64::from(seed) + .wrapping_add(domain) + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); + state ^ (state >> 32) +} + +fn value_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 1) +} + +fn updated_value_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 1_000_001) +} + +fn vft_balance_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 1_000_000) +} + +fn vft_allowance_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 10_000) +} + +fn vft_allowance_for_seed_u64(seed: u32) -> u64 { + seed as u64 + 10_000 +} + +fn vft_transfer_amount(seed: u32) -> U256 { + U256::from((seed % 7) as u64 + 1) +} + +fn vft_transfer_amount_u64(seed: u32) -> u64 { + (seed % 7) as u64 + 1 +} diff --git a/benchmarks/storage-stress/Cargo.toml b/benchmarks/storage-stress/Cargo.toml index 5b4c7b965..64dc9678a 100644 --- a/benchmarks/storage-stress/Cargo.toml +++ b/benchmarks/storage-stress/Cargo.toml @@ -8,7 +8,7 @@ repository.workspace = true [dependencies] sails-rs.workspace = true -sails-storage = { workspace = true, features = ["gear"] } +sails-storage.workspace = true [build-dependencies] sails-rs = { workspace = true, features = ["wasm-builder"] } diff --git a/benchmarks/vft-stress/Cargo.toml b/benchmarks/vft-stress/Cargo.toml new file mode 100644 index 000000000..9c1f2415b --- /dev/null +++ b/benchmarks/vft-stress/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "vft-stress" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true + +[features] +default = [] +wasm-profile = [] + +[dependencies] +sails-rs.workspace = true +sails-storage.workspace = true + +[build-dependencies] +sails-rs = { workspace = true, features = ["wasm-builder"] } diff --git a/benchmarks/vft-stress/build.rs b/benchmarks/vft-stress/build.rs new file mode 100644 index 000000000..e7b8e4c07 --- /dev/null +++ b/benchmarks/vft-stress/build.rs @@ -0,0 +1,9 @@ +fn main() { + const FIXED_CAPACITY: usize = 2048; + + let layout = sails_rs::build::StaticMemoryLayout::new(1024) + .reserve_table::<32, 32>("sails_static_balances", FIXED_CAPACITY) + .reserve_table::<64, 32>("sails_static_allowances", FIXED_CAPACITY); + + sails_rs::build::build_wasm_with_static_memory(layout); +} diff --git a/benchmarks/vft-stress/src/lib.rs b/benchmarks/vft-stress/src/lib.rs new file mode 100644 index 000000000..089f70865 --- /dev/null +++ b/benchmarks/vft-stress/src/lib.rs @@ -0,0 +1,1230 @@ +#![no_std] + +use sails_rs::{ + cell::RefCell, + collections::{BTreeMap, HashMap}, + prelude::*, +}; +use sails_storage::gear::{ + FixedAllowanceMap as SailsFixedAllowanceMap, FixedBalanceMap as SailsFixedBalanceMap, + StaticAllowanceTable, StaticBalanceTable, +}; + +mod static_storage { + include!(concat!(env!("OUT_DIR"), "/sails_static_storage.rs")); +} + +const FIXED_CAPACITY: usize = 2048; + +pub const STATIC_MEMORY_END_PAGE: u32 = static_storage::STATIC_MEMORY_END_PAGE; + +type SailsStaticBalanceTable = StaticBalanceTable; +type SailsStaticAllowanceTable = StaticAllowanceTable; + +#[sails_type] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum VftStorageBackend { + BTree, + HashMap, + SailsFixed, + SailsStatic, + SailsStaticFast, +} + +#[sails_type] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum VftTransferOp { + Transfer, + TransferFrom, +} + +#[sails_type] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VftTransferResult { + pub from_balance: U256, + pub to_balance: U256, + pub allowance: U256, + pub balance_len: u32, + pub allowance_len: u32, + pub transferred: bool, +} + +#[sails_type] +#[cfg(feature = "wasm-profile")] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum VftPhase { + ProbeOverhead, + NoopBody, + EchoBody, + KeyDerive, + AllowanceGet, + AllowancePut, + BalanceGetFrom, + BalanceGetTo, + BalancePutFrom, + BalancePutTo, + BalanceTransfer, + BalanceTransferFrom, + ResultBuild, +} + +#[sails_type] +#[cfg(feature = "wasm-profile")] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VftPhaseGas { + pub phase: VftPhase, + pub gas: u64, +} + +#[sails_type] +#[cfg(feature = "wasm-profile")] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct VftProfileResult { + pub result: VftTransferResult, + pub phases: Vec, +} + +struct VftStressService<'a> { + state: &'a RefCell, +} + +impl<'a> VftStressService<'a> { + fn new(state: &'a RefCell) -> Self { + Self { state } + } +} + +#[cfg(not(feature = "wasm-profile"))] +#[sails_rs::service] +impl VftStressService<'_> { + #[export] + pub fn prepare_vft(&mut self, backend: VftStorageBackend, len: u32) -> VftTransferResult { + self.state.borrow_mut().prepare_vft(backend, len) + } + + #[export] + pub fn bench_vft_transfer( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + ) -> VftTransferResult { + self.state + .borrow_mut() + .bench_vft_transfer(backend, op, seed) + } + + #[export] + pub fn bench_vft_transfer_fresh_bool(&mut self, backend: VftStorageBackend, seed: u32) -> bool { + self.state + .borrow_mut() + .bench_vft_transfer_fresh_bool(backend, seed) + } + + #[export] + pub fn bench_vft_approve_bool( + &mut self, + backend: VftStorageBackend, + owner_seed: u32, + spender_seed: u32, + ) -> bool { + self.state + .borrow_mut() + .bench_vft_approve_bool(backend, owner_seed, spender_seed) + } +} + +#[cfg(feature = "wasm-profile")] +#[sails_rs::service] +impl VftStressService<'_> { + #[cfg(feature = "wasm-profile")] + #[export] + pub fn bench_noop(&mut self) -> bool { + self.state.borrow_mut().bench_noop() + } + + #[cfg(feature = "wasm-profile")] + #[export] + pub fn bench_echo_vft_args( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + ) -> bool { + self.state + .borrow_mut() + .bench_echo_vft_args(backend, op, seed) + } + + #[export] + pub fn prepare_vft(&mut self, backend: VftStorageBackend, len: u32) -> VftTransferResult { + self.state.borrow_mut().prepare_vft(backend, len) + } + + #[export] + pub fn bench_vft_transfer( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + ) -> VftTransferResult { + self.state + .borrow_mut() + .bench_vft_transfer(backend, op, seed) + } + + #[export] + pub fn bench_vft_transfer_fresh_bool(&mut self, backend: VftStorageBackend, seed: u32) -> bool { + self.state + .borrow_mut() + .bench_vft_transfer_fresh_bool(backend, seed) + } + + #[export] + pub fn bench_vft_approve_bool( + &mut self, + backend: VftStorageBackend, + owner_seed: u32, + spender_seed: u32, + ) -> bool { + self.state + .borrow_mut() + .bench_vft_approve_bool(backend, owner_seed, spender_seed) + } + + #[cfg(feature = "wasm-profile")] + #[export] + pub fn bench_vft_transfer_profile( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + ) -> VftProfileResult { + self.state + .borrow_mut() + .bench_vft_transfer_profile(backend, op, seed) + } + + #[cfg(feature = "wasm-profile")] + #[export] + pub fn bench_vft_transfer_fresh_profile( + &mut self, + backend: VftStorageBackend, + seed: u32, + ) -> VftProfileResult { + self.state + .borrow_mut() + .bench_vft_transfer_fresh_profile(backend, seed) + } + + #[cfg(feature = "wasm-profile")] + #[export] + pub fn bench_vft_approve_profile( + &mut self, + backend: VftStorageBackend, + owner_seed: u32, + spender_seed: u32, + ) -> VftProfileResult { + self.state + .borrow_mut() + .bench_vft_approve_profile(backend, owner_seed, spender_seed) + } +} + +pub struct VftStressProgram { + state: RefCell, +} + +#[sails_rs::program] +impl VftStressProgram { + pub fn new_for_bench() -> Self { + Self { + state: RefCell::new(VftStressState::new()), + } + } + + pub fn vft_stress(&self) -> VftStressService<'_> { + VftStressService::new(&self.state) + } +} + +struct VftStressState { + storage: ActiveVftStorage, +} + +enum ActiveVftStorage { + Empty, + BTree { + balances: BTreeMap, + allowances: BTreeMap<(ActorId, ActorId), U256>, + }, + HashMap { + balances: HashMap, + allowances: HashMap<(ActorId, ActorId), U256>, + }, + SailsFixed { + balances: SailsFixedBalanceMap, + allowances: SailsFixedAllowanceMap, + }, + SailsStatic { + balances: SailsStaticBalanceMap, + allowances: SailsStaticAllowanceMap, + }, +} + +impl VftStressState { + fn new() -> Self { + Self { + storage: ActiveVftStorage::Empty, + } + } + + #[cfg(feature = "wasm-profile")] + fn bench_noop(&mut self) -> bool { + true + } + + #[cfg(feature = "wasm-profile")] + fn bench_echo_vft_args( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + ) -> bool { + seed > 0 + && matches!( + backend, + VftStorageBackend::BTree + | VftStorageBackend::HashMap + | VftStorageBackend::SailsFixed + | VftStorageBackend::SailsStatic + | VftStorageBackend::SailsStaticFast + ) + && matches!(op, VftTransferOp::Transfer | VftTransferOp::TransferFrom) + } + + fn prepare_vft(&mut self, backend: VftStorageBackend, len: u32) -> VftTransferResult { + assert!((len as usize) < FIXED_CAPACITY); + self.clear_vft(backend); + + for seed in 1..=len { + self.vft_insert_balance(backend, actor_for_seed(seed), vft_balance_for_seed(seed)); + let owner = actor_for_seed(seed); + let spender = vft_spender_for_seed(seed); + self.vft_insert_allowance(backend, owner, spender, vft_allowance_for_seed(seed)); + } + + VftTransferResult { + from_balance: vft_balance_for_seed(len), + to_balance: U256::zero(), + allowance: U256::zero(), + balance_len: self.vft_balance_len(backend), + allowance_len: self.vft_allowance_len(backend), + transferred: false, + } + } + + fn bench_vft_transfer( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + ) -> VftTransferResult { + self.bench_vft_transfer_with_to_seed(backend, op, seed, vft_recipient_seed(seed)) + } + + fn bench_vft_transfer_fresh_bool(&mut self, backend: VftStorageBackend, seed: u32) -> bool { + let len = self.vft_balance_len(backend); + assert!(len > 1); + let to_seed = len + .checked_add(seed) + .and_then(|value| value.checked_add(1)) + .expect("fresh VFT recipient seed overflow"); + self.apply_vft_transfer_bool(backend, VftTransferOp::Transfer, seed, to_seed) + } + + fn bench_vft_approve_bool( + &mut self, + backend: VftStorageBackend, + owner_seed: u32, + spender_seed: u32, + ) -> bool { + let owner = actor_for_seed(owner_seed); + let spender = vft_spender_for_seed(spender_seed); + if owner == spender { + return false; + } + + self.vft_insert_allowance( + backend, + owner, + spender, + vft_allowance_for_seed(spender_seed), + ); + true + } + + #[cfg(feature = "wasm-profile")] + fn bench_vft_transfer_profile( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + ) -> VftProfileResult { + self.bench_vft_transfer_profile_with_to_seed(backend, op, seed, vft_recipient_seed(seed)) + } + + #[cfg(feature = "wasm-profile")] + fn bench_vft_transfer_fresh_profile( + &mut self, + backend: VftStorageBackend, + seed: u32, + ) -> VftProfileResult { + let len = self.vft_balance_len(backend); + assert!(len > 1); + let to_seed = len + .checked_add(seed) + .and_then(|value| value.checked_add(1)) + .expect("fresh VFT recipient seed overflow"); + self.bench_vft_transfer_profile_with_to_seed(backend, VftTransferOp::Transfer, seed, to_seed) + } + + #[cfg(feature = "wasm-profile")] + fn bench_vft_approve_profile( + &mut self, + backend: VftStorageBackend, + owner_seed: u32, + spender_seed: u32, + ) -> VftProfileResult { + let mut phases = VftPhaseRecorder::new(); + let owner = actor_for_seed(owner_seed); + let spender = vft_spender_for_seed(spender_seed); + let value = vft_allowance_for_seed(spender_seed); + phases.checkpoint(VftPhase::KeyDerive); + + let approved = if owner == spender { + false + } else { + self.vft_insert_allowance(backend, owner, spender, value); + true + }; + phases.checkpoint(VftPhase::AllowancePut); + + let result = VftTransferResult { + from_balance: U256::zero(), + to_balance: U256::zero(), + allowance: self + .vft_get_allowance(backend, &owner, &spender) + .unwrap_or_else(U256::zero), + balance_len: self.vft_balance_len(backend), + allowance_len: self.vft_allowance_len(backend), + transferred: approved, + }; + phases.checkpoint(VftPhase::ResultBuild); + + VftProfileResult { + result, + phases: phases.finish(), + } + } + + fn clear_vft(&mut self, backend: VftStorageBackend) { + self.storage = match backend { + VftStorageBackend::BTree => ActiveVftStorage::BTree { + balances: BTreeMap::new(), + allowances: BTreeMap::new(), + }, + VftStorageBackend::HashMap => ActiveVftStorage::HashMap { + balances: HashMap::new(), + allowances: HashMap::new(), + }, + VftStorageBackend::SailsFixed => ActiveVftStorage::SailsFixed { + balances: SailsFixedBalanceMap::new(), + allowances: SailsFixedAllowanceMap::new(), + }, + VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast => { + ActiveVftStorage::SailsStatic { + balances: SailsStaticBalanceMap::new(), + allowances: SailsStaticAllowanceMap::new(), + } + } + }; + } + + fn vft_balance_len(&self, backend: VftStorageBackend) -> u32 { + match (backend, &self.storage) { + (VftStorageBackend::BTree, ActiveVftStorage::BTree { balances, .. }) => { + balances.len() as u32 + } + (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { balances, .. }) => { + balances.len() as u32 + } + (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { balances, .. }) => { + balances.len() as u32 + } + ( + VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, + ActiveVftStorage::SailsStatic { balances, .. }, + ) => balances.len(), + _ => 0, + } + } + + fn vft_allowance_len(&self, backend: VftStorageBackend) -> u32 { + match (backend, &self.storage) { + (VftStorageBackend::BTree, ActiveVftStorage::BTree { allowances, .. }) => { + allowances.len() as u32 + } + (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { allowances, .. }) => { + allowances.len() as u32 + } + (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { allowances, .. }) => { + allowances.len() as u32 + } + ( + VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, + ActiveVftStorage::SailsStatic { allowances, .. }, + ) => allowances.len(), + _ => 0, + } + } + + fn vft_get_balance(&self, backend: VftStorageBackend, key: &ActorId) -> Option { + match (backend, &self.storage) { + (VftStorageBackend::BTree, ActiveVftStorage::BTree { balances, .. }) => { + balances.get(key).copied() + } + (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { balances, .. }) => { + balances.get(key).copied() + } + (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { balances, .. }) => { + balances + .get_actor_u256(key) + .expect("sails-storage fixed VFT balance map failed") + } + ( + VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, + ActiveVftStorage::SailsStatic { balances, .. }, + ) => balances + .get(key) + .expect("sails-storage static VFT balance map failed"), + _ => None, + } + } + + fn vft_get_allowance( + &self, + backend: VftStorageBackend, + owner: &ActorId, + spender: &ActorId, + ) -> Option { + match (backend, &self.storage) { + (VftStorageBackend::BTree, ActiveVftStorage::BTree { allowances, .. }) => { + allowances.get(&(*owner, *spender)).copied() + } + (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { allowances, .. }) => { + allowances.get(&(*owner, *spender)).copied() + } + (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { allowances, .. }) => { + allowances + .get_allowance_u256(owner, spender) + .expect("sails-storage fixed VFT allowance map failed") + } + ( + VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, + ActiveVftStorage::SailsStatic { allowances, .. }, + ) => allowances + .get(owner, spender) + .expect("sails-storage static VFT allowance map failed"), + _ => None, + } + } + + fn vft_insert_balance( + &mut self, + backend: VftStorageBackend, + key: ActorId, + value: U256, + ) -> Option { + match (backend, &mut self.storage) { + (VftStorageBackend::BTree, ActiveVftStorage::BTree { balances, .. }) => { + balances.insert(key, value) + } + (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { balances, .. }) => { + balances.insert(key, value) + } + (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { balances, .. }) => { + balances + .insert_actor_u256(key, value) + .expect("sails-storage fixed VFT balance map failed") + } + ( + VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, + ActiveVftStorage::SailsStatic { balances, .. }, + ) => balances + .try_insert(key, value) + .expect("sails-storage static VFT balance map failed"), + _ => panic!("VFT storage backend is not prepared"), + } + } + + fn vft_insert_allowance( + &mut self, + backend: VftStorageBackend, + owner: ActorId, + spender: ActorId, + value: U256, + ) -> Option { + match (backend, &mut self.storage) { + (VftStorageBackend::BTree, ActiveVftStorage::BTree { allowances, .. }) => { + allowances.insert((owner, spender), value) + } + (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { allowances, .. }) => { + allowances.insert((owner, spender), value) + } + (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { allowances, .. }) => { + allowances + .insert_allowance_u256(owner, spender, value) + .expect("sails-storage fixed VFT allowance map failed") + } + ( + VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, + ActiveVftStorage::SailsStatic { allowances, .. }, + ) => allowances + .try_insert(owner, spender, value) + .expect("sails-storage static VFT allowance map failed"), + _ => panic!("VFT storage backend is not prepared"), + } + } + + fn apply_vft_transfer_bool( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + to_seed: u32, + ) -> bool { + if matches!( + backend, + VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast + ) { + return self.bench_vft_static_transfer_bool_with_to_seed( + op, + seed, + to_seed, + matches!(backend, VftStorageBackend::SailsStaticFast), + ); + } + + self.bench_vft_transfer_with_to_seed(backend, op, seed, to_seed) + .transferred + } + + #[cfg(feature = "wasm-profile")] + fn bench_vft_transfer_profile_with_to_seed( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + to_seed: u32, + ) -> VftProfileResult { + if matches!( + backend, + VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast + ) { + return self.bench_vft_static_transfer_profile_with_to_seed( + op, + seed, + to_seed, + matches!(backend, VftStorageBackend::SailsStaticFast), + ); + } + + let mut phases = VftPhaseRecorder::new(); + let amount = vft_transfer_amount(seed); + let from = actor_for_seed(seed); + let to = actor_for_seed(to_seed); + let spender = vft_spender_for_seed(seed); + phases.checkpoint(VftPhase::KeyDerive); + + let mut allowance_key = None; + let mut transferred = false; + + if matches!(op, VftTransferOp::TransferFrom) { + allowance_key = Some(spender); + let allowance = self + .vft_get_allowance(backend, &from, &spender) + .unwrap_or_else(U256::zero); + phases.checkpoint(VftPhase::AllowanceGet); + if allowance < amount { + let result = self.vft_result(backend, &from, &to, allowance_key.as_ref(), false); + phases.checkpoint(VftPhase::ResultBuild); + return VftProfileResult { + result, + phases: phases.finish(), + }; + } + self.vft_insert_allowance(backend, from, spender, allowance - amount); + phases.checkpoint(VftPhase::AllowancePut); + } + + let from_balance = self + .vft_get_balance(backend, &from) + .unwrap_or_else(U256::zero); + phases.checkpoint(VftPhase::BalanceGetFrom); + if from_balance >= amount { + let to_balance = self.vft_get_balance(backend, &to).unwrap_or_else(U256::zero); + phases.checkpoint(VftPhase::BalanceGetTo); + self.vft_insert_balance(backend, from, from_balance - amount); + phases.checkpoint(VftPhase::BalancePutFrom); + self.vft_insert_balance(backend, to, to_balance + amount); + phases.checkpoint(VftPhase::BalancePutTo); + transferred = true; + } + + let result = self.vft_result(backend, &from, &to, allowance_key.as_ref(), transferred); + phases.checkpoint(VftPhase::ResultBuild); + VftProfileResult { + result, + phases: phases.finish(), + } + } + + fn bench_vft_transfer_with_to_seed( + &mut self, + backend: VftStorageBackend, + op: VftTransferOp, + seed: u32, + to_seed: u32, + ) -> VftTransferResult { + if matches!( + backend, + VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast + ) { + return self.bench_vft_static_transfer_with_to_seed( + op, + seed, + to_seed, + matches!(backend, VftStorageBackend::SailsStaticFast), + ); + } + + let amount = vft_transfer_amount(seed); + let from = actor_for_seed(seed); + let to = actor_for_seed(to_seed); + let spender = vft_spender_for_seed(seed); + + if matches!(op, VftTransferOp::TransferFrom) { + let allowance = self + .vft_get_allowance(backend, &from, &spender) + .unwrap_or_else(U256::zero); + if allowance < amount { + return self.vft_result(backend, &from, &to, Some(&spender), false); + } + self.vft_insert_allowance(backend, from, spender, allowance - amount); + } + + let from_balance = self + .vft_get_balance(backend, &from) + .unwrap_or_else(U256::zero); + if from_balance < amount { + let allowance_key = matches!(op, VftTransferOp::TransferFrom).then_some(&spender); + return self.vft_result(backend, &from, &to, allowance_key, false); + } + + let to_balance = self + .vft_get_balance(backend, &to) + .unwrap_or_else(U256::zero); + self.vft_insert_balance(backend, from, from_balance - amount); + self.vft_insert_balance(backend, to, to_balance + amount); + let allowance_key = matches!(op, VftTransferOp::TransferFrom).then_some(&spender); + self.vft_result(backend, &from, &to, allowance_key, true) + } + + fn bench_vft_static_transfer_with_to_seed( + &mut self, + op: VftTransferOp, + seed: u32, + to_seed: u32, + unchecked: bool, + ) -> VftTransferResult { + let amount = vft_transfer_amount(seed); + let from = actor_for_seed(seed); + let to = actor_for_seed(to_seed); + let spender = vft_spender_for_seed(seed); + let ActiveVftStorage::SailsStatic { + balances, + allowances, + } = &mut self.storage + else { + panic!("VFT static storage backend is not prepared"); + }; + + match op { + VftTransferOp::Transfer => { + let outcome = balances + .transfer(from, to, amount, unchecked) + .expect("sails-storage static VFT balance transfer failed"); + match outcome { + Some(outcome) => VftTransferResult { + from_balance: outcome.from_balance, + to_balance: outcome.to_balance, + allowance: U256::zero(), + balance_len: balances.len(), + allowance_len: allowances.len(), + transferred: true, + }, + None => VftTransferResult { + from_balance: balances.get(&from).unwrap_or(None).unwrap_or_else(U256::zero), + to_balance: balances.get(&to).unwrap_or(None).unwrap_or_else(U256::zero), + allowance: U256::zero(), + balance_len: balances.len(), + allowance_len: allowances.len(), + transferred: false, + }, + } + } + VftTransferOp::TransferFrom => { + let outcome = balances + .transfer_from(allowances, from, spender, to, amount, unchecked) + .expect("sails-storage static VFT transfer-from failed"); + match outcome { + Some(outcome) => VftTransferResult { + from_balance: outcome.from_balance, + to_balance: outcome.to_balance, + allowance: outcome.allowance, + balance_len: balances.len(), + allowance_len: allowances.len(), + transferred: true, + }, + None => VftTransferResult { + from_balance: balances.get(&from).unwrap_or(None).unwrap_or_else(U256::zero), + to_balance: balances.get(&to).unwrap_or(None).unwrap_or_else(U256::zero), + allowance: allowances + .get(&from, &spender) + .unwrap_or(None) + .unwrap_or_else(U256::zero), + balance_len: balances.len(), + allowance_len: allowances.len(), + transferred: false, + }, + } + } + } + } + + fn bench_vft_static_transfer_bool_with_to_seed( + &mut self, + op: VftTransferOp, + seed: u32, + to_seed: u32, + unchecked: bool, + ) -> bool { + let amount = vft_transfer_amount(seed); + let from = actor_for_seed(seed); + let to = actor_for_seed(to_seed); + let spender = vft_spender_for_seed(seed); + let ActiveVftStorage::SailsStatic { + balances, + allowances, + } = &mut self.storage + else { + panic!("VFT static storage backend is not prepared"); + }; + + match op { + VftTransferOp::Transfer => balances + .transfer(from, to, amount, unchecked) + .expect("sails-storage static VFT balance transfer failed") + .is_some(), + VftTransferOp::TransferFrom => balances + .transfer_from(allowances, from, spender, to, amount, unchecked) + .expect("sails-storage static VFT transfer-from failed") + .is_some(), + } + } + + #[cfg(feature = "wasm-profile")] + fn bench_vft_static_transfer_profile_with_to_seed( + &mut self, + op: VftTransferOp, + seed: u32, + to_seed: u32, + unchecked: bool, + ) -> VftProfileResult { + let mut phases = VftPhaseRecorder::new(); + let amount = vft_transfer_amount(seed); + let from = actor_for_seed(seed); + let to = actor_for_seed(to_seed); + let spender = vft_spender_for_seed(seed); + phases.checkpoint(VftPhase::KeyDerive); + + let ActiveVftStorage::SailsStatic { + balances, + allowances, + } = &mut self.storage + else { + panic!("VFT static storage backend is not prepared"); + }; + + let result = match op { + VftTransferOp::Transfer => { + let outcome = balances + .transfer(from, to, amount, unchecked) + .expect("sails-storage static VFT balance transfer failed"); + phases.checkpoint(VftPhase::BalanceTransfer); + match outcome { + Some(outcome) => VftTransferResult { + from_balance: outcome.from_balance, + to_balance: outcome.to_balance, + allowance: U256::zero(), + balance_len: balances.len(), + allowance_len: allowances.len(), + transferred: true, + }, + None => VftTransferResult { + from_balance: balances.get(&from).unwrap_or(None).unwrap_or_else(U256::zero), + to_balance: balances.get(&to).unwrap_or(None).unwrap_or_else(U256::zero), + allowance: U256::zero(), + balance_len: balances.len(), + allowance_len: allowances.len(), + transferred: false, + }, + } + } + VftTransferOp::TransferFrom => { + let outcome = balances + .transfer_from(allowances, from, spender, to, amount, unchecked) + .expect("sails-storage static VFT transfer-from failed"); + phases.checkpoint(VftPhase::BalanceTransferFrom); + match outcome { + Some(outcome) => VftTransferResult { + from_balance: outcome.from_balance, + to_balance: outcome.to_balance, + allowance: outcome.allowance, + balance_len: balances.len(), + allowance_len: allowances.len(), + transferred: true, + }, + None => VftTransferResult { + from_balance: balances.get(&from).unwrap_or(None).unwrap_or_else(U256::zero), + to_balance: balances.get(&to).unwrap_or(None).unwrap_or_else(U256::zero), + allowance: allowances + .get(&from, &spender) + .unwrap_or(None) + .unwrap_or_else(U256::zero), + balance_len: balances.len(), + allowance_len: allowances.len(), + transferred: false, + }, + } + } + }; + phases.checkpoint(VftPhase::ResultBuild); + + VftProfileResult { + result, + phases: phases.finish(), + } + } + + fn vft_result( + &self, + backend: VftStorageBackend, + from: &ActorId, + to: &ActorId, + spender: Option<&ActorId>, + transferred: bool, + ) -> VftTransferResult { + VftTransferResult { + from_balance: self + .vft_get_balance(backend, from) + .unwrap_or_else(U256::zero), + to_balance: self.vft_get_balance(backend, to).unwrap_or_else(U256::zero), + allowance: spender + .and_then(|spender| self.vft_get_allowance(backend, from, spender)) + .unwrap_or_else(U256::zero), + balance_len: self.vft_balance_len(backend), + allowance_len: self.vft_allowance_len(backend), + transferred, + } + } +} + +#[cfg(feature = "wasm-profile")] +struct VftPhaseRecorder { + previous_gas: u64, + phases: Vec, +} + +#[cfg(feature = "wasm-profile")] +impl VftPhaseRecorder { + fn new() -> Self { + let overhead = gas_probe_overhead(); + let previous_gas = gas_available(); + let mut phases = Vec::with_capacity(8); + if overhead > 0 { + phases.push(VftPhaseGas { + phase: VftPhase::ProbeOverhead, + gas: overhead, + }); + } + Self { + previous_gas, + phases, + } + } + + fn checkpoint(&mut self, phase: VftPhase) { + let current = gas_available(); + self.phases.push(VftPhaseGas { + phase, + gas: self.previous_gas.saturating_sub(current), + }); + self.previous_gas = current; + } + + fn finish(self) -> Vec { + self.phases + } +} + +#[cfg(feature = "wasm-profile")] +fn gas_probe_overhead() -> u64 { + let before = gas_available(); + let after = gas_available(); + before.saturating_sub(after) +} + +#[cfg(target_arch = "wasm32")] +#[cfg(feature = "wasm-profile")] +fn gas_available() -> u64 { + Syscall::gas_available() +} + +#[cfg(not(target_arch = "wasm32"))] +#[cfg(feature = "wasm-profile")] +fn gas_available() -> u64 { + 0 +} + +struct SailsStaticBalanceMap { + #[cfg(not(target_arch = "wasm32"))] + bytes: [u8; static_storage::SAILS_STATIC_BALANCES_BYTES], + len: u32, +} + +impl SailsStaticBalanceMap { + fn new() -> Self { + let map = Self { + #[cfg(not(target_arch = "wasm32"))] + bytes: [0; static_storage::SAILS_STATIC_BALANCES_BYTES], + len: 0, + }; + #[cfg(target_arch = "wasm32")] + map.table() + .clear() + .expect("sails static balance table can be cleared"); + map + } + + fn len(&self) -> u32 { + self.len + } + + fn get(&self, key: &ActorId) -> Result, sails_storage::TableError> { + self.table().get_actor_u256(key) + } + + fn try_insert( + &mut self, + key: ActorId, + value: U256, + ) -> Result, sails_storage::TableError> { + let previous = self.table_mut().insert_actor_u256(key, value)?; + if previous.is_none() { + self.len += 1; + } + + Ok(previous) + } + + fn transfer( + &mut self, + from: ActorId, + to: ActorId, + amount: U256, + unchecked: bool, + ) -> Result, sails_storage::TableError> + { + let outcome = if unchecked { + unsafe { + self.table_mut() + .transfer_actor_u256_nonzero_distinct_unchecked(from, to, amount)? + } + } else { + self.table_mut().transfer_actor_u256(from, to, amount)? + }; + if outcome.map_or(false, |outcome| outcome.inserted_to) { + self.len += 1; + } + Ok(outcome) + } + + fn transfer_from( + &mut self, + allowances: &SailsStaticAllowanceMap, + owner: ActorId, + spender: ActorId, + to: ActorId, + amount: U256, + unchecked: bool, + ) -> Result, sails_storage::TableError> + { + let outcome = if unchecked { + unsafe { + self.table_mut() + .transfer_actor_u256_from_nonzero_distinct_unchecked( + &allowances.table(), + owner, + spender, + to, + amount, + )? + } + } else { + self.table_mut().transfer_actor_u256_from( + &allowances.table(), + owner, + spender, + to, + amount, + )? + }; + if outcome.map_or(false, |outcome| outcome.inserted_to) { + self.len += 1; + } + Ok(outcome) + } + + fn table(&self) -> SailsStaticBalanceTable { + #[cfg(target_arch = "wasm32")] + let base = static_storage::SAILS_STATIC_BALANCES_BASE; + #[cfg(not(target_arch = "wasm32"))] + let base = self.bytes.as_ptr() as usize; + + unsafe { + SailsStaticBalanceTable::new(base, static_storage::SAILS_STATIC_BALANCES_SLOTS) + .expect("sails static balance layout is valid") + } + } + + fn table_mut(&mut self) -> SailsStaticBalanceTable { + #[cfg(target_arch = "wasm32")] + let base = static_storage::SAILS_STATIC_BALANCES_BASE; + #[cfg(not(target_arch = "wasm32"))] + let base = self.bytes.as_mut_ptr() as usize; + + unsafe { + SailsStaticBalanceTable::new(base, static_storage::SAILS_STATIC_BALANCES_SLOTS) + .expect("sails static balance layout is valid") + } + } +} + +struct SailsStaticAllowanceMap { + #[cfg(not(target_arch = "wasm32"))] + bytes: [u8; static_storage::SAILS_STATIC_ALLOWANCES_BYTES], + len: u32, +} + +impl SailsStaticAllowanceMap { + fn new() -> Self { + let map = Self { + #[cfg(not(target_arch = "wasm32"))] + bytes: [0; static_storage::SAILS_STATIC_ALLOWANCES_BYTES], + len: 0, + }; + #[cfg(target_arch = "wasm32")] + map.table() + .clear() + .expect("sails static allowance table can be cleared"); + map + } + + fn len(&self) -> u32 { + self.len + } + + fn get( + &self, + owner: &ActorId, + spender: &ActorId, + ) -> Result, sails_storage::TableError> { + self.table().get_allowance_u256(owner, spender) + } + + fn try_insert( + &mut self, + owner: ActorId, + spender: ActorId, + value: U256, + ) -> Result, sails_storage::TableError> { + let previous = self + .table_mut() + .insert_allowance_u256(owner, spender, value)?; + if previous.is_none() { + self.len += 1; + } + + Ok(previous) + } + + fn table(&self) -> SailsStaticAllowanceTable { + #[cfg(target_arch = "wasm32")] + let base = static_storage::SAILS_STATIC_ALLOWANCES_BASE; + #[cfg(not(target_arch = "wasm32"))] + let base = self.bytes.as_ptr() as usize; + + unsafe { + SailsStaticAllowanceTable::new(base, static_storage::SAILS_STATIC_ALLOWANCES_SLOTS) + .expect("sails static allowance layout is valid") + } + } + + fn table_mut(&mut self) -> SailsStaticAllowanceTable { + #[cfg(target_arch = "wasm32")] + let base = static_storage::SAILS_STATIC_ALLOWANCES_BASE; + #[cfg(not(target_arch = "wasm32"))] + let base = self.bytes.as_mut_ptr() as usize; + + unsafe { + SailsStaticAllowanceTable::new(base, static_storage::SAILS_STATIC_ALLOWANCES_SLOTS) + .expect("sails static allowance layout is valid") + } + } +} + +fn actor_for_seed(seed: u32) -> ActorId { + ActorId::from(seed as u64 + 1) +} + +fn vft_balance_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 1_000_000) +} + +fn vft_allowance_for_seed(seed: u32) -> U256 { + U256::from(seed as u64 + 10_000) +} + +fn vft_transfer_amount(seed: u32) -> U256 { + U256::from((seed % 7) as u64 + 1) +} + +fn vft_recipient_seed(seed: u32) -> u32 { + seed.saturating_add(10_000_000) +} + +fn vft_spender_for_seed(seed: u32) -> ActorId { + ActorId::from(seed as u64 + 20_000_000) +} diff --git a/docs/goals/storage-primitive-selection/ATTEMPTS.md b/docs/goals/storage-primitive-selection/ATTEMPTS.md new file mode 100644 index 000000000..2219ce590 --- /dev/null +++ b/docs/goals/storage-primitive-selection/ATTEMPTS.md @@ -0,0 +1,14 @@ +# Storage Primitive Selection Attempts + +| Date | Candidate | Command | Output Path | Result | Decision | +| --- | --- | --- | --- | --- | --- | +| 2026-05-19 | Existing storage stress evidence | `cargo test --release --manifest-path=benchmarks/Cargo.toml storage_stress_bench -- --nocapture` | `/Users/ukintvs/Documents/projects/gear-dlmalloc/docs/benchmarks/sails-storage-maps-2026-05-19.md` | `sails_static` wins steady-state balance and allowance hot ops at load 1024, but prepare can regress. | Strong enough for hot-op candidate, not enough alone for transfer-shaped recommendation. | +| 2026-05-19 | Existing aggregator tracker evidence | `cargo test --release --manifest-path=benchmarks/Cargo.toml aggregator_tracker_bench -- --nocapture` | `/Users/ukintvs/Documents/projects/gear-dlmalloc/docs/benchmarks/sails-storage-maps-2026-05-19.md` | `SailsFixed` beats `BTree` for direct lookup/update but loses badly on listing. | Recommend only for bounded direct lookup, not list-heavy state. | +| 2026-05-19 | Existing million static evidence | `cargo test --release --manifest-path=benchmarks/Cargo.toml storage_million_static_bench -- --nocapture` | `/Users/ukintvs/Documents/projects/gear-dlmalloc/docs/benchmarks/sails-storage-maps-2026-05-19.md` | WAT/page-local/control actor layouts each win specific rows; grouped variants are generally weak. | Keep specialized layouts experimental until transfer-shaped results justify promotion. | +| 2026-05-19 | VFT transfer proxy, small/medium loads | `cargo test --release --manifest-path=benchmarks/Cargo.toml vft_storage_transfer_bench -- --nocapture` | `target/storage-primitive-selection/vft-transfer-2026-05-19/bench_data.json` | `sails_static` beats best dynamic baseline at 256/1024, but not consistently at 16/64/128. | Use as transfer proxy only; not sufficient for million-address VFT default. | +| 2026-05-19 | Million-capacity VFT dynamic baseline | `cargo test --release --manifest-path=benchmarks/Cargo.toml vft_million_transfer_bench -- --nocapture` before moving dynamic baselines behind `#[ignore]` | `target/storage-primitive-selection/vft-transfer-2026-05-19/vft_million_dynamic_baseline_failed.log` | `BTreeMap` failed during 1M prepare around block 1609, before the static candidates ran. | Keep as feasibility evidence; dynamic 1M baseline is manual/ignored, not a green gate. | +| 2026-05-19 | Million-capacity VFT static transfers | `cargo test --release --manifest-path=benchmarks/Cargo.toml vft_million_transfer_bench -- --nocapture` | `target/storage-primitive-selection/vft-transfer-2026-05-19/bench_data.json` | `wat_actor_balance` is best hot transfer and transfer_from at 1,000,000 prepared balances/allowances; generic static has best prepare. | Prefer WAT-shaped token maps for VFT hot path; keep page-local/control as research until they show a decisive win. | +| 2026-05-19 | WAT transfer helper returning updated values | `cargo test --release --manifest-path=benchmarks/Cargo.toml vft_million_transfer_bench -- --nocapture` | `target/storage-primitive-selection/vft-transfer-optimized-2026-05-19/after-return-values.json` | `wat_actor_balance` improved to `1,246,778,561` transfer gas and `1,423,933,264` transfer-from gas in that build. | Useful but incomplete: transfer-from still needed a second allowance probe to stay correct. | +| 2026-05-19 | Combined WAT transfer-from helper | `cargo test --release --manifest-path=benchmarks/Cargo.toml vft_million_transfer_bench -- --nocapture` | `target/storage-primitive-selection/vft-transfer-optimized-2026-05-19/after-combined-transfer-from.json` | Final optimized WAT rows: transfer `1,253,057,422`, transfer-from `1,417,910,611`. Transfer-from is `1.97%` cheaper than the original WAT baseline and `4.21%` cheaper than generic static in the same run. | Keep `StaticActorIdU256Map` + `StaticAllowanceU256Map` as the 1M VFT default and expose transfer-shaped helpers. | +| 2026-05-19 | Allowance hash combine tuned for correlated pairs | `cargo test --release --manifest-path=benchmarks/Cargo.toml vft_million_transfer_bench -- --nocapture` | `target/storage-primitive-selection/vft-transfer-optimized-2026-05-19/final-allowance-hash.json` | WAT rows: prepare `322,774,801,460,398`, transfer `1,252,451,734`, transfer-from `1,416,295,021`. Transfer-from is `2.08%` cheaper than the original WAT baseline and `4.34%` cheaper than generic static in the same run. | Keep the allowance hash change; it is small but measurable and preserves the WAT-shaped table layout. | +| 2026-05-19 | Byte-level `U256` arithmetic inside transfer helpers | `cargo test --release --manifest-path=benchmarks/Cargo.toml vft_million_transfer_bench -- --nocapture` | `target/storage-primitive-selection/vft-transfer-optimized-2026-05-19/after-byte-arithmetic.json` | Regressed WAT transfer by `0.47%` and transfer-from by `0.37%` versus the hash-tuned build. | Backed out; `primitive_types::U256` arithmetic compiled better for this path. | diff --git a/docs/goals/storage-primitive-selection/CONTROL.md b/docs/goals/storage-primitive-selection/CONTROL.md new file mode 100644 index 000000000..753e1930f --- /dev/null +++ b/docs/goals/storage-primitive-selection/CONTROL.md @@ -0,0 +1,20 @@ +# Storage Primitive Selection Control + +Target workload: VFT token transfers on Gear/Sails. + +Capacity target: token address tables must be planned for 1-2 million entries. +Small fixed maps are not VFT defaults. + +Promotion rule: recommend a primitive only after a consistent benchmark win over +`HashMap` or `BTreeMap` on transfer-critical operations, plus million-capacity +static-map evidence. + +Non-superior variants: keep internal, benchmark-only, or experimental. + +Listing in default score: no, but list regressions must be documented. + +Million-entry loop: do not run on every iteration; reserve it for final evidence +or layout-specific hypotheses. + +Pivot approval: required before promoting control-byte, page-local, or grouped +layouts as default public primitives. diff --git a/docs/goals/storage-primitive-selection/NOTES.md b/docs/goals/storage-primitive-selection/NOTES.md new file mode 100644 index 000000000..57af3b546 --- /dev/null +++ b/docs/goals/storage-primitive-selection/NOTES.md @@ -0,0 +1,101 @@ +# Storage Primitive Selection Notes + +## 2026-05-19 Dirty Baseline + +`git status --short` before this goal work showed existing modifications in: + +- `Cargo.lock`, `Cargo.toml` +- `benchmarks/Cargo.toml`, `benchmarks/bench_data.json`, + `benchmarks/build.rs`, `benchmarks/src/benchmarks.rs`, + `benchmarks/src/clients.rs`, `benchmarks/src/entities.rs`, + `benchmarks/src/lib.rs`, `benchmarks/storage-stress/Cargo.toml` +- `docs/storage-static-maps-local-notes.md` +- `examples/aggregator/app/Cargo.toml`, + `examples/aggregator/app/src/lib.rs`, + `examples/aggregator/app/src/msg_tracker.rs`, + `examples/aggregator/app/tests/gtest.rs`, + `examples/aggregator/client/aggregator_client.idl`, + `examples/aggregator/client/src/aggregator_client.rs` +- `rs/src/build.rs`, `rs/storage/Cargo.toml`, `rs/storage/src/lib.rs` +- untracked `.claude/`, `TODOS.md`, + `benchmarks/idls/storage_million_program.idl`, + `benchmarks/src/storage_million_program.rs`, + `benchmarks/storage-million/` + +These are treated as user/work-in-progress changes. Goal edits should be narrow +and avoid reverting them. + +## 2026-05-19 Inventory + +Current candidates found in `rs/storage` and benchmarks: + +- `FixedOpenAddressMap` +- Gear aliases: `FixedBalanceMap`, `FixedAllowanceMap`, + `StaticBalanceTable`, `StaticAllowanceTable` +- `StaticOpenAddressTable` +- `StaticActorIdU256Map`, `StaticAllowanceU256Map` +- `StaticControlActorIdU256Map` +- `StaticPageLocalActorIdU256Map` +- `StaticGroupedControlActorIdU256Map` + +Benchmark entrypoints currently cover isolated storage ops, aggregator tracker +direct lookup/listing, and million-entry static actor maps. A single-message VFT +transfer path is still needed for the final recommendation. + +## 2026-05-19 Capacity Correction + +VFT token address capacity should be large: target 1-2 million entries. This +means `FixedOpenAddressMap` can only be recommended for bounded internal state, +not as the token-address default. + +The benchmark suite now has two transfer-shaped paths: + +- `vft_storage_transfer_bench`: compares `BTreeMap`, `HashMap`, + `SailsFixed`, and `SailsStatic` at small/medium loads. +- `vft_million_transfer_bench`: compares million-capacity static layouts at + 1,000,000 prepared balances and allowances. +- `vft_million_dynamic_baseline_bench`: ignored/manual feasibility check for + `BTreeMap` and `HashMap` at 1,000,000 prepared balances and allowances. + The first direct 1M `BTreeMap` run failed during prepare around block 1609, + so this is not a default green gate. + +## 2026-05-19 Implementation Correction + +The goal is not only to select a map; it is to develop a better 1M-entry VFT +map path. The current implementation work is therefore centered on +`StaticActorIdU256Map<21>` plus `StaticAllowanceU256Map<21>`. + +Implemented hot-path helpers: + +- `transfer_actor_u256`: one balance lookup per touched actor, writes both + balances, returns updated balances. +- `transfer_actor_u256_from`: validates allowance and balances before any + write, then writes allowance plus balances and returns all updated values. + +Saved benchmark artifacts: + +- `target/storage-primitive-selection/vft-transfer-optimized-2026-05-19/before.json` +- `target/storage-primitive-selection/vft-transfer-optimized-2026-05-19/after-return-values.json` +- `target/storage-primitive-selection/vft-transfer-optimized-2026-05-19/after-combined-transfer-from.json` +- `target/storage-primitive-selection/vft-transfer-optimized-2026-05-19/final-allowance-hash.json` + +## 2026-05-19 WAT Follow-Up + +The original `../vara-ft-wat` hash-pair shape is not safe to copy blindly for +the benchmark's correlated `(owner_seed, spender_seed)` pattern. A quick +distribution check at 1,000,000 entries in 2^21 slots showed: + +- old rotate/xor combine: average probe `2.50`, max probe `82`; +- literal WAT pair fold: average probe `3272.26`, max probe `26120`; +- two-multiplier additive combine: average probe `1.00`, max probe `1`. + +Kept: + +- direct 32-bit lane folding with unaligned loads; +- allowance hash as `owner_fold * 0x9E37_79B9 + spender_fold * 0x85EB_CA6B`; +- combined balance/allowance transfer-from helper. + +Rejected: + +- byte-level `U256` add/sub helpers. They passed correctness tests but + regressed the release VFT rows, so the code was removed. diff --git a/docs/goals/storage-primitive-selection/PLAN.md b/docs/goals/storage-primitive-selection/PLAN.md new file mode 100644 index 000000000..6d901e8ab --- /dev/null +++ b/docs/goals/storage-primitive-selection/PLAN.md @@ -0,0 +1,35 @@ +# Storage Primitive Selection Plan + +- [x] Record dirty worktree and current goal controls. +- [x] Inventory current storage primitives and benchmark entrypoints. +- [x] Add or verify a transfer-shaped VFT benchmark. +- [x] Add a million-capacity VFT transfer benchmark. +- [x] Run focused storage feedback checks and save output. +- [x] Compare candidates against `HashMap`/`BTreeMap` and high-capacity static layouts on the scorecard. +- [x] Implement WAT-shaped VFT transfer helpers for 1M-entry balances and allowances. +- [x] Tune the allowance hash for correlated 1M VFT owner/spender pairs. +- [x] Test and reject byte-level `U256` arithmetic when it regressed release gas. +- [x] Rerun the release million-entry VFT benchmark and save optimized results. +- [x] Write `docs/storage-primitive-selection.md`. +- [x] Audit final docs, public/internal status, and benchmark evidence. + +Current best candidates: + +- Safe bounded state: `FixedOpenAddressMap` and Gear aliases, but not as VFT + token-address defaults. +- Static token hot path: `StaticActorIdU256Map`/`StaticAllowanceU256Map` for + 1-2 million-entry VFT tables, with `StaticOpenAddressTable` as the generic + fallback. +- Research-only until proven: control-byte, page-local, and grouped actor maps. + +Next command: + +```bash +git diff --check +``` + +Next optimization target: + +- Use the WAT-shaped static maps as the default 1M VFT path. +- Add a bool-returning VFT benchmark path next, because real VFT transfer + returns success/failure, not post-transfer balances. diff --git a/docs/goals/vft-storage-better-solution/ATTEMPTS.md b/docs/goals/vft-storage-better-solution/ATTEMPTS.md new file mode 100644 index 000000000..098b74772 --- /dev/null +++ b/docs/goals/vft-storage-better-solution/ATTEMPTS.md @@ -0,0 +1,14 @@ +# ATTEMPTS + +| Time | Attempt | Evidence | Result | Next Adjustment | +| --- | --- | --- | --- | --- | +| 2026-05-19 | Stronger goal scaffold created. | `GOAL.md`, `SPEC.md`, `CONTROL.md`, `PLAN.md`, `NOTES.md`, and `RESULTS.md` added. | Ready to begin active optimization. | Inspect prior profile artifacts and choose first candidate. | +| 2026-05-19 | Candidate A1: make 1024 `SailsStaticFast` run side-by-side with baseline and skip validation in the static transfer fast path. | `rtk cargo test --release --manifest-path benchmarks/Cargo.toml vft_storage_transfer_bench -- --nocapture`; 1024 medians: baseline `transfer=1158167504`, `fresh=1137617551`, `from=1324193005`; fast `transfer=1156865312`, `fresh=1136315359`, `from=1322904505`. | Only ~0.1% improvement; not enough. | Check the same idea at 1M, where lazy-page cost may amplify the storage path. | +| 2026-05-19 | Candidate A2: add `static_balance_fused` and `static_balance_fast` to 1M real-cost harness, with env filtering to avoid unrelated backend prepares. | `rtk env SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,static_balance_fused,static_balance_fast cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture`; 1M medians: baseline `transfer=1359345524`, `fresh=1355297341`, `from=1542994469`, `approve=1184163919`; fused `1344319941`, `1342286980`, `1512076735`, `1184163919`; fast `1343051976`, `1341019015`, `1510812817`, `1184163919`. | Fast path improves transfer ~1.20%, fresh ~1.05%, transfer-from ~2.09%, approve 0%; below 3% exploratory threshold. Filtered run finished in 83.91s. | Stop trimming validation; move to a stronger layout/primitive candidate. | +| 2026-05-19 | Candidate B: add `mixed_actor_balance_fast` with bool-specific unchecked transfer methods. | `rtk env SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,mixed_actor_balance,mixed_actor_balance_fast cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture`; latest same-run medians after bool-specific path: baseline `1390458287`, `1386410104`, `1574107232`, `1215276509`; mixed-fast `1369387367`, `1366916903`, `1532011290`, `1211976014`. | Mixed-fast improves transfer ~1.52%, fresh ~1.41%, transfer-from ~2.67%, approve ~0.27%; still below 3% weighted. | Try a more creative layout candidate instead of more actor-map validation trimming. | +| 2026-05-19 | Candidate C: add experimental compact tag balance+allowance table using 64-bit actor/allowance tags and low-limb transfer arithmetic. | `rtk env SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,tag_actor_balance cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture`; final medians: baseline `transfer=1414478675`, `fresh=1410430492`, `from=1598135831`, `approve=1239333016`; tag `1384265061`, `1384010879`, `1539486980`, `1230452291`. | Best so far: transfer ~2.14%, fresh ~1.87%, transfer-from ~3.67%, approve ~0.72%, about 2.19% equal-weighted. It proves compact tags help but still misses the 3% weighted threshold. | Per goal contract, do not call complete; next move needs either a new architecture idea or approval to relax/adjust the success metric. | +| 2026-05-19 | Candidate D: test a tighter 16-byte `tag_u64_actor_balance` upper bound and raw-tag hot paths to remove `ActorId`/`U256` construction from measured operations. | `rtk env SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,tag_u64_actor_balance cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture`; final medians: baseline `transfer=1428187586`, `fresh=1424139403`, `from=1611870555`, `approve=1253063611`; tag-u64 `1397233608`, `1396977526`, `1552155701`, `1243495296`. | Similar ceiling to Candidate C: transfer ~2.17%, fresh ~1.91%, transfer-from ~3.70%, approve ~0.76%, about 2.23% equal-weighted. Even the narrower low-limb table does not clear the 3% equal-weighted threshold. | Stop rerunning unchanged backends; future attempts should run only `static_balance,` until a candidate clears the threshold. | +| 2026-05-19 | Candidate E: make the `tag_u64_actor_balance` benchmark path capacity-guaranteed so hot operations avoid per-call `Result` and full-table handling. | `rtk env SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,tag_u64_actor_balance cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture`; final medians: baseline `transfer=1428346468`, `fresh=1424298285`, `from=1612029437`, `approve=1253222493`; tag-u64 capacity `1397295506`, `1397076265`, `1552192081`, `1243604640`. | No meaningful lift over Candidate D: transfer ~2.17%, fresh ~1.91%, transfer-from ~3.71%, approve ~0.77%, about 2.23% equal-weighted. `Result`/full-table plumbing is not the limiter. | Next candidate needs to change memory/page behavior or the operation architecture, not just trim branches. | +| 2026-05-19 | Candidate F: prototype `inline_allowance_balance`, an owner-local VFT table with balance plus two inline spender allowances in one slot. | `rtk env SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,inline_allowance_balance cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture`; repeated final medians: baseline `1435035528`, `1430987345`, `1618776417`, `1259789549`; inline `1403450539`, `1403261387`, `1403561333`, `1250272544`. Profile artifacts: `/Users/ukintvs/Documents/projects/gear-dlmalloc/target/storage-primitive-selection/vft-storage-better-solution-inline-allowance/profiles`. | Clears exploratory threshold: transfer ~2.20%, fresh ~1.94%, transfer-from ~13.29%, approve ~0.76%, equal-weighted ~4.94%. Profile confirms `transfer_from` lazy-pages drops from `613542773` to `459177411` gas while residual Wasm drops from `271954531` to `211104809`. | Promote as the best architecture direction, but keep benchmark-only until it has full `U256`, tag collision handling, and overflow allowance storage. | +| 2026-05-19 | Candidate G: replace the compact prototype with `inline_owner_account_u256`, a production-shaped owner account table with full `ActorId`, full `U256`, two inline allowances, and overflow table fallback. | `rtk env SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,inline_owner_account_u256 cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture`; profile run saved under `/Users/ukintvs/Documents/projects/gear-dlmalloc/target/storage-primitive-selection/vft-account-inline-u256/profiles`. | 1M medians versus `static_balance`: prepare -48.50%, transfer -1.56%, fresh -1.47%, transfer-from -12.25%, approve-fresh -0.34%, approve-second -0.53%, overflow approve +2.32%, overflow transfer-from -1.90%. Profile shows the same lazy-page reduction on normal transfer-from and exposes overflow approve as the tradeoff. | Stabilize as benchmark-only experimental evidence; public promotion needs a real static-memory layout API and clearer overflow policy. | +| 2026-05-19 | Stabilize Candidate G as an experimental `sails-storage` API. | Added `StaticVftAccountStorage`, `StaticVftAccountMap`, `experimental-vft-account`, and `StaticMemoryLayout::reserve_vft_account_map`; reran `rtk env SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,inline_owner_account_u256 cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture`; profile artifacts: `/Users/ukintvs/Documents/projects/gear-dlmalloc/target/storage-primitive-selection/vft-account-static-storage/profiles`. | Library-backed medians versus `static_balance`: prepare -34.29%, transfer -1.27%, fresh -1.18%, transfer-from -11.86%, approve-fresh +2.99%, approve-second +2.80%, overflow approve +3.41%, overflow transfer-from -1.69%. `sails-storage` feature tests and `sails-rs` build-helper tests pass. | Keep experimental. Next work should either add a dedicated non-overlapping focused benchmark crate or optimize approve before considering default promotion. | diff --git a/docs/goals/vft-storage-better-solution/CONTROL.md b/docs/goals/vft-storage-better-solution/CONTROL.md new file mode 100644 index 000000000..905d859b4 --- /dev/null +++ b/docs/goals/vft-storage-better-solution/CONTROL.md @@ -0,0 +1,58 @@ +# CONTROL + +## Status Contract + +status_file: `PLAN.md` +attempt_log: `ATTEMPTS.md` +durable_notes: `NOTES.md` +results_file: `RESULTS.md` +update_memory_after: every_experiment +check_control_before: phase_change, strategic_pivot, expensive_step, public_api_change + +## Human Priorities + +primary_priority: low_gas +secondary_priority: evidence_quality + +## Scope Knobs + +allowed_files: +- `benchmarks/**` +- `rs/storage/**` +- `docs/goals/vft-storage-better-solution/**` +- `docs/goals/vft-storage-optimization/**` + +protected_files: +- unrelated examples and client code unless benchmark wiring requires them +- Sails dispatcher/routing internals + +max_blast_radius: storage primitives, VFT/storage benchmarks, and goal documentation + +## Acceptance Thresholds + +preferred_success: 7_percent_weighted_improvement_vs_generic_sails_static +strong_narrow_success: 10_percent_on_two_hot_ops_with_max_3_percent_regression +exploratory_progress: 3_percent_weighted_improvement_with_profile_evidence +no_winner_is_completion: false +large_capacity_target: 1_000_000_entries + +## Resource Knobs + +max_runtime_per_fast_step: 15_minutes +max_runtime_per_expensive_step: none +max_parallel_jobs: use_judgment +network_allowed: false + +## Decision Gates + +require_approval_for: +- no_winner_stop +- strategic_pivot +- dependency_change +- public_api_change +- scope_expansion +- destructive_change + +## Latest Human Nudge + +The goal should be to find a better solution: optimize existing, tweak the best performer, or develop something better. diff --git a/docs/goals/vft-storage-better-solution/GOAL.md b/docs/goals/vft-storage-better-solution/GOAL.md new file mode 100644 index 000000000..d2cf31ca6 --- /dev/null +++ b/docs/goals/vft-storage-better-solution/GOAL.md @@ -0,0 +1,184 @@ + +Find and implement a better Gear/Sails VFT storage solution than the current generic `SailsStatic` fast path. The work must actively optimize the existing best performer, tune a promising variant, or develop a new storage architecture, then prove the improvement with Gear/Sails gas measurements. + + + +Start in `/Users/ukintvs/Documents/projects/sails`. + +Read first: +- `docs/goals/vft-storage-better-solution/SPEC.md` +- `docs/goals/vft-storage-better-solution/CONTROL.md` +- `docs/goals/vft-storage-optimization/RESULTS.md` +- `benchmarks/src/benchmarks.rs` +- `benchmarks/vft-stress/src/lib.rs` +- `benchmarks/storage-million/src/lib.rs` +- `rs/storage/src/lib.rs` +- `benchmarks/bench_data.json` + +Baseline to beat: +- current generic `SailsStatic` in `vft_storage_transfer_bench` +- current generic static balance layout in `vft_million_real_cost_bench` + +Discovery commands: +- `rtk rg -n "transfer_actor_u256|transfer_actor_u256_from|lookup\\(|actor_key|allowance_key" rs/storage/src/lib.rs` +- `rtk rg -n "SailsStatic|WatActor|MixedActor|PageLocal|MillionVftBackend|vft_real" benchmarks` +- `rtk rg -n "wasm_phase|BalanceTransfer|BalanceTransferFrom|SAILS_GAS_PROFILE_DIR" benchmarks/src benchmarks/vft-stress/src` + + + +This is an optimization/build goal, not a no-winner evaluation goal. + +Do not declare completion by only rerunning existing candidates. At least one changed or new candidate must be implemented. + +Do not optimize Sails dispatcher, routing, or codec behavior for this goal. The target is storage architecture and storage hot-path gas. + +Do not publish or promote a public API unless the scorecard proves it. Experimental benchmark-only code is acceptable during exploration. + +Do not accept profile-shaped gas totals as production medians. `--features gas-profile` runs are for artifacts and phase attribution only. + +Do not rely only on native Rust microbenchmarks. Use release Gear/Sails gas runs for decisions. + +Do not revert unrelated user changes. + + + +Primary metric: weighted median gas across VFT hot operations: `transfer`, `transfer_fresh`, `transfer_from`, and `approve`. + +Primary baseline: +- current generic `SailsStatic` in `vft_storage_transfer_bench` +- current generic static balance in `vft_million_real_cost_bench` + +Passing thresholds: +- Preferred success: at least 7% weighted improvement over current generic `SailsStatic`. +- Strong narrow success: at least 10% improvement on two hot operations with no hot-op regression above 3%. +- Exploratory progress: at least 3% weighted improvement with profile evidence identifying the next bottleneck. + +Regression checks: +- No correctness regressions in `sails-storage` tests. +- No stale or profile-only rows in production `bench_data.json`. +- No large-capacity benchmark failure for the candidate unless `RESULTS.md` explains the exact technical blocker and the user approves a pivot. + +Stop condition: +- Complete only when a changed or new candidate reaches one of the success/progress thresholds and verification passes. +- If no implemented candidate reaches at least 3% weighted improvement, stop for user input instead of marking the goal complete. + + + +- `ATTEMPTS.md` records at least one implemented or tuned candidate that changes storage behavior, layout, probing, hashing, value handling, or the VFT transfer hot path. +- The candidate is benchmarked against current generic `SailsStatic` using `vft_storage_transfer_bench`. +- The candidate is benchmarked or technically evaluated at 1M-entry scale using `vft_million_real_cost_bench` or a documented equivalent. +- The candidate reaches the preferred threshold, the strong narrow-win threshold, or at least the exploratory progress threshold. +- Gas-profile artifacts are saved under `../gear-dlmalloc/target/storage-primitive-selection/ + + +Fast iterative check: + +```bash +rtk cargo test --release --manifest-path benchmarks/Cargo.toml vft_storage_transfer_bench -- --nocapture +``` + +Run after each candidate change that should affect 1024-entry VFT hot paths. + +Large-capacity check: + +```bash +rtk cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture +``` + +Run when a candidate looks promising in the fast loop or specifically targets large-state lazy-page behavior. + +Profile check: + +```bash +rtk env SAILS_GAS_PROFILE_DIR=/Users/ukintvs/Documents/projects/gear-dlmalloc/target/storage-primitive-selection/ + + +1. Read the baseline result from `docs/goals/vft-storage-optimization/RESULTS.md`. +2. Reread `CONTROL.md` and update `PLAN.md` with the active hypothesis. +3. Inspect profile artifacts from the prior no-winner run and identify the largest storage-controlled phase. +4. Choose the first candidate from one of these directions: tune generic static, tune `MixedActor`/`WatActor`, reduce transfer rereads/writes, change probing/hash strategy, or add a specialized VFT primitive. +5. Implement the smallest candidate change in benchmark-only or experimental code first. +6. Run the fast feedback loop and record exact medians in `ATTEMPTS.md`. +7. If it improves, profile it and run 1M verification. If it regresses, record why and try the next candidate. +8. Continue until one candidate reaches at least exploratory progress or until the run needs a user-approved pivot. +9. Write `RESULTS.md` with the implemented candidate, deltas, profile evidence, and next-step decision. +10. Run final verification. + + + +Maintain: +- `docs/goals/vft-storage-better-solution/PLAN.md` +- `docs/goals/vft-storage-better-solution/ATTEMPTS.md` +- `docs/goals/vft-storage-better-solution/NOTES.md` +- `docs/goals/vft-storage-better-solution/RESULTS.md` +- `docs/goals/vft-storage-better-solution/CONTROL.md` + +Update `PLAN.md` on strategy or candidate changes. +Update `ATTEMPTS.md` after every candidate implementation, benchmark, or rejection. +Update `NOTES.md` for durable findings and bottlenecks. +Update `RESULTS.md` only after there is measured evidence. + + + +Use `CONTROL.md` as the operator panel. + +Before phase changes, expensive benchmarks, strategic pivots, dependency changes, or public API changes, reread `CONTROL.md`. If it changed, adapt and note the change in `PLAN.md`. + +`CONTROL.md` can narrow priorities or require approval. It cannot turn a no-winner evaluation into a completed goal. + + + +Focused checks: + +```bash +rtk cargo test -p sails-storage --lib +rtk cargo test --manifest-path benchmarks/Cargo.toml test_data_not_overwritten +rtk cargo test --release --manifest-path benchmarks/Cargo.toml vft_storage_transfer_bench -- --nocapture +``` + +Candidate-scale checks: + +```bash +rtk cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture +rtk env SAILS_GAS_PROFILE_DIR=/Users/ukintvs/Documents/projects/gear-dlmalloc/target/storage-primitive-selection/ + + +- Prefix shell commands with `rtk`. +- Check git status before edits. +- Preserve unrelated user changes. +- Prefer `rg` over `grep`. +- Use `apply_patch` for manual file edits. +- Keep comparisons anchored to current generic `SailsStatic`. +- Treat profile runs as attribution, not production medians. +- Do not mark this goal complete without an implemented/tuned candidate and measured improvement. +- Update working-memory files after each meaningful experiment. +- Run focused tests before broad tests. +- Do not widen scope. + + + +Final output must include: +- The implemented/tuned candidate. +- A concise gas table versus current generic `SailsStatic`. +- Whether it reached preferred success, strong narrow success, or exploratory progress. +- Profile artifact paths. +- Files changed. +- Verification commands and results. +- Next-step recommendation. + diff --git a/docs/goals/vft-storage-better-solution/NOTES.md b/docs/goals/vft-storage-better-solution/NOTES.md new file mode 100644 index 000000000..8fa53505a --- /dev/null +++ b/docs/goals/vft-storage-better-solution/NOTES.md @@ -0,0 +1,21 @@ +# NOTES + +## Chronological Notes + +- 2026-05-19 This goal supersedes the previous no-winner evaluation contract. The previous result remains useful baseline evidence, but cannot complete this goal. +- 2026-05-19 Completion requires an implemented or tuned candidate with measured improvement over current generic `SailsStatic`. +- 2026-05-19 Profile artifacts show `balance_transfer` and `balance_transfer_from` dominate storage-controlled phase cost; result construction is small. First candidate targets bool transfer paths that still use manual get/update or full result construction. +- 2026-05-19 1M VFT iteration should use `SAILS_STORAGE_MILLION_VFT_BACKENDS` to avoid preparing unchanged backends. Example: `static_balance,static_balance_fused,static_balance_fast`. +- 2026-05-19 Candidate A shows validation/result trimming is not enough: same-run 1M real-cost fast path gives about 1-2% on transfer hot paths and no approve gain, below the 3% threshold. Next candidates need a layout or primitive change. +- 2026-05-19 Probe simulation showed current hot transfer seeds are mostly one-probe hits for generic and mixed maps. Collision reduction alone is not enough for the measured real-cost path. +- 2026-05-19 Compact 64-bit tag tables are the strongest direction so far. They reduce transfer-from by about 3.7%, but equal-weighted improvement remains about 2.2% because approve and simple transfers are still dominated by surrounding runtime/Sails costs. +- 2026-05-19 `tag_u64_actor_balance` removes more value-width and construction cost from the benchmark path, but still lands at about 2.23% equal-weighted. This suggests the map representation alone is not yet exposing enough controllable cost in the current real-cost harness. +- 2026-05-19 Fast iteration rule: run only `static_balance,` with `SAILS_STORAGE_MILLION_VFT_BACKENDS`; reserve the full backend matrix for final confirmation or regression sweeps. +- 2026-05-19 Capacity-guaranteed compact-tag operations did not move the result beyond about 2.23% equal-weighted. The next useful experiments should change page locality, storage semantics, or the VFT operation shape rather than removing more local branches. +- 2026-05-19 Gas profiles showed compact tags did not change lazy-page gas; all tag wins were residual Wasm. This made page-count reduction the next real lever. +- 2026-05-19 `inline_allowance_balance` validates the owner-local allowance architecture: `transfer_from` now pays the same lazy-page total as a regular transfer because owner balance and allowance are in the same slot. +- 2026-05-19 Production path should be a hybrid: inline 1-2 common spender allowances per owner, overflow table for additional spenders, full `U256` values, and explicit tag collision handling. +- 2026-05-19 `inline_owner_account_u256` validates that the architecture survives full `ActorId` and full `U256` values. The main win remains `transfer_from`; the expected weak spot is third-spender overflow approve, where the owner slot is checked before falling back to the allowance table. +- 2026-05-19 The benchmark implementation reuses the grouped static-memory regions to avoid increasing the static memory layout. That is acceptable for isolated benchmark deployments, but a production primitive needs its own `StaticMemoryLayout` reservation and generated constants. +- 2026-05-19 Stabilization added the real experimental `sails-storage` API and build-layout reservation path. The benchmark still uses storage-million's existing regions, so a dedicated focused benchmark crate remains the clean measurement follow-up. +- 2026-05-19 The library wrapper keeps correctness semantics for inline clears and overflow allowances. That preserves the large `transfer_from` lazy-page win, but approve is now the visible regression boundary. diff --git a/docs/goals/vft-storage-better-solution/PLAN.md b/docs/goals/vft-storage-better-solution/PLAN.md new file mode 100644 index 000000000..2e1ac13f7 --- /dev/null +++ b/docs/goals/vft-storage-better-solution/PLAN.md @@ -0,0 +1,45 @@ +# PLAN + +## Goal + +Find and implement a better VFT storage solution than current generic `SailsStatic`. + +## Current Strategy + +Use phase-1 no-winner results as the baseline, but measure candidate changes side-by-side with the unchanged baseline in the same run. Iteration runs should include only `static_balance` plus the candidate under test; repeated prepares for unchanged backends are not useful until the final matrix run. + +Current active result: `inline_owner_account_u256` turns the owner-local allowance architecture into a production-shaped benchmark candidate. It stores the full owner key, balance, and two hot spender allowances in one owner slot, then falls back to an overflow allowance table for third-plus spenders. + +## Candidate Backlog + +- [x] Inspect prior profile artifacts to identify the largest storage-controlled phase. +- [x] Candidate A: tune generic static transfer bool hot path. +- [x] Candidate B: tune `MixedActor` or `WatActor` where they already show small 1M wins. +- [x] Candidate C: add or prototype a specialized VFT primitive if phase evidence supports it. +- [x] Candidate F: prototype owner-local inline allowances for VFT transfer-from. +- [x] Candidate G: make owner-local inline allowances full-key/full-`U256` with overflow fallback. + +## Phases + +- [x] Re-establish baseline and active hypothesis. +- [x] Implement/tune candidate A. +- [x] Run fast VFT benchmark. +- [x] Profile promising candidate. +- [x] Run filtered 1M VFT real-cost benchmark. +- [x] Iterate or ask for pivot if no candidate reaches 3% weighted improvement. +- [x] Write `RESULTS.md`. +- [x] Run final verification. + +## Open Decisions + +- `inline_owner_account_u256` clears exploratory progress at 1M scale and removes the compact-tag shortcut. It is still benchmark-only because it reuses grouped static-memory regions for measurement; public promotion needs explicit static-memory reservation and documented overflow sizing. + +## Iteration Command + +Use backend filtering while exploring 1M changes. Replace `` with one backend such as `inline_owner_account_u256`: + +```bash +rtk env SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance, cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture +``` + +The full backend matrix remains the default when `SAILS_STORAGE_MILLION_VFT_BACKENDS` is unset. diff --git a/docs/goals/vft-storage-better-solution/RESULTS.md b/docs/goals/vft-storage-better-solution/RESULTS.md new file mode 100644 index 000000000..60c7fdcd3 --- /dev/null +++ b/docs/goals/vft-storage-better-solution/RESULTS.md @@ -0,0 +1,41 @@ +# RESULTS + +## Current Status + +Several candidates are implemented and measured. The compact `inline_allowance_balance` prototype proved the architecture, `inline_owner_account_u256` made it full-key/full-`U256`, and the stabilized version now exists as an experimental `sails-storage` API behind `experimental-vft-account`. + +Latest same-run 1M real-cost medians: + +| Operation | `static_balance` | `inline_owner_account_u256` | Delta | +| --- | ---: | ---: | ---: | +| prepare | 321556277747506 | 211288660377756 | -34.29% | +| transfer | 1478959742 | 1460169545 | -1.27% | +| transfer_fresh | 1474715541 | 1457287604 | -1.18% | +| transfer_from | 1662923723 | 1465702081 | -11.86% | +| approve_fresh | 1303371315 | 1342403479 | +2.99% | +| approve_second | 1305853837 | 1342403479 | +2.80% | +| approve_overflow_third | 1303298666 | 1347703980 | +3.41% | +| transfer_from_overflow | 1665112547 | 1636947889 | -1.69% | + +This reaches exploratory progress and a strong narrow win on `transfer_from`, but not preferred 7% weighted success across all hot operations. The primitive is experimental, not a production default. It has a real `StaticMemoryLayout::reserve_vft_account_map` reservation path, but the current 1M benchmark still reuses existing storage-million regions to avoid expanding that crowded benchmark layout. + +Profile evidence is saved under `/Users/ukintvs/Documents/projects/gear-dlmalloc/target/storage-primitive-selection/vft-account-static-storage/profiles`. + +The profile confirms the mechanism. For `transfer_from`, lazy-page gas drops from `613542773` to `459177411`, because the hot owner balance and hot allowance live in the same owner account slot. Residual Wasm drops from `272518797` to `229662517`. The remaining tradeoff is approve: the public wrapper keeps correctness and overflow behavior, so approve paths are slightly more expensive than the raw benchmark prototype. + +Next decision: either build a dedicated focused benchmark crate with non-overlapping account/overflow regions, or continue optimizing `approve` before considering default VFT storage promotion. + +## Verification + +- `rtk cargo test --release --manifest-path benchmarks/Cargo.toml vft_storage_transfer_bench -- --nocapture` +- `rtk env SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,inline_allowance_balance cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture` +- `rtk env SAILS_GAS_PROFILE_DIR=/Users/ukintvs/Documents/projects/gear-dlmalloc/target/storage-primitive-selection/vft-storage-better-solution-inline-allowance SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,inline_allowance_balance cargo test --release --manifest-path benchmarks/Cargo.toml --features gas-profile vft_million_real_cost_bench -- --nocapture` +- `rtk env SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,inline_owner_account_u256 cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture` +- `rtk env SAILS_GAS_PROFILE_DIR=/Users/ukintvs/Documents/projects/gear-dlmalloc/target/storage-primitive-selection/vft-account-inline-u256 SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,inline_owner_account_u256 cargo test --release --manifest-path benchmarks/Cargo.toml --features gas-profile vft_million_real_cost_bench -- --nocapture` +- `rtk cargo test -p sails-storage --features experimental-vft-account --lib` +- `rtk cargo test -p sails-rs --features wasm-builder,experimental-vft-account build::` +- `rtk env SAILS_GAS_PROFILE_DIR=/Users/ukintvs/Documents/projects/gear-dlmalloc/target/storage-primitive-selection/vft-account-static-storage SAILS_STORAGE_MILLION_VFT_BACKENDS=static_balance,inline_owner_account_u256 cargo test --release --manifest-path benchmarks/Cargo.toml --features gas-profile vft_million_real_cost_bench -- --nocapture` +- `rtk cargo test -p sails-storage --lib` +- `rtk cargo test --manifest-path benchmarks/Cargo.toml test_data_not_overwritten` +- `rtk cargo fmt --manifest-path benchmarks/Cargo.toml -- --check` +- `rtk git diff --check -- benchmarks/src/benchmarks.rs benchmarks/storage-million/src/lib.rs benchmarks/bench_data.json rs/storage/src/lib.rs docs/goals/vft-storage-better-solution` diff --git a/docs/goals/vft-storage-better-solution/SPEC.md b/docs/goals/vft-storage-better-solution/SPEC.md new file mode 100644 index 000000000..e7078e7b5 --- /dev/null +++ b/docs/goals/vft-storage-better-solution/SPEC.md @@ -0,0 +1,87 @@ +# VFT Storage Better Solution Spec + +## Goal + +Find and implement a better Gear/Sails VFT storage solution than the current generic `SailsStatic` fast path. + +This goal is not complete by showing that old candidates fail. The agent must actively optimize the best current performer, tune its parameters or hot path, or develop a new storage architecture, then prove the result with Gear/Sails gas measurements. + +## Baseline Evidence + +Use `docs/goals/vft-storage-optimization/RESULTS.md` as phase-1 baseline evidence: + +- Current generic `SailsStatic` beats `BTreeMap`, `HashMap`, and `SailsFixed` on the 1024-entry VFT hot path. +- Existing million-entry variants did not beat generic static enough: + - `MixedActor`: +1.34% weighted + - `WatActor`: +1.21% weighted + - `PageLocal`: +0.41% weighted + - `Control`, `Grouped64`, and `Grouped128`: regressions + +That result is a starting point, not a stopping point. + +## Required Work + +The run must implement or tune at least one real candidate that changes storage behavior, layout, probing, hashing, value handling, or the VFT transfer hot path. + +Acceptable directions: + +- Optimize current generic `StaticOpenAddressTable` or `StaticBalanceTable`. +- Tune current best performers such as `MixedActor` or `WatActor`. +- Develop a new hybrid layout based on phase/profile evidence. +- Add a specialized VFT balance/allowance primitive if it reduces hot-path gas. +- Change benchmark-only code first, then promote only if evidence is strong. + +Unacceptable directions: + +- Only rerun existing benchmarks and stop. +- Only document the phase-1 no-winner result. +- Optimize dispatcher/routing/codec instead of storage. +- Publish an API without benchmark evidence. + +## Scorecard + +Primary metric: weighted median gas across VFT hot operations. + +Hot operations: + +- `transfer` +- `transfer_fresh` +- `transfer_from` +- `approve` + +Target threshold: + +- Preferred: at least 7% weighted improvement over current generic `SailsStatic`. +- Acceptable strong narrow win: at least 10% improvement on two hot operations with no hot-op regression above 3%. +- Exploratory progress threshold: at least 3% weighted improvement with a clear path to the 7% target. + +If no implemented candidate reaches at least the exploratory threshold, the goal is not done. Pause with evidence and ask for a pivot instead of declaring completion. + +## Feedback Loop + +Fast check: + +```bash +rtk cargo test --release --manifest-path benchmarks/Cargo.toml vft_storage_transfer_bench -- --nocapture +``` + +Large-capacity check: + +```bash +rtk cargo test --release --manifest-path benchmarks/Cargo.toml vft_million_real_cost_bench -- --nocapture +``` + +Profile check: + +```bash +rtk env SAILS_GAS_PROFILE_DIR=/Users/ukintvs/Documents/projects/gear-dlmalloc/target/storage-primitive-selection/

(Some(&program_name_pascal), &idl_path).unwrap(); - ClientGenerator::from_idl_path(&idl_path) - .generate_to(client_path) - .unwrap(); -} - fn main() { let manifest_dir = env::var("CARGO_MANIFEST_DIR").unwrap(); let clients_path = PathBuf::from(&manifest_dir).join("src"); @@ -42,29 +23,4 @@ fn main() { generate_client("alloc_stress_program", &clients_path, &idls_path); generate_client("compute_stress_program", &clients_path, &idls_path); generate_client("counter_bench_program", &clients_path, &idls_path); - generate_idl_and_client::( - "noop_sails_program", - &clients_path, - &idls_path, - ); - generate_idl_and_client::( - "minimal_vft_sails_program", - &clients_path, - &idls_path, - ); - generate_idl_and_client::( - "storage_stress_program", - &clients_path, - &idls_path, - ); - generate_idl_and_client::( - "vft_stress_program", - &clients_path, - &idls_path, - ); - generate_idl_and_client::( - "storage_million_program", - &clients_path, - &idls_path, - ); } diff --git a/benchmarks/idls/minimal_vft_sails_program.idl b/benchmarks/idls/minimal_vft_sails_program.idl deleted file mode 100644 index aa8fb400a..000000000 --- a/benchmarks/idls/minimal_vft_sails_program.idl +++ /dev/null @@ -1,21 +0,0 @@ - -!@sails: 1.0.0-beta.5 - -service Vft@0x53bb8c1a98387ed4 { - functions { - Allowance(owner: ActorId, spender: ActorId) -> U256; - Approve(spender: ActorId, amount: U256) -> bool; - BalanceOf(owner: ActorId) -> U256; - Transfer(to: ActorId, amount: U256) -> bool; - TransferFrom(owner: ActorId, to: ActorId, amount: U256) -> bool; - } -} - -program MinimalVftSails { - constructors { - NewForBench(); - } - services { - Vft@0x53bb8c1a98387ed4, - } -} diff --git a/benchmarks/idls/noop_sails_program.idl b/benchmarks/idls/noop_sails_program.idl deleted file mode 100644 index 3e31236d5..000000000 --- a/benchmarks/idls/noop_sails_program.idl +++ /dev/null @@ -1,17 +0,0 @@ - -!@sails: 1.0.0-beta.5 - -service NoopSails@0x5457cc30dd94ce29 { - functions { - Noop() -> bool; - } -} - -program NoopSails { - constructors { - NewForBench(); - } - services { - NoopSails@0x5457cc30dd94ce29, - } -} diff --git a/benchmarks/idls/storage_million_program.idl b/benchmarks/idls/storage_million_program.idl deleted file mode 100644 index 4755cabb3..000000000 --- a/benchmarks/idls/storage_million_program.idl +++ /dev/null @@ -1,84 +0,0 @@ - -!@sails: 1.0.0-beta.5 - -service StorageMillion@0x4534f13d9216fc6b { - functions { - Bench(backend: MillionStorageBackend, op: MillionStorageOp, seed: u32) -> MillionStorageBenchResult; - BenchMany(backend: MillionStorageBackend, op: MillionStorageOp, start_seed: u32, count: u32) -> MillionStorageBenchResult; - BenchVftApproveBool(backend: MillionVftBackend, owner_seed: u32, spender_seed: u32) -> bool; - BenchVftTransfer(backend: MillionVftBackend, op: MillionVftTransferOp, seed: u32) -> MillionVftTransferResult; - BenchVftTransferBool(backend: MillionVftBackend, op: MillionVftTransferOp, seed: u32) -> bool; - BenchVftTransferFreshBool(backend: MillionVftBackend, seed: u32) -> bool; - BenchVftTransferFromSpenderBool(backend: MillionVftBackend, owner_seed: u32, spender_seed: u32) -> bool; - PrepareChunk(backend: MillionStorageBackend, start: u32, len: u32) -> MillionStorageBenchResult; - PrepareVftChunk(backend: MillionVftBackend, start: u32, len: u32) -> MillionVftTransferResult; - } - types { - enum MillionStorageBackend { - GenericStatic, - WatActorStatic, - MixedActorStatic, - TagActorStatic, - TagU64ActorStatic, - ControlActorStatic, - PageLocalActorStatic, - GroupedActorPages2, - GroupedActorPages4, - GroupedActorPages8, - GroupedActorPages16, - GroupedActorPages32, - GroupedActorPages64, - GroupedActorPages128, - } - struct MillionStorageBenchResult { - value: U256, - len: u32, - existed: bool, - } - enum MillionStorageOp { - InsertFresh, - UpdateExisting, - ReadExisting, - ReadMissing, - Remove, - } - enum MillionVftBackend { - BTree, - HashMap, - GenericStatic, - GenericStaticFused, - GenericStaticFast, - WatActorStatic, - MixedActorStatic, - MixedActorFast, - TagActorStatic, - TagU64ActorStatic, - ControlActorStatic, - PageLocalActorStatic, - GroupedActorPages64, - GroupedActorPages128, - InlineOwnerAccountU256, - } - enum MillionVftTransferOp { - Transfer, - TransferFrom, - } - struct MillionVftTransferResult { - from_balance: U256, - to_balance: U256, - allowance: U256, - balance_len: u32, - allowance_len: u32, - transferred: bool, - } - } -} - -program StorageMillion { - constructors { - NewForBench(); - } - services { - StorageMillion@0x4534f13d9216fc6b, - } -} diff --git a/benchmarks/idls/storage_stress_program.idl b/benchmarks/idls/storage_stress_program.idl deleted file mode 100644 index 95dee147d..000000000 --- a/benchmarks/idls/storage_stress_program.idl +++ /dev/null @@ -1,43 +0,0 @@ - -!@sails: 1.0.0-beta.5 - -service StorageStress@0x66333911850d9b10 { - functions { - Bench(backend: StorageBackend, map: StorageMap, op: StorageOp, seed: u32) -> StorageBenchResult; - Prepare(backend: StorageBackend, map: StorageMap, len: u32) -> StorageBenchResult; - } - types { - enum StorageBackend { - HashMap, - Fixed, - RawStatic, - SailsFixed, - SailsStatic, - } - struct StorageBenchResult { - value: U256, - len: u32, - existed: bool, - } - enum StorageMap { - Balance, - Allowance, - } - enum StorageOp { - InsertFresh, - UpdateExisting, - ReadExisting, - ReadMissing, - Remove, - } - } -} - -program StorageStress { - constructors { - NewForBench(); - } - services { - StorageStress@0x66333911850d9b10, - } -} diff --git a/benchmarks/idls/vft_stress_program.idl b/benchmarks/idls/vft_stress_program.idl deleted file mode 100644 index 234832337..000000000 --- a/benchmarks/idls/vft_stress_program.idl +++ /dev/null @@ -1,69 +0,0 @@ - -!@sails: 1.0.0-beta.5 - -service VftStress@0x0183a7c8595d08a0 { - functions { - BenchEchoVftArgs(backend: VftStorageBackend, op: VftTransferOp, seed: u32) -> bool; - BenchNoop() -> bool; - BenchVftApproveBool(backend: VftStorageBackend, owner_seed: u32, spender_seed: u32) -> bool; - BenchVftApproveProfile(backend: VftStorageBackend, owner_seed: u32, spender_seed: u32) -> VftProfileResult; - BenchVftTransfer(backend: VftStorageBackend, op: VftTransferOp, seed: u32) -> VftTransferResult; - BenchVftTransferFreshBool(backend: VftStorageBackend, seed: u32) -> bool; - BenchVftTransferFreshProfile(backend: VftStorageBackend, seed: u32) -> VftProfileResult; - BenchVftTransferProfile(backend: VftStorageBackend, op: VftTransferOp, seed: u32) -> VftProfileResult; - PrepareVft(backend: VftStorageBackend, len: u32) -> VftTransferResult; - } - types { - enum VftPhase { - ProbeOverhead, - NoopBody, - EchoBody, - KeyDerive, - AllowanceGet, - AllowancePut, - BalanceGetFrom, - BalanceGetTo, - BalancePutFrom, - BalancePutTo, - BalanceTransfer, - BalanceTransferFrom, - ResultBuild, - } - struct VftPhaseGas { - phase: VftPhase, - gas: u64, - } - struct VftProfileResult { - result: VftTransferResult, - phases: [VftPhaseGas], - } - enum VftStorageBackend { - BTree, - HashMap, - SailsFixed, - SailsStatic, - SailsStaticFast, - } - enum VftTransferOp { - Transfer, - TransferFrom, - } - struct VftTransferResult { - from_balance: U256, - to_balance: U256, - allowance: U256, - balance_len: u32, - allowance_len: u32, - transferred: bool, - } - } -} - -program VftStress { - constructors { - NewForBench(); - } - services { - VftStress@0x0183a7c8595d08a0, - } -} diff --git a/benchmarks/minimal-vft-hot/Cargo.toml b/benchmarks/minimal-vft-hot/Cargo.toml deleted file mode 100644 index 236379980..000000000 --- a/benchmarks/minimal-vft-hot/Cargo.toml +++ /dev/null @@ -1,16 +0,0 @@ -[package] -name = "minimal-vft-hot" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -gcore = { version = "=1.10.0", default-features = false } -gstd = { version = "=1.10.0", default-features = false, features = ["panic-handler"] } -gprimitives.workspace = true -sails-storage.workspace = true - -[build-dependencies] -sails-rs = { workspace = true, features = ["wasm-builder"] } diff --git a/benchmarks/minimal-vft-hot/build.rs b/benchmarks/minimal-vft-hot/build.rs deleted file mode 100644 index 5a9e303df..000000000 --- a/benchmarks/minimal-vft-hot/build.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - const VFT_LOG2_SLOTS: u8 = 21; - - let layout = sails_rs::build::StaticMemoryLayout::new(1024) - .reserve_actor_u256_map::("balances") - .reserve_actor_pair_u256_map::("allowances"); - - sails_rs::build::build_wasm_with_static_memory(layout); -} diff --git a/benchmarks/minimal-vft-hot/src/lib.rs b/benchmarks/minimal-vft-hot/src/lib.rs deleted file mode 100644 index ba76d4579..000000000 --- a/benchmarks/minimal-vft-hot/src/lib.rs +++ /dev/null @@ -1,182 +0,0 @@ -#![no_std] - -use gprimitives::{ActorId, U256}; -use sails_storage::__experimental::StaticVftStorage; - -mod static_storage { - include!(concat!(env!("OUT_DIR"), "/sails_static_storage.rs")); -} - -pub const STATIC_MEMORY_END_PAGE: u32 = static_storage::STATIC_MEMORY_END_PAGE; -const VFT_LOG2_SLOTS: u8 = static_storage::BALANCES_LOG2_SLOTS; -const INITIAL_SUPPLY: u64 = 1_000_000_000_000; - -const MAGIC_0: u8 = b'G'; -const MAGIC_1: u8 = b'M'; -const HEADER_LEN: usize = 16; -const ROUTE_VFT: u8 = 1; -const ENTRY_APPROVE: u16 = 0; -const ENTRY_TRANSFER: u16 = 1; -const ENTRY_TRANSFER_FROM: u16 = 2; -const ENTRY_BALANCE_OF: u16 = 3; -const ENTRY_ALLOWANCE: u16 = 4; -const INTERFACE_ID: [u8; 8] = [0x6d, 0x69, 0x6e, 0x76, 0x66, 0x74, 0x00, 0x01]; - -type Storage = StaticVftStorage; - -#[unsafe(no_mangle)] -extern "C" fn init() { - let owner = gstd::msg::source(); - let _ = storage().mint(owner, U256::from(INITIAL_SUPPLY)); -} - -#[unsafe(no_mangle)] -extern "C" fn handle() { - gcore::msg::with_read_on_stack_or_heap(|payload| { - let Ok(payload) = payload else { - reply_bool(ENTRY_TRANSFER, false); - return; - }; - - let Some((entry_id, params)) = read_header(payload) else { - reply_bool(ENTRY_TRANSFER, false); - return; - }; - - match entry_id { - ENTRY_APPROVE => { - let Some((spender, amount)) = read_actor_u256(params) else { - reply_bool(entry_id, false); - return; - }; - let changed = storage() - .approve(gstd::msg::source(), spender, amount) - .unwrap_or(false); - reply_bool(entry_id, changed); - } - ENTRY_TRANSFER => { - let Some((to, amount)) = read_actor_u256(params) else { - reply_bool(entry_id, false); - return; - }; - let changed = storage() - .transfer(gstd::msg::source(), to, amount) - .unwrap_or(false); - reply_bool(entry_id, changed); - } - ENTRY_TRANSFER_FROM => { - let Some((owner, to, amount)) = read_actor_actor_u256(params) else { - reply_bool(entry_id, false); - return; - }; - let changed = storage() - .transfer_from(gstd::msg::source(), owner, to, amount) - .unwrap_or(false); - reply_bool(entry_id, changed); - } - ENTRY_BALANCE_OF => { - let Some(owner) = read_actor(params) else { - reply_u256(entry_id, U256::zero()); - return; - }; - reply_u256( - entry_id, - storage().balance_of(owner).unwrap_or_else(|_| U256::zero()), - ); - } - ENTRY_ALLOWANCE => { - let Some((owner, spender)) = read_actor_actor(params) else { - reply_u256(entry_id, U256::zero()); - return; - }; - reply_u256( - entry_id, - storage() - .allowance(owner, spender) - .unwrap_or_else(|_| U256::zero()), - ); - } - _ => reply_bool(entry_id, false), - } - }); -} - -fn storage() -> Storage { - unsafe { - Storage::new( - static_storage::BALANCES_BASE, - static_storage::ALLOWANCES_BASE, - ) - } - .expect("static VFT layout is valid") -} - -fn read_header(payload: &[u8]) -> Option<(u16, &[u8])> { - if payload.len() < HEADER_LEN - || payload[0] != MAGIC_0 - || payload[1] != MAGIC_1 - || payload[2] != 1 - || payload[3] != HEADER_LEN as u8 - || payload[4..12] != INTERFACE_ID - || payload[14] != ROUTE_VFT - { - return None; - } - - Some(( - u16::from_le_bytes([payload[12], payload[13]]), - &payload[16..], - )) -} - -fn read_actor(payload: &[u8]) -> Option { - let bytes: [u8; 32] = payload.get(..32)?.try_into().ok()?; - Some(ActorId::new(bytes)) -} - -fn read_actor_actor(payload: &[u8]) -> Option<(ActorId, ActorId)> { - let owner = read_actor(payload)?; - let spender = read_actor(payload.get(32..)?)?; - Some((owner, spender)) -} - -fn read_u256(payload: &[u8]) -> Option { - Some(U256::from_little_endian(payload.get(..32)?)) -} - -fn read_actor_u256(payload: &[u8]) -> Option<(ActorId, U256)> { - let actor = read_actor(payload)?; - let value = read_u256(payload.get(32..)?)?; - Some((actor, value)) -} - -fn read_actor_actor_u256(payload: &[u8]) -> Option<(ActorId, ActorId, U256)> { - let first = read_actor(payload)?; - let second = read_actor(payload.get(32..)?)?; - let value = read_u256(payload.get(64..)?)?; - Some((first, second, value)) -} - -fn reply_bool(entry_id: u16, value: bool) { - let mut payload = [0u8; 17]; - write_header(&mut payload, entry_id); - payload[16] = u8::from(value); - let _ = gcore::msg::reply(&payload, 0); -} - -fn reply_u256(entry_id: u16, value: U256) { - let mut payload = [0u8; 48]; - write_header(&mut payload, entry_id); - value.to_little_endian(&mut payload[16..48]); - let _ = gcore::msg::reply(&payload, 0); -} - -fn write_header(payload: &mut [u8], entry_id: u16) { - payload[0] = MAGIC_0; - payload[1] = MAGIC_1; - payload[2] = 1; - payload[3] = HEADER_LEN as u8; - payload[4..12].copy_from_slice(&INTERFACE_ID); - payload[12..14].copy_from_slice(&entry_id.to_le_bytes()); - payload[14] = ROUTE_VFT; -} diff --git a/benchmarks/minimal-vft-sails/Cargo.toml b/benchmarks/minimal-vft-sails/Cargo.toml deleted file mode 100644 index dcac571b9..000000000 --- a/benchmarks/minimal-vft-sails/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "minimal-vft-sails" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -sails-rs.workspace = true -sails-storage.workspace = true - -[build-dependencies] -sails-rs = { workspace = true, features = ["wasm-builder"] } diff --git a/benchmarks/minimal-vft-sails/build.rs b/benchmarks/minimal-vft-sails/build.rs deleted file mode 100644 index 5a9e303df..000000000 --- a/benchmarks/minimal-vft-sails/build.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - const VFT_LOG2_SLOTS: u8 = 21; - - let layout = sails_rs::build::StaticMemoryLayout::new(1024) - .reserve_actor_u256_map::("balances") - .reserve_actor_pair_u256_map::("allowances"); - - sails_rs::build::build_wasm_with_static_memory(layout); -} diff --git a/benchmarks/minimal-vft-sails/src/lib.rs b/benchmarks/minimal-vft-sails/src/lib.rs deleted file mode 100644 index f180ea495..000000000 --- a/benchmarks/minimal-vft-sails/src/lib.rs +++ /dev/null @@ -1,77 +0,0 @@ -#![no_std] - -use sails_rs::prelude::*; -use sails_storage::__experimental::StaticVftStorage; - -mod static_storage { - include!(concat!(env!("OUT_DIR"), "/sails_static_storage.rs")); -} - -pub const STATIC_MEMORY_END_PAGE: u32 = static_storage::STATIC_MEMORY_END_PAGE; -const VFT_LOG2_SLOTS: u8 = static_storage::BALANCES_LOG2_SLOTS; -const INITIAL_SUPPLY: u64 = 1_000_000_000_000; - -type Storage = StaticVftStorage; - -pub struct MinimalVftService; - -#[sails_rs::service] -impl MinimalVftService { - #[export] - pub fn approve(&mut self, spender: ActorId, amount: U256) -> bool { - storage() - .approve(sails_rs::gstd::msg::source(), spender, amount) - .unwrap_or(false) - } - - #[export] - pub fn transfer(&mut self, to: ActorId, amount: U256) -> bool { - storage() - .transfer(sails_rs::gstd::msg::source(), to, amount) - .unwrap_or(false) - } - - #[export] - pub fn transfer_from(&mut self, owner: ActorId, to: ActorId, amount: U256) -> bool { - storage() - .transfer_from(sails_rs::gstd::msg::source(), owner, to, amount) - .unwrap_or(false) - } - - #[export] - pub fn balance_of(&mut self, owner: ActorId) -> U256 { - storage().balance_of(owner).unwrap_or_else(|_| U256::zero()) - } - - #[export] - pub fn allowance(&mut self, owner: ActorId, spender: ActorId) -> U256 { - storage() - .allowance(owner, spender) - .unwrap_or_else(|_| U256::zero()) - } -} - -pub struct MinimalVftSailsProgram; - -#[sails_rs::program] -impl MinimalVftSailsProgram { - pub fn new_for_bench() -> Self { - let owner = sails_rs::gstd::msg::source(); - let _ = storage().mint(owner, U256::from(INITIAL_SUPPLY)); - Self - } - - pub fn vft(&self) -> MinimalVftService { - MinimalVftService - } -} - -fn storage() -> Storage { - unsafe { - Storage::new( - static_storage::BALANCES_BASE, - static_storage::ALLOWANCES_BASE, - ) - } - .expect("static VFT layout is valid") -} diff --git a/benchmarks/noop-gstd/Cargo.toml b/benchmarks/noop-gstd/Cargo.toml deleted file mode 100644 index 8d851f10e..000000000 --- a/benchmarks/noop-gstd/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "noop-gstd" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -gstd = { version = "=1.10.0", default-features = false, features = ["panic-handler"] } - -[build-dependencies] -gwasm-builder.workspace = true diff --git a/benchmarks/noop-gstd/build.rs b/benchmarks/noop-gstd/build.rs deleted file mode 100644 index 6e682ab98..000000000 --- a/benchmarks/noop-gstd/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - gwasm_builder::build(); -} diff --git a/benchmarks/noop-gstd/src/lib.rs b/benchmarks/noop-gstd/src/lib.rs deleted file mode 100644 index 4ccf6de52..000000000 --- a/benchmarks/noop-gstd/src/lib.rs +++ /dev/null @@ -1,11 +0,0 @@ -#![no_std] - -pub const PROGRAM_NAME: &str = "noop-gstd"; - -#[unsafe(no_mangle)] -extern "C" fn init() {} - -#[unsafe(no_mangle)] -extern "C" fn handle() { - let _ = gstd::msg::reply_bytes([1u8], 0); -} diff --git a/benchmarks/noop-sails-hot/Cargo.toml b/benchmarks/noop-sails-hot/Cargo.toml deleted file mode 100644 index 9381e45e0..000000000 --- a/benchmarks/noop-sails-hot/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "noop-sails-hot" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -gcore = { version = "=1.10.0", default-features = false } -gstd = { version = "=1.10.0", default-features = false, features = ["panic-handler"] } - -[build-dependencies] -sails-rs = { workspace = true, features = ["wasm-builder"] } diff --git a/benchmarks/noop-sails-hot/build.rs b/benchmarks/noop-sails-hot/build.rs deleted file mode 100644 index 0b315b06e..000000000 --- a/benchmarks/noop-sails-hot/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - sails_rs::build_wasm(); -} diff --git a/benchmarks/noop-sails-hot/src/lib.rs b/benchmarks/noop-sails-hot/src/lib.rs deleted file mode 100644 index e441d87a5..000000000 --- a/benchmarks/noop-sails-hot/src/lib.rs +++ /dev/null @@ -1,58 +0,0 @@ -#![no_std] - -extern crate gstd; - -pub const PROGRAM_NAME: &str = "noop_sails_hot"; - -const MAGIC_0: u8 = b'G'; -const MAGIC_1: u8 = b'M'; -const HEADER_LEN: usize = 16; -const ROUTE_NOOP: u8 = 1; -const ENTRY_NOOP: u16 = 0; -const INTERFACE_ID: [u8; 8] = [84, 87, 204, 48, 221, 148, 206, 41]; - -#[unsafe(no_mangle)] -extern "C" fn init() {} - -#[unsafe(no_mangle)] -extern "C" fn handle() { - gcore::msg::with_read_on_stack_or_heap(|payload| { - let Ok(payload) = payload else { - return; - }; - let Some(entry_id) = read_header(payload) else { - return; - }; - if entry_id == ENTRY_NOOP { - reply_bool(true); - } - }); -} - -fn read_header(payload: &[u8]) -> Option { - if payload.len() < HEADER_LEN - || payload[0] != MAGIC_0 - || payload[1] != MAGIC_1 - || payload[2] != 1 - || payload[3] != HEADER_LEN as u8 - || payload[4..12] != INTERFACE_ID - || payload[14] != ROUTE_NOOP - { - return None; - } - - Some(u16::from_le_bytes([payload[12], payload[13]])) -} - -fn reply_bool(value: bool) { - let mut payload = [0u8; 17]; - payload[0] = MAGIC_0; - payload[1] = MAGIC_1; - payload[2] = 1; - payload[3] = HEADER_LEN as u8; - payload[4..12].copy_from_slice(&INTERFACE_ID); - payload[12..14].copy_from_slice(&ENTRY_NOOP.to_le_bytes()); - payload[14] = ROUTE_NOOP; - payload[16] = u8::from(value); - let _ = gcore::msg::reply(&payload, 0); -} diff --git a/benchmarks/noop-sails/Cargo.toml b/benchmarks/noop-sails/Cargo.toml deleted file mode 100644 index 0abf1d692..000000000 --- a/benchmarks/noop-sails/Cargo.toml +++ /dev/null @@ -1,13 +0,0 @@ -[package] -name = "noop-sails" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -sails-rs.workspace = true - -[build-dependencies] -sails-rs = { workspace = true, features = ["wasm-builder"] } diff --git a/benchmarks/noop-sails/build.rs b/benchmarks/noop-sails/build.rs deleted file mode 100644 index 0b315b06e..000000000 --- a/benchmarks/noop-sails/build.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - sails_rs::build_wasm(); -} diff --git a/benchmarks/noop-sails/src/lib.rs b/benchmarks/noop-sails/src/lib.rs deleted file mode 100644 index a97673110..000000000 --- a/benchmarks/noop-sails/src/lib.rs +++ /dev/null @@ -1,26 +0,0 @@ -#![no_std] - -use sails_rs::prelude::*; - -pub struct NoopSailsService; - -#[sails_rs::service] -impl NoopSailsService { - #[export] - pub fn noop(&mut self) -> bool { - true - } -} - -pub struct NoopSailsProgram; - -#[sails_rs::program] -impl NoopSailsProgram { - pub fn new_for_bench() -> Self { - Self - } - - pub fn noop_sails(&self) -> NoopSailsService { - NoopSailsService - } -} diff --git a/benchmarks/noop-wat/Cargo.toml b/benchmarks/noop-wat/Cargo.toml deleted file mode 100644 index 488d14084..000000000 --- a/benchmarks/noop-wat/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "noop-wat" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] - -[build-dependencies] -wat.workspace = true diff --git a/benchmarks/noop-wat/build.rs b/benchmarks/noop-wat/build.rs deleted file mode 100644 index 6dcda5933..000000000 --- a/benchmarks/noop-wat/build.rs +++ /dev/null @@ -1,15 +0,0 @@ -use std::{env, fs, path::PathBuf}; - -fn main() { - let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR is set")); - - let cases = [ - ("raw_reply", include_str!("wat/raw_reply.wat")), - ("sails_wire_reply", include_str!("wat/sails_wire_reply.wat")), - ]; - - for (name, wat_text) in cases { - let wasm = wat::parse_str(wat_text).expect("WAT noop fixture compiles"); - fs::write(out_dir.join(format!("{name}.wasm")), wasm).expect("WAT noop fixture writes"); - } -} diff --git a/benchmarks/noop-wat/src/lib.rs b/benchmarks/noop-wat/src/lib.rs deleted file mode 100644 index d563958a2..000000000 --- a/benchmarks/noop-wat/src/lib.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![no_std] - -pub const RAW_REPLY_WASM: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/raw_reply.wasm")); -pub const SAILS_WIRE_REPLY_WASM: &[u8] = - include_bytes!(concat!(env!("OUT_DIR"), "/sails_wire_reply.wasm")); diff --git a/benchmarks/noop-wat/wat/raw_reply.wat b/benchmarks/noop-wat/wat/raw_reply.wat deleted file mode 100644 index 0ab9f11fd..000000000 --- a/benchmarks/noop-wat/wat/raw_reply.wat +++ /dev/null @@ -1,17 +0,0 @@ -(module - (import "env" "memory" (memory 1)) - (import "env" "gr_reply" (func $gr_reply (param i32 i32 i32 i32))) - - (data (i32.const 256) "\01") - - (func $init) - - (func $handle - (call $gr_reply - (i32.const 256) - (i32.const 1) - (i32.const -1) - (i32.const 272))) - - (export "init" (func $init)) - (export "handle" (func $handle))) diff --git a/benchmarks/noop-wat/wat/sails_wire_reply.wat b/benchmarks/noop-wat/wat/sails_wire_reply.wat deleted file mode 100644 index b2a33e532..000000000 --- a/benchmarks/noop-wat/wat/sails_wire_reply.wat +++ /dev/null @@ -1,28 +0,0 @@ -(module - (import "env" "memory" (memory 1)) - (import "env" "gr_size" (func $gr_size (param i32))) - (import "env" "gr_read" (func $gr_read (param i32 i32 i32 i32))) - (import "env" "gr_reply" (func $gr_reply (param i32 i32 i32 i32))) - - ;; Sails v1 header for NoopSails.noop plus SCALE bool(true). - ;; "GM", version=1, hlen=16, interface=5457cc30dd94ce29, - ;; entry_id=0, route_id=1, reserved=0, bool=true. - (data (i32.const 512) "\47\4d\01\10\54\57\cc\30\dd\94\ce\29\00\00\01\00\01") - - (func $init) - - (func $handle - (call $gr_size (i32.const 256)) - (call $gr_read - (i32.const 0) - (i32.load (i32.const 256)) - (i32.const 320) - (i32.const 288)) - (call $gr_reply - (i32.const 512) - (i32.const 17) - (i32.const -1) - (i32.const 272))) - - (export "init" (func $init)) - (export "handle" (func $handle))) diff --git a/benchmarks/src/benchmarks.rs b/benchmarks/src/benchmarks.rs index b9886a02b..98e4ce613 100644 --- a/benchmarks/src/benchmarks.rs +++ b/benchmarks/src/benchmarks.rs @@ -8,9 +8,6 @@ //! - **Compute performance benchmarks** - Tests CPU-intensive operations like Fibonacci calculations (`compute_stress_bench` test) //! - **Cross-program performance** - Tests performance of cross-program communication (`cross_program_bench` test) //! - **Redirect performance** - Tests performance of redirecting calls to another program (`redirect_bench` test) -//! - **Example storage benchmarks** - Tests example-shaped state paths (`aggregator_tracker_bench` test) -//! - **Storage benchmarks** - Compares allocator-backed maps with fixed open-addressed maps (`storage_stress_bench` test) -//! - **VFT storage benchmarks** - Tests transfer-shaped balance/allowance paths (`vft_storage_transfer_bench` test) //! //! All benchmarks use the `gtest` framework to simulate on-chain execution and measure //! gas consumption. Results are stored to the shared benchmark data file for analysis. @@ -20,16 +17,6 @@ //! cargo test --release --manifest-path=benchmarks/Cargo.toml //! ``` -#[cfg(feature = "gas-profile")] -use crate::clients::minimal_vft_sails_client::{ - MinimalVftSails as _, MinimalVftSailsCtors, MinimalVftSailsProgram, vft::Vft as _, -}; -#[cfg(feature = "gas-profile")] -use crate::clients::noop_sails_client::{ - NoopSails as _, NoopSailsCtors, NoopSailsProgram, noop_sails::NoopSails as _, -}; -#[cfg(feature = "gas-profile")] -use crate::clients::vft_stress_client::vft_stress::{VftPhase, VftProfileResult}; use crate::clients::{ alloc_stress_client::{ AllocStress as _, AllocStressCtors, AllocStressProgram, alloc_stress::*, @@ -40,30 +27,7 @@ use crate::clients::{ counter_bench_client::{ CounterBench as _, CounterBenchCtors, CounterBenchProgram, counter_bench::*, }, - storage_million_client::{ - StorageMillion as _, StorageMillionCtors, StorageMillionProgram, storage_million::*, - }, - storage_stress_client::{ - StorageStress as _, StorageStressCtors, StorageStressProgram, - storage_stress::{ - StorageBackend, StorageBenchResult, StorageMap, StorageOp, StorageStress as _, - }, - }, - vft_stress_client::{ - VftStress as _, VftStressCtors, VftStressProgram, - vft_stress::{VftStorageBackend, VftStress as _, VftTransferOp, VftTransferResult}, - }, -}; -use aggregator_client::{ - AggregatorClient as _, AggregatorClientCtors, AggregatorClientProgram, - TrackerBackend as AggregatorTrackerBackend, - aggregator::{ - Aggregator as _, OpStatus as AggregatorOpStatus, - TrackerBenchResult as AggregatorTrackerBenchResult, TrackerOp as AggregatorTrackerOp, - }, }; -#[cfg(feature = "gas-profile")] -use gtest::{Program as GtestProgram, ProgramBuilder as GtestProgramBuilder}; use gtest::{System, constants::DEFAULT_USER_ALICE}; use itertools::{Either, Itertools}; use ping_pong_bench_app::client::{PingPong, PingPongCtors, PingPongProgram, ping_pong_service::*}; @@ -72,27 +36,9 @@ use redirect_proxy_client::{ RedirectProxyClient, RedirectProxyClientCtors, RedirectProxyClientProgram, proxy::*, }; use sails_rs::{client::*, prelude::*}; -#[cfg(feature = "gas-profile")] -use std::path::Path; -use std::{collections::BTreeMap, env, sync::atomic::AtomicU64}; +use std::{collections::BTreeMap, sync::atomic::AtomicU64}; static COUNTER_SALT: AtomicU64 = AtomicU64::new(0); -#[cfg(feature = "gas-profile")] -const DEFAULT_FLOOR_SAMPLES: u32 = 3; -#[cfg(feature = "gas-profile")] -const HOT_VFT_INTERFACE_ID: [u8; 8] = [0x6d, 0x69, 0x6e, 0x76, 0x66, 0x74, 0x00, 0x01]; -#[cfg(feature = "gas-profile")] -const HOT_VFT_ROUTE_ID: u8 = 1; -#[cfg(feature = "gas-profile")] -const HOT_VFT_APPROVE_ENTRY: u16 = 0; -#[cfg(feature = "gas-profile")] -const HOT_VFT_TRANSFER_ENTRY: u16 = 1; -#[cfg(feature = "gas-profile")] -const HOT_VFT_TRANSFER_FROM_ENTRY: u16 = 2; -const STORAGE_MILLION_LOAD: u32 = 1_000_000; -const STORAGE_MILLION_SAMPLES: u32 = 1; -const STORAGE_MILLION_BATCH_COUNT: u32 = 512; -const STORAGE_MILLION_PREPARE_CHUNK: u32 = 512; #[tokio::test] async fn alloc_stress_bench() { @@ -205,45 +151,6 @@ async fn counter_bench() { .unwrap(); } -#[cfg(feature = "gas-profile")] -#[tokio::test] -async fn noop_floor_bench() { - let mut benches: BTreeMap> = Default::default(); - - for sample in 0..floor_samples() { - for (key, gas) in noop_floor_test(sample).await { - benches.entry(key).or_default().push(gas); - } - } - - let medians = benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))) - .collect::>(); - - crate::write_gas_profile_summary(&medians, &render_noop_floor_markdown(&medians)).unwrap(); -} - -#[cfg(feature = "gas-profile")] -#[tokio::test] -async fn minimal_vft_floor_bench() { - let mut benches: BTreeMap> = Default::default(); - - for sample in 0..floor_samples() { - for (key, gas) in minimal_vft_floor_test(sample).await { - benches.entry(key).or_default().push(gas); - } - } - - let medians = benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))) - .collect::>(); - - crate::write_gas_profile_summary(&medians, &render_minimal_vft_floor_markdown(&medians)) - .unwrap(); -} - #[tokio::test] async fn cross_program_bench() { let wasm_path = "../target/wasm32-gear/release/ping_pong_bench_app.opt.wasm"; @@ -365,440 +272,6 @@ async fn message_stack_bench() { } } -#[tokio::test] -async fn aggregator_tracker_bench() { - let mut benches: BTreeMap> = Default::default(); - let loads = [0, 16, 256, 1024]; - - for sample in 0..10 { - for backend in [ - AggregatorTrackerBackend::BTree, - AggregatorTrackerBackend::SailsFixed, - ] { - for &load in loads.iter() { - let run = aggregator_tracker_test(backend.clone(), load, sample).await; - - for gas in run.prepare { - benches - .entry(aggregator_tracker_prepare_key(&backend, load)) - .or_default() - .push(gas); - } - - for (op, gas) in run.operations { - benches - .entry(aggregator_tracker_bench_key(&backend, &op, load)) - .or_default() - .push(gas); - } - } - } - } - - let medians = benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))) - .collect(); - - crate::store_bench_data(|bench_data| { - bench_data.replace_example_benches(medians); - }) - .unwrap(); -} - -#[tokio::test] -async fn storage_million_static_bench() { - let mut benches: BTreeMap> = Default::default(); - - for backend in [ - MillionStorageBackend::GenericStatic, - MillionStorageBackend::WatActorStatic, - MillionStorageBackend::MixedActorStatic, - MillionStorageBackend::ControlActorStatic, - MillionStorageBackend::PageLocalActorStatic, - MillionStorageBackend::GroupedActorPages2, - MillionStorageBackend::GroupedActorPages4, - MillionStorageBackend::GroupedActorPages8, - MillionStorageBackend::GroupedActorPages16, - MillionStorageBackend::GroupedActorPages32, - MillionStorageBackend::GroupedActorPages64, - MillionStorageBackend::GroupedActorPages128, - ] { - for sample in 0..STORAGE_MILLION_SAMPLES { - let run = - storage_million_static_test(backend.clone(), STORAGE_MILLION_LOAD, sample).await; - - benches - .entry(storage_million_prepare_key(&backend, STORAGE_MILLION_LOAD)) - .or_default() - .push(run.prepare_total); - - for (op, gas) in run.operations { - benches - .entry(storage_million_bench_key( - &backend, - &op, - STORAGE_MILLION_LOAD, - )) - .or_default() - .push(gas); - } - - for (op, gas) in run.batch_operations { - benches - .entry(storage_million_batch_key( - &backend, - &op, - STORAGE_MILLION_BATCH_COUNT, - STORAGE_MILLION_LOAD, - )) - .or_default() - .push(gas); - } - } - } - - let medians = benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))) - .collect(); - - crate::store_bench_data(|bench_data| { - bench_data.replace_storage_million_benches(medians); - }) - .unwrap(); -} - -#[tokio::test] -async fn vft_million_transfer_bench() { - let mut benches: BTreeMap> = Default::default(); - #[cfg(feature = "gas-profile")] - let mut profile_benches: BTreeMap> = Default::default(); - - let backends = filtered_million_vft_backends( - "SAILS_STORAGE_MILLION_VFT_BACKENDS", - &[ - MillionVftBackend::GenericStatic, - MillionVftBackend::GenericStaticFused, - MillionVftBackend::GenericStaticFast, - MillionVftBackend::WatActorStatic, - MillionVftBackend::MixedActorStatic, - MillionVftBackend::MixedActorFast, - MillionVftBackend::TagActorStatic, - MillionVftBackend::TagU64ActorStatic, - MillionVftBackend::ControlActorStatic, - MillionVftBackend::PageLocalActorStatic, - MillionVftBackend::InlineOwnerAccountU256, - ], - ); - - #[cfg(feature = "gas-profile")] - for sample in 0..STORAGE_MILLION_SAMPLES { - for (key, gas) in vft_framework_overhead_test(sample).await { - profile_benches.entry(key).or_default().push(gas); - } - } - - for backend in backends { - for sample in 0..STORAGE_MILLION_SAMPLES { - let run = - storage_million_vft_transfer_test(backend.clone(), STORAGE_MILLION_LOAD, sample) - .await; - - benches - .entry(storage_million_vft_prepare_key( - &backend, - STORAGE_MILLION_LOAD, - )) - .or_default() - .push(run.prepare_total); - - for (op, gas) in run.operations { - benches - .entry(storage_million_vft_key(&backend, &op, STORAGE_MILLION_LOAD)) - .or_default() - .push(gas); - } - } - } - - let medians = benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))) - .collect::>(); - - #[cfg(not(feature = "gas-profile"))] - crate::store_bench_data(|bench_data| { - for (key, value) in medians.clone() { - bench_data.update_storage_million_bench(key, value); - } - }) - .unwrap(); - - #[cfg(feature = "gas-profile")] - { - let mut profile_medians = medians.clone(); - profile_medians.extend( - profile_benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))), - ); - crate::write_gas_profile_summary( - &profile_medians, - &render_vft_comparison_markdown(&profile_medians), - ) - .unwrap(); - } -} - -#[tokio::test] -async fn vft_million_real_cost_bench() { - let mut benches: BTreeMap> = Default::default(); - #[cfg(feature = "gas-profile")] - let mut profile_benches: BTreeMap> = Default::default(); - - let backends = filtered_million_vft_backends( - "SAILS_STORAGE_MILLION_VFT_BACKENDS", - &[ - MillionVftBackend::GenericStatic, - MillionVftBackend::GenericStaticFused, - MillionVftBackend::GenericStaticFast, - MillionVftBackend::WatActorStatic, - MillionVftBackend::MixedActorStatic, - MillionVftBackend::MixedActorFast, - MillionVftBackend::TagActorStatic, - MillionVftBackend::TagU64ActorStatic, - MillionVftBackend::PageLocalActorStatic, - MillionVftBackend::ControlActorStatic, - MillionVftBackend::GroupedActorPages64, - MillionVftBackend::GroupedActorPages128, - MillionVftBackend::InlineOwnerAccountU256, - ], - ); - - #[cfg(feature = "gas-profile")] - for sample in 0..STORAGE_MILLION_SAMPLES { - for (key, gas) in vft_framework_overhead_test(sample).await { - profile_benches.entry(key).or_default().push(gas); - } - } - - for backend in backends { - for sample in 0..STORAGE_MILLION_SAMPLES { - let run = - storage_million_vft_real_cost_test(backend.clone(), STORAGE_MILLION_LOAD, sample) - .await; - - benches - .entry(storage_million_vft_real_prepare_key( - &backend, - STORAGE_MILLION_LOAD, - )) - .or_default() - .push(run.prepare_total); - - for (op, gas) in run.operations { - benches - .entry(storage_million_vft_real_key( - &backend, - op, - STORAGE_MILLION_LOAD, - )) - .or_default() - .push(gas); - } - } - } - - let medians = benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))) - .collect::>(); - - #[cfg(not(feature = "gas-profile"))] - crate::store_bench_data(|bench_data| { - for (key, value) in medians.clone() { - bench_data.update_storage_million_bench(key, value); - } - }) - .unwrap(); - - #[cfg(feature = "gas-profile")] - { - let mut profile_medians = medians.clone(); - profile_medians.extend( - profile_benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))), - ); - crate::write_gas_profile_summary( - &profile_medians, - &render_vft_comparison_markdown(&profile_medians), - ) - .unwrap(); - } -} - -#[tokio::test] -#[ignore = "BTreeMap/HashMap 1M VFT prepare may exhaust wasm heap; run manually for feasibility evidence"] -async fn vft_million_dynamic_baseline_bench() { - let mut benches: BTreeMap> = Default::default(); - - for backend in [MillionVftBackend::BTree, MillionVftBackend::HashMap] { - for sample in 0..STORAGE_MILLION_SAMPLES { - let run = - storage_million_vft_transfer_test(backend.clone(), STORAGE_MILLION_LOAD, sample) - .await; - - benches - .entry(storage_million_vft_prepare_key( - &backend, - STORAGE_MILLION_LOAD, - )) - .or_default() - .push(run.prepare_total); - - for (op, gas) in run.operations { - benches - .entry(storage_million_vft_key(&backend, &op, STORAGE_MILLION_LOAD)) - .or_default() - .push(gas); - } - } - } - - let medians = benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))) - .collect::>(); - - crate::store_bench_data(|bench_data| { - for (key, value) in medians { - bench_data.update_storage_million_bench(key, value); - } - }) - .unwrap(); -} - -#[tokio::test] -async fn storage_stress_bench() { - let mut benches: BTreeMap> = Default::default(); - let loads = [0, 16, 256, 1024]; - let wasm_path = "../target/wasm32-gear/release/storage_stress.opt.wasm"; - assert_storage_stress_wasm_static_memory_layout(wasm_path); - - for sample in 0..10 { - for backend in [ - StorageBackend::HashMap, - StorageBackend::Fixed, - StorageBackend::RawStatic, - StorageBackend::SailsFixed, - StorageBackend::SailsStatic, - ] { - for map in [StorageMap::Balance, StorageMap::Allowance] { - for &load in loads.iter() { - let run = storage_stress_test(backend.clone(), map.clone(), load, sample).await; - - for gas in run.prepare { - benches - .entry(storage_prepare_key(&backend, &map, load)) - .or_default() - .push(gas); - } - - for (op, gas) in run.operations { - benches - .entry(storage_bench_key(&backend, &map, &op, load)) - .or_default() - .push(gas); - } - } - } - } - } - - let medians = benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))) - .collect(); - - crate::store_bench_data(|bench_data| { - bench_data.replace_storage_benches(medians); - }) - .unwrap(); -} - -#[tokio::test] -async fn vft_storage_transfer_bench() { - let mut benches: BTreeMap> = Default::default(); - #[cfg(feature = "gas-profile")] - let mut profile_benches: BTreeMap> = Default::default(); - let loads = [16, 64, 128, 256, 1024]; - let wasm_path = "../target/wasm32-gear/release/vft_stress.opt.wasm"; - assert_vft_stress_wasm_static_memory_layout(wasm_path); - - for sample in 0..10 { - #[cfg(feature = "gas-profile")] - for (key, gas) in vft_framework_overhead_test(sample).await { - profile_benches.entry(key).or_default().push(gas); - } - - for backend in [ - VftStorageBackend::BTree, - VftStorageBackend::HashMap, - VftStorageBackend::SailsFixed, - VftStorageBackend::SailsStatic, - VftStorageBackend::SailsStaticFast, - ] { - for &load in loads.iter() { - let run = vft_storage_transfer_test(backend.clone(), load, sample).await; - - for gas in run.prepare { - benches - .entry(vft_prepare_key(&backend, load)) - .or_default() - .push(gas); - } - - for (op, gas) in run.operations { - benches - .entry(vft_named_key(&backend, &op, load)) - .or_default() - .push(gas); - } - } - } - } - - let medians = benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))) - .collect::>(); - - #[cfg(not(feature = "gas-profile"))] - crate::store_bench_data(|bench_data| { - for (key, value) in medians.clone() { - bench_data.update_storage_bench(key, value); - } - }) - .unwrap(); - - #[cfg(feature = "gas-profile")] - { - let mut profile_medians = medians.clone(); - profile_medians.extend( - profile_benches - .into_iter() - .map(|(key, gas_benches)| (key, median(gas_benches))), - ); - crate::write_gas_profile_summary( - &profile_medians, - &render_vft_comparison_markdown(&profile_medians), - ) - .unwrap(); - } -} - async fn alloc_stress_test(n: u32) -> (usize, u64) { // Path taken from the .binpath file let wasm_path = "../target/wasm32-gear/release/alloc_stress.opt.wasm"; @@ -846,2362 +319,67 @@ async fn message_stack_test(limit: u32) -> u64 { gas } -struct StorageStressRun { - prepare: Vec, - operations: Vec<(StorageOp, u64)>, -} - -struct VftTransferRun { - prepare: Vec, - operations: Vec<(String, u64)>, -} - -struct AggregatorTrackerRun { - prepare: Vec, - operations: Vec<(AggregatorTrackerOp, u64)>, -} - -struct StorageMillionRun { - prepare_total: u64, - operations: Vec<(MillionStorageOp, u64)>, - batch_operations: Vec<(MillionStorageOp, u64)>, -} - -struct MillionVftTransferRun { - prepare_total: u64, - operations: Vec<(MillionVftTransferOp, u64)>, +fn create_env() -> GtestEnv { + let system = System::new(); + system.mint_to(DEFAULT_USER_ALICE, 1_000_000_000_000_000); + GtestEnv::new(system, DEFAULT_USER_ALICE.into()) } -struct MillionVftRealCostRun { - prepare_total: u64, - operations: Vec<(&'static str, u64)>, +async fn deploy_for_bench(env: &GtestEnv, wasm_path: &str, f: F) -> Actor +where + P: Program, + IO: ServiceCall, + ::Output: PendingCtorOutput, + F: FnOnce(Deployment) -> PendingCtor, +{ + let code_id = env.system().submit_local_code_file(wasm_path); + deploy_code_for_bench(env, code_id, f).await } -async fn storage_million_static_test( - backend: MillionStorageBackend, - load: u32, - sample: u32, -) -> StorageMillionRun { - let wasm_path = "../target/wasm32-gear/release/storage_million.opt.wasm"; - let env = create_env(); - let program = - deploy_for_bench(&env, wasm_path, |d| StorageMillionCtors::new_for_bench(d)).await; - let mut service = program.storage_million(); - - let mut start = 0; - let mut prepare_total = 0u64; - while start < load { - let len = (load - start).min(STORAGE_MILLION_PREPARE_CHUNK); - let message_id = service - .prepare_chunk(backend.clone(), start, len) - .send_one_way() - .unwrap(); - let (payload, gas) = extract_reply_and_gas(env.system(), message_id); - prepare_total += gas; - start += len; - - let result = - crate::clients::storage_million_client::storage_million::io::PrepareChunk::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert_eq!(result.len, start); - } - - let mut current_len = load; - let mut operations = Vec::with_capacity(storage_million_ops().len()); - for op in storage_million_ops() { - let seed = storage_million_seed_for_op(&op, load, sample); - let message_id = service - .bench(backend.clone(), op.clone(), seed) - .send_one_way() - .unwrap(); - let (payload, gas) = extract_reply_and_gas(env.system(), message_id); - let result = - crate::clients::storage_million_client::storage_million::io::Bench::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - - assert_storage_million_result(&op, seed, &mut current_len, &result); - operations.push((op, gas)); - } - - let mut batch_operations = Vec::with_capacity(storage_million_ops().len()); - for op in storage_million_ops() { - let start_seed = storage_million_batch_seed_for_op(&op, sample); - let message_id = service - .bench_many( - backend.clone(), - op.clone(), - start_seed, - STORAGE_MILLION_BATCH_COUNT, - ) - .send_one_way() - .unwrap(); - let (payload, gas) = extract_reply_and_gas(env.system(), message_id); - let result = - crate::clients::storage_million_client::storage_million::io::BenchMany::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - - assert_storage_million_batch_result( - &op, - start_seed, - STORAGE_MILLION_BATCH_COUNT, - &mut current_len, - &result, - ); - batch_operations.push((op, gas)); - } - - StorageMillionRun { - prepare_total, - operations, - batch_operations, - } +async fn deploy_code_for_bench( + env: &GtestEnv, + code_id: CodeId, + f: F, +) -> Actor +where + P: Program, + IO: ServiceCall, + ::Output: PendingCtorOutput, + F: FnOnce(Deployment) -> PendingCtor, +{ + let salt = COUNTER_SALT + .fetch_add(1, std::sync::atomic::Ordering::SeqCst) + .to_le_bytes() + .to_vec(); + let deployment = env.deploy::

(code_id, salt); + let ctor = f(deployment); + let program = ctor + .with_value(100_000_000_000_000) + .await + .expect("failed to initialize the program"); + <::Output as PendingCtorOutput< + P, + sails_rs::client::GtestEnv, + >>::actor(program) } -async fn storage_million_vft_transfer_test( - backend: MillionVftBackend, - load: u32, - sample: u32, -) -> MillionVftTransferRun { - let wasm_path = "../target/wasm32-gear/release/storage_million.opt.wasm"; - let env = create_env(); - let program = - deploy_for_bench(&env, wasm_path, |d| StorageMillionCtors::new_for_bench(d)).await; - let mut service = program.storage_million(); - - let mut start = 0; - let mut prepare_total = 0u64; - while start < load { - let len = (load - start).min(STORAGE_MILLION_PREPARE_CHUNK); - let message_id = service - .prepare_vft_chunk(backend.clone(), start, len) - .send_one_way() - .unwrap(); - let (payload, gas) = extract_reply_and_gas(env.system(), message_id); - prepare_total += gas; - start += len; - - let result = crate::clients::storage_million_client::storage_million::io::PrepareVftChunk::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert_eq!(result.balance_len, start); - assert_eq!(result.allowance_len, start); - } - - let mut operations = Vec::with_capacity(storage_million_vft_ops().len()); - for op in storage_million_vft_ops() { - let seed = storage_million_vft_seed_for_op(&op, load, sample); - let message_id = service - .bench_vft_transfer(backend.clone(), op.clone(), seed) - .send_one_way() - .unwrap(); - let (payload, gas) = extract_reply_and_gas(env.system(), message_id); - let result = crate::clients::storage_million_client::storage_million::io::BenchVftTransfer::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - - assert_storage_million_vft_result(&op, seed, load, &result); - operations.push((op, gas)); - } - - MillionVftTransferRun { - prepare_total, - operations, - } -} - -async fn storage_million_vft_real_cost_test( - backend: MillionVftBackend, - load: u32, - sample: u32, -) -> MillionVftRealCostRun { - let wasm_path = "../target/wasm32-gear/release/storage_million.opt.wasm"; - let env = create_env(); - let program = - deploy_for_bench(&env, wasm_path, |d| StorageMillionCtors::new_for_bench(d)).await; - let mut service = program.storage_million(); - - let mut start = 0; - let mut prepare_total = 0u64; - while start < load { - let len = (load - start).min(STORAGE_MILLION_PREPARE_CHUNK); - let message_id = service - .prepare_vft_chunk(backend.clone(), start, len) - .send_one_way() - .unwrap(); - let (payload, gas) = extract_reply_and_gas(env.system(), message_id); - prepare_total += gas; - start += len; - - let result = crate::clients::storage_million_client::storage_million::io::PrepareVftChunk::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert_eq!(result.balance_len, start); - assert_eq!(result.allowance_len, start); - } - - let mut operations = Vec::with_capacity(7); - - let transfer_seed = 20_000 + sample; - let message_id = service - .bench_vft_transfer_bool( - backend.clone(), - MillionVftTransferOp::Transfer, - transfer_seed, - ) - .send_one_way() - .unwrap(); - let profile_case = (sample == 0).then(|| { - format!( - "{}_sample{sample}", - storage_million_vft_real_key(&backend, "transfer_bool", load) - ) - }); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let transferred = crate::clients::storage_million_client::storage_million::io::BenchVftTransferBool::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert!(transferred); - operations.push(("transfer_bool", gas)); - - let fresh_seed = 40_000 + sample; - let message_id = service - .bench_vft_transfer_fresh_bool(backend.clone(), fresh_seed) - .send_one_way() - .unwrap(); - let profile_case = (sample == 0).then(|| { - format!( - "{}_sample{sample}", - storage_million_vft_real_key(&backend, "transfer_fresh_bool", load) - ) - }); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let transferred = crate::clients::storage_million_client::storage_million::io::BenchVftTransferFreshBool::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert!(transferred); - operations.push(("transfer_fresh_bool", gas)); - - let transfer_from_seed = 30_000 + sample; - let message_id = service - .bench_vft_transfer_bool( - backend.clone(), - MillionVftTransferOp::TransferFrom, - transfer_from_seed, - ) - .send_one_way() - .unwrap(); - let profile_case = (sample == 0).then(|| { - format!( - "{}_sample{sample}", - storage_million_vft_real_key(&backend, "transfer_from_bool", load) - ) - }); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let transferred = crate::clients::storage_million_client::storage_million::io::BenchVftTransferBool::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert!(transferred); - operations.push(("transfer_from_bool", gas)); - - let owner_seed = 50_000 + sample; - let spender_seed = load + 60_000 + sample; - let message_id = service - .bench_vft_approve_bool(backend.clone(), owner_seed, spender_seed) - .send_one_way() - .unwrap(); - let profile_case = (sample == 0).then(|| { - format!( - "{}_sample{sample}", - storage_million_vft_real_key(&backend, "approve_fresh_bool", load) - ) - }); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let approved = crate::clients::storage_million_client::storage_million::io::BenchVftApproveBool::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert!(approved); - operations.push(("approve_fresh_bool", gas)); - - let owner_seed = 70_000 + sample; - let spender_seed = load + 170_001 + sample; - let message_id = service - .bench_vft_approve_bool(backend.clone(), owner_seed, spender_seed) - .send_one_way() - .unwrap(); - let profile_case = (sample == 0).then(|| { - format!( - "{}_sample{sample}", - storage_million_vft_real_key(&backend, "approve_second_bool", load) - ) - }); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let approved = crate::clients::storage_million_client::storage_million::io::BenchVftApproveBool::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert!(approved); - operations.push(("approve_second_bool", gas)); - - let spender_seed = load + 170_002 + sample; - let message_id = service - .bench_vft_approve_bool(backend.clone(), owner_seed, spender_seed) - .send_one_way() - .unwrap(); - let profile_case = (sample == 0).then(|| { - format!( - "{}_sample{sample}", - storage_million_vft_real_key(&backend, "approve_overflow_third_bool", load) - ) - }); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let approved = crate::clients::storage_million_client::storage_million::io::BenchVftApproveBool::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert!(approved); - operations.push(("approve_overflow_third_bool", gas)); - - let owner_seed = 80_000 + sample; - let inline_second_spender_seed = load + 180_001 + sample; - let overflow_spender_seed = load + 180_002 + sample; - let message_id = service - .bench_vft_approve_bool(backend.clone(), owner_seed, inline_second_spender_seed) - .send_one_way() - .unwrap(); - let (payload, _) = extract_reply_and_gas(env.system(), message_id); - let approved = crate::clients::storage_million_client::storage_million::io::BenchVftApproveBool::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert!(approved); - let message_id = service - .bench_vft_approve_bool(backend.clone(), owner_seed, overflow_spender_seed) - .send_one_way() - .unwrap(); - let (payload, _) = extract_reply_and_gas(env.system(), message_id); - let approved = crate::clients::storage_million_client::storage_million::io::BenchVftApproveBool::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert!(approved); - let message_id = service - .bench_vft_transfer_from_spender_bool(backend.clone(), owner_seed, overflow_spender_seed) - .send_one_way() - .unwrap(); - let profile_case = (sample == 0).then(|| { - format!( - "{}_sample{sample}", - storage_million_vft_real_key(&backend, "transfer_from_overflow_bool", load) - ) - }); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let transferred = crate::clients::storage_million_client::storage_million::io::BenchVftTransferFromSpenderBool::decode_reply( - StorageMillionProgram::ROUTE_ID_STORAGE_MILLION, - payload.as_slice(), - ) - .unwrap(); - assert!(transferred); - operations.push(("transfer_from_overflow_bool", gas)); - - MillionVftRealCostRun { - prepare_total, - operations, - } -} - -async fn aggregator_tracker_test( - backend: AggregatorTrackerBackend, - load: u32, - sample: u32, -) -> AggregatorTrackerRun { - let env = create_env(); - let code_id = env.system().submit_code(aggregator_app::WASM_BINARY); - let program = deploy_code_for_bench(&env, code_id, |d| { - AggregatorClientCtors::new_with_tracker(d, ActorId::zero(), backend.clone()) - }) - .await; - let mut service = program.aggregator(); - - let ops = aggregator_tracker_ops_for_load(load); - let mut prepare = Vec::with_capacity(ops.len()); - let mut operations = Vec::with_capacity(ops.len()); - - for op in ops { - let message_id = service.prepare_tracker(load).send_one_way().unwrap(); - let (payload, gas) = extract_reply_and_gas(env.system(), message_id); - prepare.push(gas); - let prep = aggregator_client::aggregator::io::PrepareTracker::decode_reply( - AggregatorClientProgram::ROUTE_ID_AGGREGATOR, - payload.as_slice(), - ) - .unwrap(); - assert_eq!(prep.len, load); - - let seed = aggregator_tracker_seed_for_op(&op, load, sample); - let message_id = service - .bench_tracker(op.clone(), seed) - .send_one_way() - .unwrap(); - let (payload, gas) = extract_reply_and_gas(env.system(), message_id); - let result = aggregator_client::aggregator::io::BenchTracker::decode_reply( - AggregatorClientProgram::ROUTE_ID_AGGREGATOR, - payload.as_slice(), - ) - .unwrap(); - - assert_aggregator_tracker_result(&op, load, &result); - operations.push((op, gas)); - } - - AggregatorTrackerRun { - prepare, - operations, - } -} - -fn aggregator_tracker_ops_for_load(load: u32) -> Vec { - if load == 0 { - vec![ - AggregatorTrackerOp::InsertFresh, - AggregatorTrackerOp::ListStatuses, - ] - } else { - vec![ - AggregatorTrackerOp::InsertFresh, - AggregatorTrackerOp::UpdateExisting, - AggregatorTrackerOp::ReadExisting, - AggregatorTrackerOp::ListStatuses, - ] - } -} - -fn aggregator_tracker_seed_for_op(op: &AggregatorTrackerOp, load: u32, sample: u32) -> u32 { - match op { - AggregatorTrackerOp::InsertFresh => 10_000 + load + sample, - AggregatorTrackerOp::UpdateExisting | AggregatorTrackerOp::ReadExisting => { - 1 + (sample % load) - } - AggregatorTrackerOp::ListStatuses => 0, - } -} - -fn assert_aggregator_tracker_result( - op: &AggregatorTrackerOp, - load: u32, - result: &AggregatorTrackerBenchResult, -) { - match op { - AggregatorTrackerOp::InsertFresh => { - assert_eq!(result.len, load + 1); - assert_eq!(result.status, Some(AggregatorOpStatus::Started)); - assert!(!result.existed); - } - AggregatorTrackerOp::UpdateExisting => { - assert_eq!(result.len, load); - assert_eq!(result.status, Some(AggregatorOpStatus::Finalized)); - assert!(result.existed); - } - AggregatorTrackerOp::ReadExisting => { - assert_eq!(result.len, load); - assert_eq!(result.status, Some(AggregatorOpStatus::Started)); - assert!(result.existed); - } - AggregatorTrackerOp::ListStatuses => { - assert_eq!(result.len, load); - if load == 0 { - assert_eq!(result.status, None); - assert!(!result.existed); - } else { - assert_eq!(result.status, Some(AggregatorOpStatus::Started)); - assert!(result.existed); - } - } - } -} - -async fn storage_stress_test( - backend: StorageBackend, - map: StorageMap, - load: u32, - sample: u32, -) -> StorageStressRun { - let wasm_path = "../target/wasm32-gear/release/storage_stress.opt.wasm"; - let env = create_env(); - let program = deploy_for_bench(&env, wasm_path, |d| StorageStressCtors::new_for_bench(d)).await; - let mut service = program.storage_stress(); - - let ops = storage_ops_for_load(load); - let mut prepare = Vec::with_capacity(ops.len()); - let mut operations = Vec::with_capacity(ops.len()); - - for op in ops { - let message_id = service - .prepare(backend.clone(), map.clone(), load) - .send_one_way() - .unwrap(); - let (payload, gas) = extract_reply_and_gas(env.system(), message_id); - prepare.push(gas); - let prep = - crate::clients::storage_stress_client::storage_stress::io::Prepare::decode_reply( - StorageStressProgram::ROUTE_ID_STORAGE_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert_eq!(prep.len, load); - - let seed = storage_seed_for_op(&op, load, sample); - let message_id = service - .bench(backend.clone(), map.clone(), op.clone(), seed) - .send_one_way() - .unwrap(); - let (payload, gas) = extract_reply_and_gas(env.system(), message_id); - let result = - crate::clients::storage_stress_client::storage_stress::io::Bench::decode_reply( - StorageStressProgram::ROUTE_ID_STORAGE_STRESS, - payload.as_slice(), - ) - .unwrap(); - - assert_storage_result(&op, load, seed, &result); - operations.push((op, gas)); - } - - StorageStressRun { - prepare, - operations, - } -} - -#[cfg(feature = "gas-profile")] -async fn noop_floor_test(sample: u32) -> Vec<(String, u64)> { - let mut operations = Vec::with_capacity(6); - - if floor_row_enabled("noop_wat_raw") { - operations.push(noop_wat_raw_floor_test(sample).await); - } - if floor_row_enabled("noop_wat_sails_wire") { - operations.push(noop_wat_sails_wire_floor_test(sample).await); - } - if floor_row_enabled("noop_gstd") { - operations.push(noop_gstd_floor_test(sample).await); - } - if floor_row_enabled("noop_sails_hot") { - operations.push(noop_sails_hot_floor_test(sample).await); - } - if floor_row_enabled("noop_sails") { - operations.push(noop_sails_floor_test(sample).await); - } - - let vft_wasm_path = "../target/wasm32-gear/release/vft_stress.opt.wasm"; - if Path::new(vft_wasm_path).exists() - && (floor_row_enabled("vft_framework_noop") || floor_row_enabled("vft_framework_echo_args")) - { - operations.extend(vft_framework_overhead_test(sample).await); - } - - operations -} - -#[cfg(feature = "gas-profile")] -async fn noop_wat_raw_floor_test(sample: u32) -> (String, u64) { - let env = create_env(); - let program = GtestProgramBuilder::from_binary(::noop_wat::RAW_REPLY_WASM).build(env.system()); - - let init_id = program.send_bytes(DEFAULT_USER_ALICE, Vec::new()); - let init_res = env.system().run_next_block(); - assert!( - init_res.succeed.contains(&init_id), - "noop-wat raw init failed: {init_res:?}" - ); - - let message_id = program.send_bytes(DEFAULT_USER_ALICE, Vec::new()); - let profile_case = (sample == 0).then(|| format!("noop_wat_raw_sample{sample}")); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - assert_eq!(payload, vec![1u8]); - - ("noop_wat_raw".to_owned(), gas) -} - -#[cfg(feature = "gas-profile")] -async fn noop_wat_sails_wire_floor_test(sample: u32) -> (String, u64) { - let env = create_env(); - let program = - GtestProgramBuilder::from_binary(::noop_wat::SAILS_WIRE_REPLY_WASM).build(env.system()); - - let init_id = program.send_bytes(DEFAULT_USER_ALICE, Vec::new()); - let init_res = env.system().run_next_block(); - assert!( - init_res.succeed.contains(&init_id), - "noop-wat Sails-wire init failed: {init_res:?}" - ); - - let message_id = program.send_bytes(DEFAULT_USER_ALICE, noop_sails_payload()); - let profile_case = (sample == 0).then(|| format!("noop_wat_sails_wire_sample{sample}")); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let ok = crate::clients::noop_sails_client::noop_sails::io::Noop::decode_reply( - NoopSailsProgram::ROUTE_ID_NOOP_SAILS, - payload.as_slice(), - ) - .unwrap(); - assert!(ok); - - ("noop_wat_sails_wire".to_owned(), gas) -} - -#[cfg(feature = "gas-profile")] -async fn noop_gstd_floor_test(sample: u32) -> (String, u64) { - let _ = ::noop_gstd::PROGRAM_NAME; - let wasm_path = "../target/wasm32-gear/release/noop_gstd.opt.wasm"; - let env = create_env(); - let program = GtestProgram::from_file(env.system(), wasm_path); - - let init_id = program.send_bytes(DEFAULT_USER_ALICE, Vec::new()); - let init_res = env.system().run_next_block(); - assert!( - init_res.succeed.contains(&init_id), - "noop-gstd init failed: {init_res:?}" - ); - - let message_id = program.send_bytes(DEFAULT_USER_ALICE, Vec::new()); - let profile_case = (sample == 0).then(|| format!("noop_gstd_sample{sample}")); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - assert_eq!(payload, vec![1u8]); - - ("noop_gstd".to_owned(), gas) -} - -#[cfg(feature = "gas-profile")] -async fn noop_sails_hot_floor_test(sample: u32) -> (String, u64) { - let _ = ::noop_sails_hot::PROGRAM_NAME; - let wasm_path = "../target/wasm32-gear/release/noop_sails_hot.opt.wasm"; - let env = create_env(); - let program = GtestProgram::from_file(env.system(), wasm_path); - - let init_id = program.send_bytes(DEFAULT_USER_ALICE, Vec::new()); - let init_res = env.system().run_next_block(); - assert!( - init_res.succeed.contains(&init_id), - "noop-sails-hot init failed: {init_res:?}" - ); - - let message_id = program.send_bytes(DEFAULT_USER_ALICE, noop_sails_payload()); - let profile_case = (sample == 0).then(|| format!("noop_sails_hot_sample{sample}")); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let ok = crate::clients::noop_sails_client::noop_sails::io::Noop::decode_reply( - NoopSailsProgram::ROUTE_ID_NOOP_SAILS, - payload.as_slice(), - ) - .unwrap(); - assert!(ok); - - ("noop_sails_hot".to_owned(), gas) -} - -#[cfg(feature = "gas-profile")] -async fn noop_sails_floor_test(sample: u32) -> (String, u64) { - let wasm_path = "../target/wasm32-gear/release/noop_sails.opt.wasm"; - let env = create_env(); - let program = deploy_for_bench(&env, wasm_path, |d| NoopSailsCtors::new_for_bench(d)).await; - let mut service = program.noop_sails(); - - let message_id = service.noop().send_one_way().unwrap(); - let profile_case = (sample == 0).then(|| format!("noop_sails_sample{sample}")); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let ok = crate::clients::noop_sails_client::noop_sails::io::Noop::decode_reply( - NoopSailsProgram::ROUTE_ID_NOOP_SAILS, - payload.as_slice(), - ) - .unwrap(); - assert!(ok); - - ("noop_sails".to_owned(), gas) -} - -#[cfg(feature = "gas-profile")] -async fn vft_framework_overhead_test(sample: u32) -> Vec<(String, u64)> { - let wasm_path = "../target/wasm32-gear/release/vft_stress.opt.wasm"; - let env = create_env(); - let program = deploy_for_bench(&env, wasm_path, |d| VftStressCtors::new_for_bench(d)).await; - let mut service = program.vft_stress(); - let mut operations = Vec::with_capacity(2); - - let message_id = service.bench_noop().send_one_way().unwrap(); - let profile_case = (sample == 0).then(|| format!("vft_framework_noop_sample{sample}")); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let ok = crate::clients::vft_stress_client::vft_stress::io::BenchNoop::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert!(ok); - if floor_row_enabled("vft_framework_noop") { - operations.push(("vft_framework_noop".to_owned(), gas)); - } - - let message_id = service - .bench_echo_vft_args( - VftStorageBackend::SailsStatic, - VftTransferOp::Transfer, - sample + 1, - ) - .send_one_way() - .unwrap(); - let profile_case = (sample == 0).then(|| format!("vft_framework_echo_args_sample{sample}")); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let ok = crate::clients::vft_stress_client::vft_stress::io::BenchEchoVftArgs::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert!(ok); - if floor_row_enabled("vft_framework_echo_args") { - operations.push(("vft_framework_echo_args".to_owned(), gas)); - } - - operations -} - -#[cfg(feature = "gas-profile")] -async fn minimal_vft_floor_test(sample: u32) -> Vec<(String, u64)> { - let mut operations = Vec::with_capacity(8); - - if floor_row_enabled("minimal_vft_sails_approve") { - operations.push(minimal_vft_sails_approve_test(sample).await); - } - if floor_row_enabled("minimal_vft_sails_transfer") { - operations.push(minimal_vft_sails_transfer_test(sample).await); - } - if floor_row_enabled("minimal_vft_sails_transfer_from") { - operations.push(minimal_vft_sails_transfer_from_test(sample).await); - } - if floor_row_enabled("minimal_vft_hot_approve") { - operations.push(minimal_vft_hot_test("approve", HOT_VFT_APPROVE_ENTRY, sample).await); - } - if floor_row_enabled("minimal_vft_hot_transfer") { - operations.push(minimal_vft_hot_test("transfer", HOT_VFT_TRANSFER_ENTRY, sample).await); - } - if floor_row_enabled("minimal_vft_hot_transfer_from") { - operations - .push(minimal_vft_hot_test("transfer_from", HOT_VFT_TRANSFER_FROM_ENTRY, sample).await); - } - - let vft_wasm_path = "../target/wasm32-gear/release/vft_stress.opt.wasm"; - if Path::new(vft_wasm_path).exists() - && (floor_row_enabled("vft_framework_noop") || floor_row_enabled("vft_framework_echo_args")) - { - operations.extend(vft_framework_overhead_test(sample).await); - } - - operations -} - -#[cfg(feature = "gas-profile")] -async fn minimal_vft_sails_approve_test(sample: u32) -> (String, u64) { - let wasm_path = "../target/wasm32-gear/release/minimal_vft_sails.opt.wasm"; - let env = create_env(); - let program = - deploy_for_bench(&env, wasm_path, |d| MinimalVftSailsCtors::new_for_bench(d)).await; - let mut service = program.vft(); - - let spender = ActorId::from(2u64); - let amount = U256::from(10_000u64); - let message_id = service.approve(spender, amount).send_one_way().unwrap(); - let profile_case = (sample == 0).then(|| format!("minimal_vft_sails_approve_sample{sample}")); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let ok = crate::clients::minimal_vft_sails_client::vft::io::Approve::decode_reply( - MinimalVftSailsProgram::ROUTE_ID_VFT, - payload.as_slice(), - ) - .unwrap(); - assert!(ok); - - ("minimal_vft_sails_approve".to_owned(), gas) -} - -#[cfg(feature = "gas-profile")] -async fn minimal_vft_sails_transfer_test(sample: u32) -> (String, u64) { - let wasm_path = "../target/wasm32-gear/release/minimal_vft_sails.opt.wasm"; - let env = create_env(); - let program = - deploy_for_bench(&env, wasm_path, |d| MinimalVftSailsCtors::new_for_bench(d)).await; - let mut service = program.vft(); - - let to = ActorId::from(3u64); - let amount = U256::from(7u64); - let message_id = service.transfer(to, amount).send_one_way().unwrap(); - let profile_case = (sample == 0).then(|| format!("minimal_vft_sails_transfer_sample{sample}")); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let ok = crate::clients::minimal_vft_sails_client::vft::io::Transfer::decode_reply( - MinimalVftSailsProgram::ROUTE_ID_VFT, - payload.as_slice(), - ) - .unwrap(); - assert!(ok); - - ("minimal_vft_sails_transfer".to_owned(), gas) -} - -#[cfg(feature = "gas-profile")] -async fn minimal_vft_sails_transfer_from_test(sample: u32) -> (String, u64) { - let wasm_path = "../target/wasm32-gear/release/minimal_vft_sails.opt.wasm"; - let env = create_env(); - let program = - deploy_for_bench(&env, wasm_path, |d| MinimalVftSailsCtors::new_for_bench(d)).await; - let mut service = program.vft(); - - let owner = ActorId::from(DEFAULT_USER_ALICE); - let to = ActorId::from(4u64); - let amount = U256::from(5u64); - let message_id = service - .transfer_from(owner, to, amount) - .send_one_way() - .unwrap(); - let profile_case = - (sample == 0).then(|| format!("minimal_vft_sails_transfer_from_sample{sample}")); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let ok = crate::clients::minimal_vft_sails_client::vft::io::TransferFrom::decode_reply( - MinimalVftSailsProgram::ROUTE_ID_VFT, - payload.as_slice(), - ) - .unwrap(); - assert!(ok); - - ("minimal_vft_sails_transfer_from".to_owned(), gas) -} - -#[cfg(feature = "gas-profile")] -async fn minimal_vft_hot_test(label: &str, entry_id: u16, sample: u32) -> (String, u64) { - let wasm_path = "../target/wasm32-gear/release/minimal_vft_hot.opt.wasm"; - let env = create_env(); - let program = GtestProgram::from_file(env.system(), wasm_path); - - let init_id = program.send_bytes(DEFAULT_USER_ALICE, Vec::new()); - let init_res = env.system().run_next_block(); - assert!( - init_res.succeed.contains(&init_id), - "minimal-vft-hot init failed: {init_res:?}" - ); - - let payload = match entry_id { - HOT_VFT_APPROVE_ENTRY => hot_vft_payload( - entry_id, - (ActorId::from(2u64), U256::from(10_000u64)).encode(), - ), - HOT_VFT_TRANSFER_ENTRY => { - hot_vft_payload(entry_id, (ActorId::from(3u64), U256::from(7u64)).encode()) - } - HOT_VFT_TRANSFER_FROM_ENTRY => hot_vft_payload( - entry_id, - ( - ActorId::from(DEFAULT_USER_ALICE), - ActorId::from(4u64), - U256::from(5u64), - ) - .encode(), - ), - _ => unreachable!("unknown hot VFT entry"), - }; - - let message_id = program.send_bytes(DEFAULT_USER_ALICE, payload); - let profile_case = (sample == 0).then(|| format!("minimal_vft_hot_{label}_sample{sample}")); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - assert_sails_bool_reply(entry_id, &payload); - - (format!("minimal_vft_hot_{label}"), gas) -} - -async fn vft_storage_transfer_test( - backend: VftStorageBackend, - load: u32, - sample: u32, -) -> VftTransferRun { - let wasm_path = "../target/wasm32-gear/release/vft_stress.opt.wasm"; - let env = create_env(); - let program = deploy_for_bench(&env, wasm_path, |d| VftStressCtors::new_for_bench(d)).await; - let mut service = program.vft_stress(); - - let ops = vft_transfer_ops(); - let mut prepare = Vec::with_capacity(ops.len() + 2); - let mut operations = Vec::with_capacity(ops.len() + 2); - - for op in ops { - let message_id = service - .prepare_vft(backend.clone(), load) - .send_one_way() - .unwrap(); - let profile_case = - (sample == 0).then(|| format!("{}_sample{sample}", vft_prepare_key(&backend, load))); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - prepare.push(gas); - let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert_eq!(prep.balance_len, load); - assert_eq!(prep.allowance_len, load); - - let seed = vft_transfer_seed_for_op(&op, load, sample); - let message_id = service - .bench_vft_transfer(backend.clone(), op.clone(), seed) - .send_one_way() - .unwrap(); - let profile_case = (sample == 0) - .then(|| format!("{}_sample{sample}", vft_transfer_key(&backend, &op, load))); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, profile_case.as_deref()); - let result = - crate::clients::vft_stress_client::vft_stress::io::BenchVftTransfer::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - - assert_vft_transfer_result(&op, load, seed, &result); - operations.push((vft_transfer_op_name(&op).to_owned(), gas)); - } - - let fresh_prepare_id = service - .prepare_vft(backend.clone(), load) - .send_one_way() - .unwrap(); - let profile_case = - (sample == 0).then(|| format!("{}_sample{sample}", vft_prepare_key(&backend, load))); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), fresh_prepare_id, profile_case.as_deref()); - prepare.push(gas); - let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert_eq!(prep.balance_len, load); - assert_eq!(prep.allowance_len, load); - - let fresh_seed = 1 + (sample % load); - let fresh_id = service - .bench_vft_transfer_fresh_bool(backend.clone(), fresh_seed) - .send_one_way() - .unwrap(); - let profile_case = (sample == 0).then(|| { - format!( - "{}_sample{sample}", - vft_named_key(&backend, "transfer_fresh", load) - ) - }); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), fresh_id, profile_case.as_deref()); - let transferred = - crate::clients::vft_stress_client::vft_stress::io::BenchVftTransferFreshBool::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert!(transferred); - operations.push(("transfer_fresh".to_owned(), gas)); - - let approve_prepare_id = service - .prepare_vft(backend.clone(), load) - .send_one_way() - .unwrap(); - let profile_case = - (sample == 0).then(|| format!("{}_sample{sample}", vft_prepare_key(&backend, load))); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), approve_prepare_id, profile_case.as_deref()); - prepare.push(gas); - let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert_eq!(prep.balance_len, load); - assert_eq!(prep.allowance_len, load); - - let owner_seed = 1 + (sample % load); - let spender_seed = 40_000 + sample; - let approve_id = service - .bench_vft_approve_bool(backend.clone(), owner_seed, spender_seed) - .send_one_way() - .unwrap(); - let profile_case = (sample == 0).then(|| { - format!( - "{}_sample{sample}", - vft_named_key(&backend, "approve_fresh", load) - ) - }); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), approve_id, profile_case.as_deref()); - let approved = - crate::clients::vft_stress_client::vft_stress::io::BenchVftApproveBool::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert!(approved); - operations.push(("approve_fresh".to_owned(), gas)); - - #[cfg(feature = "gas-profile")] - if sample == 0 { - for op in vft_transfer_ops() { - let prepare_id = service - .prepare_vft(backend.clone(), load) - .send_one_way() - .unwrap(); - let (payload, _) = extract_reply_and_gas(env.system(), prepare_id); - let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert_eq!(prep.balance_len, load); - assert_eq!(prep.allowance_len, load); - - let seed = vft_transfer_seed_for_op(&op, load, sample); - let message_id = service - .bench_vft_transfer_profile(backend.clone(), op.clone(), seed) - .send_one_way() - .unwrap(); - let profile_case = format!( - "{}_sample{sample}", - vft_named_key( - &backend, - &format!("{}_profile", vft_transfer_op_name(&op)), - load - ) - ); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, Some(&profile_case)); - let profile = - crate::clients::vft_stress_client::vft_stress::io::BenchVftTransferProfile::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert_vft_transfer_result(&op, load, seed, &profile.result); - write_vft_phase_profile(&profile_case, gas, &profile); - } - - let prepare_id = service - .prepare_vft(backend.clone(), load) - .send_one_way() - .unwrap(); - let (payload, _) = extract_reply_and_gas(env.system(), prepare_id); - let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert_eq!(prep.balance_len, load); - assert_eq!(prep.allowance_len, load); - - let fresh_seed = 1 + (sample % load); - let profile_case = format!( - "{}_sample{sample}", - vft_named_key(&backend, "transfer_fresh_profile", load) - ); - let message_id = service - .bench_vft_transfer_fresh_profile(backend.clone(), fresh_seed) - .send_one_way() - .unwrap(); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, Some(&profile_case)); - let profile = - crate::clients::vft_stress_client::vft_stress::io::BenchVftTransferFreshProfile::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert_vft_transfer_result(&VftTransferOp::Transfer, load, fresh_seed, &profile.result); - write_vft_phase_profile(&profile_case, gas, &profile); - - let prepare_id = service - .prepare_vft(backend.clone(), load) - .send_one_way() - .unwrap(); - let (payload, _) = extract_reply_and_gas(env.system(), prepare_id); - let prep = crate::clients::vft_stress_client::vft_stress::io::PrepareVft::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert_eq!(prep.balance_len, load); - assert_eq!(prep.allowance_len, load); - - let owner_seed = 1 + (sample % load); - let spender_seed = 40_000 + sample; - let profile_case = format!( - "{}_sample{sample}", - vft_named_key(&backend, "approve_fresh_profile", load) - ); - let message_id = service - .bench_vft_approve_profile(backend.clone(), owner_seed, spender_seed) - .send_one_way() - .unwrap(); - let (payload, gas) = - extract_reply_and_gas_profiled(env.system(), message_id, Some(&profile_case)); - let profile = - crate::clients::vft_stress_client::vft_stress::io::BenchVftApproveProfile::decode_reply( - VftStressProgram::ROUTE_ID_VFT_STRESS, - payload.as_slice(), - ) - .unwrap(); - assert!(profile.result.transferred); - write_vft_phase_profile(&profile_case, gas, &profile); - } - - VftTransferRun { - prepare, - operations, - } -} - -fn storage_ops_for_load(load: u32) -> Vec { - if load == 0 { - vec![StorageOp::InsertFresh, StorageOp::ReadMissing] - } else { - vec![ - StorageOp::InsertFresh, - StorageOp::UpdateExisting, - StorageOp::ReadExisting, - StorageOp::ReadMissing, - StorageOp::Remove, - ] - } -} - -fn vft_transfer_ops() -> [VftTransferOp; 2] { - [VftTransferOp::Transfer, VftTransferOp::TransferFrom] -} - -fn storage_seed_for_op(op: &StorageOp, load: u32, sample: u32) -> u32 { - match op { - StorageOp::InsertFresh | StorageOp::ReadMissing => 10_000 + load + sample, - StorageOp::UpdateExisting | StorageOp::ReadExisting | StorageOp::Remove => { - 1 + (sample % load) - } - } -} - -fn vft_transfer_seed_for_op(_op: &VftTransferOp, load: u32, sample: u32) -> u32 { - 1 + (sample % load) -} - -fn assert_storage_result(op: &StorageOp, load: u32, seed: u32, result: &StorageBenchResult) { - match op { - StorageOp::InsertFresh => { - assert_eq!(result.value, storage_value_for_seed(seed)); - assert_eq!(result.len, load + 1); - assert!(!result.existed); - } - StorageOp::UpdateExisting => { - assert_eq!(result.value, storage_updated_value_for_seed(seed)); - assert_eq!(result.len, load); - assert!(result.existed); - } - StorageOp::ReadExisting => { - assert_eq!(result.value, storage_value_for_seed(seed)); - assert_eq!(result.len, load); - assert!(result.existed); - } - StorageOp::ReadMissing => { - assert_eq!(result.value, U256::zero()); - assert_eq!(result.len, load); - assert!(!result.existed); - } - StorageOp::Remove => { - assert_eq!(result.value, storage_value_for_seed(seed)); - assert_eq!(result.len, load - 1); - assert!(result.existed); - } - } -} - -fn assert_vft_transfer_result( - op: &VftTransferOp, - load: u32, - seed: u32, - result: &VftTransferResult, -) { - let amount = vft_transfer_amount(seed); - assert!(result.transferred); - assert_eq!(result.from_balance, vft_balance_for_seed(seed) - amount); - assert_eq!(result.to_balance, amount); - assert_eq!(result.balance_len, load + 1); - - match op { - VftTransferOp::Transfer => { - assert_eq!(result.allowance, U256::zero()); - assert_eq!(result.allowance_len, load); - } - VftTransferOp::TransferFrom => { - assert_eq!(result.allowance, vft_allowance_for_seed(seed) - amount); - assert_eq!(result.allowance_len, load); - } - } -} - -fn storage_value_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 1) -} - -fn storage_updated_value_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 1_000_001) -} - -fn vft_balance_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 1_000_000) -} - -fn vft_allowance_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 10_000) -} - -fn vft_transfer_amount(seed: u32) -> U256 { - U256::from((seed % 7) as u64 + 1) -} - -fn storage_million_ops() -> [MillionStorageOp; 5] { - [ - MillionStorageOp::ReadExisting, - MillionStorageOp::UpdateExisting, - MillionStorageOp::ReadMissing, - MillionStorageOp::InsertFresh, - MillionStorageOp::Remove, - ] -} - -fn storage_million_vft_ops() -> [MillionVftTransferOp; 2] { - [ - MillionVftTransferOp::Transfer, - MillionVftTransferOp::TransferFrom, - ] -} - -fn storage_million_seed_for_op(op: &MillionStorageOp, load: u32, sample: u32) -> u32 { - match op { - MillionStorageOp::InsertFresh | MillionStorageOp::ReadMissing => 10_000_000 + load + sample, - MillionStorageOp::UpdateExisting - | MillionStorageOp::ReadExisting - | MillionStorageOp::Remove => 1 + (sample % load), - } -} - -fn storage_million_vft_seed_for_op(op: &MillionVftTransferOp, _load: u32, sample: u32) -> u32 { - match op { - MillionVftTransferOp::Transfer => 20_000 + sample, - MillionVftTransferOp::TransferFrom => 30_000 + sample, - } -} - -fn storage_million_batch_seed_for_op(op: &MillionStorageOp, sample: u32) -> u32 { - let sample_offset = sample * 10_000; - match op { - MillionStorageOp::ReadExisting => 20_000 + sample_offset, - MillionStorageOp::UpdateExisting => 30_000 + sample_offset, - MillionStorageOp::ReadMissing => 30_000_000 + sample_offset, - MillionStorageOp::InsertFresh => 40_000_000 + sample_offset, - MillionStorageOp::Remove => 40_000 + sample_offset, - } -} - -fn assert_storage_million_result( - op: &MillionStorageOp, - seed: u32, - current_len: &mut u32, - result: &MillionStorageBenchResult, -) { - match op { - MillionStorageOp::InsertFresh => { - *current_len += 1; - assert_eq!(result.value, storage_value_for_seed(seed)); - assert_eq!(result.len, *current_len); - assert!(!result.existed); - } - MillionStorageOp::UpdateExisting => { - assert_eq!(result.value, storage_updated_value_for_seed(seed)); - assert_eq!(result.len, *current_len); - assert!(result.existed); - } - MillionStorageOp::ReadExisting => { - assert_eq!(result.value, storage_value_for_seed(seed)); - assert_eq!(result.len, *current_len); - assert!(result.existed); - } - MillionStorageOp::ReadMissing => { - assert_eq!(result.value, U256::zero()); - assert_eq!(result.len, *current_len); - assert!(!result.existed); - } - MillionStorageOp::Remove => { - *current_len -= 1; - assert_eq!(result.value, storage_updated_value_for_seed(seed)); - assert_eq!(result.len, *current_len); - assert!(result.existed); - } - } -} - -fn assert_storage_million_vft_result( - op: &MillionVftTransferOp, - seed: u32, - load: u32, - result: &MillionVftTransferResult, -) { - let amount = vft_transfer_amount(seed); - let to_seed = seed + 1; - - assert!(result.transferred); - assert_eq!(result.from_balance, vft_balance_for_seed(seed) - amount); - assert_eq!(result.to_balance, vft_balance_for_seed(to_seed) + amount); - assert_eq!(result.balance_len, load); - assert_eq!(result.allowance_len, load); - - match op { - MillionVftTransferOp::Transfer => assert_eq!(result.allowance, U256::zero()), - MillionVftTransferOp::TransferFrom => { - assert_eq!(result.allowance, vft_allowance_for_seed(seed) - amount) - } - } -} - -fn assert_storage_million_batch_result( - op: &MillionStorageOp, - start_seed: u32, - count: u32, - current_len: &mut u32, - result: &MillionStorageBenchResult, -) { - let last_seed = start_seed + count - 1; - match op { - MillionStorageOp::InsertFresh => { - *current_len += count; - assert_eq!(result.value, storage_value_for_seed(last_seed)); - assert_eq!(result.len, *current_len); - assert!(!result.existed); - } - MillionStorageOp::UpdateExisting => { - assert_eq!(result.value, storage_updated_value_for_seed(last_seed)); - assert_eq!(result.len, *current_len); - assert!(result.existed); - } - MillionStorageOp::ReadExisting => { - assert_eq!(result.value, storage_value_for_seed(last_seed)); - assert_eq!(result.len, *current_len); - assert!(result.existed); - } - MillionStorageOp::ReadMissing => { - assert_eq!(result.value, U256::zero()); - assert_eq!(result.len, *current_len); - assert!(!result.existed); - } - MillionStorageOp::Remove => { - *current_len -= count; - assert_eq!(result.value, storage_value_for_seed(last_seed)); - assert_eq!(result.len, *current_len); - assert!(result.existed); - } - } -} - -fn storage_million_bench_key( - backend: &MillionStorageBackend, - op: &MillionStorageOp, - load: u32, -) -> String { - format!( - "{}_{}_{}", - storage_million_backend_name(backend), - storage_million_op_name(op), - load - ) -} - -fn storage_million_batch_key( - backend: &MillionStorageBackend, - op: &MillionStorageOp, - count: u32, - load: u32, -) -> String { - format!( - "{}_{}_batch{count}_{load}", - storage_million_backend_name(backend), - storage_million_op_name(op) - ) -} - -fn storage_million_vft_key( - backend: &MillionVftBackend, - op: &MillionVftTransferOp, - load: u32, -) -> String { - format!( - "vft_{}_{}_{}", - storage_million_vft_backend_name(backend), - storage_million_vft_op_name(op), - load - ) -} - -fn storage_million_vft_real_key( - backend: &MillionVftBackend, - op: &'static str, - load: u32, -) -> String { - format!( - "vft_real_{}_{}_{}", - storage_million_vft_backend_name(backend), - op, - load - ) -} - -fn storage_million_prepare_key(backend: &MillionStorageBackend, load: u32) -> String { - format!("{}_prepare_{load}", storage_million_backend_name(backend)) -} - -fn storage_million_vft_prepare_key(backend: &MillionVftBackend, load: u32) -> String { - format!( - "vft_{}_prepare_{load}", - storage_million_vft_backend_name(backend) - ) -} - -fn storage_million_vft_real_prepare_key(backend: &MillionVftBackend, load: u32) -> String { - format!( - "vft_real_{}_prepare_{load}", - storage_million_vft_backend_name(backend) - ) -} - -fn storage_million_backend_name(backend: &MillionStorageBackend) -> &'static str { - match backend { - MillionStorageBackend::GenericStatic => "static_balance", - MillionStorageBackend::WatActorStatic => "wat_actor_balance", - MillionStorageBackend::MixedActorStatic => "mixed_actor_balance", - MillionStorageBackend::TagActorStatic => "tag_actor_balance", - MillionStorageBackend::TagU64ActorStatic => "tag_u64_actor_balance", - MillionStorageBackend::ControlActorStatic => "control_actor_balance", - MillionStorageBackend::PageLocalActorStatic => "page_local_actor_balance", - MillionStorageBackend::GroupedActorPages2 => "grouped_actor_balance_pages2", - MillionStorageBackend::GroupedActorPages4 => "grouped_actor_balance_pages4", - MillionStorageBackend::GroupedActorPages8 => "grouped_actor_balance_pages8", - MillionStorageBackend::GroupedActorPages16 => "grouped_actor_balance_pages16", - MillionStorageBackend::GroupedActorPages32 => "grouped_actor_balance_pages32", - MillionStorageBackend::GroupedActorPages64 => "grouped_actor_balance_pages64", - MillionStorageBackend::GroupedActorPages128 => "grouped_actor_balance_pages128", - } -} - -fn storage_million_vft_backend_name(backend: &MillionVftBackend) -> &'static str { - match backend { - MillionVftBackend::BTree => "btree", - MillionVftBackend::HashMap => "hashmap", - MillionVftBackend::GenericStatic => "static_balance", - MillionVftBackend::GenericStaticFused => "static_balance_fused", - MillionVftBackend::GenericStaticFast => "static_balance_fast", - MillionVftBackend::WatActorStatic => "wat_actor_balance", - MillionVftBackend::MixedActorStatic => "mixed_actor_balance", - MillionVftBackend::MixedActorFast => "mixed_actor_balance_fast", - MillionVftBackend::TagActorStatic => "tag_actor_balance", - MillionVftBackend::TagU64ActorStatic => "tag_u64_actor_balance", - MillionVftBackend::ControlActorStatic => "control_actor_balance", - MillionVftBackend::PageLocalActorStatic => "page_local_actor_balance", - MillionVftBackend::GroupedActorPages64 => "grouped_actor_balance_pages64", - MillionVftBackend::GroupedActorPages128 => "grouped_actor_balance_pages128", - MillionVftBackend::InlineOwnerAccountU256 => "inline_owner_account_u256", - } -} - -fn filtered_million_vft_backends( - env_key: &str, - defaults: &[MillionVftBackend], -) -> Vec { - let Ok(value) = env::var(env_key) else { - return defaults.to_vec(); - }; - if value.trim().is_empty() { - return defaults.to_vec(); - } - - value - .split(',') - .map(str::trim) - .filter(|name| !name.is_empty()) - .map(|name| parse_million_vft_backend(env_key, name)) - .collect() -} - -fn parse_million_vft_backend(env_key: &str, name: &str) -> MillionVftBackend { - match name { - "btree" => MillionVftBackend::BTree, - "hashmap" => MillionVftBackend::HashMap, - "static_balance" | "generic_static" => MillionVftBackend::GenericStatic, - "static_balance_fused" | "generic_static_fused" => MillionVftBackend::GenericStaticFused, - "static_balance_fast" | "generic_static_fast" => MillionVftBackend::GenericStaticFast, - "wat_actor_balance" | "wat_actor_static" => MillionVftBackend::WatActorStatic, - "mixed_actor_balance" | "mixed_actor_static" => MillionVftBackend::MixedActorStatic, - "mixed_actor_balance_fast" | "mixed_actor_fast" => MillionVftBackend::MixedActorFast, - "tag_actor_balance" | "tag_actor_static" => MillionVftBackend::TagActorStatic, - "tag_u64_actor_balance" | "tag_u64_actor_static" => MillionVftBackend::TagU64ActorStatic, - "control_actor_balance" | "control_actor_static" => MillionVftBackend::ControlActorStatic, - "page_local_actor_balance" | "page_local_actor_static" => { - MillionVftBackend::PageLocalActorStatic - } - "grouped_actor_balance_pages64" | "grouped_actor_pages64" => { - MillionVftBackend::GroupedActorPages64 - } - "grouped_actor_balance_pages128" | "grouped_actor_pages128" => { - MillionVftBackend::GroupedActorPages128 - } - "inline_owner_account_u256" - | "inline_owner_account" - | "inline_allowance_u256" - | "inline_allowance_balance" - | "inline_allowance" => MillionVftBackend::InlineOwnerAccountU256, - other => std::panic!("unknown {env_key} backend `{other}`"), - } -} - -fn storage_million_vft_op_name(op: &MillionVftTransferOp) -> &'static str { - match op { - MillionVftTransferOp::Transfer => "transfer", - MillionVftTransferOp::TransferFrom => "transfer_from", - } -} - -fn storage_million_op_name(op: &MillionStorageOp) -> &'static str { - match op { - MillionStorageOp::InsertFresh => "insert_fresh", - MillionStorageOp::UpdateExisting => "update_existing", - MillionStorageOp::ReadExisting => "read_existing", - MillionStorageOp::ReadMissing => "read_missing", - MillionStorageOp::Remove => "remove", - } -} - -fn aggregator_tracker_bench_key( - backend: &AggregatorTrackerBackend, - op: &AggregatorTrackerOp, - load: u32, -) -> String { - format!( - "aggregator_{}_{}_{}", - aggregator_tracker_backend_name(backend), - aggregator_tracker_op_name(op), - load - ) -} - -fn aggregator_tracker_prepare_key(backend: &AggregatorTrackerBackend, load: u32) -> String { - format!( - "aggregator_{}_prepare_{}", - aggregator_tracker_backend_name(backend), - load - ) -} - -fn aggregator_tracker_backend_name(backend: &AggregatorTrackerBackend) -> &'static str { - match backend { - AggregatorTrackerBackend::BTree => "btree", - AggregatorTrackerBackend::SailsFixed => "sails_fixed", - } -} - -fn aggregator_tracker_op_name(op: &AggregatorTrackerOp) -> &'static str { - match op { - AggregatorTrackerOp::InsertFresh => "insert_fresh", - AggregatorTrackerOp::UpdateExisting => "update_existing", - AggregatorTrackerOp::ReadExisting => "read_existing", - AggregatorTrackerOp::ListStatuses => "list_statuses", - } -} - -fn storage_bench_key( - backend: &StorageBackend, - map: &StorageMap, - op: &StorageOp, - load: u32, -) -> String { - format!( - "{}_{}_{}_{}", - storage_backend_name(backend), - storage_map_name(map), - storage_op_name(op), - load - ) -} - -fn storage_prepare_key(backend: &StorageBackend, map: &StorageMap, load: u32) -> String { - format!( - "{}_{}_prepare_{}", - storage_backend_name(backend), - storage_map_name(map), - load - ) -} - -fn vft_transfer_key(backend: &VftStorageBackend, op: &VftTransferOp, load: u32) -> String { - vft_named_key(backend, vft_transfer_op_name(op), load) -} - -fn vft_prepare_key(backend: &VftStorageBackend, load: u32) -> String { - format!("vft_{}_prepare_{load}", vft_backend_name(backend)) -} - -fn vft_named_key(backend: &VftStorageBackend, op: &str, load: u32) -> String { - format!("vft_{}_{}_{}", vft_backend_name(backend), op, load) -} - -fn storage_backend_name(backend: &StorageBackend) -> &'static str { - match backend { - StorageBackend::HashMap => "hashmap", - StorageBackend::Fixed => "fixed", - StorageBackend::RawStatic => "raw_static", - StorageBackend::SailsFixed => "sails_fixed", - StorageBackend::SailsStatic => "sails_static", - } -} - -fn vft_backend_name(backend: &VftStorageBackend) -> &'static str { - match backend { - VftStorageBackend::BTree => "btree", - VftStorageBackend::HashMap => "hashmap", - VftStorageBackend::SailsFixed => "sails_fixed", - VftStorageBackend::SailsStatic => "sails_static", - VftStorageBackend::SailsStaticFast => "sails_static_fast", - } -} - -fn vft_transfer_op_name(op: &VftTransferOp) -> &'static str { - match op { - VftTransferOp::Transfer => "transfer", - VftTransferOp::TransferFrom => "transfer_from", - } -} - -fn storage_map_name(map: &StorageMap) -> &'static str { - match map { - StorageMap::Balance => "balance", - StorageMap::Allowance => "allowance", - } -} - -fn storage_op_name(op: &StorageOp) -> &'static str { - match op { - StorageOp::InsertFresh => "insert_fresh", - StorageOp::UpdateExisting => "update_existing", - StorageOp::ReadExisting => "read_existing", - StorageOp::ReadMissing => "read_missing", - StorageOp::Remove => "remove", - } -} - -fn assert_storage_stress_wasm_static_memory_layout(wasm_path: &str) { - let wasm = std::fs::read(wasm_path).expect("storage-stress optimized WASM exists"); - let mut imported_memory_pages = None; - - 'payloads: for payload in wasmparser::Parser::new(0).parse_all(&wasm) { - let wasmparser::Payload::ImportSection(imports) = - payload.expect("storage-stress optimized WASM parses") - else { - continue; - }; - - for imports in imports { - match imports.expect("storage-stress import parses") { - wasmparser::Imports::Single(_, import) => { - if let wasmparser::TypeRef::Memory(memory) = import.ty { - imported_memory_pages = Some(memory.initial); - break 'payloads; - } - } - wasmparser::Imports::Compact1 { items, .. } => { - for item in items { - let item = item.expect("storage-stress compact import parses"); - if let wasmparser::TypeRef::Memory(memory) = item.ty { - imported_memory_pages = Some(memory.initial); - break 'payloads; - } - } - } - wasmparser::Imports::Compact2 { ty, .. } => { - if let wasmparser::TypeRef::Memory(memory) = ty { - imported_memory_pages = Some(memory.initial); - break 'payloads; - } - } - } - } - } - - let imported_memory_pages = imported_memory_pages.expect("storage-stress imports memory"); - assert!( - imported_memory_pages >= u64::from(::storage_stress::STATIC_MEMORY_END_PAGE), - "storage-stress imported memory has {imported_memory_pages} pages, expected at least {}", - ::storage_stress::STATIC_MEMORY_END_PAGE - ); -} - -fn assert_vft_stress_wasm_static_memory_layout(wasm_path: &str) { - let wasm = std::fs::read(wasm_path).expect("vft-stress optimized WASM exists"); - let mut imported_memory_pages = None; - - 'payloads: for payload in wasmparser::Parser::new(0).parse_all(&wasm) { - let wasmparser::Payload::ImportSection(imports) = - payload.expect("vft-stress optimized WASM parses") - else { - continue; - }; - - for imports in imports { - match imports.expect("vft-stress import parses") { - wasmparser::Imports::Single(_, import) => { - if let wasmparser::TypeRef::Memory(memory) = import.ty { - imported_memory_pages = Some(memory.initial); - break 'payloads; - } - } - wasmparser::Imports::Compact1 { items, .. } => { - for item in items { - let item = item.expect("vft-stress compact import parses"); - if let wasmparser::TypeRef::Memory(memory) = item.ty { - imported_memory_pages = Some(memory.initial); - break 'payloads; - } - } - } - wasmparser::Imports::Compact2 { ty, .. } => { - if let wasmparser::TypeRef::Memory(memory) = ty { - imported_memory_pages = Some(memory.initial); - break 'payloads; - } - } - } - } - } - - let imported_memory_pages = imported_memory_pages.expect("vft-stress imports memory"); - assert!( - imported_memory_pages >= u64::from(::vft_stress::STATIC_MEMORY_END_PAGE), - "vft-stress imported memory has {imported_memory_pages} pages, expected at least {}", - ::vft_stress::STATIC_MEMORY_END_PAGE - ); -} - -fn create_env() -> GtestEnv { - let system = System::new(); - system.mint_to(DEFAULT_USER_ALICE, 1_000_000_000_000_000); - GtestEnv::new(system, DEFAULT_USER_ALICE.into()) -} - -async fn deploy_for_bench(env: &GtestEnv, wasm_path: &str, f: F) -> Actor -where - P: Program, - IO: ServiceCall, - ::Output: PendingCtorOutput, - F: FnOnce(Deployment) -> PendingCtor, -{ - let code_id = env.system().submit_local_code_file(wasm_path); - deploy_code_for_bench(env, code_id, f).await -} - -async fn deploy_code_for_bench( - env: &GtestEnv, - code_id: CodeId, - f: F, -) -> Actor -where - P: Program, - IO: ServiceCall, - ::Output: PendingCtorOutput, - F: FnOnce(Deployment) -> PendingCtor, -{ - let salt = COUNTER_SALT - .fetch_add(1, std::sync::atomic::Ordering::SeqCst) - .to_le_bytes() - .to_vec(); - let deployment = env.deploy::

(code_id, salt); - let ctor = f(deployment); - let program = ctor - .with_value(100_000_000_000_000) - .await - .expect("failed to initialize the program"); - <::Output as PendingCtorOutput< - P, - sails_rs::client::GtestEnv, - >>::actor(program) -} - -fn extract_reply_and_gas(system: &System, message_id: MessageId) -> (Vec, u64) { - extract_reply_and_gas_profiled(system, message_id, None) -} - -fn extract_reply_and_gas_profiled( - system: &System, - message_id: MessageId, - #[allow(unused_variables)] benchmark: Option<&str>, -) -> (Vec, u64) { - let block_res = system.run_next_block(); - assert!( - block_res.succeed.contains(&message_id), - "message {message_id:?} did not succeed: {block_res:?}" - ); - let payload = block_res - .log() - .iter() - .find_map(|log| { - log.reply_to() - .filter(|reply_to| *reply_to == message_id) - .map(|_| log.payload().to_vec()) - }) - .expect("reply found"); +fn extract_reply_and_gas(system: &System, message_id: MessageId) -> (Vec, u64) { + let block_res = system.run_next_block(); + assert!(block_res.succeed.contains(&message_id)); + let payload = block_res + .log() + .iter() + .find_map(|log| { + log.reply_to() + .filter(|reply_to| *reply_to == message_id) + .map(|_| log.payload().to_vec()) + }) + .expect("reply found"); let gas = *block_res.gas_burned.get(&message_id).expect("gas recorded"); - - #[cfg(feature = "gas-profile")] - if let Some(benchmark) = benchmark { - let profile = block_res - .gas_profiles - .get(&message_id) - .expect("gas profile should be recorded"); - let buckets = profile - .buckets() - .iter() - .map(|(key, amount)| { - ( - key.category.as_str().to_owned(), - key.label.to_owned(), - *amount, - ) - }) - .collect::>(); - crate::write_gas_profile_artifact(benchmark, gas, buckets) - .expect("failed to persist gas profile artifact"); - } - (payload, gas) } -#[cfg(feature = "gas-profile")] -fn write_vft_phase_profile(benchmark: &str, gas: u64, profile: &VftProfileResult) { - let buckets = profile - .phases - .iter() - .map(|phase| { - ( - "wasm_phase".to_owned(), - vft_phase_name(&phase.phase).to_owned(), - phase.gas, - ) - }) - .collect::>(); - crate::write_gas_profile_artifact(&format!("{benchmark}_wasm_phases"), gas, buckets) - .expect("failed to persist wasm phase profile artifact"); -} - -#[cfg(feature = "gas-profile")] -fn vft_phase_name(phase: &VftPhase) -> &'static str { - match phase { - VftPhase::ProbeOverhead => "probe_overhead", - VftPhase::NoopBody => "noop_body", - VftPhase::EchoBody => "echo_body", - VftPhase::KeyDerive => "key_derive", - VftPhase::AllowanceGet => "allowance_get", - VftPhase::AllowancePut => "allowance_put", - VftPhase::BalanceGetFrom => "balance_get_from", - VftPhase::BalanceGetTo => "balance_get_to", - VftPhase::BalancePutFrom => "balance_put_from", - VftPhase::BalancePutTo => "balance_put_to", - VftPhase::BalanceTransfer => "balance_transfer", - VftPhase::BalanceTransferFrom => "balance_transfer_from", - VftPhase::ResultBuild => "result_build", - } -} - -#[cfg(feature = "gas-profile")] -fn render_noop_floor_markdown(medians: &BTreeMap) -> String { - let mut lines = vec![ - "# Noop Floor Gas Profile Summary".to_owned(), - "".to_owned(), - "Use a fresh `SAILS_GAS_PROFILE_DIR` for each comparison; the profile breakdown reads every JSON artifact under `profiles/`." - .to_owned(), - "".to_owned(), - "| Key | Median gas | Delta vs `noop_wat_raw` | Delta vs `noop_gstd` |".to_owned(), - "| --- | ---: | ---: | ---: |".to_owned(), - ]; - - let wat_floor = medians.get("noop_wat_raw").copied().unwrap_or(0); - let gstd_floor = medians.get("noop_gstd").copied().unwrap_or(0); - for (key, value) in medians { - lines.push(format!( - "| `{key}` | {value} | {} | {} |", - format_signed_delta(*value, wat_floor), - format_signed_delta(*value, gstd_floor), - )); - } - - render_route_reply_shape_markdown(&mut lines); - render_wasm_section_sizes_markdown(&mut lines); - render_profile_breakdown_markdown(&mut lines); - - lines.join("\n") -} - -#[cfg(feature = "gas-profile")] -fn format_signed_delta(value: u64, baseline: u64) -> String { - if value >= baseline { - (value - baseline).to_string() - } else { - format!("-{}", baseline - value) - } -} - -#[cfg(feature = "gas-profile")] -fn render_minimal_vft_floor_markdown(medians: &BTreeMap) -> String { - let mut lines = vec![ - "# Minimal VFT Floor Gas Profile Summary".to_owned(), - "".to_owned(), - "This compares a production-shaped single-backend Sails VFT with a manual Sails-wire hot path and the current benchmark VFT framework rows." - .to_owned(), - "".to_owned(), - "| Key | Median gas | Delta vs matching hot row |".to_owned(), - "| --- | ---: | ---: |".to_owned(), - ]; - - for (key, value) in medians { - let hot_key = key.replace("minimal_vft_sails_", "minimal_vft_hot_"); - let delta = medians - .get(&hot_key) - .map(|hot| value.saturating_sub(*hot)) - .unwrap_or(0); - lines.push(format!("| `{key}` | {value} | {delta} |")); - } - - render_route_reply_shape_markdown(&mut lines); - render_wasm_section_sizes_markdown(&mut lines); - render_profile_breakdown_markdown(&mut lines); - - lines.join("\n") -} - -#[cfg(feature = "gas-profile")] -fn render_vft_comparison_markdown(medians: &BTreeMap) -> String { - let mut lines = vec![ - "# VFT Gas Profile Summary".to_owned(), - "".to_owned(), - "Use a fresh `SAILS_GAS_PROFILE_DIR` for each comparison; the profile breakdown reads every JSON artifact under `profiles/`." - .to_owned(), - "".to_owned(), - "| Key | Median gas |".to_owned(), - "| --- | ---: |".to_owned(), - ]; - - for (key, value) in medians { - lines.push(format!("| `{key}` | {value} |")); - } - - render_framework_overhead_markdown(medians, &mut lines); - render_profile_breakdown_markdown(&mut lines); - - lines.join("\n") -} - -#[cfg(feature = "gas-profile")] -fn render_framework_overhead_markdown(medians: &BTreeMap, lines: &mut Vec) { - let Some(noop) = medians.get("vft_framework_noop").copied() else { - return; - }; - - lines.extend([ - "".to_owned(), - "## Framework Baselines".to_owned(), - "".to_owned(), - "| Baseline | Median gas | Delta vs noop |".to_owned(), - "| --- | ---: | ---: |".to_owned(), - format!("| `vft_framework_noop` | {noop} | 0 |"), - ]); - - if let Some(echo_args) = medians.get("vft_framework_echo_args").copied() { - lines.push(format!( - "| `vft_framework_echo_args` | {echo_args} | {} |", - echo_args.saturating_sub(noop) - )); - } - - lines.extend([ - "".to_owned(), - "`vft_framework_noop` is the lower-bound Sails call cost for this service. \ - The echo delta estimates argument decode plus minimal method-body work without storage." - .to_owned(), - ]); -} - -#[cfg(feature = "gas-profile")] -fn render_route_reply_shape_markdown(lines: &mut Vec) { - lines.extend([ - "".to_owned(), - "## Route And Reply Shape".to_owned(), - "".to_owned(), - "| Row family | Input shape | Reply shape |".to_owned(), - "| --- | --- | --- |".to_owned(), - "| `noop_wat_raw` | empty bytes | one raw byte `[1]` |".to_owned(), - "| `noop_wat_sails_wire` | 16-byte Sails header, no params | 16-byte Sails header plus SCALE `bool` |".to_owned(), - "| `noop_gstd` | empty bytes | one raw byte `[1]` via `gstd::msg::reply_bytes` |".to_owned(), - "| `noop_sails_hot` | 16-byte Sails header, no params | 16-byte Sails header plus SCALE `bool` via direct Rust dispatch |".to_owned(), - "| `noop_sails` | generated Sails call | generated Sails reply with `bool` |".to_owned(), - "| `minimal_vft_sails_*` | generated Sails call with VFT params | generated Sails reply with `bool` |".to_owned(), - "| `minimal_vft_hot_*` | manual Sails header with fixed VFT params | manual Sails header plus SCALE `bool` |".to_owned(), - ]); -} - -#[cfg(feature = "gas-profile")] -fn render_profile_breakdown_markdown(lines: &mut Vec) { - let artifacts = crate::read_gas_profile_artifacts().unwrap_or_default(); - if artifacts.is_empty() { - return; - } - - let mut phase_artifacts = BTreeMap::new(); - let mut host_artifacts = BTreeMap::new(); - for artifact in artifacts { - if let Some(base_name) = artifact.benchmark.strip_suffix("_wasm_phases") { - phase_artifacts.insert(base_name.to_owned(), artifact); - } else { - host_artifacts.insert(artifact.benchmark.clone(), artifact); - } - } - - if host_artifacts.is_empty() { - return; - } - - lines.extend([ - "".to_owned(), - "## Profile Breakdown".to_owned(), - "".to_owned(), - "| Benchmark | Total | Non-Wasm buckets | Lazy-pages | Residual Wasm | Wasm phases | Unexplained Wasm |" - .to_owned(), - "| --- | ---: | ---: | ---: | ---: | ---: | ---: |".to_owned(), - ]); - - for (benchmark, host) in host_artifacts { - let non_wasm_total = non_wasm_bucket_total(&host); - let lazy_pages = lazy_pages_bucket_total(&host); - let residual_wasm = residual_wasm_bucket_total(&host); - let phase_total = phase_artifacts - .get(&benchmark) - .map(bucket_total) - .unwrap_or(0); - let unexplained_wasm = residual_wasm.saturating_sub(phase_total); - - lines.push(format!( - "| `{benchmark}` | {} | {} | {} | {} | {} | {} |", - host.total_gas, - non_wasm_total, - lazy_pages, - residual_wasm, - phase_total, - unexplained_wasm - )); - } - - lines.extend([ - "".to_owned(), - "Unexplained Wasm is `residual;wasm` after subtracting explicit benchmark phases. \ - For profile rows this is the first place to look for Sails decode, routing, \ - reply encoding, and uninstrumented app code." - .to_owned(), - ]); -} - -#[cfg(feature = "gas-profile")] -fn render_wasm_section_sizes_markdown(lines: &mut Vec) { - let mut rows = vec![ - ( - "noop_wat_raw", - WasmSectionSizes::from_bytes(::noop_wat::RAW_REPLY_WASM), - ), - ( - "noop_wat_sails_wire", - WasmSectionSizes::from_bytes(::noop_wat::SAILS_WIRE_REPLY_WASM), - ), - ]; - - for (name, path) in [ - ( - "noop_gstd", - "../target/wasm32-gear/release/noop_gstd.opt.wasm", - ), - ( - "noop_sails", - "../target/wasm32-gear/release/noop_sails.opt.wasm", - ), - ( - "noop_sails_hot", - "../target/wasm32-gear/release/noop_sails_hot.opt.wasm", - ), - ( - "minimal_vft_sails", - "../target/wasm32-gear/release/minimal_vft_sails.opt.wasm", - ), - ( - "minimal_vft_hot", - "../target/wasm32-gear/release/minimal_vft_hot.opt.wasm", - ), - ( - "vft_stress", - "../target/wasm32-gear/release/vft_stress.opt.wasm", - ), - ( - "storage_million", - "../target/wasm32-gear/release/storage_million.opt.wasm", - ), - ] { - if let Some(sizes) = WasmSectionSizes::read(path) { - rows.push((name, sizes)); - } - } - - if rows.is_empty() { - return; - } - - lines.extend([ - "".to_owned(), - "## Wasm Section Sizes".to_owned(), - "".to_owned(), - "| Program | Bytes | Type | Import | Function | Code | Data | Global | Table | Element | Memory | Export | Custom | Other |" - .to_owned(), - "| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |" - .to_owned(), - ]); - - for (name, sizes) in rows { - lines.push(format!( - "| `{name}` | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} | {} |", - sizes.file, - sizes.r#type, - sizes.import, - sizes.function, - sizes.code, - sizes.data, - sizes.global, - sizes.table, - sizes.element, - sizes.memory, - sizes.export, - sizes.custom, - sizes.other, - )); - } - - lines.extend([ - "".to_owned(), - "`instrumented_code` precharge tracks code bytes read from storage. `module_instantiation` is driven by instantiated section sizes, especially code, data, globals, tables, elements, and types." - .to_owned(), - ]); -} - -#[cfg(feature = "gas-profile")] -#[derive(Default)] -struct WasmSectionSizes { - file: usize, - r#type: usize, - import: usize, - function: usize, - code: usize, - data: usize, - global: usize, - table: usize, - element: usize, - memory: usize, - export: usize, - custom: usize, - other: usize, -} - -#[cfg(feature = "gas-profile")] -impl WasmSectionSizes { - fn read(path: &str) -> Option { - let wasm = std::fs::read(path).ok()?; - Some(Self::from_bytes(&wasm)) - } - - fn from_bytes(wasm: &[u8]) -> Self { - let mut sizes = Self { - file: wasm.len(), - ..Self::default() - }; - - for payload in wasmparser::Parser::new(0).parse_all(&wasm) { - let Ok(payload) = payload else { - return sizes; - }; - let Some((section, range)) = payload.as_section() else { - continue; - }; - let len = range.end.saturating_sub(range.start); - match section { - 1 => sizes.r#type += len, - 2 => sizes.import += len, - 3 => sizes.function += len, - 4 => sizes.table += len, - 5 => sizes.memory += len, - 6 => sizes.global += len, - 7 => sizes.export += len, - 9 => sizes.element += len, - 10 => sizes.code += len, - 11 => sizes.data += len, - 0 => sizes.custom += len, - _ => sizes.other += len, - } - } - - sizes - } -} - -#[cfg(feature = "gas-profile")] -fn bucket_total(artifact: &crate::GasProfileArtifactSerde) -> u64 { - artifact.buckets.iter().map(|bucket| bucket.amount).sum() -} - -#[cfg(feature = "gas-profile")] -fn non_wasm_bucket_total(artifact: &crate::GasProfileArtifactSerde) -> u64 { - artifact - .buckets - .iter() - .filter(|bucket| bucket.category != "residual" || bucket.label != "wasm") - .map(|bucket| bucket.amount) - .sum() -} - -#[cfg(feature = "gas-profile")] -fn residual_wasm_bucket_total(artifact: &crate::GasProfileArtifactSerde) -> u64 { - artifact - .buckets - .iter() - .filter(|bucket| bucket.category == "residual" && bucket.label == "wasm") - .map(|bucket| bucket.amount) - .sum() -} - -#[cfg(feature = "gas-profile")] -fn lazy_pages_bucket_total(artifact: &crate::GasProfileArtifactSerde) -> u64 { - artifact - .buckets - .iter() - .filter(|bucket| bucket.category.contains("lazy") || bucket.label.contains("lazy")) - .map(|bucket| bucket.amount) - .sum() -} - -#[cfg(feature = "gas-profile")] -fn floor_samples() -> u32 { - env::var("SAILS_FLOOR_SAMPLES") - .ok() - .and_then(|value| value.parse().ok()) - .filter(|samples| *samples > 0) - .unwrap_or(DEFAULT_FLOOR_SAMPLES) -} - -#[cfg(feature = "gas-profile")] -fn floor_row_enabled(row: &str) -> bool { - let Ok(rows) = env::var("SAILS_FLOOR_ROWS") else { - return true; - }; - - rows.split(',') - .map(str::trim) - .filter(|row| !row.is_empty()) - .any(|enabled| row == enabled || row.starts_with(enabled)) -} - -#[cfg(feature = "gas-profile")] -fn noop_sails_payload() -> Vec { - let mut payload = Vec::with_capacity(16); - payload.extend_from_slice(b"GM"); - payload.push(1); - payload.push(16); - payload.extend_from_slice(&[84, 87, 204, 48, 221, 148, 206, 41]); - payload.extend_from_slice(&0u16.to_le_bytes()); - payload.push(NoopSailsProgram::ROUTE_ID_NOOP_SAILS); - payload.push(0); - payload -} - -#[cfg(feature = "gas-profile")] -fn hot_vft_payload(entry_id: u16, params: Vec) -> Vec { - let mut payload = Vec::with_capacity(16 + params.len()); - payload.extend_from_slice(b"GM"); - payload.push(1); - payload.push(16); - payload.extend_from_slice(&HOT_VFT_INTERFACE_ID); - payload.extend_from_slice(&entry_id.to_le_bytes()); - payload.push(HOT_VFT_ROUTE_ID); - payload.push(0); - payload.extend_from_slice(¶ms); - payload -} - -#[cfg(feature = "gas-profile")] -fn assert_sails_bool_reply(entry_id: u16, payload: &[u8]) { - assert_eq!(payload.len(), 17, "Sails bool reply length"); - assert_eq!(&payload[0..2], b"GM"); - assert_eq!(payload[2], 1); - assert_eq!(payload[3], 16); - assert_eq!(&payload[4..12], &HOT_VFT_INTERFACE_ID); - assert_eq!(u16::from_le_bytes([payload[12], payload[13]]), entry_id); - assert_eq!(payload[14], HOT_VFT_ROUTE_ID); - assert_eq!(payload[16], 1); -} - fn median(mut values: Vec) -> u64 { values.sort_unstable(); diff --git a/benchmarks/src/bin/bench_analyzer.rs b/benchmarks/src/bin/bench_analyzer.rs index 1588d7582..504e80c1a 100644 --- a/benchmarks/src/bin/bench_analyzer.rs +++ b/benchmarks/src/bin/bench_analyzer.rs @@ -1,13 +1,9 @@ use anyhow::{Context, Result, anyhow}; use benchmarks::{ - BenchCategory, BenchCategoryComparison, BenchCategoryComparisonReport, BenchData, BenchDataFile, + BenchCategoryComparison, BenchCategoryComparisonReport, BenchData, BenchDataFile, }; use clap::Parser; -use std::{ - collections::{BTreeMap, BTreeSet}, - fs, - path::PathBuf, -}; +use std::{fs, path::PathBuf}; #[derive(Parser)] #[command(version, about, long_about = None)] @@ -51,39 +47,34 @@ fn analyze_benches( // Flag to track if any benchmarks fail the threshold check. let mut threshold_failed = threshold.map(|_| false); - let current_data: BTreeMap<_, _> = current_data.into_iter().collect(); - let other_data: BTreeMap<_, _> = other_data.into_iter().collect(); - let categories = current_data - .keys() - .chain(other_data.keys()) - .cloned() - .collect::>(); - // Create unfinished report. - let mut report = initialize_report(); - for category in categories { - match (current_data.get(&category), other_data.get(&category)) { - (Some(current_value), Some(other_value)) => { - let comparison = - BenchCategoryComparison::new(category, *current_value, *other_value, threshold); + let mut report = current_data + .into_iter() + .zip(other_data) + .map( + // Create a comparison entity for each benchmark category. + |((current_category, current_value), (other_category, other_value))| { + assert_eq!(current_category, other_category, "Categories do not match"); + + let comparison = BenchCategoryComparison::new( + current_category, + current_value, + other_value, + threshold, + ); if matches!(threshold_failed, Some(false)) && comparison.has_failed_threshold() { let _ = threshold_failed.insert(true); } - add_comparison_to_report(&mut report, comparison); - } - (Some(current_value), None) => { - mark_threshold_failed_on_shape_change(&mut threshold_failed); - add_added_benchmark_to_report(&mut report, category, *current_value); - } - (None, Some(other_value)) => { - mark_threshold_failed_on_shape_change(&mut threshold_failed); - add_removed_benchmark_to_report(&mut report, category, *other_value); - } - (None, None) => unreachable!("category is sourced from one of the maps"), - } - } + comparison + }, + ) + .fold(initialize_report(), |mut report, comparison| { + // Add each comparison to the report. + add_comparison_to_report(&mut report, comparison); + report + }); // Finish the report. add_report_conclusion(&mut report, threshold, threshold_failed); @@ -146,24 +137,6 @@ fn add_comparison_to_report(report: &mut String, comparison: BenchCategoryCompar )); } -fn add_added_benchmark_to_report(report: &mut String, category: BenchCategory, current: u64) { - report.push_str(&format!( - "| {category} | {current} | - | - | - | 🆕 New benchmark |\n", - )); -} - -fn add_removed_benchmark_to_report(report: &mut String, category: BenchCategory, other: u64) { - report.push_str(&format!( - "| {category} | - | {other} | - | - | ⚠️ Missing from current |\n", - )); -} - -fn mark_threshold_failed_on_shape_change(threshold_failed: &mut Option) { - if matches!(threshold_failed, Some(false)) { - let _ = threshold_failed.insert(true); - } -} - fn add_report_conclusion( report: &mut String, threshold: Option, @@ -183,81 +156,3 @@ fn add_report_conclusion( } } } - -#[cfg(test)] -mod tests { - use super::*; - use benchmarks::{BenchDataSerde, ComputeBenchDataSerde, StorageStressDataSerde}; - use std::collections::BTreeMap; - - #[test] - fn report_includes_benchmarks_missing_from_baseline() { - let dir = tempfile::tempdir().unwrap(); - let current_path = dir.path().join("current.json"); - let baseline_path = dir.path().join("baseline.json"); - let output_path = dir.path().join("comparison.md"); - let storage_key = "sails_static_balance_read_existing_1024".to_owned(); - - let current = BenchDataSerde { - compute: ComputeBenchDataSerde { median: 1 }, - storage: StorageStressDataSerde(BTreeMap::from([(storage_key.clone(), 810_912_211)])), - ..Default::default() - }; - let baseline = BenchDataSerde { - compute: ComputeBenchDataSerde { median: 1 }, - ..Default::default() - }; - - fs::write( - ¤t_path, - serde_json::to_string_pretty(¤t).unwrap(), - ) - .unwrap(); - fs::write( - &baseline_path, - serde_json::to_string_pretty(&baseline).unwrap(), - ) - .unwrap(); - - analyze_benches(current_path, baseline_path, Some(output_path.clone()), None).unwrap(); - - let report = fs::read_to_string(output_path).unwrap(); - assert!(report.contains(&format!("storage_{storage_key}"))); - assert!(report.contains("New benchmark")); - } - - #[test] - fn threshold_mode_fails_when_categories_are_missing_from_baseline() { - let dir = tempfile::tempdir().unwrap(); - let current_path = dir.path().join("current.json"); - let baseline_path = dir.path().join("baseline.json"); - let storage_key = "sails_static_balance_read_existing_1024".to_owned(); - - let current = BenchDataSerde { - compute: ComputeBenchDataSerde { median: 1 }, - storage: StorageStressDataSerde(BTreeMap::from([(storage_key, 810_912_211)])), - ..Default::default() - }; - let baseline = BenchDataSerde { - compute: ComputeBenchDataSerde { median: 1 }, - ..Default::default() - }; - - fs::write( - ¤t_path, - serde_json::to_string_pretty(¤t).unwrap(), - ) - .unwrap(); - fs::write( - &baseline_path, - serde_json::to_string_pretty(&baseline).unwrap(), - ) - .unwrap(); - - let error = analyze_benches(current_path, baseline_path, None, Some(1.0)).unwrap_err(); - assert_eq!( - error.to_string(), - "Benchmark contains tests failing the threshold." - ); - } -} diff --git a/benchmarks/src/clients.rs b/benchmarks/src/clients.rs index a07edaea5..0d80a862f 100644 --- a/benchmarks/src/clients.rs +++ b/benchmarks/src/clients.rs @@ -12,28 +12,3 @@ pub(crate) mod counter_bench_client { #![allow(unused)] include!("counter_bench_program.rs"); } - -pub(crate) mod noop_sails_client { - #![allow(unused)] - include!("noop_sails_program.rs"); -} - -pub(crate) mod minimal_vft_sails_client { - #![allow(unused)] - include!("minimal_vft_sails_program.rs"); -} - -pub(crate) mod storage_stress_client { - #![allow(unused)] - include!("storage_stress_program.rs"); -} - -pub(crate) mod vft_stress_client { - #![allow(unused)] - include!("vft_stress_program.rs"); -} - -pub(crate) mod storage_million_client { - #![allow(unused)] - include!("storage_million_program.rs"); -} diff --git a/benchmarks/src/entities.rs b/benchmarks/src/entities.rs index f77d3f5d1..76bf8328f 100644 --- a/benchmarks/src/entities.rs +++ b/benchmarks/src/entities.rs @@ -27,15 +27,6 @@ impl BenchData { for (key, value) in data.message_stack.0 { map.insert(BenchCategory::MessageStack(key), value); } - for (key, value) in data.storage.0 { - map.insert(BenchCategory::Storage(key), value); - } - for (key, value) in data.storage_million.0 { - map.insert(BenchCategory::StorageMillion(key), value); - } - for (key, value) in data.examples.0 { - map.insert(BenchCategory::Example(key), value); - } Ok(Self(map)) } @@ -74,51 +65,6 @@ impl BenchData { self.0.insert(BenchCategory::MessageStack(limit), value); } - /// Update storage benchmark category value. - pub fn update_storage_bench(&mut self, key: String, value: u64) { - self.0.insert(BenchCategory::Storage(key), value); - } - - /// Replace all storage benchmark values. - pub fn replace_storage_benches(&mut self, values: BTreeMap) { - self.0 - .retain(|key, _| !matches!(key, BenchCategory::Storage(_))); - - for (key, value) in values { - self.update_storage_bench(key, value); - } - } - - /// Update million-entry storage benchmark category value. - pub fn update_storage_million_bench(&mut self, key: String, value: u64) { - self.0.insert(BenchCategory::StorageMillion(key), value); - } - - /// Replace all million-entry storage benchmark values. - pub fn replace_storage_million_benches(&mut self, values: BTreeMap) { - self.0 - .retain(|key, _| !matches!(key, BenchCategory::StorageMillion(_))); - - for (key, value) in values { - self.update_storage_million_bench(key, value); - } - } - - /// Update example benchmark category value. - pub fn update_example_bench(&mut self, key: String, value: u64) { - self.0.insert(BenchCategory::Example(key), value); - } - - /// Replace all example benchmark values. - pub fn replace_example_benches(&mut self, values: BTreeMap) { - self.0 - .retain(|key, _| !matches!(key, BenchCategory::Example(_))); - - for (key, value) in values { - self.update_example_bench(key, value); - } - } - /// Convert the benchmark data into a JSON string. pub fn into_json_string(self) -> Result { let mut bench_data = BenchDataSerde::default(); @@ -136,15 +82,6 @@ impl BenchData { BenchCategory::MessageStack(limit) => { bench_data.message_stack.0.insert(limit, value); } - BenchCategory::Storage(key) => { - bench_data.storage.0.insert(key, value); - } - BenchCategory::StorageMillion(key) => { - bench_data.storage_million.0.insert(key, value); - } - BenchCategory::Example(key) => { - bench_data.examples.0.insert(key, value); - } } } @@ -179,12 +116,6 @@ pub struct BenchDataSerde { pub redirect: RedirectBenchDataSerde, #[serde(default)] pub message_stack: MessageStackDataSerde, - #[serde(default)] - pub storage: StorageStressDataSerde, - #[serde(default)] - pub storage_million: StorageMillionDataSerde, - #[serde(default)] - pub examples: ExampleBenchDataSerde, } /// Compute benchmark data stored in the benchmarks file. @@ -222,18 +153,6 @@ pub struct CounterBenchDataSerde { #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] pub struct MessageStackDataSerde(pub BTreeMap); -/// Storage benchmark data stored in the benchmarks file. -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] -pub struct StorageStressDataSerde(pub BTreeMap); - -/// Million-entry storage benchmark data stored in the benchmarks file. -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] -pub struct StorageMillionDataSerde(pub BTreeMap); - -/// Example benchmark data stored in the benchmarks file. -#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)] -pub struct ExampleBenchDataSerde(pub BTreeMap); - /// Benchmark category that can be read (written) from (to) the benchmarks file. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] pub enum BenchCategory { @@ -244,9 +163,6 @@ pub enum BenchCategory { CrossProgram, Redirect, MessageStack(u32), - Storage(String), - StorageMillion(String), - Example(String), } impl Display for BenchCategory { @@ -259,9 +175,6 @@ impl Display for BenchCategory { BenchCategory::CrossProgram => write!(f, "cross_program"), BenchCategory::Redirect => write!(f, "redirect"), BenchCategory::MessageStack(limit) => write!(f, "stack_{limit}"), - BenchCategory::Storage(key) => write!(f, "storage_{key}"), - BenchCategory::StorageMillion(key) => write!(f, "storage_million_{key}"), - BenchCategory::Example(key) => write!(f, "example_{key}"), } } } diff --git a/benchmarks/src/lib.rs b/benchmarks/src/lib.rs index 1670fa2cb..281a49d6d 100644 --- a/benchmarks/src/lib.rs +++ b/benchmarks/src/lib.rs @@ -17,14 +17,9 @@ use anyhow::{Context, Result}; pub use entities::{ AllocBenchDataSerde, BenchCategory, BenchCategoryComparison, BenchCategoryComparisonReport, BenchData, BenchDataSerde, ComputeBenchDataSerde, CounterBenchDataSerde, - CrossProgramBenchDataSerde, ExampleBenchDataSerde, RedirectBenchDataSerde, - StorageMillionDataSerde, StorageStressDataSerde, + CrossProgramBenchDataSerde, RedirectBenchDataSerde, }; pub use file::BenchDataFile; -#[cfg(feature = "gas-profile")] -use serde::{Deserialize, Serialize}; -#[cfg(feature = "gas-profile")] -use std::fs; use std::{ env, path::{Path, PathBuf}, @@ -59,152 +54,14 @@ fn store_bench_data_to_file(path: impl AsRef, f: impl FnOnce(&mut BenchDat .context("Failed to unlock bench data file after writing") } -#[cfg(feature = "gas-profile")] -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct GasProfileBucketSerde { - pub category: String, - pub label: String, - pub amount: u64, -} - -#[cfg(feature = "gas-profile")] -#[derive(Clone, Debug, Deserialize, Serialize)] -pub struct GasProfileArtifactSerde { - pub benchmark: String, - pub total_gas: u64, - pub buckets: Vec, -} - -#[cfg(feature = "gas-profile")] -fn gas_profile_root() -> Option { - env::var_os("SAILS_GAS_PROFILE_DIR").map(PathBuf::from) -} - -#[cfg(feature = "gas-profile")] -pub fn write_gas_profile_artifact( - benchmark: &str, - total_gas: u64, - buckets: Vec<(String, String, u64)>, -) -> Result<()> { - let Some(root) = gas_profile_root() else { - return Ok(()); - }; - - fs::create_dir_all(root.join("profiles")) - .context("Failed to create gas profile artifact directory")?; - - let artifact = GasProfileArtifactSerde { - benchmark: benchmark.to_owned(), - total_gas, - buckets: buckets - .iter() - .map(|(category, label, amount)| GasProfileBucketSerde { - category: category.clone(), - label: label.clone(), - amount: *amount, - }) - .collect(), - }; - - let json_path = root.join("profiles").join(format!("{benchmark}.json")); - let folded_path = root.join("profiles").join(format!("{benchmark}.folded")); - - fs::write( - &json_path, - serde_json::to_vec_pretty(&artifact).context("Failed to encode gas profile artifact")?, - ) - .with_context(|| { - format!( - "Failed to write gas profile artifact to {}", - json_path.display() - ) - })?; - - let folded = buckets - .into_iter() - .map(|(category, label, amount)| format!("{benchmark};{category};{label} {amount}")) - .collect::>() - .join("\n"); - fs::write(&folded_path, format!("{folded}\n")).with_context(|| { - format!( - "Failed to write folded gas profile artifact to {}", - folded_path.display() - ) - })?; - - Ok(()) -} - -#[cfg(feature = "gas-profile")] -pub fn read_gas_profile_artifacts() -> Result> { - let Some(root) = gas_profile_root() else { - return Ok(Vec::new()); - }; - - let profiles = root.join("profiles"); - if !profiles.exists() { - return Ok(Vec::new()); - } - - let mut artifacts = Vec::new(); - for entry in fs::read_dir(&profiles) - .with_context(|| format!("Failed to read gas profiles from {}", profiles.display()))? - { - let entry = entry.context("Failed to read gas profile directory entry")?; - let path = entry.path(); - if path.extension().and_then(|extension| extension.to_str()) != Some("json") { - continue; - } - - let bytes = fs::read(&path) - .with_context(|| format!("Failed to read gas profile {}", path.display()))?; - let artifact: GasProfileArtifactSerde = serde_json::from_slice(&bytes) - .with_context(|| format!("Failed to decode gas profile {}", path.display()))?; - artifacts.push(artifact); - } - - artifacts.sort_by(|left, right| left.benchmark.cmp(&right.benchmark)); - Ok(artifacts) -} - -#[cfg(feature = "gas-profile")] -pub fn write_gas_profile_summary( - summary: &std::collections::BTreeMap, - comparison_markdown: &str, -) -> Result<()> { - let Some(root) = gas_profile_root() else { - return Ok(()); - }; - - fs::create_dir_all(&root).context("Failed to create gas profile output directory")?; - - let summary_path = root.join("summary.json"); - fs::write( - &summary_path, - serde_json::to_vec_pretty(summary).context("Failed to encode gas profile summary")?, - ) - .with_context(|| format!("Failed to write summary to {}", summary_path.display()))?; - - let comparison_path = root.join("comparison.md"); - fs::write(&comparison_path, comparison_markdown).with_context(|| { - format!( - "Failed to write gas profile comparison markdown to {}", - comparison_path.display() - ) - })?; - - Ok(()) -} - #[cfg(test)] mod tests { use super::*; use crate::entities::{ BenchDataSerde, ComputeBenchDataSerde, CounterBenchDataSerde, CrossProgramBenchDataSerde, - ExampleBenchDataSerde, RedirectBenchDataSerde, StorageStressDataSerde, + RedirectBenchDataSerde, }; use std::{ - collections::BTreeMap, io::{Read, Seek, SeekFrom, Write}, thread, }; @@ -223,18 +80,6 @@ mod tests { cross_program: CrossProgramBenchDataSerde { median: 42 }, redirect: RedirectBenchDataSerde { median: 4242 }, message_stack: Default::default(), - storage: StorageStressDataSerde(BTreeMap::from([( - "sails_static_balance_prepare_1024".to_owned(), - 777, - )])), - storage_million: StorageMillionDataSerde(BTreeMap::from([( - "static_balance_prepare_1000000".to_owned(), - 999, - )])), - examples: ExampleBenchDataSerde(BTreeMap::from([( - "aggregator_btree_prepare_1024".to_owned(), - 888, - )])), }; // Create a temporary file. @@ -297,18 +142,6 @@ mod tests { cross_program: CrossProgramBenchDataSerde { median: 0 }, redirect: RedirectBenchDataSerde { median: 4343 }, message_stack: Default::default(), - storage: StorageStressDataSerde(BTreeMap::from([( - "sails_static_balance_prepare_1024".to_owned(), - 777, - )])), - storage_million: StorageMillionDataSerde(BTreeMap::from([( - "static_balance_prepare_1000000".to_owned(), - 999, - )])), - examples: ExampleBenchDataSerde(BTreeMap::from([( - "aggregator_btree_prepare_1024".to_owned(), - 888, - )])), }, ) } diff --git a/benchmarks/src/minimal_vft_sails_program.rs b/benchmarks/src/minimal_vft_sails_program.rs deleted file mode 100644 index c78c22298..000000000 --- a/benchmarks/src/minimal_vft_sails_program.rs +++ /dev/null @@ -1,134 +0,0 @@ -// Code generated by sails-client-gen-v2. DO NOT EDIT. -#[allow(unused_imports)] -use sails_rs::{client::*, collections::*, prelude::*}; -pub struct MinimalVftSailsProgram; - -impl MinimalVftSailsProgram { - pub const ROUTE_ID_VFT: u8 = 1; -} - -impl sails_rs::client::Program for MinimalVftSailsProgram {} - -pub trait MinimalVftSails { - type Env: sails_rs::client::GearEnv; - fn vft(&self) -> sails_rs::client::Service; -} - -impl MinimalVftSails - for sails_rs::client::Actor -{ - type Env = E; - fn vft(&self) -> sails_rs::client::Service { - self.service(MinimalVftSailsProgram::ROUTE_ID_VFT) - } -} -pub trait MinimalVftSailsCtors { - type Env: sails_rs::client::GearEnv; - fn new_for_bench( - self, - ) -> sails_rs::client::PendingCtor; -} - -impl MinimalVftSailsCtors - for sails_rs::client::Deployment -{ - type Env = E; - fn new_for_bench( - self, - ) -> sails_rs::client::PendingCtor { - self.pending_ctor(()) - } -} - -pub mod io { - use super::*; - sails_rs::io_struct_impl!(NewForBench () -> (), 0); -} - -pub mod vft { - use super::*; - - pub trait Vft { - type Env: sails_rs::client::GearEnv; - fn allowance( - &mut self, - owner: ActorId, - spender: ActorId, - ) -> sails_rs::client::PendingCall; - fn approve( - &mut self, - spender: ActorId, - amount: U256, - ) -> sails_rs::client::PendingCall; - fn balance_of( - &mut self, - owner: ActorId, - ) -> sails_rs::client::PendingCall; - fn transfer( - &mut self, - to: ActorId, - amount: U256, - ) -> sails_rs::client::PendingCall; - fn transfer_from( - &mut self, - owner: ActorId, - to: ActorId, - amount: U256, - ) -> sails_rs::client::PendingCall; - } - - pub struct VftImpl; - - impl sails_rs::client::Identifiable for VftImpl { - const INTERFACE_ID: sails_rs::InterfaceId = - sails_rs::InterfaceId::from_bytes_8([83, 187, 140, 26, 152, 56, 126, 212]); - } - - impl Vft for sails_rs::client::Service { - type Env = E; - fn allowance( - &mut self, - owner: ActorId, - spender: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((owner, spender)) - } - fn approve( - &mut self, - spender: ActorId, - amount: U256, - ) -> sails_rs::client::PendingCall { - self.pending_call((spender, amount)) - } - fn balance_of( - &mut self, - owner: ActorId, - ) -> sails_rs::client::PendingCall { - self.pending_call((owner,)) - } - fn transfer( - &mut self, - to: ActorId, - amount: U256, - ) -> sails_rs::client::PendingCall { - self.pending_call((to, amount)) - } - fn transfer_from( - &mut self, - owner: ActorId, - to: ActorId, - amount: U256, - ) -> sails_rs::client::PendingCall { - self.pending_call((owner, to, amount)) - } - } - - pub mod io { - use super::*; - sails_rs::io_struct_impl!(Allowance (owner: ActorId, spender: ActorId) -> U256, 0, ::INTERFACE_ID); - sails_rs::io_struct_impl!(Approve (spender: ActorId, amount: U256) -> bool, 1, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BalanceOf (owner: ActorId) -> U256, 2, ::INTERFACE_ID); - sails_rs::io_struct_impl!(Transfer (to: ActorId, amount: U256) -> bool, 3, ::INTERFACE_ID); - sails_rs::io_struct_impl!(TransferFrom (owner: ActorId, to: ActorId, amount: U256) -> bool, 4, ::INTERFACE_ID); - } -} diff --git a/benchmarks/src/noop_sails_program.rs b/benchmarks/src/noop_sails_program.rs deleted file mode 100644 index 68936585b..000000000 --- a/benchmarks/src/noop_sails_program.rs +++ /dev/null @@ -1,72 +0,0 @@ -// Code generated by sails-client-gen-v2. DO NOT EDIT. -#[allow(unused_imports)] -use sails_rs::{client::*, collections::*, prelude::*}; -pub struct NoopSailsProgram; - -impl NoopSailsProgram { - pub const ROUTE_ID_NOOP_SAILS: u8 = 1; -} - -impl sails_rs::client::Program for NoopSailsProgram {} - -pub trait NoopSails { - type Env: sails_rs::client::GearEnv; - fn noop_sails(&self) -> sails_rs::client::Service; -} - -impl NoopSails for sails_rs::client::Actor { - type Env = E; - fn noop_sails(&self) -> sails_rs::client::Service { - self.service(NoopSailsProgram::ROUTE_ID_NOOP_SAILS) - } -} -pub trait NoopSailsCtors { - type Env: sails_rs::client::GearEnv; - fn new_for_bench( - self, - ) -> sails_rs::client::PendingCtor; -} - -impl NoopSailsCtors - for sails_rs::client::Deployment -{ - type Env = E; - fn new_for_bench( - self, - ) -> sails_rs::client::PendingCtor { - self.pending_ctor(()) - } -} - -pub mod io { - use super::*; - sails_rs::io_struct_impl!(NewForBench () -> (), 0); -} - -pub mod noop_sails { - use super::*; - - pub trait NoopSails { - type Env: sails_rs::client::GearEnv; - fn noop(&mut self) -> sails_rs::client::PendingCall; - } - - pub struct NoopSailsImpl; - - impl sails_rs::client::Identifiable for NoopSailsImpl { - const INTERFACE_ID: sails_rs::InterfaceId = - sails_rs::InterfaceId::from_bytes_8([84, 87, 204, 48, 221, 148, 206, 41]); - } - - impl NoopSails for sails_rs::client::Service { - type Env = E; - fn noop(&mut self) -> sails_rs::client::PendingCall { - self.pending_call(()) - } - } - - pub mod io { - use super::*; - sails_rs::io_struct_impl!(Noop () -> bool, 0, ::INTERFACE_ID); - } -} diff --git a/benchmarks/src/storage_million_program.rs b/benchmarks/src/storage_million_program.rs deleted file mode 100644 index 6ed6b4fd7..000000000 --- a/benchmarks/src/storage_million_program.rs +++ /dev/null @@ -1,280 +0,0 @@ -// Code generated by sails-client-gen-v2. DO NOT EDIT. -#[allow(unused_imports)] -use sails_rs::{client::*, collections::*, prelude::*}; -pub struct StorageMillionProgram; - -impl StorageMillionProgram { - pub const ROUTE_ID_STORAGE_MILLION: u8 = 1; -} - -impl sails_rs::client::Program for StorageMillionProgram {} - -pub trait StorageMillion { - type Env: sails_rs::client::GearEnv; - fn storage_million( - &self, - ) -> sails_rs::client::Service; -} - -impl StorageMillion - for sails_rs::client::Actor -{ - type Env = E; - fn storage_million( - &self, - ) -> sails_rs::client::Service { - self.service(StorageMillionProgram::ROUTE_ID_STORAGE_MILLION) - } -} -pub trait StorageMillionCtors { - type Env: sails_rs::client::GearEnv; - fn new_for_bench( - self, - ) -> sails_rs::client::PendingCtor; -} - -impl StorageMillionCtors - for sails_rs::client::Deployment -{ - type Env = E; - fn new_for_bench( - self, - ) -> sails_rs::client::PendingCtor { - self.pending_ctor(()) - } -} - -pub mod io { - use super::*; - sails_rs::io_struct_impl!(NewForBench () -> (), 0); -} - -pub mod storage_million { - use super::*; - - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub enum MillionStorageBackend { - GenericStatic, - WatActorStatic, - MixedActorStatic, - TagActorStatic, - TagU64ActorStatic, - ControlActorStatic, - PageLocalActorStatic, - GroupedActorPages2, - GroupedActorPages4, - GroupedActorPages8, - GroupedActorPages16, - GroupedActorPages32, - GroupedActorPages64, - GroupedActorPages128, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub struct MillionStorageBenchResult { - pub value: U256, - pub len: u32, - pub existed: bool, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub enum MillionStorageOp { - InsertFresh, - UpdateExisting, - ReadExisting, - ReadMissing, - Remove, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub enum MillionVftBackend { - BTree, - HashMap, - GenericStatic, - GenericStaticFused, - GenericStaticFast, - WatActorStatic, - MixedActorStatic, - MixedActorFast, - TagActorStatic, - TagU64ActorStatic, - ControlActorStatic, - PageLocalActorStatic, - GroupedActorPages64, - GroupedActorPages128, - InlineOwnerAccountU256, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub enum MillionVftTransferOp { - Transfer, - TransferFrom, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub struct MillionVftTransferResult { - pub from_balance: U256, - pub to_balance: U256, - pub allowance: U256, - pub balance_len: u32, - pub allowance_len: u32, - pub transferred: bool, - } - - pub trait StorageMillion { - type Env: sails_rs::client::GearEnv; - fn bench( - &mut self, - backend: MillionStorageBackend, - op: MillionStorageOp, - seed: u32, - ) -> sails_rs::client::PendingCall; - fn bench_many( - &mut self, - backend: MillionStorageBackend, - op: MillionStorageOp, - start_seed: u32, - count: u32, - ) -> sails_rs::client::PendingCall; - fn bench_vft_approve_bool( - &mut self, - backend: MillionVftBackend, - owner_seed: u32, - spender_seed: u32, - ) -> sails_rs::client::PendingCall; - fn bench_vft_transfer( - &mut self, - backend: MillionVftBackend, - op: MillionVftTransferOp, - seed: u32, - ) -> sails_rs::client::PendingCall; - fn bench_vft_transfer_bool( - &mut self, - backend: MillionVftBackend, - op: MillionVftTransferOp, - seed: u32, - ) -> sails_rs::client::PendingCall; - fn bench_vft_transfer_fresh_bool( - &mut self, - backend: MillionVftBackend, - seed: u32, - ) -> sails_rs::client::PendingCall; - fn bench_vft_transfer_from_spender_bool( - &mut self, - backend: MillionVftBackend, - owner_seed: u32, - spender_seed: u32, - ) -> sails_rs::client::PendingCall; - fn prepare_chunk( - &mut self, - backend: MillionStorageBackend, - start: u32, - len: u32, - ) -> sails_rs::client::PendingCall; - fn prepare_vft_chunk( - &mut self, - backend: MillionVftBackend, - start: u32, - len: u32, - ) -> sails_rs::client::PendingCall; - } - - pub struct StorageMillionImpl; - - impl sails_rs::client::Identifiable for StorageMillionImpl { - const INTERFACE_ID: sails_rs::InterfaceId = - sails_rs::InterfaceId::from_bytes_8([69, 52, 241, 61, 146, 22, 252, 107]); - } - - impl StorageMillion - for sails_rs::client::Service - { - type Env = E; - fn bench( - &mut self, - backend: MillionStorageBackend, - op: MillionStorageOp, - seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, op, seed)) - } - fn bench_many( - &mut self, - backend: MillionStorageBackend, - op: MillionStorageOp, - start_seed: u32, - count: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, op, start_seed, count)) - } - fn bench_vft_approve_bool( - &mut self, - backend: MillionVftBackend, - owner_seed: u32, - spender_seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, owner_seed, spender_seed)) - } - fn bench_vft_transfer( - &mut self, - backend: MillionVftBackend, - op: MillionVftTransferOp, - seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, op, seed)) - } - fn bench_vft_transfer_bool( - &mut self, - backend: MillionVftBackend, - op: MillionVftTransferOp, - seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, op, seed)) - } - fn bench_vft_transfer_fresh_bool( - &mut self, - backend: MillionVftBackend, - seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, seed)) - } - fn bench_vft_transfer_from_spender_bool( - &mut self, - backend: MillionVftBackend, - owner_seed: u32, - spender_seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, owner_seed, spender_seed)) - } - fn prepare_chunk( - &mut self, - backend: MillionStorageBackend, - start: u32, - len: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, start, len)) - } - fn prepare_vft_chunk( - &mut self, - backend: MillionVftBackend, - start: u32, - len: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, start, len)) - } - } - - pub mod io { - use super::*; - sails_rs::io_struct_impl!(Bench (backend: super::MillionStorageBackend, op: super::MillionStorageOp, seed: u32) -> super::MillionStorageBenchResult, 0, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchMany (backend: super::MillionStorageBackend, op: super::MillionStorageOp, start_seed: u32, count: u32) -> super::MillionStorageBenchResult, 1, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchVftApproveBool (backend: super::MillionVftBackend, owner_seed: u32, spender_seed: u32) -> bool, 2, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchVftTransfer (backend: super::MillionVftBackend, op: super::MillionVftTransferOp, seed: u32) -> super::MillionVftTransferResult, 3, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchVftTransferBool (backend: super::MillionVftBackend, op: super::MillionVftTransferOp, seed: u32) -> bool, 4, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchVftTransferFreshBool (backend: super::MillionVftBackend, seed: u32) -> bool, 5, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchVftTransferFromSpenderBool (backend: super::MillionVftBackend, owner_seed: u32, spender_seed: u32) -> bool, 6, ::INTERFACE_ID); - sails_rs::io_struct_impl!(PrepareChunk (backend: super::MillionStorageBackend, start: u32, len: u32) -> super::MillionStorageBenchResult, 7, ::INTERFACE_ID); - sails_rs::io_struct_impl!(PrepareVftChunk (backend: super::MillionVftBackend, start: u32, len: u32) -> super::MillionVftTransferResult, 8, ::INTERFACE_ID); - } -} diff --git a/benchmarks/src/storage_stress_program.rs b/benchmarks/src/storage_stress_program.rs deleted file mode 100644 index 67eb4e383..000000000 --- a/benchmarks/src/storage_stress_program.rs +++ /dev/null @@ -1,139 +0,0 @@ -// Code generated by sails-client-gen-v2. DO NOT EDIT. -#[allow(unused_imports)] -use sails_rs::{client::*, collections::*, prelude::*}; -pub struct StorageStressProgram; - -impl StorageStressProgram { - pub const ROUTE_ID_STORAGE_STRESS: u8 = 1; -} - -impl sails_rs::client::Program for StorageStressProgram {} - -pub trait StorageStress { - type Env: sails_rs::client::GearEnv; - fn storage_stress( - &self, - ) -> sails_rs::client::Service; -} - -impl StorageStress - for sails_rs::client::Actor -{ - type Env = E; - fn storage_stress( - &self, - ) -> sails_rs::client::Service { - self.service(StorageStressProgram::ROUTE_ID_STORAGE_STRESS) - } -} -pub trait StorageStressCtors { - type Env: sails_rs::client::GearEnv; - fn new_for_bench( - self, - ) -> sails_rs::client::PendingCtor; -} - -impl StorageStressCtors - for sails_rs::client::Deployment -{ - type Env = E; - fn new_for_bench( - self, - ) -> sails_rs::client::PendingCtor { - self.pending_ctor(()) - } -} - -pub mod io { - use super::*; - sails_rs::io_struct_impl!(NewForBench () -> (), 0); -} - -pub mod storage_stress { - use super::*; - - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub enum StorageBackend { - HashMap, - Fixed, - RawStatic, - SailsFixed, - SailsStatic, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub struct StorageBenchResult { - pub value: U256, - pub len: u32, - pub existed: bool, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub enum StorageMap { - Balance, - Allowance, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub enum StorageOp { - InsertFresh, - UpdateExisting, - ReadExisting, - ReadMissing, - Remove, - } - - pub trait StorageStress { - type Env: sails_rs::client::GearEnv; - fn bench( - &mut self, - backend: StorageBackend, - map: StorageMap, - op: StorageOp, - seed: u32, - ) -> sails_rs::client::PendingCall; - fn prepare( - &mut self, - backend: StorageBackend, - map: StorageMap, - len: u32, - ) -> sails_rs::client::PendingCall; - } - - pub struct StorageStressImpl; - - impl sails_rs::client::Identifiable for StorageStressImpl { - const INTERFACE_ID: sails_rs::InterfaceId = - sails_rs::InterfaceId::from_bytes_8([102, 51, 57, 17, 133, 13, 155, 16]); - } - - impl StorageStress - for sails_rs::client::Service - { - type Env = E; - fn bench( - &mut self, - backend: StorageBackend, - map: StorageMap, - op: StorageOp, - seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, map, op, seed)) - } - fn prepare( - &mut self, - backend: StorageBackend, - map: StorageMap, - len: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, map, len)) - } - } - - pub mod io { - use super::*; - sails_rs::io_struct_impl!(Bench (backend: super::StorageBackend, map: super::StorageMap, op: super::StorageOp, seed: u32) -> super::StorageBenchResult, 0, ::INTERFACE_ID); - sails_rs::io_struct_impl!(Prepare (backend: super::StorageBackend, map: super::StorageMap, len: u32) -> super::StorageBenchResult, 1, ::INTERFACE_ID); - } -} diff --git a/benchmarks/src/vft_stress_program.rs b/benchmarks/src/vft_stress_program.rs deleted file mode 100644 index 321a721b9..000000000 --- a/benchmarks/src/vft_stress_program.rs +++ /dev/null @@ -1,241 +0,0 @@ -// Code generated by sails-client-gen-v2. DO NOT EDIT. -#[allow(unused_imports)] -use sails_rs::{client::*, collections::*, prelude::*}; -pub struct VftStressProgram; - -impl VftStressProgram { - pub const ROUTE_ID_VFT_STRESS: u8 = 1; -} - -impl sails_rs::client::Program for VftStressProgram {} - -pub trait VftStress { - type Env: sails_rs::client::GearEnv; - fn vft_stress(&self) -> sails_rs::client::Service; -} - -impl VftStress for sails_rs::client::Actor { - type Env = E; - fn vft_stress(&self) -> sails_rs::client::Service { - self.service(VftStressProgram::ROUTE_ID_VFT_STRESS) - } -} -pub trait VftStressCtors { - type Env: sails_rs::client::GearEnv; - fn new_for_bench( - self, - ) -> sails_rs::client::PendingCtor; -} - -impl VftStressCtors - for sails_rs::client::Deployment -{ - type Env = E; - fn new_for_bench( - self, - ) -> sails_rs::client::PendingCtor { - self.pending_ctor(()) - } -} - -pub mod io { - use super::*; - sails_rs::io_struct_impl!(NewForBench () -> (), 0); -} - -pub mod vft_stress { - use super::*; - - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub enum VftPhase { - ProbeOverhead, - NoopBody, - EchoBody, - KeyDerive, - AllowanceGet, - AllowancePut, - BalanceGetFrom, - BalanceGetTo, - BalancePutFrom, - BalancePutTo, - BalanceTransfer, - BalanceTransferFrom, - ResultBuild, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub struct VftPhaseGas { - pub phase: VftPhase, - pub gas: u64, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub struct VftProfileResult { - pub result: VftTransferResult, - pub phases: Vec, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub enum VftStorageBackend { - BTree, - HashMap, - SailsFixed, - SailsStatic, - SailsStaticFast, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub enum VftTransferOp { - Transfer, - TransferFrom, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub struct VftTransferResult { - pub from_balance: U256, - pub to_balance: U256, - pub allowance: U256, - pub balance_len: u32, - pub allowance_len: u32, - pub transferred: bool, - } - - pub trait VftStress { - type Env: sails_rs::client::GearEnv; - fn bench_echo_vft_args( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> sails_rs::client::PendingCall; - fn bench_noop(&mut self) -> sails_rs::client::PendingCall; - fn bench_vft_approve_bool( - &mut self, - backend: VftStorageBackend, - owner_seed: u32, - spender_seed: u32, - ) -> sails_rs::client::PendingCall; - fn bench_vft_approve_profile( - &mut self, - backend: VftStorageBackend, - owner_seed: u32, - spender_seed: u32, - ) -> sails_rs::client::PendingCall; - fn bench_vft_transfer( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> sails_rs::client::PendingCall; - fn bench_vft_transfer_fresh_bool( - &mut self, - backend: VftStorageBackend, - seed: u32, - ) -> sails_rs::client::PendingCall; - fn bench_vft_transfer_fresh_profile( - &mut self, - backend: VftStorageBackend, - seed: u32, - ) -> sails_rs::client::PendingCall; - fn bench_vft_transfer_profile( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> sails_rs::client::PendingCall; - fn prepare_vft( - &mut self, - backend: VftStorageBackend, - len: u32, - ) -> sails_rs::client::PendingCall; - } - - pub struct VftStressImpl; - - impl sails_rs::client::Identifiable for VftStressImpl { - const INTERFACE_ID: sails_rs::InterfaceId = - sails_rs::InterfaceId::from_bytes_8([1, 131, 167, 200, 89, 93, 8, 160]); - } - - impl VftStress for sails_rs::client::Service { - type Env = E; - fn bench_echo_vft_args( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, op, seed)) - } - fn bench_noop(&mut self) -> sails_rs::client::PendingCall { - self.pending_call(()) - } - fn bench_vft_approve_bool( - &mut self, - backend: VftStorageBackend, - owner_seed: u32, - spender_seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, owner_seed, spender_seed)) - } - fn bench_vft_approve_profile( - &mut self, - backend: VftStorageBackend, - owner_seed: u32, - spender_seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, owner_seed, spender_seed)) - } - fn bench_vft_transfer( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, op, seed)) - } - fn bench_vft_transfer_fresh_bool( - &mut self, - backend: VftStorageBackend, - seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, seed)) - } - fn bench_vft_transfer_fresh_profile( - &mut self, - backend: VftStorageBackend, - seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, seed)) - } - fn bench_vft_transfer_profile( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, op, seed)) - } - fn prepare_vft( - &mut self, - backend: VftStorageBackend, - len: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((backend, len)) - } - } - - pub mod io { - use super::*; - sails_rs::io_struct_impl!(BenchEchoVftArgs (backend: super::VftStorageBackend, op: super::VftTransferOp, seed: u32) -> bool, 0, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchNoop () -> bool, 1, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchVftApproveBool (backend: super::VftStorageBackend, owner_seed: u32, spender_seed: u32) -> bool, 2, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchVftApproveProfile (backend: super::VftStorageBackend, owner_seed: u32, spender_seed: u32) -> super::VftProfileResult, 3, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchVftTransfer (backend: super::VftStorageBackend, op: super::VftTransferOp, seed: u32) -> super::VftTransferResult, 4, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchVftTransferFreshBool (backend: super::VftStorageBackend, seed: u32) -> bool, 5, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchVftTransferFreshProfile (backend: super::VftStorageBackend, seed: u32) -> super::VftProfileResult, 6, ::INTERFACE_ID); - sails_rs::io_struct_impl!(BenchVftTransferProfile (backend: super::VftStorageBackend, op: super::VftTransferOp, seed: u32) -> super::VftProfileResult, 7, ::INTERFACE_ID); - sails_rs::io_struct_impl!(PrepareVft (backend: super::VftStorageBackend, len: u32) -> super::VftTransferResult, 8, ::INTERFACE_ID); - } -} diff --git a/benchmarks/storage-million/Cargo.toml b/benchmarks/storage-million/Cargo.toml deleted file mode 100644 index 5d8a5b3b8..000000000 --- a/benchmarks/storage-million/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "storage-million" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -sails-rs.workspace = true -sails-storage = { workspace = true, features = ["experimental-vft-account"] } - -[build-dependencies] -sails-rs = { workspace = true, features = ["wasm-builder", "experimental-vft-account"] } diff --git a/benchmarks/storage-million/build.rs b/benchmarks/storage-million/build.rs deleted file mode 100644 index c18a36205..000000000 --- a/benchmarks/storage-million/build.rs +++ /dev/null @@ -1,25 +0,0 @@ -fn main() { - const MILLION_BALANCE_LOG2_SLOTS: u8 = 21; - const MILLION_BALANCE_SLOTS: usize = 1usize << MILLION_BALANCE_LOG2_SLOTS; - const PAGE_LOCAL_BALANCE_LOG2_TILES: u8 = 13; - - let layout = sails_rs::build::StaticMemoryLayout::new(1024) - .reserve_table::<32, 32>("million_balances", MILLION_BALANCE_SLOTS) - .reserve_table::<64, 32>("million_allowances", MILLION_BALANCE_SLOTS) - .reserve_actor_u256_map::("wat_actor_balances") - .reserve_actor_u256_map::("mixed_actor_balances") - .reserve_actor_pair_u256_map::("wat_allowances") - .reserve_control_actor_u256_map::("control_actor_balances") - .reserve_page_local_actor_u256_map::( - "page_local_actor_balances", - ) - .reserve_grouped_control_actor_u256_map::<12, 1>("grouped_actor_balances_pages2") - .reserve_grouped_control_actor_u256_map::<11, 2>("grouped_actor_balances_pages4") - .reserve_grouped_control_actor_u256_map::<10, 3>("grouped_actor_balances_pages8") - .reserve_grouped_control_actor_u256_map::<9, 4>("grouped_actor_balances_pages16") - .reserve_grouped_control_actor_u256_map::<8, 5>("grouped_actor_balances_pages32") - .reserve_grouped_control_actor_u256_map::<7, 6>("grouped_actor_balances_pages64") - .reserve_grouped_control_actor_u256_map::<6, 7>("grouped_actor_balances_pages128"); - - sails_rs::build::build_wasm_with_static_memory(layout); -} diff --git a/benchmarks/storage-million/src/lib.rs b/benchmarks/storage-million/src/lib.rs deleted file mode 100644 index ceb469520..000000000 --- a/benchmarks/storage-million/src/lib.rs +++ /dev/null @@ -1,2089 +0,0 @@ -#![no_std] - -use sails_rs::{ - cell::RefCell, - collections::{BTreeMap, HashMap}, - prelude::*, -}; -use sails_storage::__experimental::{ - StaticActorIdU256Map as SailsStaticActorIdU256Map, - StaticActorTagU64Map as SailsStaticActorTagU64Map, - StaticActorTagU256Map as SailsStaticActorTagU256Map, - StaticAllowanceTable as SailsStaticAllowanceTable, - StaticAllowanceU256Map as SailsStaticAllowanceU256Map, - StaticBalanceTable as SailsStaticBalanceTable, - StaticControlActorIdU256Map as SailsStaticControlActorIdU256Map, - StaticGroupedControlActorIdU256Map as SailsStaticGroupedControlActorIdU256Map, - StaticMixedActorIdU256Map as SailsStaticMixedActorIdU256Map, - StaticPageLocalActorIdU256Map as SailsStaticPageLocalActorIdU256Map, - StaticVftAccountStorage as SailsStaticVftAccountStorage, -}; - -mod static_storage { - include!(concat!(env!("OUT_DIR"), "/sails_static_storage.rs")); -} - -const GENERIC_BALANCE_CAPACITY: u32 = static_storage::MILLION_BALANCES_SLOTS as u32; -const GENERIC_ALLOWANCE_CAPACITY: u32 = static_storage::MILLION_ALLOWANCES_SLOTS as u32; -const WAT_ACTOR_BALANCE_CAPACITY: u32 = static_storage::WAT_ACTOR_BALANCES_SLOTS as u32; -const MIXED_ACTOR_BALANCE_CAPACITY: u32 = static_storage::MIXED_ACTOR_BALANCES_SLOTS as u32; -const TAG_ACTOR_BALANCE_CAPACITY: u32 = static_storage::MILLION_BALANCES_SLOTS as u32; -const TAG_U64_ACTOR_BALANCE_CAPACITY: u32 = (static_storage::MILLION_BALANCES_SLOTS * 4) as u32; -const WAT_ALLOWANCE_CAPACITY: u32 = static_storage::WAT_ALLOWANCES_SLOTS as u32; -const CONTROL_ACTOR_BALANCE_CAPACITY: u32 = static_storage::CONTROL_ACTOR_BALANCES_SLOTS as u32; -const PAGE_LOCAL_ACTOR_BALANCE_CAPACITY: u32 = - static_storage::PAGE_LOCAL_ACTOR_BALANCES_SLOTS as u32; - -pub const STATIC_MEMORY_END_PAGE: u32 = static_storage::STATIC_MEMORY_END_PAGE; - -type WatActorBalanceTable = - SailsStaticActorIdU256Map<{ static_storage::WAT_ACTOR_BALANCES_LOG2_SLOTS }>; -type MixedActorBalanceTable = - SailsStaticMixedActorIdU256Map<{ static_storage::MIXED_ACTOR_BALANCES_LOG2_SLOTS }>; -type TagActorBalanceTable = SailsStaticActorTagU256Map; -type TagU64ActorTable = SailsStaticActorTagU64Map; -type WatAllowanceTable = SailsStaticAllowanceU256Map<{ static_storage::WAT_ALLOWANCES_LOG2_SLOTS }>; -type ControlActorBalanceTable = - SailsStaticControlActorIdU256Map<{ static_storage::CONTROL_ACTOR_BALANCES_LOG2_SLOTS }>; -type PageLocalActorBalanceTable = - SailsStaticPageLocalActorIdU256Map<{ static_storage::PAGE_LOCAL_ACTOR_BALANCES_LOG2_TILES }>; -type GroupedActorPages2Table = SailsStaticGroupedControlActorIdU256Map< - { static_storage::GROUPED_ACTOR_BALANCES_PAGES2_LOG2_GROUPS }, - { static_storage::GROUPED_ACTOR_BALANCES_PAGES2_LOG2_GROUP_PAGES }, ->; -type GroupedActorPages4Table = SailsStaticGroupedControlActorIdU256Map< - { static_storage::GROUPED_ACTOR_BALANCES_PAGES4_LOG2_GROUPS }, - { static_storage::GROUPED_ACTOR_BALANCES_PAGES4_LOG2_GROUP_PAGES }, ->; -type GroupedActorPages8Table = SailsStaticGroupedControlActorIdU256Map< - { static_storage::GROUPED_ACTOR_BALANCES_PAGES8_LOG2_GROUPS }, - { static_storage::GROUPED_ACTOR_BALANCES_PAGES8_LOG2_GROUP_PAGES }, ->; -type GroupedActorPages16Table = SailsStaticGroupedControlActorIdU256Map< - { static_storage::GROUPED_ACTOR_BALANCES_PAGES16_LOG2_GROUPS }, - { static_storage::GROUPED_ACTOR_BALANCES_PAGES16_LOG2_GROUP_PAGES }, ->; -type GroupedActorPages32Table = SailsStaticGroupedControlActorIdU256Map< - { static_storage::GROUPED_ACTOR_BALANCES_PAGES32_LOG2_GROUPS }, - { static_storage::GROUPED_ACTOR_BALANCES_PAGES32_LOG2_GROUP_PAGES }, ->; -type GroupedActorPages64Table = SailsStaticGroupedControlActorIdU256Map< - { static_storage::GROUPED_ACTOR_BALANCES_PAGES64_LOG2_GROUPS }, - { static_storage::GROUPED_ACTOR_BALANCES_PAGES64_LOG2_GROUP_PAGES }, ->; -type GroupedActorPages128Table = SailsStaticGroupedControlActorIdU256Map< - { static_storage::GROUPED_ACTOR_BALANCES_PAGES128_LOG2_GROUPS }, - { static_storage::GROUPED_ACTOR_BALANCES_PAGES128_LOG2_GROUP_PAGES }, ->; -type InlineOwnerAccountTable = SailsStaticVftAccountStorage< - { static_storage::WAT_ACTOR_BALANCES_LOG2_SLOTS }, - { static_storage::WAT_ALLOWANCES_LOG2_SLOTS }, ->; - -const STORAGE_BACKEND_COUNT: usize = 14; - -#[sails_type] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum MillionStorageBackend { - GenericStatic, - WatActorStatic, - MixedActorStatic, - TagActorStatic, - TagU64ActorStatic, - ControlActorStatic, - PageLocalActorStatic, - GroupedActorPages2, - GroupedActorPages4, - GroupedActorPages8, - GroupedActorPages16, - GroupedActorPages32, - GroupedActorPages64, - GroupedActorPages128, -} - -#[sails_type] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum MillionVftBackend { - BTree, - HashMap, - GenericStatic, - GenericStaticFused, - GenericStaticFast, - WatActorStatic, - MixedActorStatic, - MixedActorFast, - TagActorStatic, - TagU64ActorStatic, - ControlActorStatic, - PageLocalActorStatic, - GroupedActorPages64, - GroupedActorPages128, - InlineOwnerAccountU256, -} - -#[sails_type] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum MillionStorageOp { - InsertFresh, - UpdateExisting, - ReadExisting, - ReadMissing, - Remove, -} - -#[sails_type] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum MillionVftTransferOp { - Transfer, - TransferFrom, -} - -#[sails_type] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct MillionStorageBenchResult { - pub value: U256, - pub len: u32, - pub existed: bool, -} - -#[sails_type] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct MillionVftTransferResult { - pub from_balance: U256, - pub to_balance: U256, - pub allowance: U256, - pub balance_len: u32, - pub allowance_len: u32, - pub transferred: bool, -} - -struct AppliedOp { - value: U256, - existed: bool, -} - -struct StorageMillionService<'a> { - state: &'a RefCell, -} - -impl<'a> StorageMillionService<'a> { - fn new(state: &'a RefCell) -> Self { - Self { state } - } -} - -#[sails_rs::service] -impl StorageMillionService<'_> { - #[export] - pub fn prepare_chunk( - &mut self, - backend: MillionStorageBackend, - start: u32, - len: u32, - ) -> MillionStorageBenchResult { - self.state.borrow_mut().prepare_chunk(backend, start, len) - } - - #[export] - pub fn bench( - &mut self, - backend: MillionStorageBackend, - op: MillionStorageOp, - seed: u32, - ) -> MillionStorageBenchResult { - self.state.borrow_mut().bench(backend, op, seed) - } - - #[export] - pub fn bench_many( - &mut self, - backend: MillionStorageBackend, - op: MillionStorageOp, - start_seed: u32, - count: u32, - ) -> MillionStorageBenchResult { - self.state - .borrow_mut() - .bench_many(backend, op, start_seed, count) - } - - #[export] - pub fn prepare_vft_chunk( - &mut self, - backend: MillionVftBackend, - start: u32, - len: u32, - ) -> MillionVftTransferResult { - self.state - .borrow_mut() - .prepare_vft_chunk(backend, start, len) - } - - #[export] - pub fn bench_vft_transfer( - &mut self, - backend: MillionVftBackend, - op: MillionVftTransferOp, - seed: u32, - ) -> MillionVftTransferResult { - self.state - .borrow_mut() - .bench_vft_transfer(backend, op, seed) - } - - #[export] - pub fn bench_vft_transfer_bool( - &mut self, - backend: MillionVftBackend, - op: MillionVftTransferOp, - seed: u32, - ) -> bool { - self.state - .borrow_mut() - .bench_vft_transfer_bool(backend, op, seed) - } - - #[export] - pub fn bench_vft_transfer_fresh_bool(&mut self, backend: MillionVftBackend, seed: u32) -> bool { - self.state - .borrow_mut() - .bench_vft_transfer_fresh_bool(backend, seed) - } - - #[export] - pub fn bench_vft_transfer_from_spender_bool( - &mut self, - backend: MillionVftBackend, - owner_seed: u32, - spender_seed: u32, - ) -> bool { - self.state - .borrow_mut() - .bench_vft_transfer_from_spender_bool(backend, owner_seed, spender_seed) - } - - #[export] - pub fn bench_vft_approve_bool( - &mut self, - backend: MillionVftBackend, - owner_seed: u32, - spender_seed: u32, - ) -> bool { - self.state - .borrow_mut() - .bench_vft_approve_bool(backend, owner_seed, spender_seed) - } -} - -pub struct StorageMillionProgram { - state: RefCell, -} - -#[sails_rs::program] -impl StorageMillionProgram { - pub fn new_for_bench() -> Self { - Self { - state: RefCell::new(StorageMillionState::new()), - } - } - - pub fn storage_million(&self) -> StorageMillionService<'_> { - StorageMillionService::new(&self.state) - } -} - -struct StorageMillionState { - lens: [u32; STORAGE_BACKEND_COUNT], - allowance_lens: [u32; STORAGE_BACKEND_COUNT], - dynamic_vft: DynamicMillionVftStorage, -} - -enum DynamicMillionVftStorage { - Empty, - BTree { - balances: BTreeMap, - allowances: BTreeMap<(ActorId, ActorId), U256>, - }, - HashMap { - balances: HashMap, - allowances: HashMap<(ActorId, ActorId), U256>, - }, -} - -impl StorageMillionState { - fn new() -> Self { - Self { - lens: [0; STORAGE_BACKEND_COUNT], - allowance_lens: [0; STORAGE_BACKEND_COUNT], - dynamic_vft: DynamicMillionVftStorage::Empty, - } - } - - fn prepare_chunk( - &mut self, - backend: MillionStorageBackend, - start: u32, - len: u32, - ) -> MillionStorageBenchResult { - let end = start.checked_add(len).expect("million prepare overflow"); - assert!(end <= backend_capacity(backend)); - - for seed in start + 1..=end { - self.insert(backend, seed, value_for_seed(seed)); - } - - MillionStorageBenchResult { - value: value_for_seed(end), - len: self.len(backend), - existed: false, - } - } - - fn bench( - &mut self, - backend: MillionStorageBackend, - op: MillionStorageOp, - seed: u32, - ) -> MillionStorageBenchResult { - let applied = self.apply_op(backend, op, seed); - MillionStorageBenchResult { - value: applied.value, - len: self.len(backend), - existed: applied.existed, - } - } - - fn bench_many( - &mut self, - backend: MillionStorageBackend, - op: MillionStorageOp, - start_seed: u32, - count: u32, - ) -> MillionStorageBenchResult { - assert!(count > 0); - - let mut applied = AppliedOp { - value: U256::zero(), - existed: false, - }; - for offset in 0..count { - let seed = start_seed - .checked_add(offset) - .expect("million batch seed overflow"); - applied = self.apply_op(backend, op, seed); - } - - MillionStorageBenchResult { - value: applied.value, - len: self.len(backend), - existed: applied.existed, - } - } - - fn prepare_vft_chunk( - &mut self, - backend: MillionVftBackend, - start: u32, - len: u32, - ) -> MillionVftTransferResult { - let end = start - .checked_add(len) - .expect("million VFT prepare overflow"); - if let Some(static_backend) = backend.static_backend() { - assert!(end <= backend_capacity(static_backend)); - assert!(end <= allowance_capacity(static_backend)); - } - if start == 0 { - self.dynamic_vft = match backend { - MillionVftBackend::BTree => DynamicMillionVftStorage::BTree { - balances: BTreeMap::new(), - allowances: BTreeMap::new(), - }, - MillionVftBackend::HashMap => DynamicMillionVftStorage::HashMap { - balances: HashMap::new(), - allowances: HashMap::new(), - }, - _ => DynamicMillionVftStorage::Empty, - }; - } - - for seed in start + 1..=end { - self.vft_insert_balance(backend, seed, vft_balance_for_seed(seed)); - self.insert_allowance( - backend, - vft_actor_for_seed(seed), - vft_spender_for_seed(seed), - vft_allowance_for_seed(seed), - ); - } - - MillionVftTransferResult { - from_balance: vft_balance_for_seed(end), - to_balance: U256::zero(), - allowance: U256::zero(), - balance_len: self.vft_balance_len(backend), - allowance_len: self.vft_allowance_len(backend), - transferred: false, - } - } - - fn bench_vft_transfer( - &mut self, - backend: MillionVftBackend, - op: MillionVftTransferOp, - seed: u32, - ) -> MillionVftTransferResult { - let len = self.vft_balance_len(backend); - assert!(len > 1); - let to_seed = if seed == len { 1 } else { seed + 1 }; - let amount = vft_transfer_amount(seed); - let spender = vft_spender_for_seed(seed); - - if backend == MillionVftBackend::WatActorStatic { - let owner = vft_actor_for_seed(seed); - let recipient = vft_actor_for_seed(to_seed); - let allowance_key = matches!(op, MillionVftTransferOp::TransferFrom).then_some(spender); - - if allowance_key.is_some() { - let Some(transfer) = wat_actor_balance_table() - .transfer_actor_u256_from( - &wat_allowance_table(), - owner, - spender, - recipient, - amount, - ) - .expect("wat actor million transfer-from failed") - else { - return self.vft_result(backend, seed, to_seed, allowance_key, false); - }; - - return MillionVftTransferResult { - from_balance: transfer.from_balance, - to_balance: transfer.to_balance, - allowance: transfer.allowance, - balance_len: self.vft_balance_len(backend), - allowance_len: self.vft_allowance_len(backend), - transferred: true, - }; - } - - let Some(transfer) = wat_actor_balance_table() - .transfer_actor_u256(owner, recipient, amount) - .expect("wat actor million balance transfer failed") - else { - return self.vft_result(backend, seed, to_seed, allowance_key, false); - }; - - return MillionVftTransferResult { - from_balance: transfer.from_balance, - to_balance: transfer.to_balance, - allowance: U256::zero(), - balance_len: self.vft_balance_len(backend), - allowance_len: self.vft_allowance_len(backend), - transferred: true, - }; - } - if backend == MillionVftBackend::MixedActorStatic { - let owner = vft_actor_for_seed(seed); - let recipient = vft_actor_for_seed(to_seed); - let allowance_key = matches!(op, MillionVftTransferOp::TransferFrom).then_some(spender); - - if allowance_key.is_some() { - let Some(transfer) = mixed_actor_balance_table() - .transfer_actor_u256_from( - &wat_allowance_table(), - owner, - spender, - recipient, - amount, - ) - .expect("mixed actor million transfer-from failed") - else { - return self.vft_result(backend, seed, to_seed, allowance_key, false); - }; - - return MillionVftTransferResult { - from_balance: transfer.from_balance, - to_balance: transfer.to_balance, - allowance: transfer.allowance, - balance_len: self.vft_balance_len(backend), - allowance_len: self.vft_allowance_len(backend), - transferred: true, - }; - } - - let Some(transfer) = mixed_actor_balance_table() - .transfer_actor_u256(owner, recipient, amount) - .expect("mixed actor million balance transfer failed") - else { - return self.vft_result(backend, seed, to_seed, allowance_key, false); - }; - - return MillionVftTransferResult { - from_balance: transfer.from_balance, - to_balance: transfer.to_balance, - allowance: U256::zero(), - balance_len: self.vft_balance_len(backend), - allowance_len: self.vft_allowance_len(backend), - transferred: true, - }; - } - if backend == MillionVftBackend::MixedActorFast { - let owner = vft_actor_for_seed(seed); - let recipient = vft_actor_for_seed(to_seed); - let allowance_key = matches!(op, MillionVftTransferOp::TransferFrom).then_some(spender); - - if allowance_key.is_some() { - let Some(transfer) = (unsafe { - mixed_actor_balance_table() - .transfer_actor_u256_from_nonzero_distinct_unchecked( - &wat_allowance_table(), - owner, - spender, - recipient, - amount, - ) - .expect("mixed actor fast million transfer-from failed") - }) else { - return self.vft_result(backend, seed, to_seed, allowance_key, false); - }; - - return MillionVftTransferResult { - from_balance: transfer.from_balance, - to_balance: transfer.to_balance, - allowance: transfer.allowance, - balance_len: self.vft_balance_len(backend), - allowance_len: self.vft_allowance_len(backend), - transferred: true, - }; - } - - let Some(transfer) = (unsafe { - mixed_actor_balance_table() - .transfer_actor_u256_nonzero_distinct_unchecked(owner, recipient, amount) - .expect("mixed actor fast million balance transfer failed") - }) else { - return self.vft_result(backend, seed, to_seed, allowance_key, false); - }; - - return MillionVftTransferResult { - from_balance: transfer.from_balance, - to_balance: transfer.to_balance, - allowance: U256::zero(), - balance_len: self.vft_balance_len(backend), - allowance_len: self.vft_allowance_len(backend), - transferred: true, - }; - } - if matches!(op, MillionVftTransferOp::TransferFrom) { - let allowance = self - .get_allowance(backend, vft_actor_for_seed(seed), spender) - .unwrap_or_else(U256::zero); - if allowance < amount { - return self.vft_result(backend, seed, to_seed, Some(spender), false); - } - self.insert_allowance( - backend, - vft_actor_for_seed(seed), - spender, - allowance - amount, - ); - } - - let from_balance = self - .vft_get_balance(backend, seed) - .unwrap_or_else(U256::zero); - if from_balance < amount { - let allowance_key = matches!(op, MillionVftTransferOp::TransferFrom).then_some(spender); - return self.vft_result(backend, seed, to_seed, allowance_key, false); - } - - let to_balance = self - .vft_get_balance(backend, to_seed) - .unwrap_or_else(U256::zero); - self.vft_insert_balance(backend, seed, from_balance - amount); - self.vft_insert_balance(backend, to_seed, to_balance + amount); - - let allowance_key = matches!(op, MillionVftTransferOp::TransferFrom).then_some(spender); - self.vft_result(backend, seed, to_seed, allowance_key, true) - } - - fn bench_vft_transfer_bool( - &mut self, - backend: MillionVftBackend, - op: MillionVftTransferOp, - seed: u32, - ) -> bool { - let len = self.vft_balance_len(backend); - assert!(len > 1); - let to_seed = if seed == len { 1 } else { seed + 1 }; - self.apply_vft_transfer_bool(backend, op, seed, to_seed) - } - - fn bench_vft_transfer_fresh_bool(&mut self, backend: MillionVftBackend, seed: u32) -> bool { - let len = self.vft_balance_len(backend); - assert!(len > 1); - let to_seed = len - .checked_add(seed) - .and_then(|seed| seed.checked_add(1)) - .expect("fresh VFT recipient seed overflow"); - self.apply_vft_transfer_bool(backend, MillionVftTransferOp::Transfer, seed, to_seed) - } - - fn bench_vft_transfer_from_spender_bool( - &mut self, - backend: MillionVftBackend, - owner_seed: u32, - spender_seed: u32, - ) -> bool { - let len = self.vft_balance_len(backend); - assert!(len > 1); - let to_seed = if owner_seed == len { 1 } else { owner_seed + 1 }; - self.apply_vft_transfer_from_spender_bool(backend, owner_seed, spender_seed, to_seed) - } - - fn bench_vft_approve_bool( - &mut self, - backend: MillionVftBackend, - owner_seed: u32, - spender_seed: u32, - ) -> bool { - let owner = vft_actor_for_seed(owner_seed); - let spender = vft_spender_for_seed(spender_seed); - if owner == spender { - return false; - } - - if backend == MillionVftBackend::TagActorStatic { - return unsafe { - tag_allowance_table() - .insert_allowance_u256_low64_bool_unchecked( - owner, - spender, - vft_allowance_for_seed(spender_seed), - ) - .expect("tag million allowance bool insert failed") - }; - } - if backend == MillionVftBackend::TagU64ActorStatic { - return unsafe { - tag_u64_allowance_table().insert_tag_value_bool_capacity_unchecked( - vft_allowance_tag_for_seeds(owner_seed, spender_seed), - vft_allowance_for_seed_u64(spender_seed), - ) - }; - } - - self.insert_allowance( - backend, - owner, - spender, - vft_allowance_for_seed(spender_seed), - ); - true - } - - fn apply_vft_transfer_bool( - &mut self, - backend: MillionVftBackend, - op: MillionVftTransferOp, - seed: u32, - to_seed: u32, - ) -> bool { - let amount = vft_transfer_amount(seed); - let spender = vft_spender_for_seed(seed); - - if matches!( - backend, - MillionVftBackend::GenericStaticFused | MillionVftBackend::GenericStaticFast - ) { - let owner = vft_actor_for_seed(seed); - let recipient = vft_actor_for_seed(to_seed); - let inserted_recipient = to_seed > self.vft_balance_len(backend); - let transferred = match op { - MillionVftTransferOp::Transfer - if backend == MillionVftBackend::GenericStaticFast => - unsafe { - generic_balance_table() - .transfer_actor_u256_nonzero_distinct_unchecked(owner, recipient, amount) - .expect("generic million balance transfer failed") - .is_some() - }, - MillionVftTransferOp::Transfer => generic_balance_table() - .transfer_actor_u256(owner, recipient, amount) - .expect("generic million balance transfer failed") - .is_some(), - MillionVftTransferOp::TransferFrom - if backend == MillionVftBackend::GenericStaticFast => - unsafe { - generic_balance_table() - .transfer_actor_u256_from_nonzero_distinct_unchecked( - &generic_allowance_table(), - owner, - spender, - recipient, - amount, - ) - .expect("generic million transfer-from failed") - .is_some() - }, - MillionVftTransferOp::TransferFrom => generic_balance_table() - .transfer_actor_u256_from( - &generic_allowance_table(), - owner, - spender, - recipient, - amount, - ) - .expect("generic million transfer-from failed") - .is_some(), - }; - if transferred && inserted_recipient { - *self.len_mut(MillionStorageBackend::GenericStatic) += 1; - } - return transferred; - } - - if backend == MillionVftBackend::WatActorStatic { - let owner = vft_actor_for_seed(seed); - let recipient = vft_actor_for_seed(to_seed); - return match op { - MillionVftTransferOp::Transfer => wat_actor_balance_table() - .transfer_actor_u256(owner, recipient, amount) - .expect("wat actor million balance transfer failed") - .is_some(), - MillionVftTransferOp::TransferFrom => wat_actor_balance_table() - .transfer_actor_u256_from( - &wat_allowance_table(), - owner, - spender, - recipient, - amount, - ) - .expect("wat actor million transfer-from failed") - .is_some(), - }; - } - if backend == MillionVftBackend::MixedActorStatic { - let owner = vft_actor_for_seed(seed); - let recipient = vft_actor_for_seed(to_seed); - return match op { - MillionVftTransferOp::Transfer => mixed_actor_balance_table() - .transfer_actor_u256(owner, recipient, amount) - .expect("mixed actor million balance transfer failed") - .is_some(), - MillionVftTransferOp::TransferFrom => mixed_actor_balance_table() - .transfer_actor_u256_from( - &wat_allowance_table(), - owner, - spender, - recipient, - amount, - ) - .expect("mixed actor million transfer-from failed") - .is_some(), - }; - } - - if backend == MillionVftBackend::MixedActorFast { - let owner = vft_actor_for_seed(seed); - let recipient = vft_actor_for_seed(to_seed); - return match op { - MillionVftTransferOp::Transfer => unsafe { - mixed_actor_balance_table() - .transfer_actor_u256_bool_nonzero_distinct_unchecked( - owner, recipient, amount, - ) - .expect("mixed actor fast million balance transfer failed") - }, - MillionVftTransferOp::TransferFrom => unsafe { - mixed_actor_balance_table() - .transfer_actor_u256_from_bool_nonzero_distinct_unchecked( - &wat_allowance_table(), - owner, - spender, - recipient, - amount, - ) - .expect("mixed actor fast million transfer-from failed") - }, - }; - } - if backend == MillionVftBackend::TagActorStatic { - let owner = vft_actor_for_seed(seed); - let recipient = vft_actor_for_seed(to_seed); - return match op { - MillionVftTransferOp::Transfer => unsafe { - tag_actor_balance_table() - .transfer_actor_u256_low64_bool_unchecked(owner, recipient, amount) - .expect("tag actor million balance transfer failed") - }, - MillionVftTransferOp::TransferFrom => unsafe { - tag_actor_balance_table() - .transfer_actor_u256_from_tag_low64_bool_unchecked( - &tag_allowance_table(), - owner, - spender, - recipient, - amount, - ) - .expect("tag actor million transfer-from failed") - }, - }; - } - if backend == MillionVftBackend::TagU64ActorStatic { - let owner = vft_actor_tag_for_seed(seed); - let recipient = vft_actor_tag_for_seed(to_seed); - let amount = vft_transfer_amount_u64(seed); - return match op { - MillionVftTransferOp::Transfer => unsafe { - tag_u64_balance_table() - .transfer_tags_bool_capacity_unchecked(owner, recipient, amount) - }, - MillionVftTransferOp::TransferFrom => unsafe { - tag_u64_balance_table().transfer_from_tags_bool_capacity_unchecked( - &tag_u64_allowance_table(), - vft_allowance_tag_for_seeds(seed, seed), - owner, - recipient, - amount, - ) - }, - }; - } - if backend == MillionVftBackend::InlineOwnerAccountU256 { - return match op { - MillionVftTransferOp::Transfer => inline_owner_account_table() - .transfer( - vft_actor_for_seed(seed), - vft_actor_for_seed(to_seed), - amount, - ) - .expect("inline owner account transfer failed"), - MillionVftTransferOp::TransferFrom => { - self.apply_vft_transfer_from_spender_bool(backend, seed, seed, to_seed) - } - }; - } - - if matches!(op, MillionVftTransferOp::TransferFrom) { - let allowance = self - .get_allowance(backend, vft_actor_for_seed(seed), spender) - .unwrap_or_else(U256::zero); - if allowance < amount { - return false; - } - self.insert_allowance( - backend, - vft_actor_for_seed(seed), - spender, - allowance - amount, - ); - } - - let from_balance = self - .vft_get_balance(backend, seed) - .unwrap_or_else(U256::zero); - if from_balance < amount { - return false; - } - - let to_balance = self - .vft_get_balance(backend, to_seed) - .unwrap_or_else(U256::zero); - let (to_balance, overflow) = to_balance.overflowing_add(amount); - if overflow { - return false; - } - - self.vft_insert_balance(backend, seed, from_balance - amount); - self.vft_insert_balance(backend, to_seed, to_balance); - true - } - - fn apply_vft_transfer_from_spender_bool( - &mut self, - backend: MillionVftBackend, - owner_seed: u32, - spender_seed: u32, - to_seed: u32, - ) -> bool { - let amount = vft_transfer_amount(owner_seed); - if backend == MillionVftBackend::TagActorStatic { - return unsafe { - tag_actor_balance_table() - .transfer_actor_u256_from_tag_low64_bool_unchecked( - &tag_allowance_table(), - vft_actor_for_seed(owner_seed), - vft_spender_for_seed(spender_seed), - vft_actor_for_seed(to_seed), - amount, - ) - .expect("tag actor spender transfer-from failed") - }; - } - if backend == MillionVftBackend::TagU64ActorStatic { - return unsafe { - tag_u64_balance_table().transfer_from_tags_bool_capacity_unchecked( - &tag_u64_allowance_table(), - vft_allowance_tag_for_seeds(owner_seed, spender_seed), - vft_actor_tag_for_seed(owner_seed), - vft_actor_tag_for_seed(to_seed), - vft_transfer_amount_u64(owner_seed), - ) - }; - } - if backend == MillionVftBackend::InlineOwnerAccountU256 { - return inline_owner_account_table() - .transfer_from( - vft_spender_for_seed(spender_seed), - vft_actor_for_seed(owner_seed), - vft_actor_for_seed(to_seed), - amount, - ) - .expect("inline owner account transfer-from failed"); - } - - let owner = vft_actor_for_seed(owner_seed); - let spender = vft_spender_for_seed(spender_seed); - let allowance = self - .get_allowance(backend, owner, spender) - .unwrap_or_else(U256::zero); - if allowance < amount { - return false; - } - - let from_balance = self - .vft_get_balance(backend, owner_seed) - .unwrap_or_else(U256::zero); - if from_balance < amount { - return false; - } - - let to_balance = self - .vft_get_balance(backend, to_seed) - .unwrap_or_else(U256::zero); - let (to_balance, overflow) = to_balance.overflowing_add(amount); - if overflow { - return false; - } - - self.insert_allowance(backend, owner, spender, allowance - amount); - self.vft_insert_balance(backend, owner_seed, from_balance - amount); - self.vft_insert_balance(backend, to_seed, to_balance); - true - } - - fn apply_op( - &mut self, - backend: MillionStorageBackend, - op: MillionStorageOp, - seed: u32, - ) -> AppliedOp { - match op { - MillionStorageOp::InsertFresh => { - let value = value_for_seed(seed); - let existed = self.insert(backend, seed, value).is_some(); - AppliedOp { value, existed } - } - MillionStorageOp::UpdateExisting => { - let value = updated_value_for_seed(seed); - let existed = self.insert(backend, seed, value).is_some(); - AppliedOp { value, existed } - } - MillionStorageOp::ReadExisting | MillionStorageOp::ReadMissing => { - let value = self.get(backend, seed); - AppliedOp { - existed: value.is_some(), - value: value.unwrap_or_else(U256::zero), - } - } - MillionStorageOp::Remove => { - let value = self.remove(backend, seed); - AppliedOp { - existed: value.is_some(), - value: value.unwrap_or_else(U256::zero), - } - } - } - } - - fn len(&self, backend: MillionStorageBackend) -> u32 { - self.lens[backend_index(backend)] - } - - fn len_mut(&mut self, backend: MillionStorageBackend) -> &mut u32 { - &mut self.lens[backend_index(backend)] - } - - fn allowance_len(&self, backend: MillionStorageBackend) -> u32 { - self.allowance_lens[backend_index(backend)] - } - - fn allowance_len_mut(&mut self, backend: MillionStorageBackend) -> &mut u32 { - &mut self.allowance_lens[backend_index(backend)] - } - - fn insert_actor_value( - &mut self, - backend: MillionStorageBackend, - actor: ActorId, - value: U256, - ) -> Option { - let previous = match backend { - MillionStorageBackend::GenericStatic => generic_balance_table() - .insert_actor_u256(actor, value) - .expect("generic million balance table insert failed"), - MillionStorageBackend::WatActorStatic => wat_actor_balance_table() - .insert_actor_u256(actor, value) - .expect("wat actor million balance table insert failed"), - MillionStorageBackend::MixedActorStatic => mixed_actor_balance_table() - .insert_actor_u256(actor, value) - .expect("mixed actor million balance table insert failed"), - MillionStorageBackend::TagActorStatic => tag_actor_balance_table() - .insert_actor_u256(actor, value) - .expect("tag actor million balance table insert failed"), - MillionStorageBackend::TagU64ActorStatic => tag_u64_balance_table() - .insert_actor_u256(actor, value) - .expect("tag u64 actor million balance table insert failed"), - MillionStorageBackend::ControlActorStatic => control_actor_balance_table() - .insert_actor_u256(actor, value) - .expect("control actor million balance table insert failed"), - MillionStorageBackend::PageLocalActorStatic => page_local_actor_balance_table() - .insert_actor_u256(actor, value) - .expect("page-local actor million balance table insert failed"), - MillionStorageBackend::GroupedActorPages2 => grouped_actor_pages2_table() - .insert_actor_u256(actor, value) - .expect("grouped actor pages2 million balance table insert failed"), - MillionStorageBackend::GroupedActorPages4 => grouped_actor_pages4_table() - .insert_actor_u256(actor, value) - .expect("grouped actor pages4 million balance table insert failed"), - MillionStorageBackend::GroupedActorPages8 => grouped_actor_pages8_table() - .insert_actor_u256(actor, value) - .expect("grouped actor pages8 million balance table insert failed"), - MillionStorageBackend::GroupedActorPages16 => grouped_actor_pages16_table() - .insert_actor_u256(actor, value) - .expect("grouped actor pages16 million balance table insert failed"), - MillionStorageBackend::GroupedActorPages32 => grouped_actor_pages32_table() - .insert_actor_u256(actor, value) - .expect("grouped actor pages32 million balance table insert failed"), - MillionStorageBackend::GroupedActorPages64 => grouped_actor_pages64_table() - .insert_actor_u256(actor, value) - .expect("grouped actor pages64 million balance table insert failed"), - MillionStorageBackend::GroupedActorPages128 => grouped_actor_pages128_table() - .insert_actor_u256(actor, value) - .expect("grouped actor pages128 million balance table insert failed"), - }; - if previous.is_none() && !value.is_zero() { - *self.len_mut(backend) += 1; - } - previous - } - - fn get_actor_value(&self, backend: MillionStorageBackend, actor: ActorId) -> Option { - match backend { - MillionStorageBackend::GenericStatic => generic_balance_table() - .get_actor_u256(&actor) - .expect("generic million balance table get failed"), - MillionStorageBackend::WatActorStatic => wat_actor_balance_table() - .get_actor_u256(&actor) - .expect("wat actor million balance table get failed"), - MillionStorageBackend::MixedActorStatic => mixed_actor_balance_table() - .get_actor_u256(&actor) - .expect("mixed actor million balance table get failed"), - MillionStorageBackend::TagActorStatic => tag_actor_balance_table() - .get_actor_u256(&actor) - .expect("tag actor million balance table get failed"), - MillionStorageBackend::TagU64ActorStatic => tag_u64_balance_table() - .get_actor_u256(&actor) - .expect("tag u64 actor million balance table get failed"), - MillionStorageBackend::ControlActorStatic => control_actor_balance_table() - .get_actor_u256(&actor) - .expect("control actor million balance table get failed"), - MillionStorageBackend::PageLocalActorStatic => page_local_actor_balance_table() - .get_actor_u256(&actor) - .expect("page-local actor million balance table get failed"), - MillionStorageBackend::GroupedActorPages2 => grouped_actor_pages2_table() - .get_actor_u256(&actor) - .expect("grouped actor pages2 million balance table get failed"), - MillionStorageBackend::GroupedActorPages4 => grouped_actor_pages4_table() - .get_actor_u256(&actor) - .expect("grouped actor pages4 million balance table get failed"), - MillionStorageBackend::GroupedActorPages8 => grouped_actor_pages8_table() - .get_actor_u256(&actor) - .expect("grouped actor pages8 million balance table get failed"), - MillionStorageBackend::GroupedActorPages16 => grouped_actor_pages16_table() - .get_actor_u256(&actor) - .expect("grouped actor pages16 million balance table get failed"), - MillionStorageBackend::GroupedActorPages32 => grouped_actor_pages32_table() - .get_actor_u256(&actor) - .expect("grouped actor pages32 million balance table get failed"), - MillionStorageBackend::GroupedActorPages64 => grouped_actor_pages64_table() - .get_actor_u256(&actor) - .expect("grouped actor pages64 million balance table get failed"), - MillionStorageBackend::GroupedActorPages128 => grouped_actor_pages128_table() - .get_actor_u256(&actor) - .expect("grouped actor pages128 million balance table get failed"), - } - } - - fn insert(&mut self, backend: MillionStorageBackend, seed: u32, value: U256) -> Option { - let actor = actor_for_seed(seed); - self.insert_actor_value(backend, actor, value) - } - - fn get(&self, backend: MillionStorageBackend, seed: u32) -> Option { - let actor = actor_for_seed(seed); - self.get_actor_value(backend, actor) - } - - fn remove(&mut self, backend: MillionStorageBackend, seed: u32) -> Option { - let actor = actor_for_seed(seed); - let previous = match backend { - MillionStorageBackend::GenericStatic => generic_balance_table() - .remove_actor_u256(&actor) - .expect("generic million balance table remove failed"), - MillionStorageBackend::WatActorStatic => wat_actor_balance_table() - .remove_actor_u256(&actor) - .expect("wat actor million balance table remove failed"), - MillionStorageBackend::MixedActorStatic => mixed_actor_balance_table() - .remove_actor_u256(&actor) - .expect("mixed actor million balance table remove failed"), - MillionStorageBackend::TagActorStatic => tag_actor_balance_table() - .remove_actor_u256(&actor) - .expect("tag actor million balance table remove failed"), - MillionStorageBackend::TagU64ActorStatic => tag_u64_balance_table() - .remove_actor_u256(&actor) - .expect("tag u64 actor million balance table remove failed"), - MillionStorageBackend::ControlActorStatic => control_actor_balance_table() - .remove_actor_u256(&actor) - .expect("control actor million balance table remove failed"), - MillionStorageBackend::PageLocalActorStatic => page_local_actor_balance_table() - .remove_actor_u256(&actor) - .expect("page-local actor million balance table remove failed"), - MillionStorageBackend::GroupedActorPages2 => grouped_actor_pages2_table() - .remove_actor_u256(&actor) - .expect("grouped actor pages2 million balance table remove failed"), - MillionStorageBackend::GroupedActorPages4 => grouped_actor_pages4_table() - .remove_actor_u256(&actor) - .expect("grouped actor pages4 million balance table remove failed"), - MillionStorageBackend::GroupedActorPages8 => grouped_actor_pages8_table() - .remove_actor_u256(&actor) - .expect("grouped actor pages8 million balance table remove failed"), - MillionStorageBackend::GroupedActorPages16 => grouped_actor_pages16_table() - .remove_actor_u256(&actor) - .expect("grouped actor pages16 million balance table remove failed"), - MillionStorageBackend::GroupedActorPages32 => grouped_actor_pages32_table() - .remove_actor_u256(&actor) - .expect("grouped actor pages32 million balance table remove failed"), - MillionStorageBackend::GroupedActorPages64 => grouped_actor_pages64_table() - .remove_actor_u256(&actor) - .expect("grouped actor pages64 million balance table remove failed"), - MillionStorageBackend::GroupedActorPages128 => grouped_actor_pages128_table() - .remove_actor_u256(&actor) - .expect("grouped actor pages128 million balance table remove failed"), - }; - if previous.is_some() { - *self.len_mut(backend) -= 1; - } - previous - } - - fn vft_balance_len(&self, backend: MillionVftBackend) -> u32 { - match (backend, &self.dynamic_vft) { - (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { balances, .. }) => { - balances.len() as u32 - } - (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { balances, .. }) => { - balances.len() as u32 - } - _ => backend - .static_backend() - .map_or(0, |backend| self.len(backend)), - } - } - - fn vft_allowance_len(&self, backend: MillionVftBackend) -> u32 { - match (backend, &self.dynamic_vft) { - (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { allowances, .. }) => { - allowances.len() as u32 - } - (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { allowances, .. }) => { - allowances.len() as u32 - } - _ => backend - .static_backend() - .map_or(0, |backend| self.allowance_len(backend)), - } - } - - fn vft_get_balance(&self, backend: MillionVftBackend, seed: u32) -> Option { - let actor = vft_actor_for_seed(seed); - match (backend, &self.dynamic_vft) { - (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { balances, .. }) => { - balances.get(&actor).copied() - } - (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { balances, .. }) => { - balances.get(&actor).copied() - } - (MillionVftBackend::InlineOwnerAccountU256, _) => inline_owner_account_table() - .accounts() - .get_balance(vft_actor_for_seed(seed)) - .expect("inline owner account balance get failed"), - _ => backend - .static_backend() - .and_then(|backend| self.get_actor_value(backend, actor)), - } - } - - fn vft_insert_balance( - &mut self, - backend: MillionVftBackend, - seed: u32, - value: U256, - ) -> Option { - let actor = vft_actor_for_seed(seed); - match (backend, &mut self.dynamic_vft) { - (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { balances, .. }) => { - balances.insert(actor, value) - } - (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { balances, .. }) => { - balances.insert(actor, value) - } - (MillionVftBackend::InlineOwnerAccountU256, _) => { - let previous = inline_owner_account_table() - .accounts() - .insert_balance(vft_actor_for_seed(seed), value) - .expect("inline owner account balance insert failed"); - if previous.is_none() && !value.is_zero() { - *self.len_mut(MillionStorageBackend::GroupedActorPages128) += 1; - } - previous - } - _ => { - let static_backend = backend - .static_backend() - .expect("static VFT backend expected"); - self.insert_actor_value(static_backend, actor, value) - } - } - } - - fn insert_allowance( - &mut self, - backend: MillionVftBackend, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Option { - match (backend, &mut self.dynamic_vft) { - (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { allowances, .. }) => { - return allowances.insert((owner, spender), value); - } - (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { allowances, .. }) => { - return allowances.insert((owner, spender), value); - } - _ => {} - } - - if backend == MillionVftBackend::InlineOwnerAccountU256 { - let storage = inline_owner_account_table(); - let previous = storage - .allowance(owner, spender) - .expect("inline owner account allowance get failed"); - storage - .approve(owner, spender, value) - .expect("inline owner account allowance insert failed"); - if previous.is_zero() && !value.is_zero() { - *self.allowance_len_mut(MillionStorageBackend::GroupedActorPages128) += 1; - } - return (!previous.is_zero()).then_some(previous); - } - - let static_backend = backend - .static_backend() - .expect("static VFT backend expected"); - let previous = match static_backend { - MillionStorageBackend::GenericStatic => generic_allowance_table() - .insert_allowance_u256(owner, spender, value) - .expect("generic million allowance table insert failed"), - MillionStorageBackend::TagActorStatic => tag_allowance_table() - .insert_allowance_u256(owner, spender, value) - .expect("tag million allowance table insert failed"), - MillionStorageBackend::TagU64ActorStatic => { - tag_u64_allowance_table() - .insert_allowance_u256_bool(owner, spender, value) - .expect("tag u64 million allowance table insert failed"); - None - } - _ => wat_allowance_table() - .insert_allowance_u256(owner, spender, value) - .expect("wat million allowance table insert failed"), - }; - if previous.is_none() && !value.is_zero() { - *self.allowance_len_mut(static_backend) += 1; - } - previous - } - - fn get_allowance( - &self, - backend: MillionVftBackend, - owner: ActorId, - spender: ActorId, - ) -> Option { - match (backend, &self.dynamic_vft) { - (MillionVftBackend::BTree, DynamicMillionVftStorage::BTree { allowances, .. }) => { - return allowances.get(&(owner, spender)).copied(); - } - (MillionVftBackend::HashMap, DynamicMillionVftStorage::HashMap { allowances, .. }) => { - return allowances.get(&(owner, spender)).copied(); - } - _ => {} - } - - if backend == MillionVftBackend::InlineOwnerAccountU256 { - let allowance = inline_owner_account_table() - .allowance(owner, spender) - .expect("inline owner account allowance get failed"); - return (!allowance.is_zero()).then_some(allowance); - } - - match backend - .static_backend() - .expect("static VFT backend expected") - { - MillionStorageBackend::GenericStatic => generic_allowance_table() - .get_allowance_u256(&owner, &spender) - .expect("generic million allowance table get failed"), - MillionStorageBackend::TagActorStatic => tag_allowance_table() - .get_allowance_u256(&owner, &spender) - .expect("tag million allowance table get failed"), - MillionStorageBackend::TagU64ActorStatic => tag_u64_allowance_table() - .get_allowance_u256(&owner, &spender) - .expect("tag u64 million allowance table get failed"), - _ => wat_allowance_table() - .get_allowance_u256(&owner, &spender) - .expect("wat million allowance table get failed"), - } - } - - fn vft_result( - &self, - backend: MillionVftBackend, - from_seed: u32, - to_seed: u32, - spender: Option, - transferred: bool, - ) -> MillionVftTransferResult { - let owner = vft_actor_for_seed(from_seed); - MillionVftTransferResult { - from_balance: self - .vft_get_balance(backend, from_seed) - .unwrap_or_else(U256::zero), - to_balance: self - .vft_get_balance(backend, to_seed) - .unwrap_or_else(U256::zero), - allowance: spender - .and_then(|spender| self.get_allowance(backend, owner, spender)) - .unwrap_or_else(U256::zero), - balance_len: self.vft_balance_len(backend), - allowance_len: self.vft_allowance_len(backend), - transferred, - } - } -} - -impl MillionVftBackend { - fn static_backend(self) -> Option { - match self { - Self::BTree | Self::HashMap => None, - Self::GenericStatic | Self::GenericStaticFused | Self::GenericStaticFast => { - Some(MillionStorageBackend::GenericStatic) - } - Self::WatActorStatic => Some(MillionStorageBackend::WatActorStatic), - Self::MixedActorStatic | Self::MixedActorFast => { - Some(MillionStorageBackend::MixedActorStatic) - } - Self::TagActorStatic => Some(MillionStorageBackend::TagActorStatic), - Self::TagU64ActorStatic => Some(MillionStorageBackend::TagU64ActorStatic), - Self::ControlActorStatic => Some(MillionStorageBackend::ControlActorStatic), - Self::PageLocalActorStatic => Some(MillionStorageBackend::PageLocalActorStatic), - Self::GroupedActorPages64 => Some(MillionStorageBackend::GroupedActorPages64), - Self::GroupedActorPages128 | Self::InlineOwnerAccountU256 => { - Some(MillionStorageBackend::GroupedActorPages128) - } - } - } -} - -fn backend_index(backend: MillionStorageBackend) -> usize { - match backend { - MillionStorageBackend::GenericStatic => 0, - MillionStorageBackend::WatActorStatic => 1, - MillionStorageBackend::MixedActorStatic => 2, - MillionStorageBackend::TagActorStatic => 3, - MillionStorageBackend::TagU64ActorStatic => 4, - MillionStorageBackend::ControlActorStatic => 5, - MillionStorageBackend::PageLocalActorStatic => 6, - MillionStorageBackend::GroupedActorPages2 => 7, - MillionStorageBackend::GroupedActorPages4 => 8, - MillionStorageBackend::GroupedActorPages8 => 9, - MillionStorageBackend::GroupedActorPages16 => 10, - MillionStorageBackend::GroupedActorPages32 => 11, - MillionStorageBackend::GroupedActorPages64 => 12, - MillionStorageBackend::GroupedActorPages128 => 13, - } -} - -fn backend_capacity(backend: MillionStorageBackend) -> u32 { - match backend { - MillionStorageBackend::GenericStatic => GENERIC_BALANCE_CAPACITY, - MillionStorageBackend::WatActorStatic => WAT_ACTOR_BALANCE_CAPACITY, - MillionStorageBackend::MixedActorStatic => MIXED_ACTOR_BALANCE_CAPACITY, - MillionStorageBackend::TagActorStatic => TAG_ACTOR_BALANCE_CAPACITY, - MillionStorageBackend::TagU64ActorStatic => TAG_U64_ACTOR_BALANCE_CAPACITY, - MillionStorageBackend::ControlActorStatic => CONTROL_ACTOR_BALANCE_CAPACITY, - MillionStorageBackend::PageLocalActorStatic => PAGE_LOCAL_ACTOR_BALANCE_CAPACITY, - MillionStorageBackend::GroupedActorPages2 => { - static_storage::GROUPED_ACTOR_BALANCES_PAGES2_SLOTS as u32 - } - MillionStorageBackend::GroupedActorPages4 => { - static_storage::GROUPED_ACTOR_BALANCES_PAGES4_SLOTS as u32 - } - MillionStorageBackend::GroupedActorPages8 => { - static_storage::GROUPED_ACTOR_BALANCES_PAGES8_SLOTS as u32 - } - MillionStorageBackend::GroupedActorPages16 => { - static_storage::GROUPED_ACTOR_BALANCES_PAGES16_SLOTS as u32 - } - MillionStorageBackend::GroupedActorPages32 => { - static_storage::GROUPED_ACTOR_BALANCES_PAGES32_SLOTS as u32 - } - MillionStorageBackend::GroupedActorPages64 => { - static_storage::GROUPED_ACTOR_BALANCES_PAGES64_SLOTS as u32 - } - MillionStorageBackend::GroupedActorPages128 => { - static_storage::GROUPED_ACTOR_BALANCES_PAGES128_SLOTS as u32 - } - } -} - -fn allowance_capacity(backend: MillionStorageBackend) -> u32 { - match backend { - MillionStorageBackend::GenericStatic => GENERIC_ALLOWANCE_CAPACITY, - _ => WAT_ALLOWANCE_CAPACITY, - } -} - -#[cfg(any())] -struct InlineOwnerAccountU256Table { - base: usize, - slots: usize, - mask: usize, -} - -#[cfg(any())] -impl InlineOwnerAccountU256Table { - const OCCUPIED: u8 = 1; - const OWNER_STATE_OFFSET: usize = 0; - const SPENDER0_STATE_OFFSET: usize = 1; - const SPENDER1_STATE_OFFSET: usize = 2; - const OWNER_OFFSET: usize = 8; - const BALANCE_OFFSET: usize = 40; - const SPENDER0_OFFSET: usize = 72; - const ALLOWANCE0_OFFSET: usize = 104; - const SPENDER1_OFFSET: usize = 136; - const ALLOWANCE1_OFFSET: usize = 168; - const SLOT_SIZE: usize = 200; - - #[allow(dead_code)] - unsafe fn new(base: usize, slots: usize) -> Self { - debug_assert!(slots.is_power_of_two()); - Self { - base, - slots, - mask: slots - 1, - } - } - - fn get_balance(&self, owner: ActorId) -> Option { - let slot = unsafe { self.lookup(owner)? }; - Some(unsafe { self.read_u256(slot, Self::BALANCE_OFFSET) }) - } - - unsafe fn insert_balance(&self, owner: ActorId, value: U256) -> Option { - let slot = unsafe { self.lookup_or_insert(owner) }; - let previous = unsafe { self.read_u256(slot, Self::BALANCE_OFFSET) }; - unsafe { - self.write_u256(slot, Self::BALANCE_OFFSET, value); - } - (!previous.is_zero()).then_some(previous) - } - - fn get_inline_allowance(&self, owner: ActorId, spender: ActorId) -> Option { - let slot = unsafe { self.lookup(owner)? }; - unsafe { self.inline_allowance_offset(slot, spender) } - .map(|offset| unsafe { self.read_u256(slot, offset) }) - } - - unsafe fn insert_inline_allowance( - &self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Option> { - let slot = unsafe { self.lookup_or_insert(owner) }; - unsafe { - if self.read_state(slot, Self::SPENDER0_STATE_OFFSET) == Self::OCCUPIED - && self.actor_matches(slot, Self::SPENDER0_OFFSET, spender) - { - let previous = self.read_u256(slot, Self::ALLOWANCE0_OFFSET); - self.write_u256(slot, Self::ALLOWANCE0_OFFSET, value); - return Some(Some(previous)); - } - if self.read_state(slot, Self::SPENDER1_STATE_OFFSET) == Self::OCCUPIED - && self.actor_matches(slot, Self::SPENDER1_OFFSET, spender) - { - let previous = self.read_u256(slot, Self::ALLOWANCE1_OFFSET); - self.write_u256(slot, Self::ALLOWANCE1_OFFSET, value); - return Some(Some(previous)); - } - if self.read_state(slot, Self::SPENDER0_STATE_OFFSET) != Self::OCCUPIED { - self.write_state(slot, Self::SPENDER0_STATE_OFFSET, Self::OCCUPIED); - self.write_actor(slot, Self::SPENDER0_OFFSET, spender); - self.write_u256(slot, Self::ALLOWANCE0_OFFSET, value); - return Some(None); - } - if self.read_state(slot, Self::SPENDER1_STATE_OFFSET) != Self::OCCUPIED { - self.write_state(slot, Self::SPENDER1_STATE_OFFSET, Self::OCCUPIED); - self.write_actor(slot, Self::SPENDER1_OFFSET, spender); - self.write_u256(slot, Self::ALLOWANCE1_OFFSET, value); - return Some(None); - } - } - None - } - - unsafe fn transfer(&self, from: ActorId, to: ActorId, amount: U256) -> bool { - let Some(from_slot) = (unsafe { self.lookup(from) }) else { - return false; - }; - let from_balance = unsafe { self.read_u256(from_slot, Self::BALANCE_OFFSET) }; - if from_balance < amount { - return false; - } - - let to_slot = unsafe { self.lookup_or_insert(to) }; - let to_balance = unsafe { self.read_u256(to_slot, Self::BALANCE_OFFSET) }; - let (to_balance, overflow) = to_balance.overflowing_add(amount); - if overflow { - return false; - } - unsafe { - self.write_u256(from_slot, Self::BALANCE_OFFSET, from_balance - amount); - self.write_u256(to_slot, Self::BALANCE_OFFSET, to_balance); - } - true - } - - unsafe fn transfer_from( - &self, - overflow_allowances: &WatAllowanceTable, - owner: ActorId, - spender: ActorId, - to: ActorId, - amount: U256, - ) -> bool { - let Some(owner_slot) = (unsafe { self.lookup(owner) }) else { - return false; - }; - - let inline_allowance_offset = unsafe { self.inline_allowance_offset(owner_slot, spender) }; - let allowance = if let Some(offset) = inline_allowance_offset { - unsafe { self.read_u256(owner_slot, offset) } - } else { - overflow_allowances - .get_allowance_u256(&owner, &spender) - .expect("inline owner overflow allowance get failed") - .unwrap_or_else(U256::zero) - }; - - if allowance < amount { - return false; - } - let owner_balance = unsafe { self.read_u256(owner_slot, Self::BALANCE_OFFSET) }; - if owner_balance < amount { - return false; - } - - let to_slot = unsafe { self.lookup_or_insert(to) }; - let to_balance = unsafe { self.read_u256(to_slot, Self::BALANCE_OFFSET) }; - let (to_balance, overflow) = to_balance.overflowing_add(amount); - if overflow { - return false; - } - unsafe { - if let Some(offset) = inline_allowance_offset { - self.write_u256(owner_slot, offset, allowance - amount); - } else { - overflow_allowances - .insert_allowance_u256(owner, spender, allowance - amount) - .expect("inline owner overflow allowance update failed"); - } - self.write_u256(owner_slot, Self::BALANCE_OFFSET, owner_balance - amount); - self.write_u256(to_slot, Self::BALANCE_OFFSET, to_balance); - } - true - } - - unsafe fn inline_allowance_offset(&self, slot: usize, spender: ActorId) -> Option { - if unsafe { - self.read_state(slot, Self::SPENDER0_STATE_OFFSET) == Self::OCCUPIED - && self.actor_matches(slot, Self::SPENDER0_OFFSET, spender) - } { - return Some(Self::ALLOWANCE0_OFFSET); - } - if unsafe { - self.read_state(slot, Self::SPENDER1_STATE_OFFSET) == Self::OCCUPIED - && self.actor_matches(slot, Self::SPENDER1_OFFSET, spender) - } { - return Some(Self::ALLOWANCE1_OFFSET); - } - None - } - - unsafe fn lookup(&self, owner: ActorId) -> Option { - let mut index = account_index(owner, self.mask); - for _ in 0..self.slots { - let occupied = - unsafe { self.read_state(index, Self::OWNER_STATE_OFFSET) } == Self::OCCUPIED; - if !occupied { - return None; - } - if unsafe { self.actor_matches(index, Self::OWNER_OFFSET, owner) } { - return Some(index); - } - index = (index + 1) & self.mask; - } - None - } - - unsafe fn lookup_or_insert(&self, owner: ActorId) -> usize { - let mut index = account_index(owner, self.mask); - for _ in 0..self.slots { - let occupied = - unsafe { self.read_state(index, Self::OWNER_STATE_OFFSET) } == Self::OCCUPIED; - if occupied && unsafe { self.actor_matches(index, Self::OWNER_OFFSET, owner) } { - return index; - } - if !occupied { - unsafe { - self.write_state(index, Self::OWNER_STATE_OFFSET, Self::OCCUPIED); - self.write_actor(index, Self::OWNER_OFFSET, owner); - } - return index; - } - index = (index + 1) & self.mask; - } - panic!("inline owner account table is full") - } - - unsafe fn actor_matches(&self, slot: usize, offset: usize, actor: ActorId) -> bool { - unsafe { - core::slice::from_raw_parts(self.slot_ptr(slot).add(offset), 32) == actor.as_ref() - } - } - - unsafe fn read_state(&self, slot: usize, offset: usize) -> u8 { - unsafe { ptr::read(self.slot_ptr(slot).add(offset)) } - } - - unsafe fn read_u256(&self, slot: usize, offset: usize) -> U256 { - let mut bytes = [0u8; 32]; - unsafe { - ptr::copy_nonoverlapping(self.slot_ptr(slot).add(offset), bytes.as_mut_ptr(), 32); - } - U256::from_little_endian(&bytes) - } - - unsafe fn write_state(&self, slot: usize, offset: usize, state: u8) { - unsafe { - ptr::write(self.slot_ptr(slot).add(offset), state); - } - } - - unsafe fn write_actor(&self, slot: usize, offset: usize, actor: ActorId) { - unsafe { - ptr::copy_nonoverlapping(actor.as_ref().as_ptr(), self.slot_ptr(slot).add(offset), 32); - } - } - - unsafe fn write_u256(&self, slot: usize, offset: usize, value: U256) { - let mut bytes = [0u8; 32]; - value.to_little_endian(&mut bytes); - unsafe { - ptr::copy_nonoverlapping(bytes.as_ptr(), self.slot_ptr(slot).add(offset), 32); - } - } - - fn slot_ptr(&self, slot: usize) -> *mut u8 { - (self.base + slot * Self::SLOT_SIZE) as *mut u8 - } -} - -#[cfg(target_arch = "wasm32")] -fn generic_balance_table() -> SailsStaticBalanceTable { - unsafe { - SailsStaticBalanceTable::new( - static_storage::MILLION_BALANCES_BASE, - static_storage::MILLION_BALANCES_SLOTS, - ) - .expect("million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn generic_balance_table() -> SailsStaticBalanceTable { - unimplemented!("million static balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn generic_allowance_table() -> SailsStaticAllowanceTable { - unsafe { - SailsStaticAllowanceTable::new( - static_storage::MILLION_ALLOWANCES_BASE, - static_storage::MILLION_ALLOWANCES_SLOTS, - ) - .expect("million allowance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn generic_allowance_table() -> SailsStaticAllowanceTable { - unimplemented!("million static allowance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn wat_actor_balance_table() -> WatActorBalanceTable { - unsafe { - WatActorBalanceTable::new(static_storage::WAT_ACTOR_BALANCES_BASE) - .expect("wat actor million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn wat_actor_balance_table() -> WatActorBalanceTable { - unimplemented!("wat actor million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn mixed_actor_balance_table() -> MixedActorBalanceTable { - unsafe { - MixedActorBalanceTable::new(static_storage::MIXED_ACTOR_BALANCES_BASE) - .expect("mixed actor million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn mixed_actor_balance_table() -> MixedActorBalanceTable { - unimplemented!("mixed actor million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn tag_actor_balance_table() -> TagActorBalanceTable { - unsafe { - TagActorBalanceTable::new( - static_storage::GROUPED_ACTOR_BALANCES_PAGES128_BASE, - static_storage::MILLION_BALANCES_SLOTS, - ) - .expect("tag actor million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn tag_actor_balance_table() -> TagActorBalanceTable { - unimplemented!("tag actor million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn tag_u64_balance_table() -> TagU64ActorTable { - unsafe { - TagU64ActorTable::new( - static_storage::GROUPED_ACTOR_BALANCES_PAGES128_BASE, - static_storage::MILLION_BALANCES_SLOTS * 4, - ) - .expect("tag u64 actor million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn tag_u64_balance_table() -> TagU64ActorTable { - unimplemented!("tag u64 actor million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn tag_allowance_table() -> TagActorBalanceTable { - unsafe { - TagActorBalanceTable::new( - static_storage::CONTROL_ACTOR_BALANCES_SLOTS_BASE, - static_storage::MILLION_ALLOWANCES_SLOTS, - ) - .expect("tag allowance million table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn tag_allowance_table() -> TagActorBalanceTable { - unimplemented!("tag allowance million table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn tag_u64_allowance_table() -> TagU64ActorTable { - unsafe { - TagU64ActorTable::new( - static_storage::CONTROL_ACTOR_BALANCES_SLOTS_BASE, - static_storage::MILLION_ALLOWANCES_SLOTS * 4, - ) - .expect("tag u64 allowance million table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn tag_u64_allowance_table() -> TagU64ActorTable { - unimplemented!("tag u64 allowance million table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn inline_owner_account_table() -> InlineOwnerAccountTable { - unsafe { - InlineOwnerAccountTable::new( - static_storage::GROUPED_ACTOR_BALANCES_PAGES16_BASE, - static_storage::WAT_ALLOWANCES_BASE, - ) - .expect("inline owner account storage layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn inline_owner_account_table() -> InlineOwnerAccountTable { - unimplemented!("inline owner account million table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn wat_allowance_table() -> WatAllowanceTable { - unsafe { - WatAllowanceTable::new(static_storage::WAT_ALLOWANCES_BASE) - .expect("wat million allowance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn wat_allowance_table() -> WatAllowanceTable { - unimplemented!("wat million allowance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn control_actor_balance_table() -> ControlActorBalanceTable { - unsafe { - ControlActorBalanceTable::new( - static_storage::CONTROL_ACTOR_BALANCES_CONTROL_BASE, - static_storage::CONTROL_ACTOR_BALANCES_SLOTS_BASE, - ) - .expect("control actor million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn control_actor_balance_table() -> ControlActorBalanceTable { - unimplemented!("control actor million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn page_local_actor_balance_table() -> PageLocalActorBalanceTable { - unsafe { - PageLocalActorBalanceTable::new(static_storage::PAGE_LOCAL_ACTOR_BALANCES_BASE) - .expect("page-local actor million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn page_local_actor_balance_table() -> PageLocalActorBalanceTable { - unimplemented!("page-local actor million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn grouped_actor_pages2_table() -> GroupedActorPages2Table { - unsafe { - GroupedActorPages2Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES2_BASE) - .expect("grouped actor pages2 million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn grouped_actor_pages2_table() -> GroupedActorPages2Table { - unimplemented!("grouped actor pages2 million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn grouped_actor_pages4_table() -> GroupedActorPages4Table { - unsafe { - GroupedActorPages4Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES4_BASE) - .expect("grouped actor pages4 million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn grouped_actor_pages4_table() -> GroupedActorPages4Table { - unimplemented!("grouped actor pages4 million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn grouped_actor_pages8_table() -> GroupedActorPages8Table { - unsafe { - GroupedActorPages8Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES8_BASE) - .expect("grouped actor pages8 million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn grouped_actor_pages8_table() -> GroupedActorPages8Table { - unimplemented!("grouped actor pages8 million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn grouped_actor_pages16_table() -> GroupedActorPages16Table { - unsafe { - GroupedActorPages16Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES16_BASE) - .expect("grouped actor pages16 million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn grouped_actor_pages16_table() -> GroupedActorPages16Table { - unimplemented!("grouped actor pages16 million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn grouped_actor_pages32_table() -> GroupedActorPages32Table { - unsafe { - GroupedActorPages32Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES32_BASE) - .expect("grouped actor pages32 million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn grouped_actor_pages32_table() -> GroupedActorPages32Table { - unimplemented!("grouped actor pages32 million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn grouped_actor_pages64_table() -> GroupedActorPages64Table { - unsafe { - GroupedActorPages64Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES64_BASE) - .expect("grouped actor pages64 million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn grouped_actor_pages64_table() -> GroupedActorPages64Table { - unimplemented!("grouped actor pages64 million balance table is available only on wasm32") -} - -#[cfg(target_arch = "wasm32")] -fn grouped_actor_pages128_table() -> GroupedActorPages128Table { - unsafe { - GroupedActorPages128Table::new(static_storage::GROUPED_ACTOR_BALANCES_PAGES128_BASE) - .expect("grouped actor pages128 million balance table layout is valid") - } -} - -#[cfg(not(target_arch = "wasm32"))] -fn grouped_actor_pages128_table() -> GroupedActorPages128Table { - unimplemented!("grouped actor pages128 million balance table is available only on wasm32") -} - -fn actor_for_seed(seed: u32) -> ActorId { - ActorId::from(seed as u64 + 1) -} - -fn vft_actor_for_seed(seed: u32) -> ActorId { - actor_from_u64(vft_random_u64(seed, 0xA11C_E001_D15C_A11C)) -} - -fn vft_actor_tag_for_seed(seed: u32) -> u64 { - vft_random_u64(seed, 0xA11C_E001_D15C_A11C).max(1) -} - -fn vft_spender_for_seed(seed: u32) -> ActorId { - actor_from_u64(vft_random_u64(seed, 0x5EED_5EED_A110_CAFE)) -} - -fn vft_spender_tag_for_seed(seed: u32) -> u64 { - vft_random_u64(seed, 0x5EED_5EED_A110_CAFE).max(1) -} - -#[cfg(any())] -fn account_index(actor: ActorId, mask: usize) -> usize { - let key = actor.as_ref(); - let hot_lane = unsafe { ptr::read_unaligned(key.as_ptr().add(12).cast::()) }; - let hash = if hot_lane != 0 { - hot_lane - } else { - unsafe { - ptr::read_unaligned(key.as_ptr().cast::()) - ^ ptr::read_unaligned(key.as_ptr().add(8).cast::()) - ^ ptr::read_unaligned(key.as_ptr().add(16).cast::()) - ^ ptr::read_unaligned(key.as_ptr().add(24).cast::()) - } - }; - hash as usize & mask -} - -fn vft_allowance_tag_for_seeds(owner_seed: u32, spender_seed: u32) -> u64 { - vft_actor_tag_for_seed(owner_seed) - .wrapping_mul(0x9E37_79B9_7F4A_7C15) - .wrapping_add(vft_spender_tag_for_seed(spender_seed).wrapping_mul(0xBF58_476D_1CE4_E5B9)) - .max(1) -} - -fn actor_from_u64(id: u64) -> ActorId { - ActorId::from(id.max(1)) -} - -fn vft_random_u64(seed: u32, domain: u64) -> u64 { - let state = u64::from(seed) - .wrapping_add(domain) - .wrapping_mul(6364136223846793005) - .wrapping_add(1442695040888963407); - state ^ (state >> 32) -} - -fn value_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 1) -} - -fn updated_value_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 1_000_001) -} - -fn vft_balance_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 1_000_000) -} - -fn vft_allowance_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 10_000) -} - -fn vft_allowance_for_seed_u64(seed: u32) -> u64 { - seed as u64 + 10_000 -} - -fn vft_transfer_amount(seed: u32) -> U256 { - U256::from((seed % 7) as u64 + 1) -} - -fn vft_transfer_amount_u64(seed: u32) -> u64 { - (seed % 7) as u64 + 1 -} diff --git a/benchmarks/storage-stress/Cargo.toml b/benchmarks/storage-stress/Cargo.toml deleted file mode 100644 index 64dc9678a..000000000 --- a/benchmarks/storage-stress/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "storage-stress" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[dependencies] -sails-rs.workspace = true -sails-storage.workspace = true - -[build-dependencies] -sails-rs = { workspace = true, features = ["wasm-builder"] } diff --git a/benchmarks/storage-stress/build.rs b/benchmarks/storage-stress/build.rs deleted file mode 100644 index e7b8e4c07..000000000 --- a/benchmarks/storage-stress/build.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - const FIXED_CAPACITY: usize = 2048; - - let layout = sails_rs::build::StaticMemoryLayout::new(1024) - .reserve_table::<32, 32>("sails_static_balances", FIXED_CAPACITY) - .reserve_table::<64, 32>("sails_static_allowances", FIXED_CAPACITY); - - sails_rs::build::build_wasm_with_static_memory(layout); -} diff --git a/benchmarks/storage-stress/src/lib.rs b/benchmarks/storage-stress/src/lib.rs deleted file mode 100644 index 193fb91ba..000000000 --- a/benchmarks/storage-stress/src/lib.rs +++ /dev/null @@ -1,1227 +0,0 @@ -#![no_std] - -use sails_rs::{cell::RefCell, collections::HashMap, prelude::*}; -use sails_storage::__experimental::{ - FixedAllowanceMap as SailsFixedAllowanceMap, FixedBalanceMap as SailsFixedBalanceMap, - StaticAllowanceTable as SailsStaticAllowanceTable, - StaticBalanceTable as SailsStaticBalanceTable, -}; - -mod static_storage { - include!(concat!(env!("OUT_DIR"), "/sails_static_storage.rs")); -} - -const FIXED_CAPACITY: usize = 2048; - -pub const STATIC_MEMORY_END_PAGE: u32 = static_storage::STATIC_MEMORY_END_PAGE; - -#[sails_type] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum StorageBackend { - HashMap, - Fixed, - RawStatic, - SailsFixed, - SailsStatic, -} - -#[sails_type] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum StorageMap { - Balance, - Allowance, -} - -#[sails_type] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum StorageOp { - InsertFresh, - UpdateExisting, - ReadExisting, - ReadMissing, - Remove, -} - -#[sails_type] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct StorageBenchResult { - pub value: U256, - pub len: u32, - pub existed: bool, -} - -struct StorageStressService<'a> { - state: &'a RefCell, -} - -impl<'a> StorageStressService<'a> { - fn new(state: &'a RefCell) -> Self { - Self { state } - } -} - -#[sails_rs::service] -impl StorageStressService<'_> { - #[export] - pub fn prepare( - &mut self, - backend: StorageBackend, - map: StorageMap, - len: u32, - ) -> StorageBenchResult { - self.state.borrow_mut().prepare(backend, map, len) - } - - #[export] - pub fn bench( - &mut self, - backend: StorageBackend, - map: StorageMap, - op: StorageOp, - seed: u32, - ) -> StorageBenchResult { - self.state.borrow_mut().bench(backend, map, op, seed) - } -} - -pub struct StorageStressProgram { - state: RefCell, -} - -#[sails_rs::program] -impl StorageStressProgram { - pub fn new_for_bench() -> Self { - Self { - state: RefCell::new(StorageStressState::new()), - } - } - - pub fn storage_stress(&self) -> StorageStressService<'_> { - StorageStressService::new(&self.state) - } -} - -struct StorageStressState { - storage: ActiveStorage, -} - -enum ActiveStorage { - Empty, - HashBalances(HashMap), - HashAllowances(HashMap<(ActorId, ActorId), U256>), - FixedBalances(FixedBalanceMap), - FixedAllowances(FixedAllowanceMap), - RawBalances(RawStaticBalanceMap), - RawAllowances(RawStaticAllowanceMap), - SailsFixedBalances(SailsFixedBalanceMap), - SailsFixedAllowances(SailsFixedAllowanceMap), - SailsStaticBalances(SailsStaticBalanceMap), - SailsStaticAllowances(SailsStaticAllowanceMap), -} - -impl StorageStressState { - fn new() -> Self { - Self { - storage: ActiveStorage::Empty, - } - } - - fn prepare( - &mut self, - backend: StorageBackend, - map: StorageMap, - len: u32, - ) -> StorageBenchResult { - assert!((len as usize) < FIXED_CAPACITY); - self.clear(backend, map); - - for seed in 1..=len { - self.insert(backend, map, seed, value_for_seed(seed)); - } - - StorageBenchResult { - value: value_for_seed(len), - len, - existed: false, - } - } - - fn bench( - &mut self, - backend: StorageBackend, - map: StorageMap, - op: StorageOp, - seed: u32, - ) -> StorageBenchResult { - match op { - StorageOp::InsertFresh => { - let value = value_for_seed(seed); - let existed = self.insert(backend, map, seed, value).is_some(); - StorageBenchResult { - value, - len: self.len(backend, map), - existed, - } - } - StorageOp::UpdateExisting => { - let value = updated_value_for_seed(seed); - let existed = self.insert(backend, map, seed, value).is_some(); - StorageBenchResult { - value, - len: self.len(backend, map), - existed, - } - } - StorageOp::ReadExisting | StorageOp::ReadMissing => { - let value = self.get(backend, map, seed).unwrap_or_else(U256::zero); - StorageBenchResult { - value, - len: self.len(backend, map), - existed: !value.is_zero(), - } - } - StorageOp::Remove => { - let value = self.remove(backend, map, seed).unwrap_or_else(U256::zero); - StorageBenchResult { - value, - len: self.len(backend, map), - existed: !value.is_zero(), - } - } - } - } - - fn clear(&mut self, backend: StorageBackend, map: StorageMap) { - self.storage = match (backend, map) { - (StorageBackend::HashMap, StorageMap::Balance) => { - ActiveStorage::HashBalances(HashMap::new()) - } - (StorageBackend::HashMap, StorageMap::Allowance) => { - ActiveStorage::HashAllowances(HashMap::new()) - } - (StorageBackend::Fixed, StorageMap::Balance) => { - ActiveStorage::FixedBalances(FixedBalanceMap::new()) - } - (StorageBackend::Fixed, StorageMap::Allowance) => { - ActiveStorage::FixedAllowances(FixedAllowanceMap::new()) - } - (StorageBackend::RawStatic, StorageMap::Balance) => { - ActiveStorage::RawBalances(RawStaticBalanceMap::new()) - } - (StorageBackend::RawStatic, StorageMap::Allowance) => { - ActiveStorage::RawAllowances(RawStaticAllowanceMap::new()) - } - (StorageBackend::SailsFixed, StorageMap::Balance) => { - ActiveStorage::SailsFixedBalances(SailsFixedBalanceMap::new()) - } - (StorageBackend::SailsFixed, StorageMap::Allowance) => { - ActiveStorage::SailsFixedAllowances(SailsFixedAllowanceMap::new()) - } - (StorageBackend::SailsStatic, StorageMap::Balance) => { - ActiveStorage::SailsStaticBalances(SailsStaticBalanceMap::new()) - } - (StorageBackend::SailsStatic, StorageMap::Allowance) => { - ActiveStorage::SailsStaticAllowances(SailsStaticAllowanceMap::new()) - } - }; - } - - fn len(&self, backend: StorageBackend, map: StorageMap) -> u32 { - match (backend, map, &self.storage) { - (StorageBackend::HashMap, StorageMap::Balance, ActiveStorage::HashBalances(map)) => { - map.len() as u32 - } - ( - StorageBackend::HashMap, - StorageMap::Allowance, - ActiveStorage::HashAllowances(map), - ) => map.len() as u32, - (StorageBackend::Fixed, StorageMap::Balance, ActiveStorage::FixedBalances(map)) => { - map.len() - } - (StorageBackend::Fixed, StorageMap::Allowance, ActiveStorage::FixedAllowances(map)) => { - map.len() - } - (StorageBackend::RawStatic, StorageMap::Balance, ActiveStorage::RawBalances(map)) => { - map.len() - } - ( - StorageBackend::RawStatic, - StorageMap::Allowance, - ActiveStorage::RawAllowances(map), - ) => map.len(), - ( - StorageBackend::SailsFixed, - StorageMap::Balance, - ActiveStorage::SailsFixedBalances(map), - ) => map.len() as u32, - ( - StorageBackend::SailsFixed, - StorageMap::Allowance, - ActiveStorage::SailsFixedAllowances(map), - ) => map.len() as u32, - ( - StorageBackend::SailsStatic, - StorageMap::Balance, - ActiveStorage::SailsStaticBalances(map), - ) => map.len(), - ( - StorageBackend::SailsStatic, - StorageMap::Allowance, - ActiveStorage::SailsStaticAllowances(map), - ) => map.len(), - _ => 0, - } - } - - fn insert( - &mut self, - backend: StorageBackend, - map: StorageMap, - seed: u32, - value: U256, - ) -> Option { - match (backend, map, &mut self.storage) { - (StorageBackend::HashMap, StorageMap::Balance, ActiveStorage::HashBalances(map)) => { - map.insert(actor_for_seed(seed), value) - } - ( - StorageBackend::HashMap, - StorageMap::Allowance, - ActiveStorage::HashAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.insert((owner, spender), value) - } - (StorageBackend::Fixed, StorageMap::Balance, ActiveStorage::FixedBalances(map)) => map - .try_insert(actor_for_seed(seed), value) - .expect("fixed balance map capacity exceeded"), - (StorageBackend::Fixed, StorageMap::Allowance, ActiveStorage::FixedAllowances(map)) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.try_insert(AllowanceKey { owner, spender }, value) - .expect("fixed allowance map capacity exceeded") - } - (StorageBackend::RawStatic, StorageMap::Balance, ActiveStorage::RawBalances(map)) => { - map.try_insert(actor_for_seed(seed), value) - .expect("raw static balance map capacity exceeded") - } - ( - StorageBackend::RawStatic, - StorageMap::Allowance, - ActiveStorage::RawAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.try_insert(AllowanceKey { owner, spender }, value) - .expect("raw static allowance map capacity exceeded") - } - ( - StorageBackend::SailsFixed, - StorageMap::Balance, - ActiveStorage::SailsFixedBalances(map), - ) => map - .insert_actor_u256(actor_for_seed(seed), value) - .expect("sails-storage fixed balance map failed"), - ( - StorageBackend::SailsFixed, - StorageMap::Allowance, - ActiveStorage::SailsFixedAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.insert_allowance_u256(owner, spender, value) - .expect("sails-storage fixed allowance map failed") - } - ( - StorageBackend::SailsStatic, - StorageMap::Balance, - ActiveStorage::SailsStaticBalances(map), - ) => map - .try_insert(actor_for_seed(seed), value) - .expect("sails-storage static balance map failed"), - ( - StorageBackend::SailsStatic, - StorageMap::Allowance, - ActiveStorage::SailsStaticAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.try_insert(owner, spender, value) - .expect("sails-storage static allowance map failed") - } - _ => panic!("storage backend is not prepared"), - } - } - - fn get(&self, backend: StorageBackend, map: StorageMap, seed: u32) -> Option { - match (backend, map, &self.storage) { - (StorageBackend::HashMap, StorageMap::Balance, ActiveStorage::HashBalances(map)) => { - map.get(&actor_for_seed(seed)).copied() - } - ( - StorageBackend::HashMap, - StorageMap::Allowance, - ActiveStorage::HashAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.get(&(owner, spender)).copied() - } - (StorageBackend::Fixed, StorageMap::Balance, ActiveStorage::FixedBalances(map)) => { - map.get(&actor_for_seed(seed)) - } - (StorageBackend::Fixed, StorageMap::Allowance, ActiveStorage::FixedAllowances(map)) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.get(&AllowanceKey { owner, spender }) - } - (StorageBackend::RawStatic, StorageMap::Balance, ActiveStorage::RawBalances(map)) => { - map.get(&actor_for_seed(seed)) - } - ( - StorageBackend::RawStatic, - StorageMap::Allowance, - ActiveStorage::RawAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.get(&AllowanceKey { owner, spender }) - } - ( - StorageBackend::SailsFixed, - StorageMap::Balance, - ActiveStorage::SailsFixedBalances(map), - ) => map - .get_actor_u256(&actor_for_seed(seed)) - .expect("sails-storage fixed balance map failed"), - ( - StorageBackend::SailsFixed, - StorageMap::Allowance, - ActiveStorage::SailsFixedAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.get_allowance_u256(&owner, &spender) - .expect("sails-storage fixed allowance map failed") - } - ( - StorageBackend::SailsStatic, - StorageMap::Balance, - ActiveStorage::SailsStaticBalances(map), - ) => map - .get(&actor_for_seed(seed)) - .expect("sails-storage static balance map failed"), - ( - StorageBackend::SailsStatic, - StorageMap::Allowance, - ActiveStorage::SailsStaticAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.get(&owner, &spender) - .expect("sails-storage static allowance map failed") - } - _ => None, - } - } - - fn remove(&mut self, backend: StorageBackend, map: StorageMap, seed: u32) -> Option { - match (backend, map, &mut self.storage) { - (StorageBackend::HashMap, StorageMap::Balance, ActiveStorage::HashBalances(map)) => { - map.remove(&actor_for_seed(seed)) - } - ( - StorageBackend::HashMap, - StorageMap::Allowance, - ActiveStorage::HashAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.remove(&(owner, spender)) - } - (StorageBackend::Fixed, StorageMap::Balance, ActiveStorage::FixedBalances(map)) => { - map.remove(&actor_for_seed(seed)) - } - (StorageBackend::Fixed, StorageMap::Allowance, ActiveStorage::FixedAllowances(map)) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.remove(&AllowanceKey { owner, spender }) - } - (StorageBackend::RawStatic, StorageMap::Balance, ActiveStorage::RawBalances(map)) => { - map.remove(&actor_for_seed(seed)) - } - ( - StorageBackend::RawStatic, - StorageMap::Allowance, - ActiveStorage::RawAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.remove(&AllowanceKey { owner, spender }) - } - ( - StorageBackend::SailsFixed, - StorageMap::Balance, - ActiveStorage::SailsFixedBalances(map), - ) => map - .remove_actor_u256(&actor_for_seed(seed)) - .expect("sails-storage fixed balance map failed"), - ( - StorageBackend::SailsFixed, - StorageMap::Allowance, - ActiveStorage::SailsFixedAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.remove_allowance_u256(&owner, &spender) - .expect("sails-storage fixed allowance map failed") - } - ( - StorageBackend::SailsStatic, - StorageMap::Balance, - ActiveStorage::SailsStaticBalances(map), - ) => map - .remove(&actor_for_seed(seed)) - .expect("sails-storage static balance map failed"), - ( - StorageBackend::SailsStatic, - StorageMap::Allowance, - ActiveStorage::SailsStaticAllowances(map), - ) => { - let (owner, spender) = allowance_key_for_seed(seed); - map.remove(&owner, &spender) - .expect("sails-storage static allowance map failed") - } - _ => None, - } - } -} - -#[derive(Clone, Copy)] -struct BalanceSlot { - key: Option, - value: Option, - tombstone: bool, -} - -impl BalanceSlot { - const EMPTY: Self = Self { - key: None, - value: None, - tombstone: false, - }; -} - -struct FixedBalanceMap { - slots: [BalanceSlot; CAP], - len: u32, -} - -impl FixedBalanceMap { - const fn new() -> Self { - Self { - slots: [BalanceSlot::EMPTY; CAP], - len: 0, - } - } - - fn len(&self) -> u32 { - self.len - } - - fn get(&self, key: &ActorId) -> Option { - self.find_slot(*key) - .ok() - .and_then(|idx| self.slots[idx].value) - } - - fn try_insert(&mut self, key: ActorId, value: U256) -> Result, FixedMapFull> { - match self.find_slot(key) { - Ok(idx) => Ok(self.slots[idx].value.replace(value)), - Err(idx) if idx < CAP => { - self.slots[idx] = BalanceSlot { - key: Some(key), - value: Some(value), - tombstone: false, - }; - self.len += 1; - Ok(None) - } - Err(_) => Err(FixedMapFull), - } - } - - fn remove(&mut self, key: &ActorId) -> Option { - let idx = self.find_slot(*key).ok()?; - let previous = self.slots[idx].value.take(); - self.slots[idx].key = None; - self.slots[idx].tombstone = true; - self.len -= 1; - previous - } - - fn find_slot(&self, key: ActorId) -> Result { - find_slot(CAP, hash_actor(&key), |idx| { - let slot = &self.slots[idx]; - match slot.key { - Some(existing) if existing == key => Probe::Found, - Some(_) => Probe::Occupied, - None if slot.tombstone => Probe::Tombstone, - None => Probe::Empty, - } - }) - } -} - -#[derive(Clone, Copy, PartialEq, Eq)] -struct AllowanceKey { - owner: ActorId, - spender: ActorId, -} - -#[derive(Clone, Copy)] -struct AllowanceSlot { - key: Option, - value: Option, - tombstone: bool, -} - -impl AllowanceSlot { - const EMPTY: Self = Self { - key: None, - value: None, - tombstone: false, - }; -} - -struct FixedAllowanceMap { - slots: [AllowanceSlot; CAP], - len: u32, -} - -impl FixedAllowanceMap { - const fn new() -> Self { - Self { - slots: [AllowanceSlot::EMPTY; CAP], - len: 0, - } - } - - fn len(&self) -> u32 { - self.len - } - - fn get(&self, key: &AllowanceKey) -> Option { - self.find_slot(*key) - .ok() - .and_then(|idx| self.slots[idx].value) - } - - fn try_insert(&mut self, key: AllowanceKey, value: U256) -> Result, FixedMapFull> { - match self.find_slot(key) { - Ok(idx) => Ok(self.slots[idx].value.replace(value)), - Err(idx) if idx < CAP => { - self.slots[idx] = AllowanceSlot { - key: Some(key), - value: Some(value), - tombstone: false, - }; - self.len += 1; - Ok(None) - } - Err(_) => Err(FixedMapFull), - } - } - - fn remove(&mut self, key: &AllowanceKey) -> Option { - let idx = self.find_slot(*key).ok()?; - let previous = self.slots[idx].value.take(); - self.slots[idx].key = None; - self.slots[idx].tombstone = true; - self.len -= 1; - previous - } - - fn find_slot(&self, key: AllowanceKey) -> Result { - find_slot(CAP, hash_allowance(&key), |idx| { - let slot = &self.slots[idx]; - match slot.key { - Some(existing) if existing == key => Probe::Found, - Some(_) => Probe::Occupied, - None if slot.tombstone => Probe::Tombstone, - None => Probe::Empty, - } - }) - } -} - -type RawStaticBalanceMap = RawStaticMap<32, 32, CAP>; -type RawStaticAllowanceMap = RawStaticMap<64, 32, CAP>; - -#[derive(Clone, Copy)] -struct RawStaticSlot { - key: [u8; KEY_SIZE], - value: [u8; VALUE_SIZE], -} - -impl RawStaticSlot { - const EMPTY: Self = Self { - key: [0; KEY_SIZE], - value: [0; VALUE_SIZE], - }; -} - -struct RawStaticMap { - slots: [RawStaticSlot; CAP], - len: u32, -} - -impl - RawStaticMap -{ - const fn new() -> Self { - Self { - slots: [RawStaticSlot::EMPTY; CAP], - len: 0, - } - } - - fn len(&self) -> u32 { - self.len - } - - fn get_raw_with_hash(&self, key: &[u8; KEY_SIZE], hash: usize) -> Option<[u8; VALUE_SIZE]> { - let idx = self.find_slot_with_hash(key, hash).ok()?; - let value = self.slots[idx].value; - (!is_zero(&value)).then_some(value) - } - - fn try_insert_raw( - &mut self, - key: [u8; KEY_SIZE], - value: [u8; VALUE_SIZE], - hash: usize, - ) -> Result, FixedMapFull> { - debug_assert!(!is_zero(&key)); - - if is_zero(&value) { - return Ok(self.remove_with_hash(&key, hash)); - } - - match self.find_slot_with_hash(&key, hash) { - Ok(idx) => { - let previous = self.slots[idx].value; - self.slots[idx].value = value; - if is_zero(&previous) { - self.len += 1; - Ok(None) - } else { - Ok(Some(previous)) - } - } - Err(idx) if idx < CAP => { - self.slots[idx] = RawStaticSlot { key, value }; - self.len += 1; - Ok(None) - } - Err(_) => Err(FixedMapFull), - } - } - - fn remove_with_hash(&mut self, key: &[u8; KEY_SIZE], hash: usize) -> Option<[u8; VALUE_SIZE]> { - let idx = self.find_slot_with_hash(key, hash).ok()?; - let previous = self.slots[idx].value; - if is_zero(&previous) { - None - } else { - self.slots[idx].value = [0; VALUE_SIZE]; - self.len -= 1; - Some(previous) - } - } - - fn find_slot_with_hash(&self, key: &[u8; KEY_SIZE], hash: usize) -> Result { - find_slot(CAP, hash, |idx| { - let slot = &self.slots[idx]; - if is_zero(&slot.key) { - Probe::Empty - } else if slot.key == *key { - Probe::Found - } else if is_zero(&slot.value) { - Probe::Tombstone - } else { - Probe::Occupied - } - }) - } -} - -impl RawStaticBalanceMap { - fn get(&self, key: &ActorId) -> Option { - let raw_key = raw_actor_key(*key); - self.get_raw_with_hash(&raw_key, hash_actor(key)) - .map(u256_from_raw) - } - - fn try_insert(&mut self, key: ActorId, value: U256) -> Result, FixedMapFull> { - let raw_key = raw_actor_key(key); - let raw_value = raw_u256(value); - self.try_insert_raw(raw_key, raw_value, hash_actor(&key)) - .map(|previous| previous.map(u256_from_raw)) - } - - fn remove(&mut self, key: &ActorId) -> Option { - let raw_key = raw_actor_key(*key); - self.remove_with_hash(&raw_key, hash_actor(key)) - .map(u256_from_raw) - } -} - -impl RawStaticAllowanceMap { - fn get(&self, key: &AllowanceKey) -> Option { - let raw_key = raw_allowance_key(*key); - self.get_raw_with_hash(&raw_key, hash_allowance(key)) - .map(u256_from_raw) - } - - fn try_insert(&mut self, key: AllowanceKey, value: U256) -> Result, FixedMapFull> { - let raw_key = raw_allowance_key(key); - let raw_value = raw_u256(value); - self.try_insert_raw(raw_key, raw_value, hash_allowance(&key)) - .map(|previous| previous.map(u256_from_raw)) - } - - fn remove(&mut self, key: &AllowanceKey) -> Option { - let raw_key = raw_allowance_key(*key); - self.remove_with_hash(&raw_key, hash_allowance(key)) - .map(u256_from_raw) - } -} - -struct SailsStaticBalanceMap { - #[cfg(not(target_arch = "wasm32"))] - bytes: [u8; static_storage::SAILS_STATIC_BALANCES_BYTES], - len: u32, -} - -impl SailsStaticBalanceMap { - fn new() -> Self { - let map = Self { - #[cfg(not(target_arch = "wasm32"))] - bytes: [0; static_storage::SAILS_STATIC_BALANCES_BYTES], - len: 0, - }; - #[cfg(target_arch = "wasm32")] - map.table() - .clear() - .expect("sails static balance table can be cleared"); - map - } - - fn len(&self) -> u32 { - self.len - } - - fn get(&self, key: &ActorId) -> Result, sails_storage::TableError> { - self.table().get_actor_u256(key) - } - - fn try_insert( - &mut self, - key: ActorId, - value: U256, - ) -> Result, sails_storage::TableError> { - let previous = self.table_mut().insert_actor_u256(key, value)?; - if previous.is_none() { - self.len += 1; - } - - Ok(previous) - } - - fn remove(&mut self, key: &ActorId) -> Result, sails_storage::TableError> { - let previous = self.table_mut().remove_actor_u256(key)?; - if previous.is_some() { - self.len -= 1; - } - - Ok(previous) - } - - fn table(&self) -> SailsStaticBalanceTable { - #[cfg(target_arch = "wasm32")] - let base = static_storage::SAILS_STATIC_BALANCES_BASE; - #[cfg(not(target_arch = "wasm32"))] - let base = self.bytes.as_ptr() as usize; - - unsafe { - SailsStaticBalanceTable::new(base, static_storage::SAILS_STATIC_BALANCES_SLOTS) - .expect("sails static balance layout is valid") - } - } - - fn table_mut(&mut self) -> SailsStaticBalanceTable { - #[cfg(target_arch = "wasm32")] - let base = static_storage::SAILS_STATIC_BALANCES_BASE; - #[cfg(not(target_arch = "wasm32"))] - let base = self.bytes.as_mut_ptr() as usize; - - unsafe { - SailsStaticBalanceTable::new(base, static_storage::SAILS_STATIC_BALANCES_SLOTS) - .expect("sails static balance layout is valid") - } - } -} - -struct SailsStaticAllowanceMap { - #[cfg(not(target_arch = "wasm32"))] - bytes: [u8; static_storage::SAILS_STATIC_ALLOWANCES_BYTES], - len: u32, -} - -impl SailsStaticAllowanceMap { - fn new() -> Self { - let map = Self { - #[cfg(not(target_arch = "wasm32"))] - bytes: [0; static_storage::SAILS_STATIC_ALLOWANCES_BYTES], - len: 0, - }; - #[cfg(target_arch = "wasm32")] - map.table() - .clear() - .expect("sails static allowance table can be cleared"); - map - } - - fn len(&self) -> u32 { - self.len - } - - fn get( - &self, - owner: &ActorId, - spender: &ActorId, - ) -> Result, sails_storage::TableError> { - self.table().get_allowance_u256(owner, spender) - } - - fn try_insert( - &mut self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result, sails_storage::TableError> { - let previous = self - .table_mut() - .insert_allowance_u256(owner, spender, value)?; - if previous.is_none() { - self.len += 1; - } - - Ok(previous) - } - - fn remove( - &mut self, - owner: &ActorId, - spender: &ActorId, - ) -> Result, sails_storage::TableError> { - let previous = self.table_mut().remove_allowance_u256(owner, spender)?; - if previous.is_some() { - self.len -= 1; - } - - Ok(previous) - } - - fn table(&self) -> SailsStaticAllowanceTable { - #[cfg(target_arch = "wasm32")] - let base = static_storage::SAILS_STATIC_ALLOWANCES_BASE; - #[cfg(not(target_arch = "wasm32"))] - let base = self.bytes.as_ptr() as usize; - - unsafe { - SailsStaticAllowanceTable::new(base, static_storage::SAILS_STATIC_ALLOWANCES_SLOTS) - .expect("sails static allowance layout is valid") - } - } - - fn table_mut(&mut self) -> SailsStaticAllowanceTable { - #[cfg(target_arch = "wasm32")] - let base = static_storage::SAILS_STATIC_ALLOWANCES_BASE; - #[cfg(not(target_arch = "wasm32"))] - let base = self.bytes.as_mut_ptr() as usize; - - unsafe { - SailsStaticAllowanceTable::new(base, static_storage::SAILS_STATIC_ALLOWANCES_SLOTS) - .expect("sails static allowance layout is valid") - } - } -} - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -struct FixedMapFull; - -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -enum Probe { - Found, - Occupied, - Tombstone, - Empty, -} - -fn find_slot(cap: usize, hash: usize, probe: impl Fn(usize) -> Probe) -> Result { - debug_assert!(cap > 0); - - let mut first_tombstone = None; - let mut idx = hash % cap; - - for _ in 0..cap { - match probe(idx) { - Probe::Found => return Ok(idx), - Probe::Occupied => {} - Probe::Tombstone => { - if first_tombstone.is_none() { - first_tombstone = Some(idx); - } - } - Probe::Empty => return Err(first_tombstone.unwrap_or(idx)), - } - - idx = (idx + 1) % cap; - } - - Err(first_tombstone.unwrap_or(cap)) -} - -fn actor_for_seed(seed: u32) -> ActorId { - ActorId::from(seed as u64 + 1) -} - -fn allowance_key_for_seed(seed: u32) -> (ActorId, ActorId) { - ( - ActorId::from((seed as u64).wrapping_mul(2).wrapping_add(1)), - ActorId::from((seed as u64).wrapping_mul(2).wrapping_add(2)), - ) -} - -fn value_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 1) -} - -fn updated_value_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 1_000_001) -} - -fn hash_actor(actor: &ActorId) -> usize { - let bytes: &[u8] = actor.as_ref(); - hash_bytes(bytes).wrapping_mul(0x9e37_79b9) -} - -fn hash_bytes(bytes: &[u8]) -> usize { - let mut hash = 0x811c_9dc5u32; - - for byte in bytes { - hash ^= *byte as u32; - hash = hash.wrapping_mul(0x0100_0193); - } - - hash as usize -} - -fn hash_allowance(key: &AllowanceKey) -> usize { - hash_actor(&key.owner).rotate_left(13).wrapping_mul(31) ^ hash_actor(&key.spender) -} - -fn raw_actor_key(actor: ActorId) -> [u8; 32] { - let mut key = [0u8; 32]; - key.copy_from_slice(actor.as_ref()); - key -} - -fn raw_allowance_key(key: AllowanceKey) -> [u8; 64] { - let mut raw = [0u8; 64]; - raw[..32].copy_from_slice(key.owner.as_ref()); - raw[32..].copy_from_slice(key.spender.as_ref()); - raw -} - -fn raw_u256(value: U256) -> [u8; 32] { - let mut bytes = [0u8; 32]; - value.to_little_endian(&mut bytes); - bytes -} - -fn u256_from_raw(bytes: [u8; 32]) -> U256 { - U256::from_little_endian(&bytes) -} - -fn is_zero(bytes: &[u8]) -> bool { - bytes.iter().all(|byte| *byte == 0) -} - -#[cfg(test)] -mod tests { - extern crate std; - - use super::*; - use std::thread; - - #[test] - fn fixed_balance_map_updates_existing_key() { - let mut map = FixedBalanceMap::<8>::new(); - let key = actor_for_seed(1); - - assert_eq!(map.try_insert(key, value_for_seed(1)), Ok(None)); - assert_eq!( - map.try_insert(key, updated_value_for_seed(1)), - Ok(Some(value_for_seed(1))) - ); - assert_eq!(map.get(&key), Some(updated_value_for_seed(1))); - assert_eq!(map.len(), 1); - } - - #[test] - fn fixed_balance_map_reuses_tombstone() { - let mut map = FixedBalanceMap::<1>::new(); - let first = actor_for_seed(1); - let second = actor_for_seed(2); - - assert_eq!(map.try_insert(first, value_for_seed(1)), Ok(None)); - assert_eq!(map.remove(&first), Some(value_for_seed(1))); - assert_eq!(map.try_insert(second, value_for_seed(2)), Ok(None)); - assert_eq!(map.get(&second), Some(value_for_seed(2))); - assert_eq!(map.len(), 1); - } - - #[test] - fn fixed_balance_map_reports_full_table() { - let mut map = FixedBalanceMap::<1>::new(); - - assert_eq!( - map.try_insert(actor_for_seed(1), value_for_seed(1)), - Ok(None) - ); - assert_eq!( - map.try_insert(actor_for_seed(2), value_for_seed(2)), - Err(FixedMapFull) - ); - } - - #[test] - fn fixed_allowance_map_removes_pair_key() { - let mut map = FixedAllowanceMap::<8>::new(); - let (owner, spender) = allowance_key_for_seed(1); - let key = AllowanceKey { owner, spender }; - - assert_eq!(map.try_insert(key, value_for_seed(1)), Ok(None)); - assert_eq!(map.remove(&key), Some(value_for_seed(1))); - assert_eq!(map.get(&key), None); - assert_eq!(map.len(), 0); - } - - #[test] - fn raw_static_balance_map_updates_existing_key() { - let mut map = RawStaticBalanceMap::<8>::new(); - let key = actor_for_seed(1); - - assert_eq!(map.try_insert(key, value_for_seed(1)), Ok(None)); - assert_eq!( - map.try_insert(key, updated_value_for_seed(1)), - Ok(Some(value_for_seed(1))) - ); - assert_eq!(map.get(&key), Some(updated_value_for_seed(1))); - assert_eq!(map.len(), 1); - } - - #[test] - fn raw_static_balance_map_reuses_tombstone() { - let mut map = RawStaticBalanceMap::<1>::new(); - let first = actor_for_seed(1); - let second = actor_for_seed(2); - - assert_eq!(map.try_insert(first, value_for_seed(1)), Ok(None)); - assert_eq!(map.remove(&first), Some(value_for_seed(1))); - assert_eq!(map.try_insert(second, value_for_seed(2)), Ok(None)); - assert_eq!(map.get(&second), Some(value_for_seed(2))); - assert_eq!(map.len(), 1); - } - - #[test] - fn raw_static_balance_map_zero_value_removes_existing_key() { - let mut map = RawStaticBalanceMap::<8>::new(); - let key = actor_for_seed(1); - - assert_eq!(map.try_insert(key, value_for_seed(1)), Ok(None)); - assert_eq!( - map.try_insert(key, U256::zero()), - Ok(Some(value_for_seed(1))) - ); - assert_eq!(map.get(&key), None); - assert_eq!(map.len(), 0); - - assert_eq!(map.try_insert(key, value_for_seed(2)), Ok(None)); - assert_eq!(map.get(&key), Some(value_for_seed(2))); - assert_eq!(map.len(), 1); - } - - #[test] - fn raw_static_balance_map_reports_full_table() { - let mut map = RawStaticBalanceMap::<1>::new(); - - assert_eq!( - map.try_insert(actor_for_seed(1), value_for_seed(1)), - Ok(None) - ); - assert_eq!( - map.try_insert(actor_for_seed(2), value_for_seed(2)), - Err(FixedMapFull) - ); - } - - #[test] - fn raw_static_allowance_map_removes_pair_key() { - let mut map = RawStaticAllowanceMap::<8>::new(); - let (owner, spender) = allowance_key_for_seed(1); - let key = AllowanceKey { owner, spender }; - - assert_eq!(map.try_insert(key, value_for_seed(1)), Ok(None)); - assert_eq!(map.remove(&key), Some(value_for_seed(1))); - assert_eq!(map.get(&key), None); - assert_eq!(map.len(), 0); - } - - #[test] - fn raw_static_state_matches_fixed_for_representative_ops() { - thread::Builder::new() - .stack_size(32 * 1024 * 1024) - .spawn(|| { - for map in [StorageMap::Balance, StorageMap::Allowance] { - for op in [ - StorageOp::InsertFresh, - StorageOp::UpdateExisting, - StorageOp::ReadExisting, - StorageOp::ReadMissing, - StorageOp::Remove, - ] { - let seed = match op { - StorageOp::InsertFresh | StorageOp::ReadMissing => 10_016, - StorageOp::UpdateExisting - | StorageOp::ReadExisting - | StorageOp::Remove => 4, - }; - - let fixed_result = { - let mut state = StorageStressState::new(); - assert_eq!(state.prepare(StorageBackend::Fixed, map, 16).len, 16); - state.bench(StorageBackend::Fixed, map, op, seed) - }; - for backend in [ - StorageBackend::RawStatic, - StorageBackend::SailsFixed, - StorageBackend::SailsStatic, - ] { - let candidate_result = { - let mut state = StorageStressState::new(); - assert_eq!(state.prepare(backend, map, 16).len, 16); - state.bench(backend, map, op, seed) - }; - - assert_eq!(fixed_result, candidate_result); - } - } - } - }) - .unwrap() - .join() - .unwrap(); - } -} diff --git a/benchmarks/vft-stress/Cargo.toml b/benchmarks/vft-stress/Cargo.toml deleted file mode 100644 index 9c1f2415b..000000000 --- a/benchmarks/vft-stress/Cargo.toml +++ /dev/null @@ -1,18 +0,0 @@ -[package] -name = "vft-stress" -version.workspace = true -authors.workspace = true -edition.workspace = true -license.workspace = true -repository.workspace = true - -[features] -default = [] -wasm-profile = [] - -[dependencies] -sails-rs.workspace = true -sails-storage.workspace = true - -[build-dependencies] -sails-rs = { workspace = true, features = ["wasm-builder"] } diff --git a/benchmarks/vft-stress/build.rs b/benchmarks/vft-stress/build.rs deleted file mode 100644 index e7b8e4c07..000000000 --- a/benchmarks/vft-stress/build.rs +++ /dev/null @@ -1,9 +0,0 @@ -fn main() { - const FIXED_CAPACITY: usize = 2048; - - let layout = sails_rs::build::StaticMemoryLayout::new(1024) - .reserve_table::<32, 32>("sails_static_balances", FIXED_CAPACITY) - .reserve_table::<64, 32>("sails_static_allowances", FIXED_CAPACITY); - - sails_rs::build::build_wasm_with_static_memory(layout); -} diff --git a/benchmarks/vft-stress/src/lib.rs b/benchmarks/vft-stress/src/lib.rs deleted file mode 100644 index 2a9a8cdcf..000000000 --- a/benchmarks/vft-stress/src/lib.rs +++ /dev/null @@ -1,1249 +0,0 @@ -#![no_std] - -use sails_rs::{ - cell::RefCell, - collections::{BTreeMap, HashMap}, - prelude::*, -}; -use sails_storage::__experimental::{ - FixedAllowanceMap as SailsFixedAllowanceMap, FixedBalanceMap as SailsFixedBalanceMap, - StaticAllowanceTable, StaticBalanceTable, -}; - -mod static_storage { - include!(concat!(env!("OUT_DIR"), "/sails_static_storage.rs")); -} - -const FIXED_CAPACITY: usize = 2048; - -pub const STATIC_MEMORY_END_PAGE: u32 = static_storage::STATIC_MEMORY_END_PAGE; - -type SailsStaticBalanceTable = StaticBalanceTable; -type SailsStaticAllowanceTable = StaticAllowanceTable; - -#[sails_type] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum VftStorageBackend { - BTree, - HashMap, - SailsFixed, - SailsStatic, - SailsStaticFast, -} - -#[sails_type] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum VftTransferOp { - Transfer, - TransferFrom, -} - -#[sails_type] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct VftTransferResult { - pub from_balance: U256, - pub to_balance: U256, - pub allowance: U256, - pub balance_len: u32, - pub allowance_len: u32, - pub transferred: bool, -} - -#[sails_type] -#[cfg(feature = "wasm-profile")] -#[derive(Clone, Copy, Debug, PartialEq, Eq)] -pub enum VftPhase { - ProbeOverhead, - NoopBody, - EchoBody, - KeyDerive, - AllowanceGet, - AllowancePut, - BalanceGetFrom, - BalanceGetTo, - BalancePutFrom, - BalancePutTo, - BalanceTransfer, - BalanceTransferFrom, - ResultBuild, -} - -#[sails_type] -#[cfg(feature = "wasm-profile")] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct VftPhaseGas { - pub phase: VftPhase, - pub gas: u64, -} - -#[sails_type] -#[cfg(feature = "wasm-profile")] -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct VftProfileResult { - pub result: VftTransferResult, - pub phases: Vec, -} - -struct VftStressService<'a> { - state: &'a RefCell, -} - -impl<'a> VftStressService<'a> { - fn new(state: &'a RefCell) -> Self { - Self { state } - } -} - -#[cfg(not(feature = "wasm-profile"))] -#[sails_rs::service] -impl VftStressService<'_> { - #[export] - pub fn prepare_vft(&mut self, backend: VftStorageBackend, len: u32) -> VftTransferResult { - self.state.borrow_mut().prepare_vft(backend, len) - } - - #[export] - pub fn bench_vft_transfer( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> VftTransferResult { - self.state - .borrow_mut() - .bench_vft_transfer(backend, op, seed) - } - - #[export] - pub fn bench_vft_transfer_fresh_bool(&mut self, backend: VftStorageBackend, seed: u32) -> bool { - self.state - .borrow_mut() - .bench_vft_transfer_fresh_bool(backend, seed) - } - - #[export] - pub fn bench_vft_approve_bool( - &mut self, - backend: VftStorageBackend, - owner_seed: u32, - spender_seed: u32, - ) -> bool { - self.state - .borrow_mut() - .bench_vft_approve_bool(backend, owner_seed, spender_seed) - } -} - -#[cfg(feature = "wasm-profile")] -#[sails_rs::service] -impl VftStressService<'_> { - #[cfg(feature = "wasm-profile")] - #[export] - pub fn bench_noop(&mut self) -> bool { - self.state.borrow_mut().bench_noop() - } - - #[cfg(feature = "wasm-profile")] - #[export] - pub fn bench_echo_vft_args( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> bool { - self.state - .borrow_mut() - .bench_echo_vft_args(backend, op, seed) - } - - #[export] - pub fn prepare_vft(&mut self, backend: VftStorageBackend, len: u32) -> VftTransferResult { - self.state.borrow_mut().prepare_vft(backend, len) - } - - #[export] - pub fn bench_vft_transfer( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> VftTransferResult { - self.state - .borrow_mut() - .bench_vft_transfer(backend, op, seed) - } - - #[export] - pub fn bench_vft_transfer_fresh_bool(&mut self, backend: VftStorageBackend, seed: u32) -> bool { - self.state - .borrow_mut() - .bench_vft_transfer_fresh_bool(backend, seed) - } - - #[export] - pub fn bench_vft_approve_bool( - &mut self, - backend: VftStorageBackend, - owner_seed: u32, - spender_seed: u32, - ) -> bool { - self.state - .borrow_mut() - .bench_vft_approve_bool(backend, owner_seed, spender_seed) - } - - #[cfg(feature = "wasm-profile")] - #[export] - pub fn bench_vft_transfer_profile( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> VftProfileResult { - self.state - .borrow_mut() - .bench_vft_transfer_profile(backend, op, seed) - } - - #[cfg(feature = "wasm-profile")] - #[export] - pub fn bench_vft_transfer_fresh_profile( - &mut self, - backend: VftStorageBackend, - seed: u32, - ) -> VftProfileResult { - self.state - .borrow_mut() - .bench_vft_transfer_fresh_profile(backend, seed) - } - - #[cfg(feature = "wasm-profile")] - #[export] - pub fn bench_vft_approve_profile( - &mut self, - backend: VftStorageBackend, - owner_seed: u32, - spender_seed: u32, - ) -> VftProfileResult { - self.state - .borrow_mut() - .bench_vft_approve_profile(backend, owner_seed, spender_seed) - } -} - -pub struct VftStressProgram { - state: RefCell, -} - -#[sails_rs::program] -impl VftStressProgram { - pub fn new_for_bench() -> Self { - Self { - state: RefCell::new(VftStressState::new()), - } - } - - pub fn vft_stress(&self) -> VftStressService<'_> { - VftStressService::new(&self.state) - } -} - -struct VftStressState { - storage: ActiveVftStorage, -} - -enum ActiveVftStorage { - Empty, - BTree { - balances: BTreeMap, - allowances: BTreeMap<(ActorId, ActorId), U256>, - }, - HashMap { - balances: HashMap, - allowances: HashMap<(ActorId, ActorId), U256>, - }, - SailsFixed { - balances: SailsFixedBalanceMap, - allowances: SailsFixedAllowanceMap, - }, - SailsStatic { - balances: SailsStaticBalanceMap, - allowances: SailsStaticAllowanceMap, - }, -} - -impl VftStressState { - fn new() -> Self { - Self { - storage: ActiveVftStorage::Empty, - } - } - - #[cfg(feature = "wasm-profile")] - fn bench_noop(&mut self) -> bool { - true - } - - #[cfg(feature = "wasm-profile")] - fn bench_echo_vft_args( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> bool { - seed > 0 - && matches!( - backend, - VftStorageBackend::BTree - | VftStorageBackend::HashMap - | VftStorageBackend::SailsFixed - | VftStorageBackend::SailsStatic - | VftStorageBackend::SailsStaticFast - ) - && matches!(op, VftTransferOp::Transfer | VftTransferOp::TransferFrom) - } - - fn prepare_vft(&mut self, backend: VftStorageBackend, len: u32) -> VftTransferResult { - assert!((len as usize) < FIXED_CAPACITY); - self.clear_vft(backend); - - for seed in 1..=len { - self.vft_insert_balance(backend, actor_for_seed(seed), vft_balance_for_seed(seed)); - let owner = actor_for_seed(seed); - let spender = vft_spender_for_seed(seed); - self.vft_insert_allowance(backend, owner, spender, vft_allowance_for_seed(seed)); - } - - VftTransferResult { - from_balance: vft_balance_for_seed(len), - to_balance: U256::zero(), - allowance: U256::zero(), - balance_len: self.vft_balance_len(backend), - allowance_len: self.vft_allowance_len(backend), - transferred: false, - } - } - - fn bench_vft_transfer( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> VftTransferResult { - self.bench_vft_transfer_with_to_seed(backend, op, seed, vft_recipient_seed(seed)) - } - - fn bench_vft_transfer_fresh_bool(&mut self, backend: VftStorageBackend, seed: u32) -> bool { - let len = self.vft_balance_len(backend); - assert!(len > 1); - let to_seed = len - .checked_add(seed) - .and_then(|value| value.checked_add(1)) - .expect("fresh VFT recipient seed overflow"); - self.apply_vft_transfer_bool(backend, VftTransferOp::Transfer, seed, to_seed) - } - - fn bench_vft_approve_bool( - &mut self, - backend: VftStorageBackend, - owner_seed: u32, - spender_seed: u32, - ) -> bool { - let owner = actor_for_seed(owner_seed); - let spender = vft_spender_for_seed(spender_seed); - if owner == spender { - return false; - } - - self.vft_insert_allowance( - backend, - owner, - spender, - vft_allowance_for_seed(spender_seed), - ); - true - } - - #[cfg(feature = "wasm-profile")] - fn bench_vft_transfer_profile( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - ) -> VftProfileResult { - self.bench_vft_transfer_profile_with_to_seed(backend, op, seed, vft_recipient_seed(seed)) - } - - #[cfg(feature = "wasm-profile")] - fn bench_vft_transfer_fresh_profile( - &mut self, - backend: VftStorageBackend, - seed: u32, - ) -> VftProfileResult { - let len = self.vft_balance_len(backend); - assert!(len > 1); - let to_seed = len - .checked_add(seed) - .and_then(|value| value.checked_add(1)) - .expect("fresh VFT recipient seed overflow"); - self.bench_vft_transfer_profile_with_to_seed( - backend, - VftTransferOp::Transfer, - seed, - to_seed, - ) - } - - #[cfg(feature = "wasm-profile")] - fn bench_vft_approve_profile( - &mut self, - backend: VftStorageBackend, - owner_seed: u32, - spender_seed: u32, - ) -> VftProfileResult { - let mut phases = VftPhaseRecorder::new(); - let owner = actor_for_seed(owner_seed); - let spender = vft_spender_for_seed(spender_seed); - let value = vft_allowance_for_seed(spender_seed); - phases.checkpoint(VftPhase::KeyDerive); - - let approved = if owner == spender { - false - } else { - self.vft_insert_allowance(backend, owner, spender, value); - true - }; - phases.checkpoint(VftPhase::AllowancePut); - - let result = VftTransferResult { - from_balance: U256::zero(), - to_balance: U256::zero(), - allowance: self - .vft_get_allowance(backend, &owner, &spender) - .unwrap_or_else(U256::zero), - balance_len: self.vft_balance_len(backend), - allowance_len: self.vft_allowance_len(backend), - transferred: approved, - }; - phases.checkpoint(VftPhase::ResultBuild); - - VftProfileResult { - result, - phases: phases.finish(), - } - } - - fn clear_vft(&mut self, backend: VftStorageBackend) { - self.storage = match backend { - VftStorageBackend::BTree => ActiveVftStorage::BTree { - balances: BTreeMap::new(), - allowances: BTreeMap::new(), - }, - VftStorageBackend::HashMap => ActiveVftStorage::HashMap { - balances: HashMap::new(), - allowances: HashMap::new(), - }, - VftStorageBackend::SailsFixed => ActiveVftStorage::SailsFixed { - balances: SailsFixedBalanceMap::new(), - allowances: SailsFixedAllowanceMap::new(), - }, - VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast => { - ActiveVftStorage::SailsStatic { - balances: SailsStaticBalanceMap::new(), - allowances: SailsStaticAllowanceMap::new(), - } - } - }; - } - - fn vft_balance_len(&self, backend: VftStorageBackend) -> u32 { - match (backend, &self.storage) { - (VftStorageBackend::BTree, ActiveVftStorage::BTree { balances, .. }) => { - balances.len() as u32 - } - (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { balances, .. }) => { - balances.len() as u32 - } - (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { balances, .. }) => { - balances.len() as u32 - } - ( - VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, - ActiveVftStorage::SailsStatic { balances, .. }, - ) => balances.len(), - _ => 0, - } - } - - fn vft_allowance_len(&self, backend: VftStorageBackend) -> u32 { - match (backend, &self.storage) { - (VftStorageBackend::BTree, ActiveVftStorage::BTree { allowances, .. }) => { - allowances.len() as u32 - } - (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { allowances, .. }) => { - allowances.len() as u32 - } - (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { allowances, .. }) => { - allowances.len() as u32 - } - ( - VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, - ActiveVftStorage::SailsStatic { allowances, .. }, - ) => allowances.len(), - _ => 0, - } - } - - fn vft_get_balance(&self, backend: VftStorageBackend, key: &ActorId) -> Option { - match (backend, &self.storage) { - (VftStorageBackend::BTree, ActiveVftStorage::BTree { balances, .. }) => { - balances.get(key).copied() - } - (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { balances, .. }) => { - balances.get(key).copied() - } - (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { balances, .. }) => { - balances - .get_actor_u256(key) - .expect("sails-storage fixed VFT balance map failed") - } - ( - VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, - ActiveVftStorage::SailsStatic { balances, .. }, - ) => balances - .get(key) - .expect("sails-storage static VFT balance map failed"), - _ => None, - } - } - - fn vft_get_allowance( - &self, - backend: VftStorageBackend, - owner: &ActorId, - spender: &ActorId, - ) -> Option { - match (backend, &self.storage) { - (VftStorageBackend::BTree, ActiveVftStorage::BTree { allowances, .. }) => { - allowances.get(&(*owner, *spender)).copied() - } - (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { allowances, .. }) => { - allowances.get(&(*owner, *spender)).copied() - } - (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { allowances, .. }) => { - allowances - .get_allowance_u256(owner, spender) - .expect("sails-storage fixed VFT allowance map failed") - } - ( - VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, - ActiveVftStorage::SailsStatic { allowances, .. }, - ) => allowances - .get(owner, spender) - .expect("sails-storage static VFT allowance map failed"), - _ => None, - } - } - - fn vft_insert_balance( - &mut self, - backend: VftStorageBackend, - key: ActorId, - value: U256, - ) -> Option { - match (backend, &mut self.storage) { - (VftStorageBackend::BTree, ActiveVftStorage::BTree { balances, .. }) => { - balances.insert(key, value) - } - (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { balances, .. }) => { - balances.insert(key, value) - } - (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { balances, .. }) => { - balances - .insert_actor_u256(key, value) - .expect("sails-storage fixed VFT balance map failed") - } - ( - VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, - ActiveVftStorage::SailsStatic { balances, .. }, - ) => balances - .try_insert(key, value) - .expect("sails-storage static VFT balance map failed"), - _ => panic!("VFT storage backend is not prepared"), - } - } - - fn vft_insert_allowance( - &mut self, - backend: VftStorageBackend, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Option { - match (backend, &mut self.storage) { - (VftStorageBackend::BTree, ActiveVftStorage::BTree { allowances, .. }) => { - allowances.insert((owner, spender), value) - } - (VftStorageBackend::HashMap, ActiveVftStorage::HashMap { allowances, .. }) => { - allowances.insert((owner, spender), value) - } - (VftStorageBackend::SailsFixed, ActiveVftStorage::SailsFixed { allowances, .. }) => { - allowances - .insert_allowance_u256(owner, spender, value) - .expect("sails-storage fixed VFT allowance map failed") - } - ( - VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast, - ActiveVftStorage::SailsStatic { allowances, .. }, - ) => allowances - .try_insert(owner, spender, value) - .expect("sails-storage static VFT allowance map failed"), - _ => panic!("VFT storage backend is not prepared"), - } - } - - fn apply_vft_transfer_bool( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - to_seed: u32, - ) -> bool { - if matches!( - backend, - VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast - ) { - return self.bench_vft_static_transfer_bool_with_to_seed( - op, - seed, - to_seed, - matches!(backend, VftStorageBackend::SailsStaticFast), - ); - } - - self.bench_vft_transfer_with_to_seed(backend, op, seed, to_seed) - .transferred - } - - #[cfg(feature = "wasm-profile")] - fn bench_vft_transfer_profile_with_to_seed( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - to_seed: u32, - ) -> VftProfileResult { - if matches!( - backend, - VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast - ) { - return self.bench_vft_static_transfer_profile_with_to_seed( - op, - seed, - to_seed, - matches!(backend, VftStorageBackend::SailsStaticFast), - ); - } - - let mut phases = VftPhaseRecorder::new(); - let amount = vft_transfer_amount(seed); - let from = actor_for_seed(seed); - let to = actor_for_seed(to_seed); - let spender = vft_spender_for_seed(seed); - phases.checkpoint(VftPhase::KeyDerive); - - let mut allowance_key = None; - let mut transferred = false; - - if matches!(op, VftTransferOp::TransferFrom) { - allowance_key = Some(spender); - let allowance = self - .vft_get_allowance(backend, &from, &spender) - .unwrap_or_else(U256::zero); - phases.checkpoint(VftPhase::AllowanceGet); - if allowance < amount { - let result = self.vft_result(backend, &from, &to, allowance_key.as_ref(), false); - phases.checkpoint(VftPhase::ResultBuild); - return VftProfileResult { - result, - phases: phases.finish(), - }; - } - self.vft_insert_allowance(backend, from, spender, allowance - amount); - phases.checkpoint(VftPhase::AllowancePut); - } - - let from_balance = self - .vft_get_balance(backend, &from) - .unwrap_or_else(U256::zero); - phases.checkpoint(VftPhase::BalanceGetFrom); - if from_balance >= amount { - let to_balance = self - .vft_get_balance(backend, &to) - .unwrap_or_else(U256::zero); - phases.checkpoint(VftPhase::BalanceGetTo); - self.vft_insert_balance(backend, from, from_balance - amount); - phases.checkpoint(VftPhase::BalancePutFrom); - self.vft_insert_balance(backend, to, to_balance + amount); - phases.checkpoint(VftPhase::BalancePutTo); - transferred = true; - } - - let result = self.vft_result(backend, &from, &to, allowance_key.as_ref(), transferred); - phases.checkpoint(VftPhase::ResultBuild); - VftProfileResult { - result, - phases: phases.finish(), - } - } - - fn bench_vft_transfer_with_to_seed( - &mut self, - backend: VftStorageBackend, - op: VftTransferOp, - seed: u32, - to_seed: u32, - ) -> VftTransferResult { - if matches!( - backend, - VftStorageBackend::SailsStatic | VftStorageBackend::SailsStaticFast - ) { - return self.bench_vft_static_transfer_with_to_seed( - op, - seed, - to_seed, - matches!(backend, VftStorageBackend::SailsStaticFast), - ); - } - - let amount = vft_transfer_amount(seed); - let from = actor_for_seed(seed); - let to = actor_for_seed(to_seed); - let spender = vft_spender_for_seed(seed); - - if matches!(op, VftTransferOp::TransferFrom) { - let allowance = self - .vft_get_allowance(backend, &from, &spender) - .unwrap_or_else(U256::zero); - if allowance < amount { - return self.vft_result(backend, &from, &to, Some(&spender), false); - } - self.vft_insert_allowance(backend, from, spender, allowance - amount); - } - - let from_balance = self - .vft_get_balance(backend, &from) - .unwrap_or_else(U256::zero); - if from_balance < amount { - let allowance_key = matches!(op, VftTransferOp::TransferFrom).then_some(&spender); - return self.vft_result(backend, &from, &to, allowance_key, false); - } - - let to_balance = self - .vft_get_balance(backend, &to) - .unwrap_or_else(U256::zero); - self.vft_insert_balance(backend, from, from_balance - amount); - self.vft_insert_balance(backend, to, to_balance + amount); - let allowance_key = matches!(op, VftTransferOp::TransferFrom).then_some(&spender); - self.vft_result(backend, &from, &to, allowance_key, true) - } - - fn bench_vft_static_transfer_with_to_seed( - &mut self, - op: VftTransferOp, - seed: u32, - to_seed: u32, - unchecked: bool, - ) -> VftTransferResult { - let amount = vft_transfer_amount(seed); - let from = actor_for_seed(seed); - let to = actor_for_seed(to_seed); - let spender = vft_spender_for_seed(seed); - let ActiveVftStorage::SailsStatic { - balances, - allowances, - } = &mut self.storage - else { - panic!("VFT static storage backend is not prepared"); - }; - - match op { - VftTransferOp::Transfer => { - let outcome = balances - .transfer(from, to, amount, unchecked) - .expect("sails-storage static VFT balance transfer failed"); - match outcome { - Some(outcome) => VftTransferResult { - from_balance: outcome.from_balance, - to_balance: outcome.to_balance, - allowance: U256::zero(), - balance_len: balances.len(), - allowance_len: allowances.len(), - transferred: true, - }, - None => VftTransferResult { - from_balance: balances - .get(&from) - .unwrap_or(None) - .unwrap_or_else(U256::zero), - to_balance: balances.get(&to).unwrap_or(None).unwrap_or_else(U256::zero), - allowance: U256::zero(), - balance_len: balances.len(), - allowance_len: allowances.len(), - transferred: false, - }, - } - } - VftTransferOp::TransferFrom => { - let outcome = balances - .transfer_from(allowances, from, spender, to, amount, unchecked) - .expect("sails-storage static VFT transfer-from failed"); - match outcome { - Some(outcome) => VftTransferResult { - from_balance: outcome.from_balance, - to_balance: outcome.to_balance, - allowance: outcome.allowance, - balance_len: balances.len(), - allowance_len: allowances.len(), - transferred: true, - }, - None => VftTransferResult { - from_balance: balances - .get(&from) - .unwrap_or(None) - .unwrap_or_else(U256::zero), - to_balance: balances.get(&to).unwrap_or(None).unwrap_or_else(U256::zero), - allowance: allowances - .get(&from, &spender) - .unwrap_or(None) - .unwrap_or_else(U256::zero), - balance_len: balances.len(), - allowance_len: allowances.len(), - transferred: false, - }, - } - } - } - } - - fn bench_vft_static_transfer_bool_with_to_seed( - &mut self, - op: VftTransferOp, - seed: u32, - to_seed: u32, - unchecked: bool, - ) -> bool { - let amount = vft_transfer_amount(seed); - let from = actor_for_seed(seed); - let to = actor_for_seed(to_seed); - let spender = vft_spender_for_seed(seed); - let ActiveVftStorage::SailsStatic { - balances, - allowances, - } = &mut self.storage - else { - panic!("VFT static storage backend is not prepared"); - }; - - match op { - VftTransferOp::Transfer => balances - .transfer(from, to, amount, unchecked) - .expect("sails-storage static VFT balance transfer failed") - .is_some(), - VftTransferOp::TransferFrom => balances - .transfer_from(allowances, from, spender, to, amount, unchecked) - .expect("sails-storage static VFT transfer-from failed") - .is_some(), - } - } - - #[cfg(feature = "wasm-profile")] - fn bench_vft_static_transfer_profile_with_to_seed( - &mut self, - op: VftTransferOp, - seed: u32, - to_seed: u32, - unchecked: bool, - ) -> VftProfileResult { - let mut phases = VftPhaseRecorder::new(); - let amount = vft_transfer_amount(seed); - let from = actor_for_seed(seed); - let to = actor_for_seed(to_seed); - let spender = vft_spender_for_seed(seed); - phases.checkpoint(VftPhase::KeyDerive); - - let ActiveVftStorage::SailsStatic { - balances, - allowances, - } = &mut self.storage - else { - panic!("VFT static storage backend is not prepared"); - }; - - let result = match op { - VftTransferOp::Transfer => { - let outcome = balances - .transfer(from, to, amount, unchecked) - .expect("sails-storage static VFT balance transfer failed"); - phases.checkpoint(VftPhase::BalanceTransfer); - match outcome { - Some(outcome) => VftTransferResult { - from_balance: outcome.from_balance, - to_balance: outcome.to_balance, - allowance: U256::zero(), - balance_len: balances.len(), - allowance_len: allowances.len(), - transferred: true, - }, - None => VftTransferResult { - from_balance: balances - .get(&from) - .unwrap_or(None) - .unwrap_or_else(U256::zero), - to_balance: balances.get(&to).unwrap_or(None).unwrap_or_else(U256::zero), - allowance: U256::zero(), - balance_len: balances.len(), - allowance_len: allowances.len(), - transferred: false, - }, - } - } - VftTransferOp::TransferFrom => { - let outcome = balances - .transfer_from(allowances, from, spender, to, amount, unchecked) - .expect("sails-storage static VFT transfer-from failed"); - phases.checkpoint(VftPhase::BalanceTransferFrom); - match outcome { - Some(outcome) => VftTransferResult { - from_balance: outcome.from_balance, - to_balance: outcome.to_balance, - allowance: outcome.allowance, - balance_len: balances.len(), - allowance_len: allowances.len(), - transferred: true, - }, - None => VftTransferResult { - from_balance: balances - .get(&from) - .unwrap_or(None) - .unwrap_or_else(U256::zero), - to_balance: balances.get(&to).unwrap_or(None).unwrap_or_else(U256::zero), - allowance: allowances - .get(&from, &spender) - .unwrap_or(None) - .unwrap_or_else(U256::zero), - balance_len: balances.len(), - allowance_len: allowances.len(), - transferred: false, - }, - } - } - }; - phases.checkpoint(VftPhase::ResultBuild); - - VftProfileResult { - result, - phases: phases.finish(), - } - } - - fn vft_result( - &self, - backend: VftStorageBackend, - from: &ActorId, - to: &ActorId, - spender: Option<&ActorId>, - transferred: bool, - ) -> VftTransferResult { - VftTransferResult { - from_balance: self - .vft_get_balance(backend, from) - .unwrap_or_else(U256::zero), - to_balance: self.vft_get_balance(backend, to).unwrap_or_else(U256::zero), - allowance: spender - .and_then(|spender| self.vft_get_allowance(backend, from, spender)) - .unwrap_or_else(U256::zero), - balance_len: self.vft_balance_len(backend), - allowance_len: self.vft_allowance_len(backend), - transferred, - } - } -} - -#[cfg(feature = "wasm-profile")] -struct VftPhaseRecorder { - previous_gas: u64, - phases: Vec, -} - -#[cfg(feature = "wasm-profile")] -impl VftPhaseRecorder { - fn new() -> Self { - let overhead = gas_probe_overhead(); - let previous_gas = gas_available(); - let mut phases = Vec::with_capacity(8); - if overhead > 0 { - phases.push(VftPhaseGas { - phase: VftPhase::ProbeOverhead, - gas: overhead, - }); - } - Self { - previous_gas, - phases, - } - } - - fn checkpoint(&mut self, phase: VftPhase) { - let current = gas_available(); - self.phases.push(VftPhaseGas { - phase, - gas: self.previous_gas.saturating_sub(current), - }); - self.previous_gas = current; - } - - fn finish(self) -> Vec { - self.phases - } -} - -#[cfg(feature = "wasm-profile")] -fn gas_probe_overhead() -> u64 { - let before = gas_available(); - let after = gas_available(); - before.saturating_sub(after) -} - -#[cfg(target_arch = "wasm32")] -#[cfg(feature = "wasm-profile")] -fn gas_available() -> u64 { - Syscall::gas_available() -} - -#[cfg(not(target_arch = "wasm32"))] -#[cfg(feature = "wasm-profile")] -fn gas_available() -> u64 { - 0 -} - -struct SailsStaticBalanceMap { - #[cfg(not(target_arch = "wasm32"))] - bytes: [u8; static_storage::SAILS_STATIC_BALANCES_BYTES], - len: u32, -} - -impl SailsStaticBalanceMap { - fn new() -> Self { - let map = Self { - #[cfg(not(target_arch = "wasm32"))] - bytes: [0; static_storage::SAILS_STATIC_BALANCES_BYTES], - len: 0, - }; - #[cfg(target_arch = "wasm32")] - map.table() - .clear() - .expect("sails static balance table can be cleared"); - map - } - - fn len(&self) -> u32 { - self.len - } - - fn get(&self, key: &ActorId) -> Result, sails_storage::TableError> { - self.table().get_actor_u256(key) - } - - fn try_insert( - &mut self, - key: ActorId, - value: U256, - ) -> Result, sails_storage::TableError> { - let previous = self.table_mut().insert_actor_u256(key, value)?; - if previous.is_none() { - self.len += 1; - } - - Ok(previous) - } - - fn transfer( - &mut self, - from: ActorId, - to: ActorId, - amount: U256, - unchecked: bool, - ) -> Result, sails_storage::TableError> - { - let outcome = if unchecked { - unsafe { - self.table_mut() - .transfer_actor_u256_nonzero_distinct_unchecked(from, to, amount)? - } - } else { - self.table_mut().transfer_actor_u256(from, to, amount)? - }; - if outcome.map_or(false, |outcome| outcome.inserted_to) { - self.len += 1; - } - Ok(outcome) - } - - fn transfer_from( - &mut self, - allowances: &SailsStaticAllowanceMap, - owner: ActorId, - spender: ActorId, - to: ActorId, - amount: U256, - unchecked: bool, - ) -> Result, sails_storage::TableError> - { - let outcome = if unchecked { - unsafe { - self.table_mut() - .transfer_actor_u256_from_nonzero_distinct_unchecked( - &allowances.table(), - owner, - spender, - to, - amount, - )? - } - } else { - self.table_mut().transfer_actor_u256_from( - &allowances.table(), - owner, - spender, - to, - amount, - )? - }; - if outcome.map_or(false, |outcome| outcome.inserted_to) { - self.len += 1; - } - Ok(outcome) - } - - fn table(&self) -> SailsStaticBalanceTable { - #[cfg(target_arch = "wasm32")] - let base = static_storage::SAILS_STATIC_BALANCES_BASE; - #[cfg(not(target_arch = "wasm32"))] - let base = self.bytes.as_ptr() as usize; - - unsafe { - SailsStaticBalanceTable::new(base, static_storage::SAILS_STATIC_BALANCES_SLOTS) - .expect("sails static balance layout is valid") - } - } - - fn table_mut(&mut self) -> SailsStaticBalanceTable { - #[cfg(target_arch = "wasm32")] - let base = static_storage::SAILS_STATIC_BALANCES_BASE; - #[cfg(not(target_arch = "wasm32"))] - let base = self.bytes.as_mut_ptr() as usize; - - unsafe { - SailsStaticBalanceTable::new(base, static_storage::SAILS_STATIC_BALANCES_SLOTS) - .expect("sails static balance layout is valid") - } - } -} - -struct SailsStaticAllowanceMap { - #[cfg(not(target_arch = "wasm32"))] - bytes: [u8; static_storage::SAILS_STATIC_ALLOWANCES_BYTES], - len: u32, -} - -impl SailsStaticAllowanceMap { - fn new() -> Self { - let map = Self { - #[cfg(not(target_arch = "wasm32"))] - bytes: [0; static_storage::SAILS_STATIC_ALLOWANCES_BYTES], - len: 0, - }; - #[cfg(target_arch = "wasm32")] - map.table() - .clear() - .expect("sails static allowance table can be cleared"); - map - } - - fn len(&self) -> u32 { - self.len - } - - fn get( - &self, - owner: &ActorId, - spender: &ActorId, - ) -> Result, sails_storage::TableError> { - self.table().get_allowance_u256(owner, spender) - } - - fn try_insert( - &mut self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result, sails_storage::TableError> { - let previous = self - .table_mut() - .insert_allowance_u256(owner, spender, value)?; - if previous.is_none() { - self.len += 1; - } - - Ok(previous) - } - - fn table(&self) -> SailsStaticAllowanceTable { - #[cfg(target_arch = "wasm32")] - let base = static_storage::SAILS_STATIC_ALLOWANCES_BASE; - #[cfg(not(target_arch = "wasm32"))] - let base = self.bytes.as_ptr() as usize; - - unsafe { - SailsStaticAllowanceTable::new(base, static_storage::SAILS_STATIC_ALLOWANCES_SLOTS) - .expect("sails static allowance layout is valid") - } - } - - fn table_mut(&mut self) -> SailsStaticAllowanceTable { - #[cfg(target_arch = "wasm32")] - let base = static_storage::SAILS_STATIC_ALLOWANCES_BASE; - #[cfg(not(target_arch = "wasm32"))] - let base = self.bytes.as_mut_ptr() as usize; - - unsafe { - SailsStaticAllowanceTable::new(base, static_storage::SAILS_STATIC_ALLOWANCES_SLOTS) - .expect("sails static allowance layout is valid") - } - } -} - -fn actor_for_seed(seed: u32) -> ActorId { - ActorId::from(seed as u64 + 1) -} - -fn vft_balance_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 1_000_000) -} - -fn vft_allowance_for_seed(seed: u32) -> U256 { - U256::from(seed as u64 + 10_000) -} - -fn vft_transfer_amount(seed: u32) -> U256 { - U256::from((seed % 7) as u64 + 1) -} - -fn vft_recipient_seed(seed: u32) -> u32 { - seed.saturating_add(10_000_000) -} - -fn vft_spender_for_seed(seed: u32) -> ActorId { - ActorId::from(seed as u64 + 20_000_000) -} diff --git a/examples/aggregator/app/Cargo.toml b/examples/aggregator/app/Cargo.toml index 5069ac777..2f47e3417 100644 --- a/examples/aggregator/app/Cargo.toml +++ b/examples/aggregator/app/Cargo.toml @@ -9,7 +9,6 @@ license.workspace = true [dependencies] sails-rs.workspace = true -sails-storage.workspace = true demo-client = { path = "../../demo/client" } redirect-client = { path = "../../redirect/client" } futures = { workspace = true } diff --git a/examples/aggregator/app/src/lib.rs b/examples/aggregator/app/src/lib.rs index d7ca74db2..2632b4b59 100644 --- a/examples/aggregator/app/src/lib.rs +++ b/examples/aggregator/app/src/lib.rs @@ -2,9 +2,7 @@ use demo_client::{DemoClient, DemoClientProgram, chaos::Chaos, counter::Counter}; use futures::{FutureExt, future}; -use msg_tracker::{ - MsgTracker, OpStatus, TrackerBackend, TrackerBenchResult, TrackerOp, message_id_for_seed, -}; +use msg_tracker::{MsgTracker, OpStatus}; use redirect_client::{redirect::Redirect as _, *}; use sails_rs::{cell::RefCell, client::*, gstd::msg, prelude::*}; @@ -146,61 +144,6 @@ impl AggregatorService { msg_tracker().borrow().get_statuses() } - #[export] - pub fn prepare_tracker(&mut self, len: u32) -> TrackerBenchResult { - let mut tracker = msg_tracker().borrow_mut(); - tracker.clear(); - for seed in 1..=len { - tracker.insert(message_id_for_seed(seed), OpStatus::Started); - } - TrackerBenchResult { - len: tracker.len(), - status: None, - existed: false, - } - } - - #[export] - pub fn bench_tracker(&mut self, op: TrackerOp, seed: u32) -> TrackerBenchResult { - let mut tracker = msg_tracker().borrow_mut(); - match op { - TrackerOp::InsertFresh => { - let existed = tracker - .insert_inner(message_id_for_seed(seed), OpStatus::Started) - .is_some(); - TrackerBenchResult { - len: tracker.len(), - status: Some(OpStatus::Started), - existed, - } - } - TrackerOp::UpdateExisting => { - let existed = tracker.update_status(message_id_for_seed(seed), OpStatus::Finalized); - TrackerBenchResult { - len: tracker.len(), - status: Some(OpStatus::Finalized), - existed, - } - } - TrackerOp::ReadExisting => { - let status = tracker.get_status(&message_id_for_seed(seed)); - TrackerBenchResult { - len: tracker.len(), - status, - existed: status.is_some(), - } - } - TrackerOp::ListStatuses => { - let statuses = tracker.get_statuses(); - TrackerBenchResult { - len: statuses.len() as u32, - status: statuses.last().map(|(_, status)| *status), - existed: !statuses.is_empty(), - } - } - } - } - #[export] pub async fn complex_add(&mut self) -> Result { let parent_id = msg::id(); @@ -266,12 +209,10 @@ pub struct AggregatorProgram { #[sails_rs::program] impl AggregatorProgram { pub fn new(target: ActorId) -> Self { - Self::new_with_tracker(target, TrackerBackend::BTree) - } - - pub fn new_with_tracker(target: ActorId, tracker_backend: TrackerBackend) -> Self { unsafe { - MSG_TRACKER = Some(RefCell::new(MsgTracker::new(tracker_backend))); + if (*core::ptr::addr_of_mut!(MSG_TRACKER)).is_none() { + MSG_TRACKER = Some(RefCell::new(MsgTracker::new())); + } } Self { target } } diff --git a/examples/aggregator/app/src/msg_tracker.rs b/examples/aggregator/app/src/msg_tracker.rs index cc64ba753..d9bafc3bd 100644 --- a/examples/aggregator/app/src/msg_tracker.rs +++ b/examples/aggregator/app/src/msg_tracker.rs @@ -1,10 +1,8 @@ -use sails_rs::{collections::BTreeMap, prelude::*}; -use sails_storage::FixedOpenAddressMap; - -const FIXED_TRACKER_CAPACITY: usize = 2048; +use sails_rs::collections::BTreeMap; +use sails_rs::prelude::*; #[sails_type] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] +#[derive(Clone, PartialEq, Debug)] pub enum OpStatus { Started, Step1, @@ -12,200 +10,44 @@ pub enum OpStatus { Finalized, } -#[sails_type] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum TrackerBackend { - BTree, - SailsFixed, -} - -#[sails_type] -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum TrackerOp { - InsertFresh, - UpdateExisting, - ReadExisting, - ListStatuses, -} - -#[sails_type] -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct TrackerBenchResult { - pub len: u32, - pub status: Option, - pub existed: bool, -} - pub struct MsgTracker { - storage: TrackerStorage, -} - -enum TrackerStorage { - BTree(BTreeMap), - SailsFixed(FixedOpenAddressMap<32, 1, FIXED_TRACKER_CAPACITY>), + message_info: BTreeMap, } impl MsgTracker { - pub fn new(backend: TrackerBackend) -> Self { + pub fn new() -> Self { Self { - storage: match backend { - TrackerBackend::BTree => TrackerStorage::BTree(BTreeMap::new()), - TrackerBackend::SailsFixed => { - TrackerStorage::SailsFixed(FixedOpenAddressMap::new()) - } - }, - } - } - - pub fn backend(&self) -> TrackerBackend { - match self.storage { - TrackerStorage::BTree(_) => TrackerBackend::BTree, - TrackerStorage::SailsFixed(_) => TrackerBackend::SailsFixed, + message_info: BTreeMap::new(), } } - pub fn len(&self) -> u32 { - match &self.storage { - TrackerStorage::BTree(map) => map.len() as u32, - TrackerStorage::SailsFixed(map) => map.len() as u32, - } - } - - pub fn clear(&mut self) { - let backend = self.backend(); - *self = Self::new(backend); - } - pub fn insert(&mut self, msg_id: MessageId, status: OpStatus) { - self.insert_inner(msg_id, status); - } - - pub fn insert_inner(&mut self, msg_id: MessageId, status: OpStatus) -> Option { - match &mut self.storage { - TrackerStorage::BTree(map) => map.insert(msg_id, status), - TrackerStorage::SailsFixed(map) => map - .insert(message_id_key(msg_id), status_value(status)) - .expect("fixed tracker capacity exceeded") - .map(value_status), - } + self.message_info.insert(msg_id, status); } pub fn update_status(&mut self, msg_id: MessageId, status: OpStatus) -> bool { - match &mut self.storage { - TrackerStorage::BTree(map) => { - if let Some(s) = map.get_mut(&msg_id) { - *s = status; - true - } else { - false - } - } - TrackerStorage::SailsFixed(map) => { - let key = message_id_key(msg_id); - if map - .get(&key) - .expect("fixed tracker lookup failed") - .is_some() - { - map.insert(key, status_value(status)) - .expect("fixed tracker update failed"); - true - } else { - false - } - } + if let Some(s) = self.message_info.get_mut(&msg_id) { + *s = status; + true + } else { + false } } pub fn get_status(&self, msg_id: &MessageId) -> Option { - match &self.storage { - TrackerStorage::BTree(map) => map.get(msg_id).copied(), - TrackerStorage::SailsFixed(map) => map - .get(&message_id_key(*msg_id)) - .expect("fixed tracker lookup failed") - .map(value_status), - } + self.message_info.get(msg_id).cloned() } pub fn get_statuses(&self) -> Vec<(MessageId, OpStatus)> { - match &self.storage { - TrackerStorage::BTree(map) => map.iter().map(|(&id, &status)| (id, status)).collect(), - TrackerStorage::SailsFixed(map) => { - let mut statuses = map - .entries() - .map(|(key, status)| (message_id_from_key(key), value_status(status))) - .collect::>(); - statuses.sort_unstable_by_key(|(id, _)| *id); - statuses - } - } + self.message_info + .iter() + .map(|(&id, s)| (id, s.clone())) + .collect() } } impl Default for MsgTracker { fn default() -> Self { - Self::new(TrackerBackend::BTree) - } -} - -pub fn message_id_for_seed(seed: u32) -> MessageId { - MessageId::from(seed as u64 + 1) -} - -fn message_id_key(msg_id: MessageId) -> [u8; 32] { - let mut key = [0u8; 32]; - key.copy_from_slice(msg_id.as_ref()); - key -} - -fn message_id_from_key(key: [u8; 32]) -> MessageId { - MessageId::from(key) -} - -fn status_value(status: OpStatus) -> [u8; 1] { - [match status { - OpStatus::Started => 0, - OpStatus::Step1 => 1, - OpStatus::Step2 => 2, - OpStatus::Finalized => 3, - }] -} - -fn value_status(value: [u8; 1]) -> OpStatus { - match value[0] { - 0 => OpStatus::Started, - 1 => OpStatus::Step1, - 2 => OpStatus::Step2, - 3 => OpStatus::Finalized, - _ => panic!("invalid tracker status value"), - } -} - -#[cfg(test)] -mod tests { - extern crate std; - - use super::*; - - #[test] - fn backends_match_representative_operations() { - for backend in [TrackerBackend::BTree, TrackerBackend::SailsFixed] { - let mut tracker = MsgTracker::new(backend); - let first = message_id_for_seed(1); - let second = message_id_for_seed(2); - - assert_eq!(tracker.insert_inner(first, OpStatus::Started), None); - assert_eq!(tracker.insert_inner(second, OpStatus::Step1), None); - assert_eq!( - tracker.insert_inner(first, OpStatus::Step2), - Some(OpStatus::Started) - ); - assert_eq!(tracker.get_status(&first), Some(OpStatus::Step2)); - assert!(tracker.update_status(second, OpStatus::Finalized)); - assert_eq!( - tracker.get_statuses(), - vec![(first, OpStatus::Step2), (second, OpStatus::Finalized),] - ); - } + Self::new() } } diff --git a/examples/aggregator/app/tests/gtest.rs b/examples/aggregator/app/tests/gtest.rs index ab5948cff..b0a6a469c 100644 --- a/examples/aggregator/app/tests/gtest.rs +++ b/examples/aggregator/app/tests/gtest.rs @@ -1,6 +1,5 @@ use aggregator_client::{ - AggregatorClient, AggregatorClientCtors, AggregatorClientProgram, TrackerBackend, - aggregator::Aggregator as _, + AggregatorClient, AggregatorClientCtors, AggregatorClientProgram, aggregator::Aggregator as _, }; use demo_client::{DemoClientCtors, DemoClientProgram}; use redirect_client::{ @@ -16,10 +15,6 @@ const DEMO_WASM_PATH: &str = "../../../target/wasm32-gear/debug/demo.opt.wasm"; const DEMO_WASM_PATH: &str = "../../../target/wasm32-gear/release/demo.opt.wasm"; async fn setup() -> (GtestEnv, ActorId, ActorId) { - setup_with_tracker(TrackerBackend::BTree).await -} - -async fn setup_with_tracker(tracker_backend: TrackerBackend) -> (GtestEnv, ActorId, ActorId) { let system = sails_rs::gtest::System::new(); system.init_logger(); system.mint_to(ACTOR_ID, 100_000_000_000_000); @@ -34,10 +29,7 @@ async fn setup_with_tracker(tracker_backend: TrackerBackend) -> (GtestEnv, Actor let demo_program = demo_factory.default().await.unwrap(); let aggregator_factory = env.deploy::(aggregator_code_id, vec![2]); - let aggregator_program = aggregator_factory - .new_with_tracker(demo_program.id(), tracker_backend) - .await - .unwrap(); + let aggregator_program = aggregator_factory.new(demo_program.id()).await.unwrap(); (env, demo_program.id(), aggregator_program.id()) } @@ -160,20 +152,3 @@ async fn complex_add_works() { aggregator_client::aggregator::OpStatus::Finalized ); } - -#[tokio::test] -async fn complex_add_works_with_fixed_tracker() { - let (env, _, aggregator_id) = setup_with_tracker(TrackerBackend::SailsFixed).await; - let mut aggregator = AggregatorClientProgram::client(aggregator_id) - .with_env(&env) - .aggregator(); - let final_val: u32 = aggregator.complex_add().await.unwrap().unwrap(); - assert_eq!(final_val, 60); - - let statuses = aggregator.get_statuses().await.unwrap(); - assert_eq!(statuses.len(), 1); - assert_eq!( - statuses[0].1, - aggregator_client::aggregator::OpStatus::Finalized - ); -} diff --git a/examples/aggregator/client/aggregator_client.idl b/examples/aggregator/client/aggregator_client.idl index d033840ff..d8a62d904 100644 --- a/examples/aggregator/client/aggregator_client.idl +++ b/examples/aggregator/client/aggregator_client.idl @@ -1,9 +1,8 @@ !@sails: 1.0.0-beta.5 -service Aggregator@0x4fe37e07a040ac29 { +service Aggregator@0x8e5b7b94507d0323 { functions { - BenchTracker(op: TrackerOp, seed: u32) -> TrackerBenchResult; ComplexAdd() -> Result; @query FetchFastest(target: ActorId) -> Result; @@ -19,7 +18,6 @@ service Aggregator@0x4fe37e07a040ac29 { FetchWithFallback(use_fallback: bool) -> Result; @query GetStatuses() -> [(MessageId, OpStatus)]; - PrepareTracker(len: u32) -> TrackerBenchResult; @query TestPollAfterCompletion(); } @@ -30,32 +28,14 @@ service Aggregator@0x4fe37e07a040ac29 { Step2, Finalized, } - struct TrackerBenchResult { - len: u32, - status: Option, - existed: bool, - } - enum TrackerOp { - InsertFresh, - UpdateExisting, - ReadExisting, - ListStatuses, - } } } program AggregatorClient { constructors { New(target: ActorId); - NewWithTracker(target: ActorId, tracker_backend: TrackerBackend); } services { - Aggregator@0x4fe37e07a040ac29, - } - types { - enum TrackerBackend { - BTree, - SailsFixed, - } + Aggregator@0x8e5b7b94507d0323, } } diff --git a/examples/aggregator/client/src/aggregator_client.rs b/examples/aggregator/client/src/aggregator_client.rs index df87647f7..955acfe28 100644 --- a/examples/aggregator/client/src/aggregator_client.rs +++ b/examples/aggregator/client/src/aggregator_client.rs @@ -30,11 +30,6 @@ pub trait AggregatorClientCtors { self, target: ActorId, ) -> sails_rs::client::PendingCtor; - fn new_with_tracker( - self, - target: ActorId, - tracker_backend: TrackerBackend, - ) -> sails_rs::client::PendingCtor; } impl AggregatorClientCtors @@ -47,25 +42,11 @@ impl AggregatorClientCtors ) -> sails_rs::client::PendingCtor { self.pending_ctor((target,)) } - fn new_with_tracker( - self, - target: ActorId, - tracker_backend: TrackerBackend, - ) -> sails_rs::client::PendingCtor { - self.pending_ctor((target, tracker_backend)) - } } pub mod io { use super::*; sails_rs::io_struct_impl!(New (target: ActorId) -> (), 0); - sails_rs::io_struct_impl!(NewWithTracker (target: ActorId, tracker_backend: super::TrackerBackend) -> (), 1); -} -#[sails_rs::sails_type(crate = sails_rs)] -#[derive(PartialEq, Clone, Debug)] -pub enum TrackerBackend { - BTree, - SailsFixed, } pub mod aggregator { @@ -79,29 +60,9 @@ pub mod aggregator { Step2, Finalized, } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub struct TrackerBenchResult { - pub len: u32, - pub status: Option, - pub existed: bool, - } - #[sails_rs::sails_type(crate = sails_rs)] - #[derive(PartialEq, Clone, Debug)] - pub enum TrackerOp { - InsertFresh, - UpdateExisting, - ReadExisting, - ListStatuses, - } pub trait Aggregator { type Env: sails_rs::client::GearEnv; - fn bench_tracker( - &mut self, - op: TrackerOp, - seed: u32, - ) -> sails_rs::client::PendingCall; fn complex_add(&mut self) -> sails_rs::client::PendingCall; fn fetch_fastest( &self, @@ -122,10 +83,6 @@ pub mod aggregator { use_fallback: bool, ) -> sails_rs::client::PendingCall; fn get_statuses(&self) -> sails_rs::client::PendingCall; - fn prepare_tracker( - &mut self, - len: u32, - ) -> sails_rs::client::PendingCall; fn test_poll_after_completion( &self, ) -> sails_rs::client::PendingCall; @@ -135,18 +92,11 @@ pub mod aggregator { impl sails_rs::client::Identifiable for AggregatorImpl { const INTERFACE_ID: sails_rs::InterfaceId = - sails_rs::InterfaceId::from_bytes_8([79, 227, 126, 7, 160, 64, 172, 41]); + sails_rs::InterfaceId::from_bytes_8([142, 91, 123, 148, 80, 125, 3, 35]); } impl Aggregator for sails_rs::client::Service { type Env = E; - fn bench_tracker( - &mut self, - op: TrackerOp, - seed: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((op, seed)) - } fn complex_add(&mut self) -> sails_rs::client::PendingCall { self.pending_call(()) } @@ -183,12 +133,6 @@ pub mod aggregator { fn get_statuses(&self) -> sails_rs::client::PendingCall { self.pending_call(()) } - fn prepare_tracker( - &mut self, - len: u32, - ) -> sails_rs::client::PendingCall { - self.pending_call((len,)) - } fn test_poll_after_completion( &self, ) -> sails_rs::client::PendingCall { @@ -198,16 +142,14 @@ pub mod aggregator { pub mod io { use super::*; - sails_rs::io_struct_impl!(BenchTracker (op: super::TrackerOp, seed: u32) -> super::TrackerBenchResult, 0, ::INTERFACE_ID); - sails_rs::io_struct_impl!(ComplexAdd () -> super::Result, 1, ::INTERFACE_ID); - sails_rs::io_struct_impl!(FetchFastest (target: ActorId) -> super::Result, 2, ::INTERFACE_ID); - sails_rs::io_struct_impl!(FetchFromAddress (target: ActorId) -> super::Result, 3, ::INTERFACE_ID); - sails_rs::io_struct_impl!(FetchRedirectId (target: ActorId) -> super::Result, 4, ::INTERFACE_ID); - sails_rs::io_struct_impl!(FetchSummary () -> super::Result<(u32, u32, ), String, >, 5, ::INTERFACE_ID); - sails_rs::io_struct_impl!(FetchValue () -> super::Result, 6, ::INTERFACE_ID); - sails_rs::io_struct_impl!(FetchWithFallback (use_fallback: bool) -> super::Result, 7, ::INTERFACE_ID); - sails_rs::io_struct_impl!(GetStatuses () -> Vec<(MessageId, super::OpStatus, )>, 8, ::INTERFACE_ID); - sails_rs::io_struct_impl!(PrepareTracker (len: u32) -> super::TrackerBenchResult, 9, ::INTERFACE_ID); - sails_rs::io_struct_impl!(TestPollAfterCompletion () -> (), 10, ::INTERFACE_ID); + sails_rs::io_struct_impl!(ComplexAdd () -> super::Result, 0, ::INTERFACE_ID); + sails_rs::io_struct_impl!(FetchFastest (target: ActorId) -> super::Result, 1, ::INTERFACE_ID); + sails_rs::io_struct_impl!(FetchFromAddress (target: ActorId) -> super::Result, 2, ::INTERFACE_ID); + sails_rs::io_struct_impl!(FetchRedirectId (target: ActorId) -> super::Result, 3, ::INTERFACE_ID); + sails_rs::io_struct_impl!(FetchSummary () -> super::Result<(u32, u32, ), String, >, 4, ::INTERFACE_ID); + sails_rs::io_struct_impl!(FetchValue () -> super::Result, 5, ::INTERFACE_ID); + sails_rs::io_struct_impl!(FetchWithFallback (use_fallback: bool) -> super::Result, 6, ::INTERFACE_ID); + sails_rs::io_struct_impl!(GetStatuses () -> Vec<(MessageId, super::OpStatus, )>, 7, ::INTERFACE_ID); + sails_rs::io_struct_impl!(TestPollAfterCompletion () -> (), 8, ::INTERFACE_ID); } } diff --git a/rs/macros/core/src/program/mod.rs b/rs/macros/core/src/program/mod.rs index ce25f2615..178428b1c 100644 --- a/rs/macros/core/src/program/mod.rs +++ b/rs/macros/core/src/program/mod.rs @@ -333,27 +333,23 @@ impl ProgramBuilder { extern "C" fn handle() { #payable - gstd::msg::with_read_on_stack_or_heap(|input| { - let input = input.expect("Failed to read input"); - let input: &[u8] = input; - - let program_ref = unsafe { #program_ident.as_mut() }.expect("Program not initialized"); - - #solidity_main - - let mut header_input: &[u8] = input; - if let Ok(header) = <#sails_path::meta::SailsMessageHeader as #sails_path::Decode>::decode(&mut header_input) { - let header_len = header.hlen().inner() as usize; - let (interface_id, route_id, entry_id) = header - .try_match_interfaces(INTERFACE_IDS) - .expect("Failed to find matching service") - .into_inner(); - match route_id { - #(#route_dispatches)* - _ => gstd::unknown_input_panic("Unknown route_id", &[route_id]) - } + let mut input = gstd::msg::load_bytes().expect("Failed to read input"); + + let program_ref = unsafe { #program_ident.as_mut() }.expect("Program not initialized"); + + #solidity_main + + if let Ok(header) = <#sails_path::meta::SailsMessageHeader as #sails_path::Decode>::decode(&mut input.as_slice()) { + let header_len = header.hlen().inner() as usize; + let (interface_id, route_id, entry_id) = header + .try_match_interfaces(INTERFACE_IDS) + .expect("Failed to find matching service") + .into_inner(); + match route_id { + #(#route_dispatches)* + _ => gstd::unknown_input_panic("Unknown route_id", &[route_id]) } - }); + } } ); @@ -432,14 +428,11 @@ impl ProgramBuilder { let init_fn = quote! { #[unsafe(no_mangle)] extern "C" fn init() { - gstd::msg::with_read_on_stack_or_heap(|input| { - let input = input.expect("Failed to read input"); - let mut #input_ident: &[u8] = input; + let mut #input_ident: &[u8] = &gstd::msg::load_bytes().expect("Failed to read input"); - #solidity_init + #solidity_init - #sails_init - }); + #sails_init } }; diff --git a/rs/src/gstd/macros.rs b/rs/src/gstd/macros.rs index 512590e41..be14df271 100644 --- a/rs/src/gstd/macros.rs +++ b/rs/src/gstd/macros.rs @@ -280,9 +280,8 @@ macro_rules! service_route_dispatch { .unwrap_or_else(|| $crate::gstd::unknown_input_panic("Unknown call", &[])); if is_async { - let input = $input.to_vec(); $crate::gstd::message_loop(async move { - $svc.try_handle_async($interface_id, $entry_id, &input, |encoded_result, value| { + $svc.try_handle_async($interface_id, $entry_id, $input, |encoded_result, value| { $crate::gstd::msg::reply_bytes(encoded_result, value) .expect("Failed to send output"); }) diff --git a/scripts/analyze-vft-storage.py b/scripts/analyze-vft-storage.py deleted file mode 100644 index d8d4d5b06..000000000 --- a/scripts/analyze-vft-storage.py +++ /dev/null @@ -1,329 +0,0 @@ -#!/usr/bin/env python3 -"""Static/probabilistic analysis for VFT static storage layouts. - -This script does not execute a Gear program. It mirrors the hash/index shape -used by sails-storage WAT-shaped VFT maps, simulates linear probing for loaded -tables, estimates lazy-page touches for transfer-like operations, and can count -Wasm instructions from a WAT file or a Wasm binary via `wasm2wat`. -""" - -from __future__ import annotations - -import argparse -import json -import os -import re -import subprocess -from collections import Counter -from dataclasses import dataclass -from typing import Iterable - - -MASK64 = (1 << 64) - 1 -HASH_GOLDEN_RATIO = 0x9E37_79B9 -HASH_PAIR_SPENDER = 0x85EB_CA6B -LCG_MUL = 6364136223846793005 -LCG_ADD = 1442695040888963407 -OWNER_DOMAIN = 0xA11C_E001_D15C_A11C -SPENDER_DOMAIN = 0x5EED_5EED_A110_CAFE - - -def actor_bytes(actor: int) -> bytes: - data = bytearray(32) - data[12:20] = max(actor, 1).to_bytes(8, "little") - return bytes(data) - - -def random_u64(seed: int, domain: int) -> int: - state = ((seed + domain) & MASK64) - state = (state * LCG_MUL + LCG_ADD) & MASK64 - return (state ^ (state >> 32)) & MASK64 - - -def actor_for_seed(seed: int) -> bytes: - return actor_bytes(random_u64(seed, OWNER_DOMAIN)) - - -def spender_for_seed(seed: int) -> bytes: - return actor_bytes(random_u64(seed, SPENDER_DOMAIN)) - - -def fold_words(data: bytes) -> int: - out = 0 - for offset in range(0, 32, 4): - out ^= int.from_bytes(data[offset : offset + 4], "little") - return out & 0xFFFF_FFFF - - -def hash_actor_current(key: bytes) -> int: - return (fold_words(key) * HASH_GOLDEN_RATIO) & 0xFFFF_FFFF - - -def fmix32(hash_value: int) -> int: - hash_value ^= hash_value >> 16 - hash_value = (hash_value * 0x85EB_CA6B) & 0xFFFF_FFFF - hash_value ^= hash_value >> 13 - hash_value = (hash_value * 0xC2B2_AE35) & 0xFFFF_FFFF - return (hash_value ^ (hash_value >> 16)) & 0xFFFF_FFFF - - -def hash_actor_mixed(key: bytes) -> int: - return fmix32(fold_words(key)) - - -def hash_actor(key: bytes, variant: str) -> int: - if variant == "current": - return hash_actor_current(key) - if variant == "mixed": - return hash_actor_mixed(key) - raise ValueError(f"unknown actor hash variant: {variant}") - - -def hash_allowance(owner: bytes, spender: bytes) -> int: - return ( - fold_words(owner) * HASH_GOLDEN_RATIO - + fold_words(spender) * HASH_PAIR_SPENDER - ) & 0xFFFF_FFFF - - -def index_for_hash(hash_value: int, log2_slots: int) -> int: - if log2_slots == 0: - return 0 - return hash_value >> (32 - log2_slots) - - -def percentile(sorted_values: list[int], numerator: int, denominator: int) -> int: - if not sorted_values: - return 0 - index = (len(sorted_values) - 1) * numerator // denominator - return sorted_values[index] - - -def summarize(values: Iterable[int]) -> dict[str, float | int]: - data = sorted(values) - if not data: - return {"avg": 0.0, "p50": 0, "p95": 0, "p99": 0, "max": 0} - return { - "avg": sum(data) / len(data), - "p50": percentile(data, 1, 2), - "p95": percentile(data, 95, 100), - "p99": percentile(data, 99, 100), - "max": data[-1], - } - - -@dataclass -class LookupTrace: - found: bool - probes: int - pages: set[int] - - -class LinearTable: - def __init__(self, log2_slots: int, slot_size: int, page_size: int): - self.log2_slots = log2_slots - self.slots = 1 << log2_slots - self.mask = self.slots - 1 - self.slot_size = slot_size - self.page_size = page_size - self.data: dict[int, bytes] = {} - - def _page(self, slot: int) -> int: - return (slot * self.slot_size) // self.page_size - - def lookup(self, key: bytes, hash_value: int) -> LookupTrace: - index = index_for_hash(hash_value, self.log2_slots) - probes = 0 - pages: set[int] = set() - while probes < self.slots: - pages.add(self._page(index)) - stored = self.data.get(index) - probes += 1 - if stored is None: - return LookupTrace(False, probes, pages) - if stored == key: - return LookupTrace(True, probes, pages) - index = (index + 1) & self.mask - return LookupTrace(False, probes, pages) - - def insert(self, key: bytes, hash_value: int) -> LookupTrace: - index = index_for_hash(hash_value, self.log2_slots) - probes = 0 - pages: set[int] = set() - while probes < self.slots: - pages.add(self._page(index)) - stored = self.data.get(index) - probes += 1 - if stored is None or stored == key: - self.data[index] = key - return LookupTrace(stored == key, probes, pages) - index = (index + 1) & self.mask - raise RuntimeError("table is full") - - -def load_tables(args: argparse.Namespace) -> tuple[LinearTable, LinearTable, dict[str, object]]: - balances = LinearTable(args.balance_log2, 64, args.lazy_page_size) - allowances = LinearTable(args.allowance_log2, 96, args.lazy_page_size) - balance_probes: list[int] = [] - allowance_probes: list[int] = [] - - for seed in range(1, args.load + 1): - owner = actor_for_seed(seed) - spender = spender_for_seed(seed) - balance_trace = balances.insert(owner, hash_actor(owner, args.actor_hash)) - allowance_key = owner + spender - allowance_trace = allowances.insert( - allowance_key, hash_allowance(owner, spender) - ) - balance_probes.append(balance_trace.probes) - allowance_probes.append(allowance_trace.probes) - - return balances, allowances, { - "balance_prepare_probes": summarize(balance_probes), - "allowance_prepare_probes": summarize(allowance_probes), - } - - -def operation_stats( - balances: LinearTable, allowances: LinearTable, args: argparse.Namespace -) -> dict[str, object]: - transfer_probes: list[int] = [] - transfer_pages: list[int] = [] - fresh_probes: list[int] = [] - fresh_pages: list[int] = [] - transfer_from_probes: list[int] = [] - transfer_from_pages: list[int] = [] - approve_probes: list[int] = [] - approve_pages: list[int] = [] - - for offset in range(args.ops): - seed = args.sample_seed + offset - owner = actor_for_seed(seed) - recipient = actor_for_seed(seed + 1 if seed < args.load else 1) - fresh = actor_for_seed(args.load + seed + 1) - spender = spender_for_seed(seed) - fresh_spender = spender_for_seed(args.load + args.sample_seed + offset + 60_000) - - owner_trace = balances.lookup(owner, hash_actor(owner, args.actor_hash)) - recipient_trace = balances.lookup(recipient, hash_actor(recipient, args.actor_hash)) - transfer_probes.append(owner_trace.probes + recipient_trace.probes) - transfer_pages.append(len(owner_trace.pages | recipient_trace.pages)) - - fresh_trace = balances.lookup(fresh, hash_actor(fresh, args.actor_hash)) - fresh_probes.append(owner_trace.probes + fresh_trace.probes) - fresh_pages.append(len(owner_trace.pages | fresh_trace.pages)) - - allowance_key = owner + spender - allowance_trace = allowances.lookup( - allowance_key, hash_allowance(owner, spender) - ) - transfer_from_probes.append( - owner_trace.probes + recipient_trace.probes + allowance_trace.probes - ) - transfer_from_pages.append( - len(owner_trace.pages | recipient_trace.pages | allowance_trace.pages) - ) - - approve_key = owner + fresh_spender - approve_trace = allowances.lookup( - approve_key, hash_allowance(owner, fresh_spender) - ) - approve_probes.append(approve_trace.probes) - approve_pages.append(len(approve_trace.pages)) - - return { - "transfer_existing": { - "probes": summarize(transfer_probes), - "lazy_pages": summarize(transfer_pages), - }, - "transfer_fresh": { - "probes": summarize(fresh_probes), - "lazy_pages": summarize(fresh_pages), - }, - "transfer_from": { - "probes": summarize(transfer_from_probes), - "lazy_pages": summarize(transfer_from_pages), - }, - "approve_fresh": { - "probes": summarize(approve_probes), - "lazy_pages": summarize(approve_pages), - }, - } - - -def wasm_audit(path: str) -> dict[str, object]: - if not path: - return {} - if path.endswith(".wasm"): - text = subprocess.check_output(["wasm2wat", path], text=True) - else: - with open(path, "r", encoding="utf-8") as file: - text = file.read() - - op_names = [ - "i32.load", - "i64.load", - "i32.store", - "i64.store", - "i32.add", - "i64.add", - "i64.sub", - "call", - "br_if", - "if", - "loop", - ] - counts = { - op: len(re.findall(r"(? None: - parser = argparse.ArgumentParser() - parser.add_argument("--load", type=int, default=1_000_000) - parser.add_argument("--balance-log2", type=int, default=21) - parser.add_argument("--allowance-log2", type=int, default=21) - parser.add_argument("--actor-hash", choices=["current", "mixed"], default="current") - parser.add_argument("--ops", type=int, default=4096) - parser.add_argument("--sample-seed", type=int, default=20_000) - parser.add_argument("--lazy-page-size", type=int, default=16 * 1024) - parser.add_argument("--wat", help="Optional .wat or .wasm path for static instruction counts") - parser.add_argument("--out", help="Optional JSON output path") - args = parser.parse_args() - - balances, allowances, prepare = load_tables(args) - result = { - "config": { - "load": args.load, - "balance_log2": args.balance_log2, - "allowance_log2": args.allowance_log2, - "actor_hash": args.actor_hash, - "balance_load_factor": args.load / balances.slots, - "allowance_load_factor": args.load / allowances.slots, - "ops": args.ops, - "lazy_page_size": args.lazy_page_size, - }, - "prepare": prepare, - "operations": operation_stats(balances, allowances, args), - "wasm_audit": wasm_audit(args.wat) if args.wat else {}, - } - - encoded = json.dumps(result, indent=2, sort_keys=True) - if args.out: - os.makedirs(os.path.dirname(args.out), exist_ok=True) - with open(args.out, "w", encoding="utf-8") as file: - file.write(encoded) - file.write("\n") - print(encoded) - - -if __name__ == "__main__": - main() From 5d183f4161356193208afb51e23827fc83e959a0 Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Wed, 20 May 2026 16:32:44 +0400 Subject: [PATCH 08/14] chore: remove benchmark artifacts from storage PR --- Cargo.lock | 1394 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 840 insertions(+), 554 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1d61830e..5cecab7ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,19 @@ dependencies = [ [[package]] name = "actor-system-error" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8862bc579cb6407ed5ff150781881003f9a09b724fa70ddbde1572a0ff87fd0" dependencies = [ "derive_more 2.1.1", - "gear-workspace-hack", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.3", ] [[package]] @@ -142,7 +152,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap", + "indexmap 2.14.0", "proc-macro-error2", "proc-macro2", "quote", @@ -186,6 +196,15 @@ dependencies = [ "libc", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anstream" version = "1.0.0" @@ -265,6 +284,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object 0.37.3", +] + [[package]] name = "ark-bls12-377" version = "0.4.0" @@ -656,7 +684,7 @@ version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ - "addr2line", + "addr2line 0.25.1", "cfg-if", "libc", "miniz_oxide", @@ -716,6 +744,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.70.1" @@ -901,12 +938,13 @@ dependencies = [ [[package]] name = "builtins-common" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a54b46c994eee08098e021332099c898037316c0776f9f09885a9dc37a3f5c" dependencies = [ "derive_more 2.1.1", "gbuiltin-bls381", "gbuiltin-eth-bridge", "gear-core", - "gear-workspace-hack", "gprimitives", "log", "parity-scale-codec", @@ -1411,6 +1449,24 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-processor" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c6661d52b0cc6d2157640dffe0298e42a9babb38d4629198939b70b7a23aaa" +dependencies = [ + "actor-system-error", + "derive_more 2.1.1", + "gear-core", + "gear-core-backend", + "gear-core-errors", + "gear-lazy-pages-common", + "gear-wasm-instrument", + "gsys", + "log", + "parity-scale-codec", +] + [[package]] name = "corosensei" version = "0.3.3" @@ -1431,6 +1487,15 @@ dependencies = [ "sails-rs", ] +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -1449,6 +1514,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cranelift-entity" +version = "0.95.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" +dependencies = [ + "serde", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -2251,6 +2325,12 @@ dependencies = [ "rand 0.8.6", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -2372,8 +2452,9 @@ dependencies = [ [[package]] name = "frame-benchmarking" -version = "38.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "38.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a0f983d69640f90a0ce87f107cff07f6f8f7f5ef9334ffb6f37a9c6e224ca1d" dependencies = [ "frame-support", "frame-support-procedural", @@ -2385,12 +2466,12 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto 38.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-io 38.0.0", - "sp-runtime 39.0.1", - "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-storage 21.0.0", "static_assertions", ] @@ -2406,7 +2487,7 @@ dependencies = [ "scale-encode", "scale-info", "scale-type-resolver", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-crypto-hashing", "thiserror 2.0.18", ] @@ -2436,8 +2517,9 @@ dependencies = [ [[package]] name = "frame-support" -version = "38.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "38.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7dd8b9f161a8289e3b9fe6c1068519358dbff2270d38097a923d3d1b4459dca" dependencies = [ "aquamarine", "array-bytes", @@ -2454,31 +2536,32 @@ dependencies = [ "paste", "scale-info", "serde", - "serde-json-wasm", + "serde_json", "smallvec", "sp-api", - "sp-arithmetic 26.0.0", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-arithmetic", + "sp-core", "sp-crypto-hashing-proc-macro", - "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-debug-derive 14.0.0", "sp-genesis-builder", "sp-inherents", - "sp-io 38.0.0", + "sp-io", "sp-metadata-ir", - "sp-runtime 39.0.1", + "sp-runtime", "sp-staking", - "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-tracing 17.0.1", - "sp-weights 31.0.0", + "sp-state-machine", + "sp-std 14.0.0", + "sp-tracing 17.1.0", + "sp-weights", "static_assertions", "tt-call", ] [[package]] name = "frame-support-procedural" -version = "30.0.3" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "30.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da784d943f2a945be923ab081a7c0837355b38045c50945d7ec1a138e2f3c52" dependencies = [ "Inflector", "cfg-expr", @@ -2491,17 +2574,18 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-crypto-hashing", "syn 2.0.117", ] [[package]] name = "frame-support-procedural-tools" -version = "13.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a088fd6fda5f53ff0c17fc7551ce8bd0ead14ba742228443c8196296a7369b" dependencies = [ "frame-support-procedural-tools-derive", - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -2510,7 +2594,8 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "12.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed971c6435503a099bdac99fe4c5bea08981709e5b5a0a8535a1856f48561191" dependencies = [ "proc-macro2", "quote", @@ -2520,7 +2605,8 @@ dependencies = [ [[package]] name = "frame-system" version = "38.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c7fa02f8c305496d2ae52edaecdb9d165f11afa965e05686d7d7dd1ce93611" dependencies = [ "cfg-if", "docify", @@ -2529,12 +2615,12 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-io 38.0.0", - "sp-runtime 39.0.1", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", "sp-version", - "sp-weights 31.0.0", + "sp-weights", ] [[package]] @@ -2556,6 +2642,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "fs4" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e871a4cfa68bb224863b53149d973df1ac8d1ed2fa1d1bfc37ac1bb65dd37207" +dependencies = [ + "rustix 0.38.44", + "windows-sys 0.52.0", +] + [[package]] name = "funty" version = "2.0.0" @@ -2672,21 +2768,23 @@ dependencies = [ [[package]] name = "galloc" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d95f9357f55c811d9b1f5c7bb1ef3b3f0094dee912a2bdb5155eb46611d8f87" dependencies = [ "gear-dlmalloc", - "gear-workspace-hack", ] [[package]] name = "gbuiltin-bls381" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919a1d0d8397053524bd9785788afb5a4dedc5c50e72650dae6857df2409a3f1" dependencies = [ "ark-bls12-381", "ark-ec", "ark-ff", "ark-scale", "ark-serialize", - "gear-workspace-hack", "parity-scale-codec", "scale-info", ] @@ -2694,8 +2792,9 @@ dependencies = [ [[package]] name = "gbuiltin-eth-bridge" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ea714934fa347452f0b33afa8a9afcdaa4079ea2fc2d7ee46ecdd03681ed72" dependencies = [ - "gear-workspace-hack", "gprimitives", "parity-scale-codec", "scale-info", @@ -2712,11 +2811,11 @@ dependencies = [ "futures", "gear-core", "gear-node-wrapper", - "gear-utils 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gear-utils", "gsdk", "hex", "parity-scale-codec", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core", "thiserror 2.0.18", "url", ] @@ -2724,11 +2823,12 @@ dependencies = [ [[package]] name = "gcore" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "015093eb24aa69f98f6474a9547a51a1a9230204e55a1778e9c8588cd8ee6047" dependencies = [ "arrayvec 0.7.6", "gear-core-errors", "gear-stack-buffer", - "gear-workspace-hack", "gprimitives", "gsys", "paste", @@ -2738,6 +2838,8 @@ dependencies = [ [[package]] name = "gear-common" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6802013b852310c7a9e1e4e48e6bf56a877c4d746783ab3b93e521c7d70225f3" dependencies = [ "enum-iterator 1.5.0", "fail", @@ -2747,24 +2849,24 @@ dependencies = [ "gear-common-codegen", "gear-core", "gear-wasm-instrument", - "gear-workspace-hack", "gsys", "log", "primitive-types 0.12.2", "scale-decode", "scale-encode", - "sp-arithmetic 26.0.0", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-io 38.0.0", - "sp-runtime 39.0.1", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", ] [[package]] name = "gear-common-codegen" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ec789cbf723ef6bcb29552cbbe72bd9a1f46a07feaab5a0647a2f42480cf8d" dependencies = [ - "gear-workspace-hack", "quote", "syn 2.0.117", ] @@ -2772,6 +2874,8 @@ dependencies = [ [[package]] name = "gear-core" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7172ef6fd588e780ed2d9f8e99f6cbc2c65a19e4fd61b4686cc5a74c04ab07b" dependencies = [ "blake2", "bytemuck", @@ -2780,7 +2884,6 @@ dependencies = [ "enum-iterator 1.5.0", "gear-core-errors", "gear-wasm-instrument", - "gear-workspace-hack", "gprimitives", "gsys", "hex", @@ -2800,6 +2903,8 @@ dependencies = [ [[package]] name = "gear-core-backend" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4af189ad011d19bf4a42acbfe946b267ef2224b74f9f87778387a2a545495c78" dependencies = [ "actor-system-error", "blake2", @@ -2811,7 +2916,6 @@ dependencies = [ "gear-sandbox", "gear-sandbox-env", "gear-wasm-instrument", - "gear-workspace-hack", "gsys", "log", "parity-scale-codec", @@ -2820,9 +2924,10 @@ dependencies = [ [[package]] name = "gear-core-errors" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a068b7e99afbedd710b959a20c8ac91b2cc77f8a0ce87af687b673bfbf26a809" dependencies = [ "enum-iterator 1.5.0", - "gear-workspace-hack", "parity-scale-codec", "scale-decode", "scale-encode", @@ -2831,23 +2936,6 @@ dependencies = [ "thiserror 2.0.18", ] -[[package]] -name = "gear-core-processor" -version = "1.10.0" -dependencies = [ - "actor-system-error", - "derive_more 2.1.1", - "gear-core", - "gear-core-backend", - "gear-core-errors", - "gear-lazy-pages-common", - "gear-wasm-instrument", - "gear-workspace-hack", - "gsys", - "log", - "parity-scale-codec", -] - [[package]] name = "gear-dlmalloc" version = "0.2.0" @@ -2864,6 +2952,8 @@ dependencies = [ [[package]] name = "gear-lazy-pages" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e94788c25494ddb5ca7a73b71b61429990caba31dea7e3b55e0dfe33ae3634c" dependencies = [ "cfg-if", "derive_more 2.1.1", @@ -2871,7 +2961,6 @@ dependencies = [ "gear-core", "gear-lazy-pages-common", "gear-sandbox-host", - "gear-workspace-hack", "libc", "log", "mach", @@ -2886,9 +2975,10 @@ dependencies = [ [[package]] name = "gear-lazy-pages-common" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1bd7e4e3d11c037298b2908c6ea1f3061a90bc98dbca188adf147599a88a96" dependencies = [ "gear-core", - "gear-workspace-hack", "num_enum", "parity-scale-codec", ] @@ -2896,11 +2986,12 @@ dependencies = [ [[package]] name = "gear-lazy-pages-native-interface" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affb988d8baa32dced50d696046e9a3470602abf3effe0fe06bb546dd05581d0" dependencies = [ "gear-core", "gear-lazy-pages", "gear-lazy-pages-common", - "gear-workspace-hack", "log", ] @@ -2919,16 +3010,17 @@ dependencies = [ [[package]] name = "gear-sandbox" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ca22d8209f2c3077c0c97063e0804bf4e1c5b60a4a2e6200da664311d1920c" dependencies = [ "gear-sandbox-env", "gear-sandbox-interface", "gear-wasmer-cache", - "gear-workspace-hack", "log", "parity-scale-codec", "pathdiff", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core", + "sp-std 14.0.0", "sp-wasm-interface-common", "wasmer", "wasmer-compiler", @@ -2939,28 +3031,30 @@ dependencies = [ [[package]] name = "gear-sandbox-env" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6570e31af2bd208e6ce48d6a5e86c62815e57465df3f812670f40021e6a6a38b" dependencies = [ - "gear-workspace-hack", "parity-scale-codec", - "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-debug-derive 14.0.0", + "sp-std 14.0.0", "sp-wasm-interface-common", ] [[package]] name = "gear-sandbox-host" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c08257ac1e9ba954883fd229254fa54abc150cdc0c62380905f22d335c46c44" dependencies = [ "atomic_enum", "defer", "environmental", "gear-sandbox-env", "gear-wasmer-cache", - "gear-workspace-hack", + "gp-allocator", "log", "parity-scale-codec", "region", - "sp-allocator", "sp-wasm-interface-common", "tempfile", "thiserror 2.0.18", @@ -2972,45 +3066,32 @@ dependencies = [ [[package]] name = "gear-sandbox-interface" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9b75e5d27247deff3569a5cfa246e8e8289ea021fd16cc8a80b784eb85ff13" dependencies = [ "gear-sandbox-host", - "gear-workspace-hack", + "gp-runtime-interface", + "gp-wasm-interface", "log", - "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-wasm-interface 21.0.1 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "parity-scale-codec", ] [[package]] name = "gear-ss58" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94745919b16a98a5b8d7b7766bcb9127eb8bf013eff098ad828e452744bee6e6" dependencies = [ "blake2", "bs58", - "gear-workspace-hack", "hex", ] [[package]] name = "gear-stack-buffer" version = "1.10.0" -dependencies = [ - "gear-workspace-hack", -] - -[[package]] -name = "gear-utils" -version = "1.10.0" -dependencies = [ - "gear-core", - "gear-workspace-hack", - "hex", - "nonempty", - "parity-scale-codec", - "path-clean", - "serde", - "serde_json", - "tracing-subscriber", -] +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d30f39766a97c23c841c55e868d93e01e146ace585aa36b291b4c46b9d0c15" [[package]] name = "gear-utils" @@ -3025,12 +3106,14 @@ dependencies = [ "path-clean", "serde", "serde_json", - "tracing-subscriber", + "tracing-subscriber 0.3.23", ] [[package]] name = "gear-wasm-builder" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bed5dd34648a27e7dc217dc3b00eea4282bd451f4cf76bf627a05c5dbcc84f9" dependencies = [ "anyhow", "cargo_metadata 0.20.0", @@ -3039,7 +3122,6 @@ dependencies = [ "gear-core", "gear-wasm-instrument", "gear-wasm-optimizer", - "gear-workspace-hack", "itertools 0.13.0", "log", "pathdiff", @@ -3051,10 +3133,11 @@ dependencies = [ [[package]] name = "gear-wasm-instrument" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf819c76e5002586f7778e4acd10b775bd797e2baece1c31fa1b95a630eec238" dependencies = [ "derive_more 2.1.1", "enum-iterator 1.5.0", - "gear-workspace-hack", "log", "wasm-encoder 0.230.0", "wasmparser 0.230.0", @@ -3063,11 +3146,12 @@ dependencies = [ [[package]] name = "gear-wasm-optimizer" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed336b47eecdcee1c91dbedb8e6f6f702734f405afae68fdca3f1ddea4c3669d" dependencies = [ "anyhow", "colored", "gear-wasm-instrument", - "gear-workspace-hack", "log", "regex", "rustc_version", @@ -3078,22 +3162,18 @@ dependencies = [ [[package]] name = "gear-wasmer-cache" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1715238463dc4a8436e81bef5faadf28d2ad9f7320ef15e0f752da9568a71c48" dependencies = [ "bytes", "derive_more 2.1.1", - "gear-workspace-hack", + "fs4", "log", "uluru", "wasmer", "wasmer-cache", ] -[[package]] -name = "gear-workspace-hack" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ac67118eef8a153d4df06bd865d0a5c3ac51888c238673a203a5c424db0327" - [[package]] name = "genco" version = "0.19.0" @@ -3175,14 +3255,25 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +dependencies = [ + "fallible-iterator 0.2.0", + "indexmap 1.9.3", + "stable_deref_trait", +] + [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" dependencies = [ - "fallible-iterator", - "indexmap", + "fallible-iterator 0.3.0", + "indexmap 2.14.0", "stable_deref_trait", ] @@ -3209,16 +3300,77 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "gp-allocator" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93cfb4525b5362966a1971125ff8f9825e6a511403635dccf883273d20c43bdf" +dependencies = [ + "log", + "parity-scale-codec", + "sp-wasm-interface-common", + "thiserror 1.0.69", +] + +[[package]] +name = "gp-runtime-interface" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dfb3786bf8fe51099c67dfe7c9a925fe0bfb78ffe938aa07b598995ee48d0e3" +dependencies = [ + "bytes", + "gp-runtime-interface-proc-macro", + "gp-wasm-interface", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types 0.12.2", + "sp-externalities 0.20.0", + "sp-std 9.0.0", + "sp-storage 14.0.0", + "sp-tracing 10.0.0", + "static_assertions", +] + +[[package]] +name = "gp-runtime-interface-proc-macro" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1e4d907f86db64e8b9d9af0b5adfd59e62b4023fd88511e13d62028ed101f2c" +dependencies = [ + "Inflector", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "gp-wasm-interface" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de334e40147f969276463ad04d0ddfe519aebb6254832ea0637a300c6d637c11" +dependencies = [ + "anyhow", + "gp-allocator", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 9.0.0", + "sp-wasm-interface-common", + "wasmtime", +] + [[package]] name = "gprimitives" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f79911d3caeb051ae07b0d4b57c196fdc6e105f7be7cc51365416d7248ca5ce" dependencies = [ "alloy-primitives", "alloy-sol-types", "bytemuck", "derive_more 2.1.1", "gear-ss58", - "gear-workspace-hack", "hex", "primitive-types 0.12.2", "scale-decode", @@ -3252,14 +3404,14 @@ dependencies = [ "gear-core-errors", "gsdk-codegen", "hex", - "indexmap", + "indexmap 2.14.0", "jsonrpsee", "log", "parity-scale-codec", "parking_lot", "serde", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 39.0.5", + "sp-core", + "sp-runtime", "subxt", "thiserror 2.0.18", "url", @@ -3279,6 +3431,8 @@ dependencies = [ [[package]] name = "gstd" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "232fec5b812a76c016c11faceef031fbe7608bb4db07051744201a2dabff1072" dependencies = [ "arrayvec 0.7.6", "const_format", @@ -3286,7 +3440,6 @@ dependencies = [ "galloc", "gcore", "gear-core-errors", - "gear-workspace-hack", "gprimitives", "gstd-codegen", "hashbrown 0.14.5", @@ -3300,8 +3453,9 @@ dependencies = [ [[package]] name = "gstd-codegen" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b347d845d9fdc17b47288d082fd7001390d0652acb5d221c29a1b3f16e9a238" dependencies = [ - "gear-workspace-hack", "gprimitives", "proc-macro2", "quote", @@ -3311,28 +3465,30 @@ dependencies = [ [[package]] name = "gsys" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b08a4ef9c0f6aed528b42117cea34f53af2a4a7f080c04b4356eb63adc9302" dependencies = [ "bytemuck", - "gear-workspace-hack", ] [[package]] name = "gtest" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cba01a0d56d5ff4a072ce3dfc142ecba271f097435985fe623a8b69918e785c" dependencies = [ "builtins-common", "cargo_toml", "colored", + "core-processor", "etc", "gear-common", "gear-core", "gear-core-errors", - "gear-core-processor", "gear-lazy-pages", "gear-lazy-pages-common", "gear-lazy-pages-native-interface", - "gear-utils 1.10.0", - "gear-workspace-hack", + "gear-utils", "gprimitives", "gsys", "log", @@ -3341,7 +3497,7 @@ dependencies = [ "rand 0.8.6", "scale-info", "thiserror 2.0.18", - "tracing-subscriber", + "tracing-subscriber 0.3.23", ] [[package]] @@ -3356,7 +3512,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -3394,6 +3550,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.13.2" @@ -3453,6 +3615,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "hermit-abi" version = "0.5.2" @@ -3826,6 +3994,17 @@ dependencies = [ "quote", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -3893,13 +4072,24 @@ dependencies = [ "num-traits", ] +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "is-terminal" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ - "hermit-abi", + "hermit-abi 0.5.2", "libc", "windows-sys 0.61.2", ] @@ -4185,7 +4375,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax", + "regex-syntax 0.8.10", "string_cache", "term", "tiny-keccak", @@ -4199,7 +4389,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata", + "regex-automata 0.4.14", ] [[package]] @@ -4326,6 +4516,12 @@ dependencies = [ "nalgebra", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -4377,8 +4573,8 @@ dependencies = [ "fnv", "proc-macro2", "quote", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.14", + "regex-syntax 0.8.10", "syn 2.0.117", ] @@ -4488,13 +4684,22 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchers" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata", + "regex-automata 0.4.14", ] [[package]] @@ -4513,6 +4718,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memfd" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" +dependencies = [ + "rustix 1.1.4", +] + [[package]] name = "memmap2" version = "0.6.2" @@ -4540,6 +4754,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -4860,13 +5083,26 @@ dependencies = [ [[package]] name = "numerated" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb9cd51526a9244bccb59b31a21af8fabc6a70a23707ff0e9095abda2a5033a" dependencies = [ "derive_more 2.1.1", - "gear-workspace-hack", "num-traits", "scale-info", ] +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "crc32fast", + "hashbrown 0.13.2", + "indexmap 1.9.3", + "memchr", +] + [[package]] name = "object" version = "0.32.2" @@ -4876,7 +5112,7 @@ dependencies = [ "crc32fast", "flate2", "hashbrown 0.14.5", - "indexmap", + "indexmap 2.14.0", "memchr", "ruzstd 0.5.0", ] @@ -4979,7 +5215,7 @@ version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -4991,11 +5227,6 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" -[[package]] -name = "parity-wasm" -version = "0.45.0" -source = "git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext#bad3e1ec78f655f3eec5a0c9c12a3546c8c9d432" - [[package]] name = "parking" version = "2.2.1" @@ -5146,7 +5377,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.14.0", ] [[package]] @@ -5286,7 +5517,7 @@ checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi", + "hermit-abi 0.5.2", "pin-project-lite", "rustix 1.1.4", "windows-sys 0.61.2", @@ -5397,9 +5628,19 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.5.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ "toml_edit 0.25.11+spec-1.1.0", ] @@ -5493,6 +5734,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "psm" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +dependencies = [ + "ar_archive_writer", + "cc", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -5760,8 +6011,17 @@ checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.14", + "regex-syntax 0.8.10", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -5772,9 +6032,15 @@ checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.10", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.10" @@ -5841,7 +6107,7 @@ dependencies = [ "bytecheck 0.8.2", "bytes", "hashbrown 0.17.0", - "indexmap", + "indexmap 2.14.0", "munge", "ptr_meta 0.3.1", "rancor", @@ -5964,6 +6230,20 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "rustix" +version = "0.36.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + [[package]] name = "rustix" version = "0.38.44" @@ -6278,7 +6558,7 @@ dependencies = [ name = "sails-reflect-hash-derive" version = "1.0.0-beta.5" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -6363,7 +6643,7 @@ version = "1.0.0-beta.5" dependencies = [ "insta", "prettyplease", - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -6439,7 +6719,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17020f2d59baabf2ddcdc20a4e567f8210baf089b8a8d4785f5fd5e716f92038" dependencies = [ "darling 0.20.11", - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -6465,7 +6745,7 @@ version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -6649,15 +6929,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-json-wasm" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" -dependencies = [ - "serde", -] - [[package]] name = "serde-untagged" version = "0.1.9" @@ -7039,21 +7310,11 @@ dependencies = [ "sha1", ] -[[package]] -name = "sp-allocator" -version = "29.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "log", - "parity-scale-codec", - "sp-wasm-interface-common", - "thiserror 1.0.69", -] - [[package]] name = "sp-api" version = "34.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbce492e0482134128b7729ea36f5ef1a9f9b4de2d48ff8dde7b5e464e28ce75" dependencies = [ "docify", "hash-db", @@ -7061,26 +7322,27 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api-proc-macro", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core", + "sp-externalities 0.29.0", "sp-metadata-ir", - "sp-runtime 39.0.1", - "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime", + "sp-runtime-interface", + "sp-state-machine", + "sp-trie", "sp-version", "thiserror 1.0.69", ] [[package]] name = "sp-api-proc-macro" -version = "20.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "20.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3ec5a1a14307e21d2356e73e01573b6c82330a7e30eaceed59a90161b0c2d2" dependencies = [ "Inflector", "blake2", "expander", - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -7095,34 +7357,8 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-io 38.0.2", -] - -[[package]] -name = "sp-application-crypto" -version = "38.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-io 38.0.0", -] - -[[package]] -name = "sp-arithmetic" -version = "26.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "docify", - "integer-sqrt", - "num-traits", - "parity-scale-codec", - "scale-info", - "serde", - "static_assertions", + "sp-core", + "sp-io", ] [[package]] @@ -7173,60 +7409,14 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime-interface 28.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ss58-registry", - "substrate-bip39 0.6.1", - "thiserror 1.0.69", - "tracing", - "w3f-bls", - "zeroize", -] - -[[package]] -name = "sp-core" -version = "34.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "array-bytes", - "bitflags 1.3.2", - "blake2", - "bounded-collections", - "bs58", - "dyn-clonable", - "ed25519-zebra", - "futures", - "hash-db", - "hash256-std-hasher", - "impl-serde 0.4.0", - "itertools 0.11.0", - "k256", - "libsecp256k1", - "log", - "merlin", - "parity-bip39", - "parity-scale-codec", - "parking_lot", - "paste", - "primitive-types 0.12.2", - "rand 0.8.6", - "scale-info", - "schnorrkel", - "secp256k1", - "secrecy", - "serde", - "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-crypto-hashing", + "sp-debug-derive 14.0.0", + "sp-externalities 0.29.0", + "sp-runtime-interface", + "sp-std 14.0.0", + "sp-storage 21.0.0", "ss58-registry", - "substrate-bip39 0.6.0", + "substrate-bip39", "thiserror 1.0.69", "tracing", "w3f-bls", @@ -7247,34 +7437,22 @@ dependencies = [ "twox-hash 1.6.3", ] -[[package]] -name = "sp-crypto-hashing" -version = "0.1.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "blake2b_simd", - "byteorder", - "digest 0.10.7", - "sha2 0.10.9", - "sha3", - "twox-hash 1.6.3", -] - [[package]] name = "sp-crypto-hashing-proc-macro" version = "0.1.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b85d0f1f1e44bd8617eb2a48203ee854981229e3e79e6f468c7175d5fd37489b" dependencies = [ "quote", - "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-crypto-hashing", "syn 2.0.117", ] [[package]] name = "sp-debug-derive" -version = "14.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" +checksum = "f12dae7cf6c1e825d13ffd4ce16bd9309db7c539929d0302b4443ed451a9f4e5" dependencies = [ "proc-macro2", "quote", @@ -7284,7 +7462,8 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2", "quote", @@ -7293,76 +7472,54 @@ dependencies = [ [[package]] name = "sp-externalities" -version = "0.29.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a904407d61cb94228c71b55a9d3708e9d6558991f9e83bd42bd91df37a159d30" +checksum = "3313e2c5f2523b06062e541dff9961bde88ad5a28861621dc7b7b47a32bb0f7c" dependencies = [ "environmental", "parity-scale-codec", - "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-std 9.0.0", + "sp-storage 14.0.0", ] [[package]] name = "sp-externalities" version = "0.29.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a904407d61cb94228c71b55a9d3708e9d6558991f9e83bd42bd91df37a159d30" dependencies = [ "environmental", "parity-scale-codec", - "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-storage 21.0.0", ] [[package]] name = "sp-genesis-builder" version = "0.15.1" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a646ed222fd86d5680faa4a8967980eb32f644cae6c8523e1c689a6deda3e8" dependencies = [ "parity-scale-codec", "scale-info", "serde_json", "sp-api", - "sp-runtime 39.0.1", + "sp-runtime", ] [[package]] name = "sp-inherents" version = "34.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afffbddc380d99a90c459ba1554bbbc01d62e892de9f1485af6940b89c4c0d57" dependencies = [ "async-trait", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.1", + "sp-runtime", "thiserror 1.0.69", ] -[[package]] -name = "sp-io" -version = "38.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "bytes", - "docify", - "ed25519-dalek", - "libsecp256k1", - "log", - "parity-scale-codec", - "polkavm-derive", - "rustversion", - "secp256k1", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-keystore 0.40.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-tracing 17.0.1", - "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "tracing", - "tracing-core", -] - [[package]] name = "sp-io" version = "38.0.2" @@ -7378,14 +7535,14 @@ dependencies = [ "polkavm-derive", "rustversion", "secp256k1", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-keystore 0.40.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime-interface 28.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-state-machine 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core", + "sp-crypto-hashing", + "sp-externalities 0.29.0", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", "sp-tracing 17.1.0", - "sp-trie 37.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-trie", "tracing", "tracing-core", ] @@ -7398,41 +7555,21 @@ checksum = "0248b4d784cb4a01472276928977121fa39d977a5bb24793b6b15e64b046df42" dependencies = [ "parity-scale-codec", "parking_lot", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sp-keystore" -version = "0.40.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "parity-scale-codec", - "parking_lot", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core", + "sp-externalities 0.29.0", ] [[package]] name = "sp-metadata-ir" version = "0.7.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a616fa51350b35326682a472ee8e6ba742fdacb18babac38ecd46b3e05ead869" dependencies = [ "frame-metadata 16.0.0", "parity-scale-codec", "scale-info", ] -[[package]] -name = "sp-panic-handler" -version = "13.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "backtrace", - "lazy_static", - "regex", -] - [[package]] name = "sp-panic-handler" version = "13.0.2" @@ -7443,32 +7580,6 @@ dependencies = [ "regex", ] -[[package]] -name = "sp-runtime" -version = "39.0.1" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "docify", - "either", - "hash256-std-hasher", - "impl-trait-for-tuples", - "log", - "num-traits", - "parity-scale-codec", - "paste", - "rand 0.8.6", - "scale-info", - "serde", - "simple-mermaid", - "sp-application-crypto 38.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-arithmetic 26.0.0", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-io 38.0.0", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-weights 31.0.0", - "tracing", -] - [[package]] name = "sp-runtime" version = "39.0.5" @@ -7487,12 +7598,12 @@ dependencies = [ "scale-info", "serde", "simple-mermaid", - "sp-application-crypto 38.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-arithmetic 26.1.0", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-io 38.0.2", - "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-weights 31.1.0", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std 14.0.0", + "sp-weights", "tracing", ] @@ -7507,31 +7618,12 @@ dependencies = [ "parity-scale-codec", "polkavm-derive", "primitive-types 0.12.2", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime-interface-proc-macro 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0", + "sp-runtime-interface-proc-macro", + "sp-std 14.0.0", + "sp-storage 21.0.0", "sp-tracing 17.1.0", - "sp-wasm-interface 21.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions", -] - -[[package]] -name = "sp-runtime-interface" -version = "28.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec", - "polkavm-derive", - "primitive-types 0.12.2", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-runtime-interface-proc-macro 18.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-tracing 17.0.1", - "sp-wasm-interface 21.0.1 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-wasm-interface", "static_assertions", ] @@ -7543,20 +7635,7 @@ checksum = "0195f32c628fee3ce1dfbbf2e7e52a30ea85f3589da9fe62a8b816d70fc06294" dependencies = [ "Inflector", "expander", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sp-runtime-interface-proc-macro" -version = "18.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "Inflector", - "expander", - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -7565,14 +7644,15 @@ dependencies = [ [[package]] name = "sp-staking" version = "36.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a73eedb4b85f4cd420d31764827546aa22f82ce1646d0fd258993d051de7a90" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-runtime 39.0.1", + "sp-core", + "sp-runtime", ] [[package]] @@ -7587,80 +7667,71 @@ dependencies = [ "parking_lot", "rand 0.8.6", "smallvec", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-panic-handler 13.0.2", - "sp-trie 37.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core", + "sp-externalities 0.29.0", + "sp-panic-handler", + "sp-trie", "thiserror 1.0.69", "tracing", "trie-db", ] [[package]] -name = "sp-state-machine" -version = "0.43.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "hash-db", - "log", - "parity-scale-codec", - "parking_lot", - "rand 0.8.6", - "smallvec", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-panic-handler 13.0.0", - "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "thiserror 1.0.69", - "tracing", - "trie-db", -] +name = "sp-std" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53458e3c57df53698b3401ec0934bea8e8cfce034816873c0b0abbd83d7bac0d" [[package]] name = "sp-std" -version = "14.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8ee986414b0a9ad741776762f4083cd3a5128449b982a3919c4df36874834" +checksum = "2d5bbc9339227d1b6a9b7ccd9b2920c818653d40eef1512f1e2e824d72e7a336" [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8ee986414b0a9ad741776762f4083cd3a5128449b982a3919c4df36874834" [[package]] name = "sp-storage" -version = "21.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c82989b3a4979a7e1ad848aad9f5d0b4388f1f454cc131766526601ab9e8f8" +checksum = "a21245c3a7799ff6d3f1f159b496f9ac72eb32cd6fe68c6f73013155289aa9f1" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-debug-derive 9.0.0", + "sp-std 9.0.0", ] [[package]] name = "sp-storage" version = "21.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99c82989b3a4979a7e1ad848aad9f5d0b4388f1f454cc131766526601ab9e8f8" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-debug-derive 14.0.0", ] [[package]] name = "sp-tracing" -version = "17.0.1" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357f7591980dd58305956d32f8f6646d0a8ea9ea0e7e868e46f53b68ddf00cec" dependencies = [ "parity-scale-codec", + "sp-std 8.0.0", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.2.25", ] [[package]] @@ -7672,7 +7743,7 @@ dependencies = [ "parity-scale-codec", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.3.23", ] [[package]] @@ -7691,31 +7762,8 @@ dependencies = [ "rand 0.8.6", "scale-info", "schnellru", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.69", - "tracing", - "trie-db", - "trie-root", -] - -[[package]] -name = "sp-trie" -version = "37.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "ahash", - "hash-db", - "lazy_static", - "memory-db", - "nohash-hasher", - "parity-scale-codec", - "parking_lot", - "rand 0.8.6", - "scale-info", - "schnellru", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core", + "sp-externalities 0.29.0", "thiserror 1.0.69", "tracing", "trie-db", @@ -7725,16 +7773,17 @@ dependencies = [ [[package]] name = "sp-version" version = "37.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d521a405707b5be561367cd3d442ff67588993de24062ce3adefcf8437ee9fe1" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", - "parity-wasm 0.45.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm", "scale-info", "serde", "sp-crypto-hashing-proc-macro", - "sp-runtime 39.0.1", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime", + "sp-std 14.0.0", "sp-version-proc-macro", "thiserror 1.0.69", ] @@ -7742,7 +7791,8 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "14.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aee8f6730641a65fcf0c8f9b1e448af4b3bb083d08058b47528188bccc7b7a7" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -7762,43 +7812,17 @@ dependencies = [ "parity-scale-codec", ] -[[package]] -name = "sp-wasm-interface" -version = "21.0.1" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "anyhow", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "sp-allocator", - "sp-wasm-interface-common", -] - [[package]] name = "sp-wasm-interface-common" -version = "7.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03223ee788e1490f6341eb5b76501f83abf931fa8b5d83b5f49a4fecaf83f4ae" dependencies = [ "parity-scale-codec", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 9.0.0", "wasmi 0.13.2", ] -[[package]] -name = "sp-weights" -version = "31.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "bounded-collections", - "parity-scale-codec", - "scale-info", - "serde", - "smallvec", - "sp-arithmetic 26.0.0", - "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", -] - [[package]] name = "sp-weights" version = "31.1.0" @@ -7810,8 +7834,8 @@ dependencies = [ "scale-info", "serde", "smallvec", - "sp-arithmetic 26.1.0", - "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-arithmetic", + "sp-debug-derive 14.0.0", ] [[package]] @@ -7898,18 +7922,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "substrate-bip39" -version = "0.6.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "hmac 0.12.1", - "pbkdf2", - "schnorrkel", - "sha2 0.10.9", - "zeroize", -] - [[package]] name = "substrate-bip39" version = "0.6.1" @@ -7951,7 +7963,7 @@ dependencies = [ "scale-value", "serde", "serde_json", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-crypto-hashing", "subxt-core", "subxt-lightclient", "subxt-macro", @@ -8007,7 +8019,7 @@ dependencies = [ "scale-value", "serde", "serde_json", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-crypto-hashing", "subxt-metadata", "thiserror 2.0.18", "tracing", @@ -8058,7 +8070,7 @@ dependencies = [ "hashbrown 0.14.5", "parity-scale-codec", "scale-info", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-crypto-hashing", "thiserror 2.0.18", ] @@ -8405,7 +8417,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ - "indexmap", + "indexmap 2.14.0", "serde_core", "serde_spanned 1.1.1", "toml_datetime 1.1.1+spec-1.1.0", @@ -8432,13 +8444,24 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap", + "indexmap 2.14.0", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -8452,7 +8475,7 @@ version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ - "indexmap", + "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", @@ -8540,6 +8563,17 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-log" version = "0.2.0" @@ -8551,23 +8585,55 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers 0.0.1", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log 0.1.4", + "tracing-serde", +] + [[package]] name = "tracing-subscriber" version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ - "matchers", + "matchers 0.2.0", "nu-ansi-term", "once_cell", - "regex-automata", + "regex-automata 0.4.14", "sharded-slab", "smallvec", "thread_local", "time", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.2.0", ] [[package]] @@ -8935,7 +9001,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.14.0", "wasm-encoder 0.244.0", "wasmparser 0.244.0", ] @@ -8951,7 +9017,7 @@ dependencies = [ "cfg-if", "cmake", "derive_more 2.1.1", - "indexmap", + "indexmap 2.14.0", "js-sys", "more-asserts", "paste", @@ -9056,7 +9122,7 @@ dependencies = [ "enumset", "getrandom 0.2.17", "hex", - "indexmap", + "indexmap 2.14.0", "more-asserts", "rkyv", "sha2 0.10.9", @@ -9079,7 +9145,7 @@ dependencies = [ "dashmap", "enum-iterator 0.7.0", "fnv", - "indexmap", + "indexmap 2.14.0", "libc", "libunwind", "mach2", @@ -9096,9 +9162,10 @@ dependencies = [ [[package]] name = "wasmi" version = "0.13.2" -source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" dependencies = [ - "parity-wasm 0.45.0 (git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext)", + "parity-wasm", "wasmi-validation", "wasmi_core 0.2.1", ] @@ -9138,9 +9205,10 @@ dependencies = [ [[package]] name = "wasmi-validation" version = "0.5.0" -source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" dependencies = [ - "parity-wasm 0.45.0 (git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext)", + "parity-wasm", ] [[package]] @@ -9163,7 +9231,8 @@ checksum = "e80d6b275b1c922021939d561574bf376613493ae2b61c6963b15db0e8813562" [[package]] name = "wasmi_core" version = "0.2.1" -source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" dependencies = [ "downcast-rs", "libm", @@ -9212,6 +9281,16 @@ dependencies = [ "wasmi_core 0.40.0", ] +[[package]] +name = "wasmparser" +version = "0.102.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" +dependencies = [ + "indexmap 1.9.3", + "url", +] + [[package]] name = "wasmparser" version = "0.221.3" @@ -9237,7 +9316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" dependencies = [ "bitflags 2.11.1", - "indexmap", + "indexmap 2.14.0", ] [[package]] @@ -9248,7 +9327,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.1", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.14.0", "semver", "serde", ] @@ -9262,6 +9341,138 @@ dependencies = [ "indexmap-nostd", ] +[[package]] +name = "wasmtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "object 0.30.4", + "once_cell", + "paste", + "psm", + "serde", + "target-lexicon", + "wasmparser 0.102.0", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-environ" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.27.3", + "indexmap 1.9.3", + "log", + "object 0.30.4", + "serde", + "target-lexicon", + "thiserror 1.0.69", + "wasmparser 0.102.0", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" +dependencies = [ + "addr2line 0.19.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.27.3", + "log", + "object 0.30.4", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" +dependencies = [ + "once_cell", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.8.0", + "paste", + "rand 0.8.6", + "rustix 0.36.17", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-types" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror 1.0.69", + "wasmparser 0.102.0", +] + [[package]] name = "web-time" version = "1.1.0" @@ -9411,6 +9622,15 @@ dependencies = [ "windows-targets 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -9453,6 +9673,21 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -9475,6 +9710,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -9487,6 +9728,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -9499,6 +9746,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -9517,6 +9770,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -9529,6 +9788,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -9541,6 +9806,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -9553,12 +9824,27 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.7.15" @@ -9611,7 +9897,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.14.0", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -9642,7 +9928,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.11.1", - "indexmap", + "indexmap 2.14.0", "log", "serde", "serde_derive", @@ -9661,7 +9947,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.14.0", "log", "semver", "serde", From bebd0a18ee4449745cad218e79be132e8f7a6d04 Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Wed, 20 May 2026 16:40:48 +0400 Subject: [PATCH 09/14] chore: drop experimental VFT account feature --- Cargo.lock | 1394 ++++++++++++++++------------------------- rs/Cargo.toml | 1 - rs/src/build.rs | 48 -- rs/storage/Cargo.toml | 3 - rs/storage/src/lib.rs | 758 ---------------------- 5 files changed, 554 insertions(+), 1650 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5cecab7ba..c1d61830e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,19 +15,9 @@ dependencies = [ [[package]] name = "actor-system-error" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8862bc579cb6407ed5ff150781881003f9a09b724fa70ddbde1572a0ff87fd0" dependencies = [ "derive_more 2.1.1", -] - -[[package]] -name = "addr2line" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" -dependencies = [ - "gimli 0.27.3", + "gear-workspace-hack", ] [[package]] @@ -152,7 +142,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap 2.14.0", + "indexmap", "proc-macro-error2", "proc-macro2", "quote", @@ -196,15 +186,6 @@ dependencies = [ "libc", ] -[[package]] -name = "ansi_term" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" -dependencies = [ - "winapi", -] - [[package]] name = "anstream" version = "1.0.0" @@ -284,15 +265,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "ar_archive_writer" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" -dependencies = [ - "object 0.37.3", -] - [[package]] name = "ark-bls12-377" version = "0.4.0" @@ -684,7 +656,7 @@ version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ - "addr2line 0.25.1", + "addr2line", "cfg-if", "libc", "miniz_oxide", @@ -744,15 +716,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bindgen" version = "0.70.1" @@ -938,13 +901,12 @@ dependencies = [ [[package]] name = "builtins-common" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13a54b46c994eee08098e021332099c898037316c0776f9f09885a9dc37a3f5c" dependencies = [ "derive_more 2.1.1", "gbuiltin-bls381", "gbuiltin-eth-bridge", "gear-core", + "gear-workspace-hack", "gprimitives", "log", "parity-scale-codec", @@ -1449,24 +1411,6 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" -[[package]] -name = "core-processor" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c6661d52b0cc6d2157640dffe0298e42a9babb38d4629198939b70b7a23aaa" -dependencies = [ - "actor-system-error", - "derive_more 2.1.1", - "gear-core", - "gear-core-backend", - "gear-core-errors", - "gear-lazy-pages-common", - "gear-wasm-instrument", - "gsys", - "log", - "parity-scale-codec", -] - [[package]] name = "corosensei" version = "0.3.3" @@ -1487,15 +1431,6 @@ dependencies = [ "sails-rs", ] -[[package]] -name = "cpp_demangle" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" -dependencies = [ - "cfg-if", -] - [[package]] name = "cpufeatures" version = "0.2.17" @@ -1514,15 +1449,6 @@ dependencies = [ "libc", ] -[[package]] -name = "cranelift-entity" -version = "0.95.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" -dependencies = [ - "serde", -] - [[package]] name = "crc32fast" version = "1.5.0" @@ -2325,12 +2251,6 @@ dependencies = [ "rand 0.8.6", ] -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - [[package]] name = "fallible-iterator" version = "0.3.0" @@ -2452,9 +2372,8 @@ dependencies = [ [[package]] name = "frame-benchmarking" -version = "38.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a0f983d69640f90a0ce87f107cff07f6f8f7f5ef9334ffb6f37a9c6e224ca1d" +version = "38.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "frame-support", "frame-support-procedural", @@ -2466,12 +2385,12 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto", - "sp-core", - "sp-io", - "sp-runtime", - "sp-runtime-interface", - "sp-storage 21.0.0", + "sp-application-crypto 38.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-io 38.0.0", + "sp-runtime 39.0.1", + "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "static_assertions", ] @@ -2487,7 +2406,7 @@ dependencies = [ "scale-encode", "scale-info", "scale-type-resolver", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.18", ] @@ -2517,9 +2436,8 @@ dependencies = [ [[package]] name = "frame-support" -version = "38.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7dd8b9f161a8289e3b9fe6c1068519358dbff2270d38097a923d3d1b4459dca" +version = "38.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "aquamarine", "array-bytes", @@ -2536,32 +2454,31 @@ dependencies = [ "paste", "scale-info", "serde", - "serde_json", + "serde-json-wasm", "smallvec", "sp-api", - "sp-arithmetic", - "sp-core", + "sp-arithmetic 26.0.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-crypto-hashing-proc-macro", - "sp-debug-derive 14.0.0", + "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-genesis-builder", "sp-inherents", - "sp-io", + "sp-io 38.0.0", "sp-metadata-ir", - "sp-runtime", + "sp-runtime 39.0.1", "sp-staking", - "sp-state-machine", - "sp-std 14.0.0", - "sp-tracing 17.1.0", - "sp-weights", + "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-tracing 17.0.1", + "sp-weights 31.0.0", "static_assertions", "tt-call", ] [[package]] name = "frame-support-procedural" -version = "30.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8da784d943f2a945be923ab081a7c0837355b38045c50945d7ec1a138e2f3c52" +version = "30.0.3" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "Inflector", "cfg-expr", @@ -2574,18 +2491,17 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "syn 2.0.117", ] [[package]] name = "frame-support-procedural-tools" -version = "13.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81a088fd6fda5f53ff0c17fc7551ce8bd0ead14ba742228443c8196296a7369b" +version = "13.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "frame-support-procedural-tools-derive", - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -2594,8 +2510,7 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed971c6435503a099bdac99fe4c5bea08981709e5b5a0a8535a1856f48561191" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "proc-macro2", "quote", @@ -2605,8 +2520,7 @@ dependencies = [ [[package]] name = "frame-system" version = "38.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c7fa02f8c305496d2ae52edaecdb9d165f11afa965e05686d7d7dd1ce93611" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "cfg-if", "docify", @@ -2615,12 +2529,12 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std 14.0.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-io 38.0.0", + "sp-runtime 39.0.1", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-version", - "sp-weights", + "sp-weights 31.0.0", ] [[package]] @@ -2642,16 +2556,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "fs4" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e871a4cfa68bb224863b53149d973df1ac8d1ed2fa1d1bfc37ac1bb65dd37207" -dependencies = [ - "rustix 0.38.44", - "windows-sys 0.52.0", -] - [[package]] name = "funty" version = "2.0.0" @@ -2768,23 +2672,21 @@ dependencies = [ [[package]] name = "galloc" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d95f9357f55c811d9b1f5c7bb1ef3b3f0094dee912a2bdb5155eb46611d8f87" dependencies = [ "gear-dlmalloc", + "gear-workspace-hack", ] [[package]] name = "gbuiltin-bls381" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "919a1d0d8397053524bd9785788afb5a4dedc5c50e72650dae6857df2409a3f1" dependencies = [ "ark-bls12-381", "ark-ec", "ark-ff", "ark-scale", "ark-serialize", + "gear-workspace-hack", "parity-scale-codec", "scale-info", ] @@ -2792,9 +2694,8 @@ dependencies = [ [[package]] name = "gbuiltin-eth-bridge" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ea714934fa347452f0b33afa8a9afcdaa4079ea2fc2d7ee46ecdd03681ed72" dependencies = [ + "gear-workspace-hack", "gprimitives", "parity-scale-codec", "scale-info", @@ -2811,11 +2712,11 @@ dependencies = [ "futures", "gear-core", "gear-node-wrapper", - "gear-utils", + "gear-utils 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "gsdk", "hex", "parity-scale-codec", - "sp-core", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.18", "url", ] @@ -2823,12 +2724,11 @@ dependencies = [ [[package]] name = "gcore" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "015093eb24aa69f98f6474a9547a51a1a9230204e55a1778e9c8588cd8ee6047" dependencies = [ "arrayvec 0.7.6", "gear-core-errors", "gear-stack-buffer", + "gear-workspace-hack", "gprimitives", "gsys", "paste", @@ -2838,8 +2738,6 @@ dependencies = [ [[package]] name = "gear-common" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6802013b852310c7a9e1e4e48e6bf56a877c4d746783ab3b93e521c7d70225f3" dependencies = [ "enum-iterator 1.5.0", "fail", @@ -2849,24 +2747,24 @@ dependencies = [ "gear-common-codegen", "gear-core", "gear-wasm-instrument", + "gear-workspace-hack", "gsys", "log", "primitive-types 0.12.2", "scale-decode", "scale-encode", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-runtime", - "sp-std 14.0.0", + "sp-arithmetic 26.0.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-io 38.0.0", + "sp-runtime 39.0.1", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", ] [[package]] name = "gear-common-codegen" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ec789cbf723ef6bcb29552cbbe72bd9a1f46a07feaab5a0647a2f42480cf8d" dependencies = [ + "gear-workspace-hack", "quote", "syn 2.0.117", ] @@ -2874,8 +2772,6 @@ dependencies = [ [[package]] name = "gear-core" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7172ef6fd588e780ed2d9f8e99f6cbc2c65a19e4fd61b4686cc5a74c04ab07b" dependencies = [ "blake2", "bytemuck", @@ -2884,6 +2780,7 @@ dependencies = [ "enum-iterator 1.5.0", "gear-core-errors", "gear-wasm-instrument", + "gear-workspace-hack", "gprimitives", "gsys", "hex", @@ -2903,8 +2800,6 @@ dependencies = [ [[package]] name = "gear-core-backend" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4af189ad011d19bf4a42acbfe946b267ef2224b74f9f87778387a2a545495c78" dependencies = [ "actor-system-error", "blake2", @@ -2916,6 +2811,7 @@ dependencies = [ "gear-sandbox", "gear-sandbox-env", "gear-wasm-instrument", + "gear-workspace-hack", "gsys", "log", "parity-scale-codec", @@ -2924,10 +2820,9 @@ dependencies = [ [[package]] name = "gear-core-errors" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a068b7e99afbedd710b959a20c8ac91b2cc77f8a0ce87af687b673bfbf26a809" dependencies = [ "enum-iterator 1.5.0", + "gear-workspace-hack", "parity-scale-codec", "scale-decode", "scale-encode", @@ -2936,6 +2831,23 @@ dependencies = [ "thiserror 2.0.18", ] +[[package]] +name = "gear-core-processor" +version = "1.10.0" +dependencies = [ + "actor-system-error", + "derive_more 2.1.1", + "gear-core", + "gear-core-backend", + "gear-core-errors", + "gear-lazy-pages-common", + "gear-wasm-instrument", + "gear-workspace-hack", + "gsys", + "log", + "parity-scale-codec", +] + [[package]] name = "gear-dlmalloc" version = "0.2.0" @@ -2952,8 +2864,6 @@ dependencies = [ [[package]] name = "gear-lazy-pages" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e94788c25494ddb5ca7a73b71b61429990caba31dea7e3b55e0dfe33ae3634c" dependencies = [ "cfg-if", "derive_more 2.1.1", @@ -2961,6 +2871,7 @@ dependencies = [ "gear-core", "gear-lazy-pages-common", "gear-sandbox-host", + "gear-workspace-hack", "libc", "log", "mach", @@ -2975,10 +2886,9 @@ dependencies = [ [[package]] name = "gear-lazy-pages-common" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1bd7e4e3d11c037298b2908c6ea1f3061a90bc98dbca188adf147599a88a96" dependencies = [ "gear-core", + "gear-workspace-hack", "num_enum", "parity-scale-codec", ] @@ -2986,12 +2896,11 @@ dependencies = [ [[package]] name = "gear-lazy-pages-native-interface" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "affb988d8baa32dced50d696046e9a3470602abf3effe0fe06bb546dd05581d0" dependencies = [ "gear-core", "gear-lazy-pages", "gear-lazy-pages-common", + "gear-workspace-hack", "log", ] @@ -3010,17 +2919,16 @@ dependencies = [ [[package]] name = "gear-sandbox" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4ca22d8209f2c3077c0c97063e0804bf4e1c5b60a4a2e6200da664311d1920c" dependencies = [ "gear-sandbox-env", "gear-sandbox-interface", "gear-wasmer-cache", + "gear-workspace-hack", "log", "parity-scale-codec", "pathdiff", - "sp-core", - "sp-std 14.0.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-wasm-interface-common", "wasmer", "wasmer-compiler", @@ -3031,30 +2939,28 @@ dependencies = [ [[package]] name = "gear-sandbox-env" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6570e31af2bd208e6ce48d6a5e86c62815e57465df3f812670f40021e6a6a38b" dependencies = [ + "gear-workspace-hack", "parity-scale-codec", - "sp-debug-derive 14.0.0", - "sp-std 14.0.0", + "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-wasm-interface-common", ] [[package]] name = "gear-sandbox-host" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c08257ac1e9ba954883fd229254fa54abc150cdc0c62380905f22d335c46c44" dependencies = [ "atomic_enum", "defer", "environmental", "gear-sandbox-env", "gear-wasmer-cache", - "gp-allocator", + "gear-workspace-hack", "log", "parity-scale-codec", "region", + "sp-allocator", "sp-wasm-interface-common", "tempfile", "thiserror 2.0.18", @@ -3066,32 +2972,45 @@ dependencies = [ [[package]] name = "gear-sandbox-interface" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da9b75e5d27247deff3569a5cfa246e8e8289ea021fd16cc8a80b784eb85ff13" dependencies = [ "gear-sandbox-host", - "gp-runtime-interface", - "gp-wasm-interface", + "gear-workspace-hack", "log", - "parity-scale-codec", + "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-wasm-interface 21.0.1 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", ] [[package]] name = "gear-ss58" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94745919b16a98a5b8d7b7766bcb9127eb8bf013eff098ad828e452744bee6e6" dependencies = [ "blake2", "bs58", + "gear-workspace-hack", "hex", ] [[package]] name = "gear-stack-buffer" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51d30f39766a97c23c841c55e868d93e01e146ace585aa36b291b4c46b9d0c15" +dependencies = [ + "gear-workspace-hack", +] + +[[package]] +name = "gear-utils" +version = "1.10.0" +dependencies = [ + "gear-core", + "gear-workspace-hack", + "hex", + "nonempty", + "parity-scale-codec", + "path-clean", + "serde", + "serde_json", + "tracing-subscriber", +] [[package]] name = "gear-utils" @@ -3106,14 +3025,12 @@ dependencies = [ "path-clean", "serde", "serde_json", - "tracing-subscriber 0.3.23", + "tracing-subscriber", ] [[package]] name = "gear-wasm-builder" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bed5dd34648a27e7dc217dc3b00eea4282bd451f4cf76bf627a05c5dbcc84f9" dependencies = [ "anyhow", "cargo_metadata 0.20.0", @@ -3122,6 +3039,7 @@ dependencies = [ "gear-core", "gear-wasm-instrument", "gear-wasm-optimizer", + "gear-workspace-hack", "itertools 0.13.0", "log", "pathdiff", @@ -3133,11 +3051,10 @@ dependencies = [ [[package]] name = "gear-wasm-instrument" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf819c76e5002586f7778e4acd10b775bd797e2baece1c31fa1b95a630eec238" dependencies = [ "derive_more 2.1.1", "enum-iterator 1.5.0", + "gear-workspace-hack", "log", "wasm-encoder 0.230.0", "wasmparser 0.230.0", @@ -3146,12 +3063,11 @@ dependencies = [ [[package]] name = "gear-wasm-optimizer" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed336b47eecdcee1c91dbedb8e6f6f702734f405afae68fdca3f1ddea4c3669d" dependencies = [ "anyhow", "colored", "gear-wasm-instrument", + "gear-workspace-hack", "log", "regex", "rustc_version", @@ -3162,18 +3078,22 @@ dependencies = [ [[package]] name = "gear-wasmer-cache" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1715238463dc4a8436e81bef5faadf28d2ad9f7320ef15e0f752da9568a71c48" dependencies = [ "bytes", "derive_more 2.1.1", - "fs4", + "gear-workspace-hack", "log", "uluru", "wasmer", "wasmer-cache", ] +[[package]] +name = "gear-workspace-hack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ac67118eef8a153d4df06bd865d0a5c3ac51888c238673a203a5c424db0327" + [[package]] name = "genco" version = "0.19.0" @@ -3255,25 +3175,14 @@ dependencies = [ "rand_core 0.6.4", ] -[[package]] -name = "gimli" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" -dependencies = [ - "fallible-iterator 0.2.0", - "indexmap 1.9.3", - "stable_deref_trait", -] - [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" dependencies = [ - "fallible-iterator 0.3.0", - "indexmap 2.14.0", + "fallible-iterator", + "indexmap", "stable_deref_trait", ] @@ -3300,77 +3209,16 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "gp-allocator" -version = "4.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93cfb4525b5362966a1971125ff8f9825e6a511403635dccf883273d20c43bdf" -dependencies = [ - "log", - "parity-scale-codec", - "sp-wasm-interface-common", - "thiserror 1.0.69", -] - -[[package]] -name = "gp-runtime-interface" -version = "18.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dfb3786bf8fe51099c67dfe7c9a925fe0bfb78ffe938aa07b598995ee48d0e3" -dependencies = [ - "bytes", - "gp-runtime-interface-proc-macro", - "gp-wasm-interface", - "impl-trait-for-tuples", - "parity-scale-codec", - "primitive-types 0.12.2", - "sp-externalities 0.20.0", - "sp-std 9.0.0", - "sp-storage 14.0.0", - "sp-tracing 10.0.0", - "static_assertions", -] - -[[package]] -name = "gp-runtime-interface-proc-macro" -version = "12.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1e4d907f86db64e8b9d9af0b5adfd59e62b4023fd88511e13d62028ed101f2c" -dependencies = [ - "Inflector", - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "gp-wasm-interface" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de334e40147f969276463ad04d0ddfe519aebb6254832ea0637a300c6d637c11" -dependencies = [ - "anyhow", - "gp-allocator", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "sp-std 9.0.0", - "sp-wasm-interface-common", - "wasmtime", -] - [[package]] name = "gprimitives" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f79911d3caeb051ae07b0d4b57c196fdc6e105f7be7cc51365416d7248ca5ce" dependencies = [ "alloy-primitives", "alloy-sol-types", "bytemuck", "derive_more 2.1.1", "gear-ss58", + "gear-workspace-hack", "hex", "primitive-types 0.12.2", "scale-decode", @@ -3404,14 +3252,14 @@ dependencies = [ "gear-core-errors", "gsdk-codegen", "hex", - "indexmap 2.14.0", + "indexmap", "jsonrpsee", "log", "parity-scale-codec", "parking_lot", "serde", - "sp-core", - "sp-runtime", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime 39.0.5", "subxt", "thiserror 2.0.18", "url", @@ -3431,8 +3279,6 @@ dependencies = [ [[package]] name = "gstd" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "232fec5b812a76c016c11faceef031fbe7608bb4db07051744201a2dabff1072" dependencies = [ "arrayvec 0.7.6", "const_format", @@ -3440,6 +3286,7 @@ dependencies = [ "galloc", "gcore", "gear-core-errors", + "gear-workspace-hack", "gprimitives", "gstd-codegen", "hashbrown 0.14.5", @@ -3453,9 +3300,8 @@ dependencies = [ [[package]] name = "gstd-codegen" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b347d845d9fdc17b47288d082fd7001390d0652acb5d221c29a1b3f16e9a238" dependencies = [ + "gear-workspace-hack", "gprimitives", "proc-macro2", "quote", @@ -3465,30 +3311,28 @@ dependencies = [ [[package]] name = "gsys" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31b08a4ef9c0f6aed528b42117cea34f53af2a4a7f080c04b4356eb63adc9302" dependencies = [ "bytemuck", + "gear-workspace-hack", ] [[package]] name = "gtest" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cba01a0d56d5ff4a072ce3dfc142ecba271f097435985fe623a8b69918e785c" dependencies = [ "builtins-common", "cargo_toml", "colored", - "core-processor", "etc", "gear-common", "gear-core", "gear-core-errors", + "gear-core-processor", "gear-lazy-pages", "gear-lazy-pages-common", "gear-lazy-pages-native-interface", - "gear-utils", + "gear-utils 1.10.0", + "gear-workspace-hack", "gprimitives", "gsys", "log", @@ -3497,7 +3341,7 @@ dependencies = [ "rand 0.8.6", "scale-info", "thiserror 2.0.18", - "tracing-subscriber 0.3.23", + "tracing-subscriber", ] [[package]] @@ -3512,7 +3356,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap 2.14.0", + "indexmap", "slab", "tokio", "tokio-util", @@ -3550,12 +3394,6 @@ dependencies = [ "crunchy", ] -[[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - [[package]] name = "hashbrown" version = "0.13.2" @@ -3615,12 +3453,6 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" -[[package]] -name = "hermit-abi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" - [[package]] name = "hermit-abi" version = "0.5.2" @@ -3994,17 +3826,6 @@ dependencies = [ "quote", ] -[[package]] -name = "indexmap" -version = "1.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" -dependencies = [ - "autocfg", - "hashbrown 0.12.3", - "serde", -] - [[package]] name = "indexmap" version = "2.14.0" @@ -4072,24 +3893,13 @@ dependencies = [ "num-traits", ] -[[package]] -name = "io-lifetimes" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" -dependencies = [ - "hermit-abi 0.3.9", - "libc", - "windows-sys 0.48.0", -] - [[package]] name = "is-terminal" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ - "hermit-abi 0.5.2", + "hermit-abi", "libc", "windows-sys 0.61.2", ] @@ -4375,7 +4185,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax 0.8.10", + "regex-syntax", "string_cache", "term", "tiny-keccak", @@ -4389,7 +4199,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata 0.4.14", + "regex-automata", ] [[package]] @@ -4516,12 +4326,6 @@ dependencies = [ "nalgebra", ] -[[package]] -name = "linux-raw-sys" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" - [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -4573,8 +4377,8 @@ dependencies = [ "fnv", "proc-macro2", "quote", - "regex-automata 0.4.14", - "regex-syntax 0.8.10", + "regex-automata", + "regex-syntax", "syn 2.0.117", ] @@ -4684,22 +4488,13 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "matchers" -version = "0.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" -dependencies = [ - "regex-automata 0.1.10", -] - [[package]] name = "matchers" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.4.14", + "regex-automata", ] [[package]] @@ -4718,15 +4513,6 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" -[[package]] -name = "memfd" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" -dependencies = [ - "rustix 1.1.4", -] - [[package]] name = "memmap2" version = "0.6.2" @@ -4754,15 +4540,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "memoffset" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" -dependencies = [ - "autocfg", -] - [[package]] name = "memoffset" version = "0.9.1" @@ -5083,26 +4860,13 @@ dependencies = [ [[package]] name = "numerated" version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb9cd51526a9244bccb59b31a21af8fabc6a70a23707ff0e9095abda2a5033a" dependencies = [ "derive_more 2.1.1", + "gear-workspace-hack", "num-traits", "scale-info", ] -[[package]] -name = "object" -version = "0.30.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" -dependencies = [ - "crc32fast", - "hashbrown 0.13.2", - "indexmap 1.9.3", - "memchr", -] - [[package]] name = "object" version = "0.32.2" @@ -5112,7 +4876,7 @@ dependencies = [ "crc32fast", "flate2", "hashbrown 0.14.5", - "indexmap 2.14.0", + "indexmap", "memchr", "ruzstd 0.5.0", ] @@ -5215,7 +4979,7 @@ version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -5227,6 +4991,11 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" +[[package]] +name = "parity-wasm" +version = "0.45.0" +source = "git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext#bad3e1ec78f655f3eec5a0c9c12a3546c8c9d432" + [[package]] name = "parking" version = "2.2.1" @@ -5377,7 +5146,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap 2.14.0", + "indexmap", ] [[package]] @@ -5517,7 +5286,7 @@ checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi 0.5.2", + "hermit-abi", "pin-project-lite", "rustix 1.1.4", "windows-sys 0.61.2", @@ -5628,19 +5397,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "1.3.1" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ "toml_edit 0.25.11+spec-1.1.0", ] @@ -5734,16 +5493,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "psm" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" -dependencies = [ - "ar_archive_writer", - "cc", -] - [[package]] name = "ptr_meta" version = "0.1.4" @@ -6011,17 +5760,8 @@ checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.14", - "regex-syntax 0.8.10", -] - -[[package]] -name = "regex-automata" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", + "regex-automata", + "regex-syntax", ] [[package]] @@ -6032,15 +5772,9 @@ checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.10", + "regex-syntax", ] -[[package]] -name = "regex-syntax" -version = "0.6.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" - [[package]] name = "regex-syntax" version = "0.8.10" @@ -6107,7 +5841,7 @@ dependencies = [ "bytecheck 0.8.2", "bytes", "hashbrown 0.17.0", - "indexmap 2.14.0", + "indexmap", "munge", "ptr_meta 0.3.1", "rancor", @@ -6230,20 +5964,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "rustix" -version = "0.36.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" -dependencies = [ - "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", - "linux-raw-sys 0.1.4", - "windows-sys 0.45.0", -] - [[package]] name = "rustix" version = "0.38.44" @@ -6558,7 +6278,7 @@ dependencies = [ name = "sails-reflect-hash-derive" version = "1.0.0-beta.5" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -6643,7 +6363,7 @@ version = "1.0.0-beta.5" dependencies = [ "insta", "prettyplease", - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -6719,7 +6439,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17020f2d59baabf2ddcdc20a4e567f8210baf089b8a8d4785f5fd5e716f92038" dependencies = [ "darling 0.20.11", - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -6745,7 +6465,7 @@ version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -6929,6 +6649,15 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-json-wasm" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" +dependencies = [ + "serde", +] + [[package]] name = "serde-untagged" version = "0.1.9" @@ -7310,11 +7039,21 @@ dependencies = [ "sha1", ] +[[package]] +name = "sp-allocator" +version = "29.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "log", + "parity-scale-codec", + "sp-wasm-interface-common", + "thiserror 1.0.69", +] + [[package]] name = "sp-api" version = "34.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbce492e0482134128b7729ea36f5ef1a9f9b4de2d48ff8dde7b5e464e28ce75" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "docify", "hash-db", @@ -7322,27 +7061,26 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api-proc-macro", - "sp-core", - "sp-externalities 0.29.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-metadata-ir", - "sp-runtime", - "sp-runtime-interface", - "sp-state-machine", - "sp-trie", + "sp-runtime 39.0.1", + "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-version", "thiserror 1.0.69", ] [[package]] name = "sp-api-proc-macro" -version = "20.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e3ec5a1a14307e21d2356e73e01573b6c82330a7e30eaceed59a90161b0c2d2" +version = "20.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "Inflector", "blake2", "expander", - "proc-macro-crate 3.5.0", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -7357,8 +7095,34 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-io", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-io 38.0.2", +] + +[[package]] +name = "sp-application-crypto" +version = "38.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "parity-scale-codec", + "scale-info", + "serde", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-io 38.0.0", +] + +[[package]] +name = "sp-arithmetic" +version = "26.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "docify", + "integer-sqrt", + "num-traits", + "parity-scale-codec", + "scale-info", + "serde", + "static_assertions", ] [[package]] @@ -7409,14 +7173,60 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-crypto-hashing", - "sp-debug-derive 14.0.0", - "sp-externalities 0.29.0", - "sp-runtime-interface", - "sp-std 14.0.0", - "sp-storage 21.0.0", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime-interface 28.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "ss58-registry", + "substrate-bip39 0.6.1", + "thiserror 1.0.69", + "tracing", + "w3f-bls", + "zeroize", +] + +[[package]] +name = "sp-core" +version = "34.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "array-bytes", + "bitflags 1.3.2", + "blake2", + "bounded-collections", + "bs58", + "dyn-clonable", + "ed25519-zebra", + "futures", + "hash-db", + "hash256-std-hasher", + "impl-serde 0.4.0", + "itertools 0.11.0", + "k256", + "libsecp256k1", + "log", + "merlin", + "parity-bip39", + "parity-scale-codec", + "parking_lot", + "paste", + "primitive-types 0.12.2", + "rand 0.8.6", + "scale-info", + "schnorrkel", + "secp256k1", + "secrecy", + "serde", + "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "ss58-registry", - "substrate-bip39", + "substrate-bip39 0.6.0", "thiserror 1.0.69", "tracing", "w3f-bls", @@ -7437,22 +7247,34 @@ dependencies = [ "twox-hash 1.6.3", ] +[[package]] +name = "sp-crypto-hashing" +version = "0.1.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "blake2b_simd", + "byteorder", + "digest 0.10.7", + "sha2 0.10.9", + "sha3", + "twox-hash 1.6.3", +] + [[package]] name = "sp-crypto-hashing-proc-macro" version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b85d0f1f1e44bd8617eb2a48203ee854981229e3e79e6f468c7175d5fd37489b" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "quote", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "syn 2.0.117", ] [[package]] name = "sp-debug-derive" -version = "9.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12dae7cf6c1e825d13ffd4ce16bd9309db7c539929d0302b4443ed451a9f4e5" +checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2", "quote", @@ -7462,8 +7284,7 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "proc-macro2", "quote", @@ -7472,54 +7293,76 @@ dependencies = [ [[package]] name = "sp-externalities" -version = "0.20.0" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3313e2c5f2523b06062e541dff9961bde88ad5a28861621dc7b7b47a32bb0f7c" +checksum = "a904407d61cb94228c71b55a9d3708e9d6558991f9e83bd42bd91df37a159d30" dependencies = [ "environmental", "parity-scale-codec", - "sp-std 9.0.0", - "sp-storage 14.0.0", + "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-externalities" version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a904407d61cb94228c71b55a9d3708e9d6558991f9e83bd42bd91df37a159d30" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "environmental", "parity-scale-codec", - "sp-storage 21.0.0", + "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", ] [[package]] name = "sp-genesis-builder" version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a646ed222fd86d5680faa4a8967980eb32f644cae6c8523e1c689a6deda3e8" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "parity-scale-codec", "scale-info", "serde_json", "sp-api", - "sp-runtime", + "sp-runtime 39.0.1", ] [[package]] name = "sp-inherents" version = "34.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afffbddc380d99a90c459ba1554bbbc01d62e892de9f1485af6940b89c4c0d57" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "async-trait", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-runtime", + "sp-runtime 39.0.1", "thiserror 1.0.69", ] +[[package]] +name = "sp-io" +version = "38.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "bytes", + "docify", + "ed25519-dalek", + "libsecp256k1", + "log", + "parity-scale-codec", + "polkavm-derive", + "rustversion", + "secp256k1", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-keystore 0.40.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-tracing 17.0.1", + "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "tracing", + "tracing-core", +] + [[package]] name = "sp-io" version = "38.0.2" @@ -7535,14 +7378,14 @@ dependencies = [ "polkavm-derive", "rustversion", "secp256k1", - "sp-core", - "sp-crypto-hashing", - "sp-externalities 0.29.0", - "sp-keystore", - "sp-runtime-interface", - "sp-state-machine", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-keystore 0.40.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime-interface 28.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-state-machine 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-tracing 17.1.0", - "sp-trie", + "sp-trie 37.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "tracing", "tracing-core", ] @@ -7555,21 +7398,41 @@ checksum = "0248b4d784cb4a01472276928977121fa39d977a5bb24793b6b15e64b046df42" dependencies = [ "parity-scale-codec", "parking_lot", - "sp-core", - "sp-externalities 0.29.0", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "sp-keystore" +version = "0.40.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "parity-scale-codec", + "parking_lot", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", ] [[package]] name = "sp-metadata-ir" version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a616fa51350b35326682a472ee8e6ba742fdacb18babac38ecd46b3e05ead869" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "frame-metadata 16.0.0", "parity-scale-codec", "scale-info", ] +[[package]] +name = "sp-panic-handler" +version = "13.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "backtrace", + "lazy_static", + "regex", +] + [[package]] name = "sp-panic-handler" version = "13.0.2" @@ -7580,6 +7443,32 @@ dependencies = [ "regex", ] +[[package]] +name = "sp-runtime" +version = "39.0.1" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "docify", + "either", + "hash256-std-hasher", + "impl-trait-for-tuples", + "log", + "num-traits", + "parity-scale-codec", + "paste", + "rand 0.8.6", + "scale-info", + "serde", + "simple-mermaid", + "sp-application-crypto 38.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-arithmetic 26.0.0", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-io 38.0.0", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-weights 31.0.0", + "tracing", +] + [[package]] name = "sp-runtime" version = "39.0.5" @@ -7598,12 +7487,12 @@ dependencies = [ "scale-info", "serde", "simple-mermaid", - "sp-application-crypto", - "sp-arithmetic", - "sp-core", - "sp-io", - "sp-std 14.0.0", - "sp-weights", + "sp-application-crypto 38.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-arithmetic 26.1.0", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-io 38.0.2", + "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-weights 31.1.0", "tracing", ] @@ -7618,12 +7507,31 @@ dependencies = [ "parity-scale-codec", "polkavm-derive", "primitive-types 0.12.2", - "sp-externalities 0.29.0", - "sp-runtime-interface-proc-macro", - "sp-std 14.0.0", - "sp-storage 21.0.0", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-runtime-interface-proc-macro 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "sp-tracing 17.1.0", - "sp-wasm-interface", + "sp-wasm-interface 21.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "static_assertions", +] + +[[package]] +name = "sp-runtime-interface" +version = "28.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "bytes", + "impl-trait-for-tuples", + "parity-scale-codec", + "polkavm-derive", + "primitive-types 0.12.2", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime-interface-proc-macro 18.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-tracing 17.0.1", + "sp-wasm-interface 21.0.1 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "static_assertions", ] @@ -7635,7 +7543,20 @@ checksum = "0195f32c628fee3ce1dfbbf2e7e52a30ea85f3589da9fe62a8b816d70fc06294" dependencies = [ "Inflector", "expander", - "proc-macro-crate 3.5.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "sp-runtime-interface-proc-macro" +version = "18.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "Inflector", + "expander", + "proc-macro-crate", "proc-macro2", "quote", "syn 2.0.117", @@ -7644,15 +7565,14 @@ dependencies = [ [[package]] name = "sp-staking" version = "36.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a73eedb4b85f4cd420d31764827546aa22f82ce1646d0fd258993d051de7a90" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", - "sp-core", - "sp-runtime", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime 39.0.1", ] [[package]] @@ -7667,71 +7587,80 @@ dependencies = [ "parking_lot", "rand 0.8.6", "smallvec", - "sp-core", - "sp-externalities 0.29.0", - "sp-panic-handler", - "sp-trie", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-panic-handler 13.0.2", + "sp-trie 37.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 1.0.69", "tracing", "trie-db", ] [[package]] -name = "sp-std" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53458e3c57df53698b3401ec0934bea8e8cfce034816873c0b0abbd83d7bac0d" +name = "sp-state-machine" +version = "0.43.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "hash-db", + "log", + "parity-scale-codec", + "parking_lot", + "rand 0.8.6", + "smallvec", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-panic-handler 13.0.0", + "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "thiserror 1.0.69", + "tracing", + "trie-db", +] [[package]] name = "sp-std" -version = "9.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5bbc9339227d1b6a9b7ccd9b2920c818653d40eef1512f1e2e824d72e7a336" +checksum = "12f8ee986414b0a9ad741776762f4083cd3a5128449b982a3919c4df36874834" [[package]] name = "sp-std" version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8ee986414b0a9ad741776762f4083cd3a5128449b982a3919c4df36874834" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" [[package]] name = "sp-storage" -version = "14.0.0" +version = "21.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21245c3a7799ff6d3f1f159b496f9ac72eb32cd6fe68c6f73013155289aa9f1" +checksum = "99c82989b3a4979a7e1ad848aad9f5d0b4388f1f454cc131766526601ab9e8f8" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 9.0.0", - "sp-std 9.0.0", + "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "sp-storage" version = "21.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c82989b3a4979a7e1ad848aad9f5d0b4388f1f454cc131766526601ab9e8f8" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 14.0.0", + "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", ] [[package]] name = "sp-tracing" -version = "10.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357f7591980dd58305956d32f8f6646d0a8ea9ea0e7e868e46f53b68ddf00cec" +version = "17.0.1" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "parity-scale-codec", - "sp-std 8.0.0", "tracing", "tracing-core", - "tracing-subscriber 0.2.25", + "tracing-subscriber", ] [[package]] @@ -7743,7 +7672,7 @@ dependencies = [ "parity-scale-codec", "tracing", "tracing-core", - "tracing-subscriber 0.3.23", + "tracing-subscriber", ] [[package]] @@ -7762,8 +7691,31 @@ dependencies = [ "rand 0.8.6", "scale-info", "schnellru", - "sp-core", - "sp-externalities 0.29.0", + "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", + "thiserror 1.0.69", + "tracing", + "trie-db", + "trie-root", +] + +[[package]] +name = "sp-trie" +version = "37.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "ahash", + "hash-db", + "lazy_static", + "memory-db", + "nohash-hasher", + "parity-scale-codec", + "parking_lot", + "rand 0.8.6", + "scale-info", + "schnellru", + "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "thiserror 1.0.69", "tracing", "trie-db", @@ -7773,17 +7725,16 @@ dependencies = [ [[package]] name = "sp-version" version = "37.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d521a405707b5be561367cd3d442ff67588993de24062ce3adefcf8437ee9fe1" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", - "parity-wasm", + "parity-wasm 0.45.0 (registry+https://github.com/rust-lang/crates.io-index)", "scale-info", "serde", "sp-crypto-hashing-proc-macro", - "sp-runtime", - "sp-std 14.0.0", + "sp-runtime 39.0.1", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "sp-version-proc-macro", "thiserror 1.0.69", ] @@ -7791,8 +7742,7 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "14.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aee8f6730641a65fcf0c8f9b1e448af4b3bb083d08058b47528188bccc7b7a7" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -7812,17 +7762,43 @@ dependencies = [ "parity-scale-codec", ] +[[package]] +name = "sp-wasm-interface" +version = "21.0.1" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "anyhow", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-allocator", + "sp-wasm-interface-common", +] + [[package]] name = "sp-wasm-interface-common" -version = "15.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03223ee788e1490f6341eb5b76501f83abf931fa8b5d83b5f49a4fecaf83f4ae" +version = "7.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" dependencies = [ "parity-scale-codec", - "sp-std 9.0.0", + "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", "wasmi 0.13.2", ] +[[package]] +name = "sp-weights" +version = "31.0.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "bounded-collections", + "parity-scale-codec", + "scale-info", + "serde", + "smallvec", + "sp-arithmetic 26.0.0", + "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", +] + [[package]] name = "sp-weights" version = "31.1.0" @@ -7834,8 +7810,8 @@ dependencies = [ "scale-info", "serde", "smallvec", - "sp-arithmetic", - "sp-debug-derive 14.0.0", + "sp-arithmetic 26.1.0", + "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -7922,6 +7898,18 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "substrate-bip39" +version = "0.6.0" +source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +dependencies = [ + "hmac 0.12.1", + "pbkdf2", + "schnorrkel", + "sha2 0.10.9", + "zeroize", +] + [[package]] name = "substrate-bip39" version = "0.6.1" @@ -7963,7 +7951,7 @@ dependencies = [ "scale-value", "serde", "serde_json", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "subxt-core", "subxt-lightclient", "subxt-macro", @@ -8019,7 +8007,7 @@ dependencies = [ "scale-value", "serde", "serde_json", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "subxt-metadata", "thiserror 2.0.18", "tracing", @@ -8070,7 +8058,7 @@ dependencies = [ "hashbrown 0.14.5", "parity-scale-codec", "scale-info", - "sp-crypto-hashing", + "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "thiserror 2.0.18", ] @@ -8417,7 +8405,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ - "indexmap 2.14.0", + "indexmap", "serde_core", "serde_spanned 1.1.1", "toml_datetime 1.1.1+spec-1.1.0", @@ -8444,24 +8432,13 @@ dependencies = [ "serde_core", ] -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap 2.14.0", - "toml_datetime 0.6.11", - "winnow 0.5.40", -] - [[package]] name = "toml_edit" version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap 2.14.0", + "indexmap", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -8475,7 +8452,7 @@ version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ - "indexmap 2.14.0", + "indexmap", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", @@ -8563,17 +8540,6 @@ dependencies = [ "valuable", ] -[[package]] -name = "tracing-log" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-log" version = "0.2.0" @@ -8585,55 +8551,23 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-serde" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" -dependencies = [ - "serde", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.2.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" -dependencies = [ - "ansi_term", - "chrono", - "lazy_static", - "matchers 0.0.1", - "regex", - "serde", - "serde_json", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log 0.1.4", - "tracing-serde", -] - [[package]] name = "tracing-subscriber" version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ - "matchers 0.2.0", + "matchers", "nu-ansi-term", "once_cell", - "regex-automata 0.4.14", + "regex-automata", "sharded-slab", "smallvec", "thread_local", "time", "tracing", "tracing-core", - "tracing-log 0.2.0", + "tracing-log", ] [[package]] @@ -9001,7 +8935,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap 2.14.0", + "indexmap", "wasm-encoder 0.244.0", "wasmparser 0.244.0", ] @@ -9017,7 +8951,7 @@ dependencies = [ "cfg-if", "cmake", "derive_more 2.1.1", - "indexmap 2.14.0", + "indexmap", "js-sys", "more-asserts", "paste", @@ -9122,7 +9056,7 @@ dependencies = [ "enumset", "getrandom 0.2.17", "hex", - "indexmap 2.14.0", + "indexmap", "more-asserts", "rkyv", "sha2 0.10.9", @@ -9145,7 +9079,7 @@ dependencies = [ "dashmap", "enum-iterator 0.7.0", "fnv", - "indexmap 2.14.0", + "indexmap", "libc", "libunwind", "mach2", @@ -9162,10 +9096,9 @@ dependencies = [ [[package]] name = "wasmi" version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" +source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" dependencies = [ - "parity-wasm", + "parity-wasm 0.45.0 (git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext)", "wasmi-validation", "wasmi_core 0.2.1", ] @@ -9205,10 +9138,9 @@ dependencies = [ [[package]] name = "wasmi-validation" version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" +source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" dependencies = [ - "parity-wasm", + "parity-wasm 0.45.0 (git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext)", ] [[package]] @@ -9231,8 +9163,7 @@ checksum = "e80d6b275b1c922021939d561574bf376613493ae2b61c6963b15db0e8813562" [[package]] name = "wasmi_core" version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" +source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" dependencies = [ "downcast-rs", "libm", @@ -9281,16 +9212,6 @@ dependencies = [ "wasmi_core 0.40.0", ] -[[package]] -name = "wasmparser" -version = "0.102.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" -dependencies = [ - "indexmap 1.9.3", - "url", -] - [[package]] name = "wasmparser" version = "0.221.3" @@ -9316,7 +9237,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" dependencies = [ "bitflags 2.11.1", - "indexmap 2.14.0", + "indexmap", ] [[package]] @@ -9327,7 +9248,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.1", "hashbrown 0.15.5", - "indexmap 2.14.0", + "indexmap", "semver", "serde", ] @@ -9341,138 +9262,6 @@ dependencies = [ "indexmap-nostd", ] -[[package]] -name = "wasmtime" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" -dependencies = [ - "anyhow", - "bincode", - "cfg-if", - "indexmap 1.9.3", - "libc", - "log", - "object 0.30.4", - "once_cell", - "paste", - "psm", - "serde", - "target-lexicon", - "wasmparser 0.102.0", - "wasmtime-environ", - "wasmtime-jit", - "wasmtime-runtime", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-asm-macros" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "wasmtime-environ" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" -dependencies = [ - "anyhow", - "cranelift-entity", - "gimli 0.27.3", - "indexmap 1.9.3", - "log", - "object 0.30.4", - "serde", - "target-lexicon", - "thiserror 1.0.69", - "wasmparser 0.102.0", - "wasmtime-types", -] - -[[package]] -name = "wasmtime-jit" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" -dependencies = [ - "addr2line 0.19.0", - "anyhow", - "bincode", - "cfg-if", - "cpp_demangle", - "gimli 0.27.3", - "log", - "object 0.30.4", - "rustc-demangle", - "serde", - "target-lexicon", - "wasmtime-environ", - "wasmtime-jit-icache-coherence", - "wasmtime-runtime", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-jit-debug" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" -dependencies = [ - "once_cell", -] - -[[package]] -name = "wasmtime-jit-icache-coherence" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" -dependencies = [ - "cfg-if", - "libc", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-runtime" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" -dependencies = [ - "anyhow", - "cc", - "cfg-if", - "indexmap 1.9.3", - "libc", - "log", - "mach", - "memfd", - "memoffset 0.8.0", - "paste", - "rand 0.8.6", - "rustix 0.36.17", - "wasmtime-asm-macros", - "wasmtime-environ", - "wasmtime-jit-debug", - "windows-sys 0.45.0", -] - -[[package]] -name = "wasmtime-types" -version = "8.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" -dependencies = [ - "cranelift-entity", - "serde", - "thiserror 1.0.69", - "wasmparser 0.102.0", -] - [[package]] name = "web-time" version = "1.1.0" @@ -9622,15 +9411,6 @@ dependencies = [ "windows-targets 0.42.2", ] -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets 0.48.5", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -9673,21 +9453,6 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -9710,12 +9475,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -9728,12 +9487,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -9746,12 +9499,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -9770,12 +9517,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -9788,12 +9529,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -9806,12 +9541,6 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -9824,27 +9553,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - [[package]] name = "winnow" version = "0.7.15" @@ -9897,7 +9611,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap 2.14.0", + "indexmap", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -9928,7 +9642,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.11.1", - "indexmap 2.14.0", + "indexmap", "log", "serde", "serde_derive", @@ -9947,7 +9661,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap 2.14.0", + "indexmap", "log", "semver", "serde", diff --git a/rs/Cargo.toml b/rs/Cargo.toml index 3ee0531fd..91ed7d8f1 100644 --- a/rs/Cargo.toml +++ b/rs/Cargo.toml @@ -71,7 +71,6 @@ ethexe = [ gclient = ["dep:gclient", "dep:tokio-stream"] gstd = ["dep:gstd", "dep:gear-core"] gtest = ["std", "dep:gtest", "dep:log", "dep:tokio-stream"] -experimental-vft-account = ["sails-storage?/experimental-vft-account"] idl-gen = ["dep:sails-idl-gen"] client-builder = ["std", "idl-gen", "idl-embed", "dep:sails-client-gen-v2", "dep:convert_case"] idl-embed = ["dep:sails-idl-embed"] diff --git a/rs/src/build.rs b/rs/src/build.rs index 34bc07958..67d78b441 100644 --- a/rs/src/build.rs +++ b/rs/src/build.rs @@ -3,8 +3,6 @@ pub use gwasm_builder::build as build_wasm; use gwasm_builder::{PreProcessor, PreProcessorTarget, WasmBuilder}; -#[cfg(feature = "experimental-vft-account")] -use sails_storage::VFT_ACCOUNT_U256_SLOT_SIZE; use sails_storage::{ ACTOR_ID_U256_SLOT_SIZE, ACTOR_PAIR_U256_SLOT_SIZE, PAGE_LOCAL_ACTOR_U256_DATA_OFFSET, PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE, PAGE_LOCAL_ACTOR_U256_TILE_BYTES, StaticLayout, @@ -223,29 +221,6 @@ impl StaticMemoryLayout { }); self } - - /// Reserves an experimental owner-local VFT account map. - /// - /// The reserved table uses `2^LOG2_SLOTS` slots. Each slot is exactly 200 - /// bytes: owner state bytes, a 32-byte owner actor id, a 32-byte `U256` - /// balance, and two inline spender actor id plus `U256` allowance pairs. - #[cfg(feature = "experimental-vft-account")] - #[doc(hidden)] - pub fn reserve_vft_account_map( - mut self, - name: impl Into, - ) -> Self { - self.tables.push(StaticTable { - name: name.into(), - slots: StaticTableSlots::Log2(LOG2_SLOTS), - kind: StaticTableKind::SingleRegion { - slot_size: VFT_ACCOUNT_U256_SLOT_SIZE, - align: 8, - }, - }); - self - } - /// Reserves a control-byte `ActorId -> U256` static map. /// /// The reserved table uses a one-byte control region followed by a 64-byte @@ -1104,29 +1079,6 @@ mod tests { assert!(generated.contains("pub const GROUPED_BALANCES_DATA_OFFSET: usize = 2048;")); } - #[cfg(feature = "experimental-vft-account")] - #[test] - fn resolves_experimental_vft_account_map() { - let layout = StaticMemoryLayout::new(1024) - .reserve_table::<1, 1>("prefix", 1) - .reserve_vft_account_map::<2>("vft_accounts") - .resolve() - .unwrap(); - - assert_eq!(layout.tables[1].base % 8, 0); - assert_eq!(layout.tables[1].slots, 4); - assert_eq!(layout.tables[1].log2_slots, Some(2)); - assert_eq!(layout.tables[1].mask, Some(3)); - assert_eq!(layout.tables[1].bytes, 4 * VFT_ACCOUNT_U256_SLOT_SIZE); - - let dir = tempfile::tempdir().unwrap(); - emit_static_storage_to_dir(&layout, dir.path()).unwrap(); - let generated = fs::read_to_string(dir.path().join(GENERATED_STATIC_STORAGE)).unwrap(); - assert!(generated.contains("pub const VFT_ACCOUNTS_LOG2_SLOTS: u8 = 2;")); - assert!(generated.contains("pub const VFT_ACCOUNTS_MASK: usize = 3;")); - assert!(generated.contains("pub const VFT_ACCOUNTS_BYTES: usize = 800;")); - } - #[test] fn rejects_invalid_duplicate_and_reserved_names() { assert!(matches!( diff --git a/rs/storage/Cargo.toml b/rs/storage/Cargo.toml index 657080e6a..192c8c745 100644 --- a/rs/storage/Cargo.toml +++ b/rs/storage/Cargo.toml @@ -12,6 +12,3 @@ rust-version.workspace = true [dependencies] gprimitives.workspace = true -[features] -default = [] -experimental-vft-account = [] diff --git a/rs/storage/src/lib.rs b/rs/storage/src/lib.rs index b5739b2c9..f3eec7ed8 100644 --- a/rs/storage/src/lib.rs +++ b/rs/storage/src/lib.rs @@ -127,10 +127,6 @@ pub const PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE: usize = 252; pub const PAGE_LOCAL_ACTOR_U256_DATA_OFFSET: usize = 256; /// Slot size used by the experimental owner-local VFT account map. -#[cfg(feature = "experimental-vft-account")] -#[doc(hidden)] -pub const VFT_ACCOUNT_U256_SLOT_SIZE: usize = 200; - #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct FixedSlot { state: SlotState, @@ -584,8 +580,6 @@ impl StaticOpenAddressTable = StaticActorIdU256Map; /// WAT-shaped static VFT allowance map. pub type VftAllowances = StaticAllowanceU256Map; - /// Experimental owner-local static VFT account map. - #[cfg(feature = "experimental-vft-account")] - pub type StaticVftAccountMap = StaticVftAccountU256Map; - /// Result returned by an optimized actor balance transfer. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct ActorU256Transfer { @@ -3635,629 +3625,6 @@ pub mod __experimental { (self.base + slot * Self::slot_size()) as *mut u8 } } - - /// Experimental owner-local VFT account map. - /// - /// Each account slot stores one owner, one balance, and two inline - /// `(spender, allowance)` pairs. Additional spender allowances should be - /// stored in an overflow [`StaticAllowanceU256Map`]. - #[cfg(feature = "experimental-vft-account")] - pub struct StaticVftAccountU256Map { - base: usize, - slots: usize, - mask: usize, - _marker: PhantomData<*mut u8>, - } - - #[cfg(feature = "experimental-vft-account")] - impl StaticVftAccountU256Map { - const OWNER_STATE_OFFSET: usize = 0; - const SPENDER0_STATE_OFFSET: usize = 1; - const SPENDER1_STATE_OFFSET: usize = 2; - const OWNER_OFFSET: usize = 8; - const BALANCE_OFFSET: usize = 40; - const SPENDER0_OFFSET: usize = 72; - const ALLOWANCE0_OFFSET: usize = 104; - const SPENDER1_OFFSET: usize = 136; - const ALLOWANCE1_OFFSET: usize = 168; - - /// Returns the byte length of one account slot. - pub const fn slot_size() -> usize { - VFT_ACCOUNT_U256_SLOT_SIZE - } - - /// Returns the configured slot count. - pub fn slots() -> Result { - static_map_slots(LOG2_SLOTS) - } - - /// Returns the mask used for power-of-two probing. - pub fn mask() -> Result { - Ok(Self::slots()? - 1) - } - - /// Returns the byte length required for this account map. - pub fn bytes_len() -> Result { - Self::slots()? - .checked_mul(Self::slot_size()) - .ok_or(TableError::InvalidLayout) - } - - /// Creates an account map over `2^LOG2_SLOTS * 200` bytes at `base`. - /// - /// # Safety - /// - /// The caller must ensure the memory interval is valid for reads and - /// writes for the whole lifetime of the map and does not overlap other - /// mutable state. - pub unsafe fn new(base: usize) -> Result { - let slots = Self::slots()?; - let bytes = Self::bytes_len()?; - StaticRegion::new(base, bytes)?; - Ok(Self { - base, - slots, - mask: slots - 1, - _marker: PhantomData, - }) - } - - /// Returns the configured base address. - pub const fn base(&self) -> usize { - self.base - } - - /// Returns the total byte length occupied by this table. - pub fn bytes(&self) -> Result { - Self::bytes_len() - } - - /// Returns the visible balance for `owner`. - pub fn get_balance(&self, owner: ActorId) -> Result, TableError> { - let owner = actor_key(owner); - if is_zero_32(&owner) { - return Ok(None); - } - let LookupResult::Found(index) = self.lookup(&owner)? else { - return Ok(None); - }; - Ok(Some(unsafe { self.read_u256(index, Self::BALANCE_OFFSET) })) - } - - /// Inserts or updates an owner balance. - pub fn insert_balance( - &self, - owner: ActorId, - value: U256, - ) -> Result, TableError> { - let owner = actor_key(owner); - reject_zero_key(&owner)?; - let index = self.lookup_or_insert(&owner)?; - let previous = unsafe { self.read_u256(index, Self::BALANCE_OFFSET) }; - unsafe { - self.write_u256(index, Self::BALANCE_OFFSET, value); - } - Ok((!previous.is_zero()).then_some(previous)) - } - - /// Returns the inline allowance for `owner` and `spender`. - pub fn get_inline_allowance( - &self, - owner: ActorId, - spender: ActorId, - ) -> Result, TableError> { - let owner = actor_key(owner); - let spender = actor_key(spender); - if is_zero_32(&owner) || is_zero_32(&spender) { - return Ok(None); - } - let LookupResult::Found(index) = self.lookup(&owner)? else { - return Ok(None); - }; - Ok(self - .inline_allowance_offset(index, &spender)? - .map(|offset| unsafe { self.read_u256(index, offset) })) - } - - /// Inserts or updates an inline allowance. - /// - /// Returns `Ok(Some(previous))` when the allowance fit in an inline - /// slot. Returns `Ok(None)` when both inline slots are occupied by - /// other spenders and the caller should use overflow storage. - pub fn insert_inline_allowance( - &self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result>, TableError> { - let owner = actor_key(owner); - let spender = actor_key(spender); - reject_zero_key(&owner)?; - reject_zero_key(&spender)?; - let index = self.lookup_or_insert(&owner)?; - - if let Some((state_offset, spender_offset, value_offset)) = - self.inline_slot(index, &spender)? - { - let previous = unsafe { self.read_u256(index, value_offset) }; - unsafe { - if value.is_zero() { - self.clear_inline_slot(index, state_offset, spender_offset, value_offset); - } else { - self.write_u256(index, value_offset, value); - } - } - return Ok(Some(Some(previous))); - } - if value.is_zero() { - return Ok(Some(None)); - } - - if self.read_state(index, Self::SPENDER0_STATE_OFFSET)? != SlotState::Full { - unsafe { - self.write_state(index, Self::SPENDER0_STATE_OFFSET, SlotState::Full); - self.write_actor(index, Self::SPENDER0_OFFSET, &spender); - self.write_u256(index, Self::ALLOWANCE0_OFFSET, value); - } - return Ok(Some(None)); - } - if self.read_state(index, Self::SPENDER1_STATE_OFFSET)? != SlotState::Full { - unsafe { - self.write_state(index, Self::SPENDER1_STATE_OFFSET, SlotState::Full); - self.write_actor(index, Self::SPENDER1_OFFSET, &spender); - self.write_u256(index, Self::ALLOWANCE1_OFFSET, value); - } - return Ok(Some(None)); - } - - Ok(None) - } - - /// Moves `amount` from `from` to `to`. - pub fn transfer( - &self, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - if from == to || amount.is_zero() { - return Ok(false); - } - let from = actor_key(from); - let to = actor_key(to); - reject_zero_key(&from)?; - reject_zero_key(&to)?; - - let LookupResult::Found(from_index) = self.lookup(&from)? else { - return Ok(false); - }; - let from_balance = unsafe { self.read_u256(from_index, Self::BALANCE_OFFSET) }; - if from_balance < amount { - return Ok(false); - } - - let to_index = self.lookup_or_insert(&to)?; - let to_balance = unsafe { self.read_u256(to_index, Self::BALANCE_OFFSET) }; - let (to_balance, overflow) = to_balance.overflowing_add(amount); - if overflow { - return Ok(false); - } - unsafe { - self.write_u256(from_index, Self::BALANCE_OFFSET, from_balance - amount); - self.write_u256(to_index, Self::BALANCE_OFFSET, to_balance); - } - Ok(true) - } - - /// Clears every account slot to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - let bytes = self.bytes()?; - unsafe { - ptr::write_bytes(self.base as *mut u8, 0, bytes); - } - Ok(()) - } - - fn lookup(&self, owner: &[u8; 32]) -> Result { - let owner_words = words_32(owner); - let mut index = self.account_index(owner); - for _ in 0..self.slots { - match self.read_state(index, Self::OWNER_STATE_OFFSET)? { - SlotState::Empty => return Ok(LookupResult::Vacant(index)), - SlotState::Full => { - if unsafe { read_words_32(self.owner_ptr(index)) } == owner_words { - return Ok(LookupResult::Found(index)); - } - } - SlotState::Deleted => {} - } - index = (index + 1) & self.mask; - } - Ok(LookupResult::Full) - } - - fn lookup_or_insert(&self, owner: &[u8; 32]) -> Result { - match self.lookup(owner)? { - LookupResult::Found(index) => Ok(index), - LookupResult::Vacant(index) => { - unsafe { - self.write_state(index, Self::OWNER_STATE_OFFSET, SlotState::Full); - self.write_actor(index, Self::OWNER_OFFSET, owner); - } - Ok(index) - } - LookupResult::Full => Err(TableError::CapacityOverflow), - } - } - - fn inline_allowance_offset( - &self, - index: usize, - spender: &[u8; 32], - ) -> Result, TableError> { - Ok(self - .inline_slot(index, spender)? - .map(|(_, _, value_offset)| value_offset)) - } - - fn inline_slot( - &self, - index: usize, - spender: &[u8; 32], - ) -> Result, TableError> { - let spender_words = words_32(spender); - if self.read_state(index, Self::SPENDER0_STATE_OFFSET)? == SlotState::Full - && unsafe { read_words_32(self.spender_ptr(index, Self::SPENDER0_OFFSET)) } - == spender_words - { - return Ok(Some(( - Self::SPENDER0_STATE_OFFSET, - Self::SPENDER0_OFFSET, - Self::ALLOWANCE0_OFFSET, - ))); - } - if self.read_state(index, Self::SPENDER1_STATE_OFFSET)? == SlotState::Full - && unsafe { read_words_32(self.spender_ptr(index, Self::SPENDER1_OFFSET)) } - == spender_words - { - return Ok(Some(( - Self::SPENDER1_STATE_OFFSET, - Self::SPENDER1_OFFSET, - Self::ALLOWANCE1_OFFSET, - ))); - } - Ok(None) - } - - fn account_index(&self, owner: &[u8; 32]) -> usize { - let tag = unsafe { ptr::read_unaligned(owner.as_ptr().add(12).cast::()) }; - let hash = if tag != 0 { - tag - } else { - unsafe { - ptr::read_unaligned(owner.as_ptr().cast::()) - ^ ptr::read_unaligned(owner.as_ptr().add(8).cast::()) - ^ ptr::read_unaligned(owner.as_ptr().add(16).cast::()) - ^ ptr::read_unaligned(owner.as_ptr().add(24).cast::()) - } - }; - hash as usize & self.mask - } - - fn read_state(&self, slot: usize, offset: usize) -> Result { - SlotState::from_byte(unsafe { ptr::read(self.slot_ptr(slot).add(offset)) }) - } - - unsafe fn read_u256(&self, slot: usize, offset: usize) -> U256 { - let mut bytes = [0u8; 32]; - unsafe { - ptr::copy_nonoverlapping(self.slot_ptr(slot).add(offset), bytes.as_mut_ptr(), 32); - } - U256::from_little_endian(&bytes) - } - - unsafe fn write_state(&self, slot: usize, offset: usize, state: SlotState) { - unsafe { - ptr::write(self.slot_ptr(slot).add(offset), state as u8); - } - } - - unsafe fn write_actor(&self, slot: usize, offset: usize, actor: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(actor.as_ptr(), self.slot_ptr(slot).add(offset), 32); - } - } - - unsafe fn write_u256(&self, slot: usize, offset: usize, value: U256) { - let bytes = u256_value(value); - unsafe { - ptr::copy_nonoverlapping(bytes.as_ptr(), self.slot_ptr(slot).add(offset), 32); - } - } - - unsafe fn clear_inline_slot( - &self, - slot: usize, - state_offset: usize, - spender_offset: usize, - value_offset: usize, - ) { - unsafe { - self.write_state(slot, state_offset, SlotState::Empty); - ptr::write_bytes(self.slot_ptr(slot).add(spender_offset), 0, 32); - ptr::write_bytes(self.slot_ptr(slot).add(value_offset), 0, 32); - } - } - - fn owner_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(Self::OWNER_OFFSET) } - } - - fn spender_ptr(&self, slot: usize, offset: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(offset) } - } - - fn slot_ptr(&self, slot: usize) -> *mut u8 { - (self.base + slot * Self::slot_size()) as *mut u8 - } - } - - /// Experimental VFT storage with owner-local hot allowances. - #[cfg(feature = "experimental-vft-account")] - pub struct StaticVftAccountStorage< - const ACCOUNT_LOG2_SLOTS: u8, - const OVERFLOW_ALLOWANCE_LOG2_SLOTS: u8, - > { - accounts: StaticVftAccountMap, - overflow_allowances: VftAllowances, - } - - #[cfg(feature = "experimental-vft-account")] - impl - StaticVftAccountStorage - { - /// Creates owner-local VFT storage over separately reserved static regions. - /// - /// # Safety - /// - /// The caller must ensure both memory intervals are valid for reads and - /// writes for the whole lifetime of the storage and do not overlap other - /// mutable state. - pub unsafe fn new( - account_base: usize, - overflow_allowance_base: usize, - ) -> Result { - let account_region = StaticRegion::new( - account_base, - StaticVftAccountMap::::bytes_len()?, - )?; - let overflow_region = StaticRegion::new( - overflow_allowance_base, - VftAllowances::::bytes_len()?, - )?; - if regions_overlap(account_region, overflow_region)? { - return Err(TableError::InvalidLayout); - } - - Ok(Self { - accounts: unsafe { StaticVftAccountMap::::new(account_base)? }, - overflow_allowances: unsafe { - VftAllowances::::new(overflow_allowance_base)? - }, - }) - } - - /// Returns the account map backing this storage. - pub fn accounts(&self) -> &StaticVftAccountMap { - &self.accounts - } - - /// Returns the overflow allowance map backing this storage. - pub fn overflow_allowances(&self) -> &VftAllowances { - &self.overflow_allowances - } - - /// Returns the visible balance of `account`. - pub fn balance_of(&self, account: ActorId) -> Result { - Ok(self - .accounts - .get_balance(account)? - .unwrap_or_else(U256::zero)) - } - - /// Returns the visible allowance from `owner` to `spender`. - pub fn allowance(&self, owner: ActorId, spender: ActorId) -> Result { - if let Some(allowance) = self.accounts.get_inline_allowance(owner, spender)? { - return Ok(allowance); - } - Ok(self - .overflow_allowances - .get_allowance_u256(&owner, &spender)? - .unwrap_or_else(U256::zero)) - } - - /// Sets an allowance and returns whether the visible value changed. - pub fn approve( - &self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result { - if owner == spender { - return Ok(false); - } - match self - .accounts - .insert_inline_allowance(owner, spender, value)? - { - Some(previous) => { - if value.is_zero() { - self.overflow_allowances.insert_allowance_u256( - owner, - spender, - U256::zero(), - )?; - } - Ok(previous.unwrap_or_else(U256::zero) != value) - } - None => { - let previous = self - .overflow_allowances - .insert_allowance_u256(owner, spender, value)? - .unwrap_or_else(U256::zero); - Ok(previous != value) - } - } - } - - /// Adds `amount` to `to` and returns whether state changed. - pub fn mint(&self, to: ActorId, amount: U256) -> Result { - if amount.is_zero() { - return Ok(false); - } - - let previous = self.balance_of(to)?; - let (next, overflow) = previous.overflowing_add(amount); - if overflow { - return Ok(false); - } - - self.accounts.insert_balance(to, next)?; - Ok(true) - } - - /// Removes `amount` from `from` and returns whether state changed. - pub fn burn(&self, from: ActorId, amount: U256) -> Result { - if amount.is_zero() { - return Ok(false); - } - - let previous = self.balance_of(from)?; - if previous < amount { - return Ok(false); - } - - self.accounts.insert_balance(from, previous - amount)?; - Ok(true) - } - - /// Moves `amount` from `from` to `to` and returns whether state changed. - pub fn transfer( - &self, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - self.accounts.transfer(from, to, amount) - } - - /// Moves `amount` after validating and decreasing `spender` allowance. - pub fn transfer_from( - &self, - spender: ActorId, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - if spender == from { - return self.transfer(from, to, amount); - } - if from == to || amount.is_zero() { - return Ok(false); - } - - let Some(from_index) = self.accounts.lookup(&actor_key(from))?.found() else { - return Ok(false); - }; - let spender_key = actor_key(spender); - let inline_slot = self.accounts.inline_slot(from_index, &spender_key)?; - let allowance = if let Some((_, _, offset)) = inline_slot { - unsafe { self.accounts.read_u256(from_index, offset) } - } else { - self.overflow_allowances - .get_allowance_u256(&from, &spender)? - .unwrap_or_else(U256::zero) - }; - if allowance < amount { - return Ok(false); - } - - let owner_balance = unsafe { - self.accounts.read_u256( - from_index, - StaticVftAccountMap::::BALANCE_OFFSET, - ) - }; - if owner_balance < amount { - return Ok(false); - } - - let to_key = actor_key(to); - reject_zero_key(&to_key)?; - let to_index = self.accounts.lookup_or_insert(&to_key)?; - let to_balance = unsafe { - self.accounts.read_u256( - to_index, - StaticVftAccountMap::::BALANCE_OFFSET, - ) - }; - let (to_balance, overflow) = to_balance.overflowing_add(amount); - if overflow { - return Ok(false); - } - - unsafe { - if let Some((state_offset, spender_offset, value_offset)) = inline_slot { - let allowance = allowance - amount; - if allowance.is_zero() { - self.accounts.clear_inline_slot( - from_index, - state_offset, - spender_offset, - value_offset, - ); - } else { - self.accounts - .write_u256(from_index, value_offset, allowance); - } - } else { - self.overflow_allowances.insert_allowance_u256( - from, - spender, - allowance - amount, - )?; - } - self.accounts.write_u256( - from_index, - StaticVftAccountMap::::BALANCE_OFFSET, - owner_balance - amount, - ); - self.accounts.write_u256( - to_index, - StaticVftAccountMap::::BALANCE_OFFSET, - to_balance, - ); - } - Ok(true) - } - - /// Clears both account and overflow allowance maps. - pub fn clear(&self) -> Result<(), TableError> { - self.accounts.clear()?; - self.overflow_allowances.clear() - } - } - - #[cfg(feature = "experimental-vft-account")] - impl LookupResult { - fn found(self) -> Option { - match self { - Self::Found(index) => Some(index), - Self::Vacant(_) | Self::Full => None, - } - } - } - /// Core VFT storage over WAT-shaped static balance and allowance maps. /// /// This is intended for high-cardinality token hot paths where transfer, @@ -5702,98 +5069,6 @@ mod experimental_tests { assert_eq!(storage.balance_of(to), Ok(U256::zero())); assert_eq!(storage.allowance(owner, spender), Ok(U256::from(2))); } - - #[cfg(feature = "experimental-vft-account")] - #[test] - fn static_vft_account_storage_handles_inline_and_overflow_allowances() { - let mut account_memory = vec![0u8; StaticVftAccountMap::<3>::bytes_len().unwrap()]; - let mut overflow_memory = vec![0u8; VftAllowances::<3>::bytes_len().unwrap()]; - let storage = unsafe { - StaticVftAccountStorage::<3, 3>::new( - account_memory.as_mut_ptr() as usize, - overflow_memory.as_mut_ptr() as usize, - ) - .unwrap() - }; - let owner = ActorId::from(1u64); - let spender_a = ActorId::from(2u64); - let spender_b = ActorId::from(3u64); - let spender_c = ActorId::from(4u64); - let to = ActorId::from(5u64); - - assert_eq!(storage.mint(owner, U256::from(20)), Ok(true)); - assert_eq!(storage.approve(owner, spender_a, U256::from(7)), Ok(true)); - assert_eq!(storage.approve(owner, spender_b, U256::from(8)), Ok(true)); - assert_eq!(storage.approve(owner, spender_c, U256::from(9)), Ok(true)); - assert_eq!(storage.allowance(owner, spender_a), Ok(U256::from(7))); - assert_eq!(storage.allowance(owner, spender_b), Ok(U256::from(8))); - assert_eq!(storage.allowance(owner, spender_c), Ok(U256::from(9))); - - assert_eq!( - storage.transfer_from(spender_a, owner, to, U256::from(3)), - Ok(true) - ); - assert_eq!(storage.allowance(owner, spender_a), Ok(U256::from(4))); - assert_eq!( - storage.transfer_from(spender_c, owner, to, U256::from(4)), - Ok(true) - ); - assert_eq!(storage.allowance(owner, spender_c), Ok(U256::from(5))); - assert_eq!(storage.balance_of(owner), Ok(U256::from(13))); - assert_eq!(storage.balance_of(to), Ok(U256::from(7))); - } - - #[cfg(feature = "experimental-vft-account")] - #[test] - fn static_vft_account_storage_reuses_cleared_inline_allowance_slot() { - let mut account_memory = vec![0u8; StaticVftAccountMap::<2>::bytes_len().unwrap()]; - let mut overflow_memory = vec![0u8; VftAllowances::<2>::bytes_len().unwrap()]; - let storage = unsafe { - StaticVftAccountStorage::<2, 2>::new( - account_memory.as_mut_ptr() as usize, - overflow_memory.as_mut_ptr() as usize, - ) - .unwrap() - }; - let owner = ActorId::from(1u64); - let spender_a = ActorId::from(2u64); - let spender_b = ActorId::from(3u64); - let spender_c = ActorId::from(4u64); - - assert_eq!(storage.approve(owner, spender_a, U256::from(1)), Ok(true)); - assert_eq!(storage.approve(owner, spender_b, U256::from(2)), Ok(true)); - assert_eq!(storage.approve(owner, spender_a, U256::zero()), Ok(true)); - assert_eq!(storage.allowance(owner, spender_a), Ok(U256::zero())); - assert_eq!(storage.approve(owner, spender_c, U256::from(3)), Ok(true)); - assert_eq!(storage.allowance(owner, spender_c), Ok(U256::from(3))); - assert_eq!( - storage - .overflow_allowances() - .get_allowance_u256(&owner, &spender_c), - Ok(None) - ); - } - - #[cfg(feature = "experimental-vft-account")] - #[test] - fn static_vft_account_storage_preserves_probe_chain_on_collisions() { - let (first, second) = colliding_vft_account_pair::<1>(); - let mut account_memory = vec![0u8; StaticVftAccountMap::<1>::bytes_len().unwrap()]; - let mut overflow_memory = vec![0u8; VftAllowances::<1>::bytes_len().unwrap()]; - let storage = unsafe { - StaticVftAccountStorage::<1, 1>::new( - account_memory.as_mut_ptr() as usize, - overflow_memory.as_mut_ptr() as usize, - ) - .unwrap() - }; - - assert_eq!(storage.mint(first, U256::from(11)), Ok(true)); - assert_eq!(storage.mint(second, U256::from(13)), Ok(true)); - assert_eq!(storage.balance_of(first), Ok(U256::from(11))); - assert_eq!(storage.balance_of(second), Ok(U256::from(13))); - } - #[test] fn static_actor_map_tombstone_preserves_probe_chain() { let (first, second) = colliding_actor_pair::<1>(); @@ -6351,21 +5626,6 @@ mod experimental_tests { unreachable!("small actor key space must contain grouped-control collisions") } - #[cfg(feature = "experimental-vft-account")] - fn colliding_vft_account_pair() -> (ActorId, ActorId) { - let mut seen = vec![None; 1usize << LOG2_SLOTS]; - for seed in 1u64..=4096 { - let actor = ActorId::from(seed); - let index = vft_account_index::(actor); - if let Some(first) = seen[index] { - return (first, actor); - } - seen[index] = Some(actor); - } - - unreachable!("small actor key space must contain VFT account collisions") - } - fn actor_index(actor: ActorId) -> usize { let key = actor_key(actor); let hash = hash_words_for_test(&key); @@ -6409,24 +5669,6 @@ mod experimental_tests { + group_slot } - #[cfg(feature = "experimental-vft-account")] - fn vft_account_index(actor: ActorId) -> usize { - let key = actor_key(actor); - let mask = (1usize << LOG2_SLOTS) - 1; - let tag = unsafe { ptr::read_unaligned(key.as_ptr().add(12).cast::()) }; - let hash = if tag != 0 { - tag - } else { - unsafe { - ptr::read_unaligned(key.as_ptr().cast::()) - ^ ptr::read_unaligned(key.as_ptr().add(8).cast::()) - ^ ptr::read_unaligned(key.as_ptr().add(16).cast::()) - ^ ptr::read_unaligned(key.as_ptr().add(24).cast::()) - } - }; - hash as usize & mask - } - fn hash_words_for_test(bytes: &[u8; 32]) -> u32 { let mut hash = 0u32; let mut offset = 0; From 254804240ea46f631f69e5a2e7b6af249c64515d Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Wed, 20 May 2026 16:52:49 +0400 Subject: [PATCH 10/14] chore: reduce storage PR to stable primitives --- rs/src/build.rs | 258 +- rs/storage/src/lib.rs | 5486 +++++------------------------------------ 2 files changed, 598 insertions(+), 5146 deletions(-) diff --git a/rs/src/build.rs b/rs/src/build.rs index 67d78b441..3eb91431d 100644 --- a/rs/src/build.rs +++ b/rs/src/build.rs @@ -4,9 +4,7 @@ pub use gwasm_builder::build as build_wasm; use gwasm_builder::{PreProcessor, PreProcessorTarget, WasmBuilder}; use sails_storage::{ - ACTOR_ID_U256_SLOT_SIZE, ACTOR_PAIR_U256_SLOT_SIZE, PAGE_LOCAL_ACTOR_U256_DATA_OFFSET, - PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE, PAGE_LOCAL_ACTOR_U256_TILE_BYTES, StaticLayout, - StaticOpenAddressTable, + ACTOR_ID_U256_SLOT_SIZE, ACTOR_PAIR_U256_SLOT_SIZE, StaticLayout, StaticOpenAddressTable, }; use std::{ boxed::Box, @@ -50,9 +48,6 @@ enum StaticTableSlots { #[derive(Clone, Debug)] enum StaticTableKind { SingleRegion { slot_size: usize, align: usize }, - ControlActorU256, - PageLocalActorU256, - GroupedControlActorU256 { log2_group_pages: u8 }, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -221,65 +216,6 @@ impl StaticMemoryLayout { }); self } - /// Reserves a control-byte `ActorId -> U256` static map. - /// - /// The reserved table uses a one-byte control region followed by a 64-byte - /// aligned data slot region. Each data slot stores a 32-byte actor id and a - /// 32-byte `U256` value. - #[doc(hidden)] - pub fn reserve_control_actor_u256_map( - mut self, - name: impl Into, - ) -> Self { - self.tables.push(StaticTable { - name: name.into(), - slots: StaticTableSlots::Log2(LOG2_SLOTS), - kind: StaticTableKind::ControlActorU256, - }); - self - } - - /// Reserves a page-local control-byte `ActorId -> U256` static map. - /// - /// The reserved table uses `2^LOG2_TILES` Gear-page-sized tiles. Each tile - /// stores its control bytes and 64-byte actor/value slots in the same - /// 16 KiB page to avoid split control/data page touches on hit paths. - #[doc(hidden)] - pub fn reserve_page_local_actor_u256_map( - mut self, - name: impl Into, - ) -> Self { - self.tables.push(StaticTable { - name: name.into(), - slots: StaticTableSlots::Log2(LOG2_TILES), - kind: StaticTableKind::PageLocalActorU256, - }); - self - } - - /// Reserves a grouped-control `ActorId -> U256` static map. - /// - /// The reserved table uses `2^LOG2_GROUPS` groups. Each group spans - /// `2^LOG2_GROUP_PAGES` Gear pages and stores its control bytes before its - /// 64-byte actor/value slots. - #[doc(hidden)] - pub fn reserve_grouped_control_actor_u256_map< - const LOG2_GROUPS: u8, - const LOG2_GROUP_PAGES: u8, - >( - mut self, - name: impl Into, - ) -> Self { - self.tables.push(StaticTable { - name: name.into(), - slots: StaticTableSlots::Log2(LOG2_GROUPS), - kind: StaticTableKind::GroupedControlActorU256 { - log2_group_pages: LOG2_GROUP_PAGES, - }, - }); - self - } - fn resolve(self) -> Result { let start_base = page_offset(self.start_page)?; let max_bytes = page_offset(MAX_STATIC_MEMORY_PAGES)?; @@ -328,128 +264,6 @@ impl StaticMemoryLayout { bytes: region.bytes(), } } - StaticTableKind::ControlActorU256 => { - let control_bytes = slots; - let control_region = layout - .reserve_aligned_bytes(control_bytes, 64) - .map_err(Error::StorageLayout)?; - let slots_bytes = slots - .checked_mul(ACTOR_ID_U256_SLOT_SIZE) - .ok_or(Error::LayoutOverflow)?; - let slots_region = layout - .reserve_aligned_bytes(slots_bytes, 64) - .map_err(Error::StorageLayout)?; - let span_bytes = slots_region - .end() - .map_err(Error::StorageLayout)? - .checked_sub(control_region.base()) - .ok_or(Error::LayoutOverflow)?; - ResolvedStaticTable { - name: table.name, - base: control_region.base(), - control_base: Some(control_region.base()), - slots_base: Some(slots_region.base()), - slots, - log2_slots, - tiles: None, - log2_tiles: None, - slots_per_tile: None, - tile_bytes: None, - groups: None, - log2_groups: None, - group_pages: None, - log2_group_pages: None, - slots_per_group: None, - group_bytes: None, - data_offset: None, - mask: log2_slots.map(|_| slots - 1), - control_bytes: Some(control_region.bytes()), - slots_bytes: Some(slots_region.bytes()), - bytes: span_bytes, - } - } - StaticTableKind::PageLocalActorU256 => { - let log2_tiles = table.slots.log2(); - let tiles = slots; - let slots = tiles - .checked_mul(PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE) - .ok_or(Error::LayoutOverflow)?; - let bytes = tiles - .checked_mul(PAGE_LOCAL_ACTOR_U256_TILE_BYTES) - .ok_or(Error::LayoutOverflow)?; - let region = layout - .reserve_aligned_bytes(bytes, PAGE_LOCAL_ACTOR_U256_TILE_BYTES) - .map_err(Error::StorageLayout)?; - ResolvedStaticTable { - name: table.name, - base: region.base(), - control_base: None, - slots_base: None, - slots, - log2_slots: None, - tiles: Some(tiles), - log2_tiles, - slots_per_tile: Some(PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE), - tile_bytes: Some(PAGE_LOCAL_ACTOR_U256_TILE_BYTES), - groups: None, - log2_groups: None, - group_pages: None, - log2_group_pages: None, - slots_per_group: None, - group_bytes: None, - data_offset: None, - mask: None, - control_bytes: None, - slots_bytes: None, - bytes: region.bytes(), - } - } - StaticTableKind::GroupedControlActorU256 { log2_group_pages } => { - let log2_groups = table.slots.log2(); - let groups = slots; - let group_pages = StaticTableSlots::Log2(log2_group_pages).resolve()?; - let slots_per_group = group_pages - .checked_mul(PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE) - .ok_or(Error::LayoutOverflow)?; - let group_bytes = group_pages - .checked_mul(PAGE_LOCAL_ACTOR_U256_TILE_BYTES) - .ok_or(Error::LayoutOverflow)?; - let data_offset = group_pages - .checked_mul(PAGE_LOCAL_ACTOR_U256_DATA_OFFSET) - .ok_or(Error::LayoutOverflow)?; - let slots = groups - .checked_mul(slots_per_group) - .ok_or(Error::LayoutOverflow)?; - let bytes = groups - .checked_mul(group_bytes) - .ok_or(Error::LayoutOverflow)?; - let region = layout - .reserve_aligned_bytes(bytes, PAGE_LOCAL_ACTOR_U256_TILE_BYTES) - .map_err(Error::StorageLayout)?; - ResolvedStaticTable { - name: table.name, - base: region.base(), - control_base: None, - slots_base: None, - slots, - log2_slots: None, - tiles: None, - log2_tiles: None, - slots_per_tile: None, - tile_bytes: None, - groups: Some(groups), - log2_groups, - group_pages: Some(group_pages), - log2_group_pages: Some(log2_group_pages), - slots_per_group: Some(slots_per_group), - group_bytes: Some(group_bytes), - data_offset: Some(data_offset), - mask: None, - control_bytes: None, - slots_bytes: None, - bytes: region.bytes(), - } - } }; tables.push(resolved); } @@ -986,9 +800,6 @@ mod tests { .reserve_table::<1, 1>("prefix", 1) .reserve_actor_u256_map::<2>("fast_balances") .reserve_actor_pair_u256_map::<1>("fast_allowances") - .reserve_control_actor_u256_map::<6>("control_balances") - .reserve_page_local_actor_u256_map::<2>("page_balances") - .reserve_grouped_control_actor_u256_map::<2, 3>("grouped_balances") .resolve() .unwrap(); @@ -1002,58 +813,6 @@ mod tests { assert_eq!(layout.tables[2].log2_slots, Some(1)); assert_eq!(layout.tables[2].mask, Some(1)); assert_eq!(layout.tables[2].bytes, 2 * ACTOR_PAIR_U256_SLOT_SIZE); - assert_eq!(layout.tables[3].control_base.unwrap() % 64, 0); - assert_eq!(layout.tables[3].slots_base.unwrap() % 64, 0); - assert_eq!(layout.tables[3].slots, 64); - assert_eq!(layout.tables[3].log2_slots, Some(6)); - assert_eq!(layout.tables[3].mask, Some(63)); - assert_eq!(layout.tables[3].control_bytes, Some(64)); - assert_eq!( - layout.tables[3].slots_bytes, - Some(64 * ACTOR_ID_U256_SLOT_SIZE) - ); - assert_eq!(layout.tables[3].bytes, 64 + 64 * ACTOR_ID_U256_SLOT_SIZE); - assert_eq!(layout.tables[4].base % PAGE_LOCAL_ACTOR_U256_TILE_BYTES, 0); - assert_eq!( - layout.tables[4].slots, - 4 * PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE - ); - assert_eq!(layout.tables[4].tiles, Some(4)); - assert_eq!(layout.tables[4].log2_tiles, Some(2)); - assert_eq!( - layout.tables[4].slots_per_tile, - Some(PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE) - ); - assert_eq!( - layout.tables[4].tile_bytes, - Some(PAGE_LOCAL_ACTOR_U256_TILE_BYTES) - ); - assert_eq!(layout.tables[4].bytes, 4 * PAGE_LOCAL_ACTOR_U256_TILE_BYTES); - assert_eq!(layout.tables[5].base % PAGE_LOCAL_ACTOR_U256_TILE_BYTES, 0); - assert_eq!(layout.tables[5].groups, Some(4)); - assert_eq!(layout.tables[5].log2_groups, Some(2)); - assert_eq!(layout.tables[5].group_pages, Some(8)); - assert_eq!(layout.tables[5].log2_group_pages, Some(3)); - assert_eq!( - layout.tables[5].slots_per_group, - Some(8 * PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE) - ); - assert_eq!( - layout.tables[5].group_bytes, - Some(8 * PAGE_LOCAL_ACTOR_U256_TILE_BYTES) - ); - assert_eq!( - layout.tables[5].data_offset, - Some(8 * PAGE_LOCAL_ACTOR_U256_DATA_OFFSET) - ); - assert_eq!( - layout.tables[5].slots, - 4 * 8 * PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE - ); - assert_eq!( - layout.tables[5].bytes, - 4 * 8 * PAGE_LOCAL_ACTOR_U256_TILE_BYTES - ); let dir = tempfile::tempdir().unwrap(); emit_static_storage_to_dir(&layout, dir.path()).unwrap(); @@ -1062,21 +821,6 @@ mod tests { assert!(generated.contains("pub const FAST_BALANCES_MASK: usize = 3;")); assert!(generated.contains("pub const FAST_ALLOWANCES_LOG2_SLOTS: u8 = 1;")); assert!(generated.contains("pub const FAST_ALLOWANCES_MASK: usize = 1;")); - assert!(generated.contains("pub const CONTROL_BALANCES_CONTROL_BASE: usize = ")); - assert!(generated.contains("pub const CONTROL_BALANCES_SLOTS_BASE: usize = ")); - assert!(generated.contains("pub const CONTROL_BALANCES_CONTROL_BYTES: usize = 64;")); - assert!(generated.contains("pub const CONTROL_BALANCES_SLOTS_BYTES: usize = 4096;")); - assert!(generated.contains("pub const PAGE_BALANCES_LOG2_TILES: u8 = 2;")); - assert!(generated.contains("pub const PAGE_BALANCES_TILES: usize = 4;")); - assert!(generated.contains("pub const PAGE_BALANCES_SLOTS_PER_TILE: usize = 252;")); - assert!(generated.contains("pub const PAGE_BALANCES_TILE_BYTES: usize = 16384;")); - assert!(generated.contains("pub const GROUPED_BALANCES_LOG2_GROUPS: u8 = 2;")); - assert!(generated.contains("pub const GROUPED_BALANCES_GROUPS: usize = 4;")); - assert!(generated.contains("pub const GROUPED_BALANCES_LOG2_GROUP_PAGES: u8 = 3;")); - assert!(generated.contains("pub const GROUPED_BALANCES_GROUP_PAGES: usize = 8;")); - assert!(generated.contains("pub const GROUPED_BALANCES_SLOTS_PER_GROUP: usize = 2016;")); - assert!(generated.contains("pub const GROUPED_BALANCES_GROUP_BYTES: usize = 131072;")); - assert!(generated.contains("pub const GROUPED_BALANCES_DATA_OFFSET: usize = 2048;")); } #[test] diff --git a/rs/storage/src/lib.rs b/rs/storage/src/lib.rs index f3eec7ed8..21fe1f6d5 100644 --- a/rs/storage/src/lib.rs +++ b/rs/storage/src/lib.rs @@ -111,22 +111,6 @@ pub const ACTOR_ID_U256_SLOT_SIZE: usize = 64; /// Slot size used by the specialized actor-pair static map. pub const ACTOR_PAIR_U256_SLOT_SIZE: usize = 96; -#[doc(hidden)] -pub const ALLOWANCE_U256_SLOT_SIZE: usize = ACTOR_PAIR_U256_SLOT_SIZE; - -/// Gear page-sized tile used by the page-local actor-id static map. -#[doc(hidden)] -pub const PAGE_LOCAL_ACTOR_U256_TILE_BYTES: usize = 16 * 1024; - -/// Actor/value slots fitting in one page-local actor-id static map tile. -#[doc(hidden)] -pub const PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE: usize = 252; - -/// Offset where page-local actor-id static map data slots begin inside a tile. -#[doc(hidden)] -pub const PAGE_LOCAL_ACTOR_U256_DATA_OFFSET: usize = 256; - -/// Slot size used by the experimental owner-local VFT account map. #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct FixedSlot { state: SlotState, @@ -578,5110 +562,834 @@ impl StaticOpenAddressTable = FixedOpenAddressMap<32, 32, CAP>; - /// Fixed allowance map keyed by `(owner, spender)` and storing `U256`. - pub type FixedAllowanceMap = FixedOpenAddressMap<64, 32, CAP>; - /// Static balance table keyed by `ActorId` and storing `U256`. - pub type StaticBalanceTable = StaticOpenAddressTable<32, 32>; - /// Static allowance table keyed by `(owner, spender)` and storing `U256`. - pub type StaticAllowanceTable = StaticOpenAddressTable<64, 32>; - /// WAT-shaped static balance map using the current golden-ratio actor hash. - pub type StaticActorIdU256Map = - StaticActorIdU256MapWithHash; - /// WAT-shaped static balance map using an avalanche-mixed actor hash. - pub type StaticMixedActorIdU256Map = - StaticActorIdU256MapWithHash; - /// Experimental static balance map keyed by a compact 64-bit actor tag. - pub type StaticActorTagU256Map = StaticActorTagU256Table; - /// Experimental static balance map keyed by a compact 64-bit actor tag and storing `u64`. - pub type StaticActorTagU64Map = StaticActorTagU64Table; - /// WAT-shaped static VFT balance map. - pub type VftBalances = StaticActorIdU256Map; - /// WAT-shaped static VFT allowance map. - pub type VftAllowances = StaticAllowanceU256Map; - /// Result returned by an optimized actor balance transfer. - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub struct ActorU256Transfer { - pub from_balance: U256, - pub to_balance: U256, +fn static_map_slots(log2_slots: u8) -> Result { + let shift = u32::from(log2_slots); + if shift > 32 || shift >= usize::BITS { + return Err(TableError::InvalidLayout); } - /// Result returned by an optimized actor transfer-from operation. - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub struct ActorU256TransferFrom { - pub from_balance: U256, - pub to_balance: U256, - pub allowance: U256, - } + 1usize.checked_shl(shift).ok_or(TableError::InvalidLayout) +} - /// Result returned by a static-table actor balance transfer. - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub struct StaticActorU256Transfer { - pub from_balance: U256, - pub to_balance: U256, - pub inserted_to: bool, +fn static_map_index(hash: u32, log2_slots: u8) -> usize { + if log2_slots == 0 { + 0 + } else { + (hash >> (32 - u32::from(log2_slots))) as usize } +} - /// Result returned by a static-table actor transfer-from operation. - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub struct StaticActorU256TransferFrom { - pub from_balance: U256, - pub to_balance: U256, - pub allowance: U256, - pub inserted_to: bool, - } +fn actor_key(actor: ActorId) -> [u8; 32] { + let mut key = [0u8; 32]; + key.copy_from_slice(actor.as_ref()); + key +} - /// Hash strategy used by specialized actor-id static maps. - pub trait ActorKeyHash { - /// Returns a 32-bit hash for a raw actor-id key. - fn hash(key: &[u8; 32]) -> u32; - } +fn u256_value(value: U256) -> [u8; 32] { + let mut bytes = [0u8; 32]; + value.to_little_endian(&mut bytes); + bytes +} - /// Current WAT-shaped actor-id hash. - #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] - pub struct GoldenActorHash; +fn u256_from_value(bytes: [u8; 32]) -> U256 { + U256::from_little_endian(&bytes) +} - impl ActorKeyHash for GoldenActorHash { - fn hash(key: &[u8; 32]) -> u32 { - hash_words(key) - } +fn reject_zero_key(bytes: &[u8; 32]) -> Result<(), TableError> { + if is_zero_32(bytes) { + Err(TableError::InvalidKey) + } else { + Ok(()) } +} - /// Avalanche-mixed actor-id hash candidate for high-cardinality VFT maps. - #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] - pub struct MixedActorHash; - - impl ActorKeyHash for MixedActorHash { - fn hash(key: &[u8; 32]) -> u32 { - fmix32(fold_words(key)) - } - } +fn is_zero_32(bytes: &[u8; 32]) -> bool { + bytes.iter().all(|byte| *byte == 0) +} - /// Converts an actor id into a raw 32-byte key. - pub fn actor_key(actor: ActorId) -> [u8; 32] { - let mut key = [0u8; 32]; - key.copy_from_slice(actor.as_ref()); - key - } +fn words_32(bytes: &[u8; 32]) -> [u64; 4] { + [ + u64::from_le_bytes([ + bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], + ]), + u64::from_le_bytes([ + bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], + ]), + u64::from_le_bytes([ + bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23], + ]), + u64::from_le_bytes([ + bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31], + ]), + ] +} - /// Converts an allowance pair into a raw 64-byte key. - pub fn allowance_key(owner: ActorId, spender: ActorId) -> [u8; 64] { - let mut key = [0u8; 64]; - key[..32].copy_from_slice(owner.as_ref()); - key[32..].copy_from_slice(spender.as_ref()); - key +unsafe fn read_words_32(ptr: *const u8) -> [u64; 4] { + unsafe { + [ + ptr::read_unaligned(ptr.cast::()), + ptr::read_unaligned(ptr.add(8).cast::()), + ptr::read_unaligned(ptr.add(16).cast::()), + ptr::read_unaligned(ptr.add(24).cast::()), + ] } +} - /// Converts a `U256` into the little-endian value bytes used by the helpers. - pub fn u256_value(value: U256) -> [u8; 32] { - let mut bytes = [0u8; 32]; - value.to_little_endian(&mut bytes); - bytes - } +unsafe fn words_at_zero(ptr: *const u8) -> bool { + words_are_zero(unsafe { read_words_32(ptr) }) +} - /// Converts little-endian value bytes into a `U256`. - pub fn u256_from_value(bytes: [u8; 32]) -> U256 { - U256::from_little_endian(&bytes) - } +fn words_are_zero(words: [u64; 4]) -> bool { + (words[0] | words[1] | words[2] | words[3]) == 0 +} - /// A WAT-shaped static balance map keyed by `ActorId` and storing `U256`. - /// - /// Each slot is exactly 64 bytes: 32 bytes of key followed by 32 bytes of - /// value. A zero key terminates probing, and a zero value is treated as an - /// absent/tombstoned entry for an already-written key. - pub struct StaticActorIdU256MapWithHash { - base: usize, - slots: usize, - mask: usize, - _marker: PhantomData<(*mut u8, H)>, - } +const HASH_GOLDEN_RATIO: u32 = 0x9E37_79B9; +const HASH_PAIR_SPENDER: u32 = 0x85EB_CA6B; - impl StaticActorIdU256MapWithHash { - const VALUE_OFFSET: usize = 32; +fn hash_words(bytes: &[u8; 32]) -> u32 { + fold_words(bytes).wrapping_mul(HASH_GOLDEN_RATIO) +} - /// Returns the byte length of one slot. - pub const fn slot_size() -> usize { - ACTOR_ID_U256_SLOT_SIZE - } +fn hash_actor_key(key: &[u8; 32]) -> u32 { + hash_words(key) +} - /// Returns the configured slot count. - pub fn slots() -> Result { - static_map_slots(LOG2_SLOTS) - } +fn hash_actor_pair_key(left: &[u8; 32], right: &[u8; 32]) -> u32 { + fold_words(left) + .wrapping_mul(HASH_GOLDEN_RATIO) + .wrapping_add(fold_words(right).wrapping_mul(HASH_PAIR_SPENDER)) +} - /// Returns the mask used for power-of-two probing. - pub fn mask() -> Result { - Ok(Self::slots()? - 1) - } +fn fold_words(bytes: &[u8; 32]) -> u32 { + let ptr = bytes.as_ptr(); + unsafe { + ptr::read_unaligned(ptr.cast::()) + ^ ptr::read_unaligned(ptr.add(4).cast::()) + ^ ptr::read_unaligned(ptr.add(8).cast::()) + ^ ptr::read_unaligned(ptr.add(12).cast::()) + ^ ptr::read_unaligned(ptr.add(16).cast::()) + ^ ptr::read_unaligned(ptr.add(20).cast::()) + ^ ptr::read_unaligned(ptr.add(24).cast::()) + ^ ptr::read_unaligned(ptr.add(28).cast::()) + } +} - /// Returns the byte length required for this table. - pub fn bytes_len() -> Result { - Self::slots()? - .checked_mul(Self::slot_size()) - .ok_or(TableError::InvalidLayout) - } +/// A static-memory map keyed by `ActorId` and storing `U256`. +/// +/// The layout uses `2^LOG2_SLOTS` slots. Each slot is exactly 64 bytes: +/// a 32-byte actor id followed by a 32-byte little-endian `U256` value. +pub struct StaticActorIdU256Map { + base: usize, + slots: usize, + mask: usize, + _marker: PhantomData<*mut u8>, +} - /// Creates a table over `2^LOG2_SLOTS * 64` bytes at `base`. - /// - /// # Safety - /// - /// The caller must ensure the memory interval is valid for reads and - /// writes for the whole lifetime of the table and does not overlap - /// other mutable state. - pub unsafe fn new(base: usize) -> Result { - let slots = Self::slots()?; - let bytes = slots - .checked_mul(Self::slot_size()) - .ok_or(TableError::InvalidLayout)?; - StaticRegion::new(base, bytes)?; - Ok(Self { - base, - slots, - mask: slots - 1, - _marker: PhantomData, - }) - } +impl StaticActorIdU256Map { + /// Returns the byte length of one slot. + pub const fn slot_size() -> usize { + ACTOR_ID_U256_SLOT_SIZE + } - /// Returns the configured base address. - pub const fn base(&self) -> usize { - self.base - } + /// Returns the configured slot count. + pub fn slots() -> Result { + static_map_slots(LOG2_SLOTS) + } - /// Returns the total byte length occupied by this table. - pub fn bytes(&self) -> Result { - Self::bytes_len() - } + /// Returns the mask used for power-of-two probing. + pub fn mask() -> Result { + Ok(Self::slots()? - 1) + } - /// Returns a balance value by actor id. - pub fn get_actor_u256(&self, key: &ActorId) -> Result, TableError> { - let key = actor_key(*key); - if is_zero_32(&key) { - return Ok(None); - } + /// Returns the byte length required for this map. + pub fn bytes_len() -> Result { + Self::slots()? + .checked_mul(Self::slot_size()) + .ok_or(TableError::InvalidLayout) + } - let LookupResult::Found(index) = self.lookup(&key)? else { - return Ok(None); - }; - Ok(self.visible_value(index).map(u256_from_value)) - } + /// Creates a map over `2^LOG2_SLOTS * 64` bytes at `base`. + /// + /// # Safety + /// + /// The caller must ensure the memory interval is valid for reads and writes + /// for the whole lifetime of the map and does not overlap other mutable + /// state. + pub unsafe fn new(base: usize) -> Result { + let slots = Self::slots()?; + StaticRegion::new(base, Self::bytes_len()?)?; + Ok(Self { + base, + slots, + mask: slots - 1, + _marker: PhantomData, + }) + } - /// Inserts or updates a balance value by actor id. - /// - /// A zero value removes the visible value without writing a new absent - /// key. Zero actor ids are rejected because zero key bytes mark empty - /// slots in this layout. - pub fn insert_actor_u256( - &self, - key: ActorId, - value: U256, - ) -> Result, TableError> { - let key = actor_key(key); - reject_zero_key(&key)?; - if value.is_zero() { - return self - .remove_key(&key) - .map(|previous| previous.map(u256_from_value)); - } + /// Returns the configured base address. + pub const fn base(&self) -> usize { + self.base + } - let value = u256_value(value); - match self.lookup(&key)? { - LookupResult::Found(index) => { - let previous = self.visible_value(index); - unsafe { - self.write_value(index, &value); - } - Ok(previous.map(u256_from_value)) - } - LookupResult::Vacant(index) => { - unsafe { - self.write_key(index, &key); - self.write_value(index, &value); - } - Ok(None) - } - LookupResult::Full => Err(TableError::CapacityOverflow), - } - } + /// Returns the total byte length occupied by this map. + pub fn bytes(&self) -> Result { + Self::bytes_len() + } - /// Removes a balance value by actor id. - pub fn remove_actor_u256(&self, key: &ActorId) -> Result, TableError> { - let key = actor_key(*key); - reject_zero_key(&key)?; - self.remove_key(&key) - .map(|previous| previous.map(u256_from_value)) + /// Returns the visible value for `actor`. + pub fn get_actor_u256(&self, actor: &ActorId) -> Result, TableError> { + let key = actor_key(*actor); + if is_zero_32(&key) { + return Ok(None); } - /// Moves `amount` from one actor balance to another using one lookup per key. - /// - /// Returns `Ok(None)` without modifying storage when the sender has no - /// visible balance, has insufficient balance, the recipient insertion - /// would overflow `U256`, or the table has no vacant slot for a new - /// recipient. - pub fn transfer_actor_u256( - &self, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result, TableError> { - let from_key = actor_key(from); - let to_key = actor_key(to); - reject_zero_key(&from_key)?; - reject_zero_key(&to_key)?; - if amount.is_zero() { - let from_balance = self.get_actor_u256(&from)?.unwrap_or_else(U256::zero); - let to_balance = if from_key == to_key { - from_balance - } else { - self.get_actor_u256(&to)?.unwrap_or_else(U256::zero) - }; - return Ok(Some(ActorU256Transfer { - from_balance, - to_balance, - })); - } - let LookupResult::Found(from_index) = self.lookup(&from_key)? else { - return Ok(None); - }; - let Some(from_value) = self.visible_value(from_index).map(u256_from_value) else { - return Ok(None); - }; - if from_value < amount { - return Ok(None); - } - if from_key == to_key { - return Ok(Some(ActorU256Transfer { - from_balance: from_value, - to_balance: from_value, - })); - } - - let to_lookup = self.lookup(&to_key)?; - let to_value = match to_lookup { - LookupResult::Found(index) => self - .visible_value(index) - .map_or_else(U256::zero, u256_from_value), - LookupResult::Vacant(_) => U256::zero(), - LookupResult::Full => return Ok(None), - }; - let (to_value, overflow) = to_value.overflowing_add(amount); - if overflow { - return Ok(None); - } + let StaticLookup::Found(index) = self.lookup(&key) else { + return Ok(None); + }; + Ok(self.visible_value(index).map(u256_from_value)) + } - let from_value = from_value - amount; - let from_value_bytes = u256_value(from_value); - let to_value_bytes = u256_value(to_value); - unsafe { - self.write_value(from_index, &from_value_bytes); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, &to_value_bytes), - LookupResult::Vacant(index) => { - self.write_key(index, &to_key); - self.write_value(index, &to_value_bytes); - } - LookupResult::Full => unreachable!("full table handled before writes"), - } - } - Ok(Some(ActorU256Transfer { - from_balance: from_value, - to_balance: to_value, - })) + /// Inserts or updates `actor`, returning the previous visible value. + /// + /// A zero value removes the visible value. Zero actor ids are rejected + /// because zero key bytes mark empty slots in this layout. + pub fn insert_actor_u256( + &self, + actor: ActorId, + value: U256, + ) -> Result, TableError> { + let key = actor_key(actor); + reject_zero_key(&key)?; + if value.is_zero() { + return self + .remove_key(&key) + .map(|previous| previous.map(u256_from_value)); } - /// Moves `amount` after validating a spender allowance. - /// - /// This validates allowance, sender balance, recipient overflow, and - /// recipient capacity before writing any value. - pub fn transfer_actor_u256_from( - &self, - allowances: &StaticAllowanceU256Map, - owner: ActorId, - spender: ActorId, - to: ActorId, - amount: U256, - ) -> Result, TableError> { - let owner_key = actor_key(owner); - let spender_key = actor_key(spender); - let to_key = actor_key(to); - reject_zero_key(&owner_key)?; - reject_zero_key(&spender_key)?; - reject_zero_key(&to_key)?; - - if amount.is_zero() { - let from_balance = self.get_actor_u256(&owner)?.unwrap_or_else(U256::zero); - let to_balance = if owner_key == to_key { - from_balance - } else { - self.get_actor_u256(&to)?.unwrap_or_else(U256::zero) - }; - let allowance = allowances - .get_allowance_u256(&owner, &spender)? - .unwrap_or_else(U256::zero); - return Ok(Some(ActorU256TransferFrom { - from_balance, - to_balance, - allowance, - })); - } - let LookupResult::Found(allowance_index) = - allowances.lookup(&owner_key, &spender_key)? - else { - return Ok(None); - }; - let Some(allowance) = allowances - .visible_value(allowance_index) - .map(u256_from_value) - else { - return Ok(None); - }; - if allowance < amount { - return Ok(None); - } - - let LookupResult::Found(from_index) = self.lookup(&owner_key)? else { - return Ok(None); - }; - let Some(from_balance) = self.visible_value(from_index).map(u256_from_value) else { - return Ok(None); - }; - if from_balance < amount { - return Ok(None); - } - - let allowance = allowance - amount; - if owner_key == to_key { - let allowance_bytes = u256_value(allowance); + let value = u256_value(value); + match self.lookup(&key) { + StaticLookup::Found(index) => { + let previous = self.visible_value(index); unsafe { - allowances.write_value(allowance_index, &allowance_bytes); - } - return Ok(Some(ActorU256TransferFrom { - from_balance, - to_balance: from_balance, - allowance, - })); - } - - let to_lookup = self.lookup(&to_key)?; - let to_balance = match to_lookup { - LookupResult::Found(index) => self - .visible_value(index) - .map_or_else(U256::zero, u256_from_value), - LookupResult::Vacant(_) => U256::zero(), - LookupResult::Full => return Ok(None), - }; - let (to_balance, overflow) = to_balance.overflowing_add(amount); - if overflow { - return Ok(None); - } - - let from_balance = from_balance - amount; - let from_balance_bytes = u256_value(from_balance); - let to_balance_bytes = u256_value(to_balance); - let allowance_bytes = u256_value(allowance); - unsafe { - self.write_value(from_index, &from_balance_bytes); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, &to_balance_bytes), - LookupResult::Vacant(index) => { - self.write_key(index, &to_key); - self.write_value(index, &to_balance_bytes); - } - LookupResult::Full => unreachable!("full table handled before writes"), + self.write_value(index, &value); } - allowances.write_value(allowance_index, &allowance_bytes); - } - - Ok(Some(ActorU256TransferFrom { - from_balance, - to_balance, - allowance, - })) - } - - /// Moves a known nonzero amount between known distinct, nonzero actors. - /// - /// # Safety - /// - /// The caller must ensure `from` and `to` are nonzero actor ids, `from != to`, - /// and `amount != 0`. This skips validation intended for the safe public path. - pub unsafe fn transfer_actor_u256_nonzero_distinct_unchecked( - &self, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result, TableError> { - let from_key = actor_key(from); - let to_key = actor_key(to); - - let LookupResult::Found(from_index) = self.lookup(&from_key)? else { - return Ok(None); - }; - let Some(from_value) = self.visible_value(from_index).map(u256_from_value) else { - return Ok(None); - }; - if from_value < amount { - return Ok(None); - } - - let to_lookup = self.lookup(&to_key)?; - let to_value = match to_lookup { - LookupResult::Found(index) => self - .visible_value(index) - .map_or_else(U256::zero, u256_from_value), - LookupResult::Vacant(_) => U256::zero(), - LookupResult::Full => return Ok(None), - }; - let (to_value, overflow) = to_value.overflowing_add(amount); - if overflow { - return Ok(None); + Ok(previous.map(u256_from_value)) } - - let from_value = from_value - amount; - let from_value_bytes = u256_value(from_value); - let to_value_bytes = u256_value(to_value); - unsafe { - self.write_value(from_index, &from_value_bytes); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, &to_value_bytes), - LookupResult::Vacant(index) => { - self.write_key(index, &to_key); - self.write_value(index, &to_value_bytes); - } - LookupResult::Full => unreachable!("full table handled before writes"), + StaticLookup::Vacant(index) => { + unsafe { + self.write_key(index, &key); + self.write_value(index, &value); } + Ok(None) } - Ok(Some(ActorU256Transfer { - from_balance: from_value, - to_balance: to_value, - })) + StaticLookup::Full => Err(TableError::CapacityOverflow), } + } - /// Moves a known nonzero amount after checking allowance for known distinct, nonzero actors. - /// - /// # Safety - /// - /// The caller must ensure `owner`, `spender`, and `to` are nonzero actor ids, - /// `owner != to`, and `amount != 0`. This skips validation intended for the - /// safe public path. - pub unsafe fn transfer_actor_u256_from_nonzero_distinct_unchecked< - const ALLOWANCE_LOG2_SLOTS: u8, - >( - &self, - allowances: &StaticAllowanceU256Map, - owner: ActorId, - spender: ActorId, - to: ActorId, - amount: U256, - ) -> Result, TableError> { - let owner_key = actor_key(owner); - let spender_key = actor_key(spender); - let to_key = actor_key(to); - - let LookupResult::Found(allowance_index) = - allowances.lookup(&owner_key, &spender_key)? - else { - return Ok(None); - }; - let Some(allowance) = allowances - .visible_value(allowance_index) - .map(u256_from_value) - else { - return Ok(None); - }; - if allowance < amount { - return Ok(None); - } - - let LookupResult::Found(from_index) = self.lookup(&owner_key)? else { - return Ok(None); - }; - let Some(from_balance) = self.visible_value(from_index).map(u256_from_value) else { - return Ok(None); - }; - if from_balance < amount { - return Ok(None); - } - - let to_lookup = self.lookup(&to_key)?; - let to_balance = match to_lookup { - LookupResult::Found(index) => self - .visible_value(index) - .map_or_else(U256::zero, u256_from_value), - LookupResult::Vacant(_) => U256::zero(), - LookupResult::Full => return Ok(None), - }; - let (to_balance, overflow) = to_balance.overflowing_add(amount); - if overflow { - return Ok(None); - } + /// Removes the visible value for `actor`. + pub fn remove_actor_u256(&self, actor: &ActorId) -> Result, TableError> { + let key = actor_key(*actor); + reject_zero_key(&key)?; + self.remove_key(&key) + .map(|previous| previous.map(u256_from_value)) + } - let allowance = allowance - amount; - let from_balance = from_balance - amount; - let allowance_bytes = u256_value(allowance); - let from_balance_bytes = u256_value(from_balance); - let to_balance_bytes = u256_value(to_balance); - unsafe { - allowances.write_value(allowance_index, &allowance_bytes); - self.write_value(from_index, &from_balance_bytes); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, &to_balance_bytes), - LookupResult::Vacant(index) => { - self.write_key(index, &to_key); - self.write_value(index, &to_balance_bytes); - } - LookupResult::Full => unreachable!("full table handled before writes"), - } - } - Ok(Some(ActorU256TransferFrom { - from_balance, - to_balance, - allowance, - })) + /// Clears every slot to the empty state. + pub fn clear(&self) -> Result<(), TableError> { + unsafe { + ptr::write_bytes(self.base as *mut u8, 0, self.bytes()?); } + Ok(()) + } - /// Moves a known nonzero amount and returns only whether it succeeded. - /// - /// # Safety - /// - /// The caller must ensure `from` and `to` are nonzero actor ids, `from != to`, - /// and `amount != 0`. This skips validation intended for the safe public path. - pub unsafe fn transfer_actor_u256_bool_nonzero_distinct_unchecked( - &self, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - let from_key = actor_key(from); - let to_key = actor_key(to); - - let LookupResult::Found(from_index) = self.lookup(&from_key)? else { - return Ok(false); - }; - let Some(from_value) = self.visible_value(from_index).map(u256_from_value) else { - return Ok(false); - }; - if from_value < amount { - return Ok(false); - } - - let to_lookup = self.lookup(&to_key)?; - let to_value = match to_lookup { - LookupResult::Found(index) => self - .visible_value(index) - .map_or_else(U256::zero, u256_from_value), - LookupResult::Vacant(_) => U256::zero(), - LookupResult::Full => return Ok(false), - }; - let (to_value, overflow) = to_value.overflowing_add(amount); - if overflow { - return Ok(false); - } + fn remove_key(&self, key: &[u8; 32]) -> Result, TableError> { + let StaticLookup::Found(index) = self.lookup(key) else { + return Ok(None); + }; - let from_value_bytes = u256_value(from_value - amount); - let to_value_bytes = u256_value(to_value); + let previous = self.visible_value(index); + if previous.is_some() { unsafe { - self.write_value(from_index, &from_value_bytes); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, &to_value_bytes), - LookupResult::Vacant(index) => { - self.write_key(index, &to_key); - self.write_value(index, &to_value_bytes); - } - LookupResult::Full => unreachable!("full table handled before writes"), - } + self.write_value(index, &[0; 32]); } - Ok(true) } + Ok(previous) + } - /// Moves a known nonzero amount after checking allowance and returns only success. - /// - /// # Safety - /// - /// The caller must ensure `owner`, `spender`, and `to` are nonzero actor ids, - /// `owner != to`, and `amount != 0`. This skips validation intended for the - /// safe public path. - pub unsafe fn transfer_actor_u256_from_bool_nonzero_distinct_unchecked< - const ALLOWANCE_LOG2_SLOTS: u8, - >( - &self, - allowances: &StaticAllowanceU256Map, - owner: ActorId, - spender: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - let owner_key = actor_key(owner); - let spender_key = actor_key(spender); - let to_key = actor_key(to); - - let LookupResult::Found(allowance_index) = - allowances.lookup(&owner_key, &spender_key)? - else { - return Ok(false); - }; - let Some(allowance) = allowances - .visible_value(allowance_index) - .map(u256_from_value) - else { - return Ok(false); - }; - if allowance < amount { - return Ok(false); - } - - let LookupResult::Found(from_index) = self.lookup(&owner_key)? else { - return Ok(false); - }; - let Some(from_balance) = self.visible_value(from_index).map(u256_from_value) else { - return Ok(false); - }; - if from_balance < amount { - return Ok(false); - } + fn lookup(&self, key: &[u8; 32]) -> StaticLookup { + let key_words = words_32(key); + let mut index = static_map_index(hash_actor_key(key), LOG2_SLOTS); - let to_lookup = self.lookup(&to_key)?; - let to_balance = match to_lookup { - LookupResult::Found(index) => self - .visible_value(index) - .map_or_else(U256::zero, u256_from_value), - LookupResult::Vacant(_) => U256::zero(), - LookupResult::Full => return Ok(false), - }; - let (to_balance, overflow) = to_balance.overflowing_add(amount); - if overflow { - return Ok(false); + for _ in 0..self.slots { + let stored_key = unsafe { read_words_32(self.key_ptr(index)) }; + if stored_key == key_words { + return StaticLookup::Found(index); } - - let allowance_bytes = u256_value(allowance - amount); - let from_balance_bytes = u256_value(from_balance - amount); - let to_balance_bytes = u256_value(to_balance); - unsafe { - allowances.write_value(allowance_index, &allowance_bytes); - self.write_value(from_index, &from_balance_bytes); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, &to_balance_bytes), - LookupResult::Vacant(index) => { - self.write_key(index, &to_key); - self.write_value(index, &to_balance_bytes); - } - LookupResult::Full => unreachable!("full table handled before writes"), - } + if words_are_zero(stored_key) { + return StaticLookup::Vacant(index); } - Ok(true) - } - /// Clears every slot to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - let bytes = self.bytes()?; - unsafe { - ptr::write_bytes(self.base as *mut u8, 0, bytes); - } - Ok(()) + index = (index + 1) & self.mask; } - fn remove_key(&self, key: &[u8; 32]) -> Result, TableError> { - let LookupResult::Found(index) = self.lookup(key)? else { - return Ok(None); - }; + StaticLookup::Full + } - let previous = self.visible_value(index); - if previous.is_some() { - unsafe { - self.write_value(index, &[0; 32]); - } - } - Ok(previous) + fn visible_value(&self, slot: usize) -> Option<[u8; 32]> { + let value = unsafe { self.read_value(slot) }; + if is_zero_32(&value) { + None + } else { + Some(value) } + } - fn lookup(&self, key: &[u8; 32]) -> Result { - let key_words = words_32(key); - let mut index = static_map_index(H::hash(key), LOG2_SLOTS); - - for _ in 0..self.slots { - let stored_key = unsafe { read_words_32(self.key_ptr(index)) }; - if stored_key == key_words { - return Ok(LookupResult::Found(index)); - } - if words_are_zero(stored_key) { - return Ok(LookupResult::Vacant(index)); - } - - index = (index + 1) & self.mask; - } - - Ok(LookupResult::Full) + unsafe fn read_value(&self, slot: usize) -> [u8; 32] { + let mut bytes = [0u8; 32]; + unsafe { + ptr::copy_nonoverlapping(self.value_ptr(slot), bytes.as_mut_ptr(), 32); } + bytes + } - fn visible_value(&self, slot: usize) -> Option<[u8; 32]> { - let value = unsafe { self.read_value(slot) }; - if is_zero_32(&value) { - None - } else { - Some(value) - } + unsafe fn write_key(&self, slot: usize, key: &[u8; 32]) { + unsafe { + ptr::copy_nonoverlapping(key.as_ptr(), self.key_ptr(slot), 32); } + } - unsafe fn read_value(&self, slot: usize) -> [u8; 32] { - let mut bytes = [0u8; 32]; - unsafe { - ptr::copy_nonoverlapping(self.value_ptr(slot), bytes.as_mut_ptr(), 32); - } - bytes + unsafe fn write_value(&self, slot: usize, value: &[u8; 32]) { + unsafe { + ptr::copy_nonoverlapping(value.as_ptr(), self.value_ptr(slot), 32); } + } - unsafe fn write_key(&self, slot: usize, key: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(key.as_ptr(), self.key_ptr(slot), 32); - } - } + fn key_ptr(&self, slot: usize) -> *mut u8 { + self.slot_ptr(slot) + } - unsafe fn write_value(&self, slot: usize, value: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(value.as_ptr(), self.value_ptr(slot), 32); - } - } + fn value_ptr(&self, slot: usize) -> *mut u8 { + unsafe { self.slot_ptr(slot).add(32) } + } - fn key_ptr(&self, slot: usize) -> *mut u8 { - self.slot_ptr(slot) - } + fn slot_ptr(&self, slot: usize) -> *mut u8 { + (self.base + slot * Self::slot_size()) as *mut u8 + } +} - fn value_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(Self::VALUE_OFFSET) } - } +/// A static-memory map keyed by two `ActorId`s and storing `U256`. +/// +/// The layout uses `2^LOG2_SLOTS` slots. Each slot is exactly 96 bytes: +/// the two 32-byte actor ids followed by a 32-byte little-endian `U256` value. +pub struct StaticActorPairU256Map { + base: usize, + slots: usize, + mask: usize, + _marker: PhantomData<*mut u8>, +} - fn slot_ptr(&self, slot: usize) -> *mut u8 { - (self.base + slot * Self::slot_size()) as *mut u8 - } +impl StaticActorPairU256Map { + /// Returns the byte length of one slot. + pub const fn slot_size() -> usize { + ACTOR_PAIR_U256_SLOT_SIZE } - /// Experimental static balance map keyed by a compact 64-bit actor fingerprint. - /// - /// Each slot is exactly 40 bytes: an 8-byte nonzero actor tag followed by a - /// 32-byte `U256` value. This is a benchmark architecture candidate: it - /// reduces slot size and key-compare work, but it does not store the full - /// `ActorId`, so a production version needs an explicit collision policy. - pub struct StaticActorTagU256Table { - base: usize, - slots: usize, - mask: usize, - _marker: PhantomData<*mut u8>, + /// Returns the configured slot count. + pub fn slots() -> Result { + static_map_slots(LOG2_SLOTS) } - impl StaticActorTagU256Table { - const TAG_SIZE: usize = 8; - const VALUE_OFFSET: usize = 8; - - /// Returns the byte length of one slot. - pub const fn slot_size() -> usize { - 40 - } + /// Returns the mask used for power-of-two probing. + pub fn mask() -> Result { + Ok(Self::slots()? - 1) + } - /// Returns the byte length required for `slots`. - pub fn bytes_len(slots: usize) -> Result { - slots - .checked_mul(Self::slot_size()) - .ok_or(TableError::InvalidLayout) - } + /// Returns the byte length required for this map. + pub fn bytes_len() -> Result { + Self::slots()? + .checked_mul(Self::slot_size()) + .ok_or(TableError::InvalidLayout) + } - /// Creates a table over `slots * 40` bytes at `base`. - /// - /// # Safety - /// - /// The caller must ensure the memory interval is valid for reads and - /// writes for the whole lifetime of the table and does not overlap - /// other mutable state. - pub unsafe fn new(base: usize, slots: usize) -> Result { - if slots == 0 || !slots.is_power_of_two() { - return Err(TableError::InvalidLayout); - } - StaticRegion::new(base, Self::bytes_len(slots)?)?; - Ok(Self { - base, - slots, - mask: slots - 1, - _marker: PhantomData, - }) - } - - /// Returns a balance value by actor id. - pub fn get_actor_u256(&self, key: &ActorId) -> Result, TableError> { - let tag = actor_tag(*key); - let LookupResult::Found(index) = self.lookup(tag)? else { - return Ok(None); - }; - Ok(self.visible_value(index).map(u256_from_value)) - } - - /// Inserts or updates a balance value by actor id. - pub fn insert_actor_u256( - &self, - key: ActorId, - value: U256, - ) -> Result, TableError> { - let tag = actor_tag(key); - let value = u256_value(value); - if is_zero_32(&value) { - return self - .remove_tag(tag) - .map(|previous| previous.map(u256_from_value)); - } - - match self.lookup(tag)? { - LookupResult::Found(index) => { - let previous = self.visible_value(index); - unsafe { - self.write_value(index, &value); - } - Ok(previous.map(u256_from_value)) - } - LookupResult::Vacant(index) => { - unsafe { - self.write_tag(index, tag); - self.write_value(index, &value); - } - Ok(None) - } - LookupResult::Full => Err(TableError::CapacityOverflow), - } - } - - /// Removes a balance value by actor id. - pub fn remove_actor_u256(&self, key: &ActorId) -> Result, TableError> { - self.remove_tag(actor_tag(*key)) - .map(|previous| previous.map(u256_from_value)) - } + /// Creates a map over `2^LOG2_SLOTS * 96` bytes at `base`. + /// + /// # Safety + /// + /// The caller must ensure the memory interval is valid for reads and writes + /// for the whole lifetime of the map and does not overlap other mutable + /// state. + pub unsafe fn new(base: usize) -> Result { + let slots = Self::slots()?; + StaticRegion::new(base, Self::bytes_len()?)?; + Ok(Self { + base, + slots, + mask: slots - 1, + _marker: PhantomData, + }) + } - /// Returns an allowance value by owner and spender tags. - pub fn get_allowance_u256( - &self, - owner: &ActorId, - spender: &ActorId, - ) -> Result, TableError> { - let tag = allowance_tag(*owner, *spender); - let LookupResult::Found(index) = self.lookup(tag)? else { - return Ok(None); - }; - Ok(self.visible_value(index).map(u256_from_value)) - } + /// Returns the configured base address. + pub const fn base(&self) -> usize { + self.base + } - /// Inserts or updates an allowance value by owner and spender tags. - pub fn insert_allowance_u256( - &self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result, TableError> { - let tag = allowance_tag(owner, spender); - let value = u256_value(value); - if is_zero_32(&value) { - return self - .remove_tag(tag) - .map(|previous| previous.map(u256_from_value)); - } + /// Returns the total byte length occupied by this map. + pub fn bytes(&self) -> Result { + Self::bytes_len() + } - match self.lookup(tag)? { - LookupResult::Found(index) => { - let previous = self.visible_value(index); - unsafe { - self.write_value(index, &value); - } - Ok(previous.map(u256_from_value)) - } - LookupResult::Vacant(index) => { - unsafe { - self.write_tag(index, tag); - self.write_value(index, &value); - } - Ok(None) - } - LookupResult::Full => Err(TableError::CapacityOverflow), - } + /// Returns the visible value for `(left, right)`. + pub fn get_actor_pair_u256( + &self, + left: &ActorId, + right: &ActorId, + ) -> Result, TableError> { + let left = actor_key(*left); + let right = actor_key(*right); + if is_zero_32(&left) || is_zero_32(&right) { + return Ok(None); } - /// Inserts or updates an allowance and returns only whether it was written. - pub fn insert_allowance_u256_bool( - &self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result { - if value.is_zero() { - return Ok(false); - } + let StaticLookup::Found(index) = self.lookup(&left, &right) else { + return Ok(None); + }; + Ok(self.visible_value(index).map(u256_from_value)) + } - let tag = allowance_tag(owner, spender); - let value = u256_value(value); - match self.lookup(tag)? { - LookupResult::Found(index) => unsafe { - self.write_value(index, &value); - Ok(true) - }, - LookupResult::Vacant(index) => unsafe { - self.write_tag(index, tag); + /// Inserts or updates `(left, right)`, returning the previous visible value. + /// + /// A zero value removes the visible value. Zero actor ids are rejected + /// because zero key bytes mark empty slots in this layout. + pub fn insert_actor_pair_u256( + &self, + left: ActorId, + right: ActorId, + value: U256, + ) -> Result, TableError> { + let left = actor_key(left); + let right = actor_key(right); + reject_zero_key(&left)?; + reject_zero_key(&right)?; + if value.is_zero() { + return self + .remove_key(&left, &right) + .map(|previous| previous.map(u256_from_value)); + } + + let value = u256_value(value); + match self.lookup(&left, &right) { + StaticLookup::Found(index) => { + let previous = self.visible_value(index); + unsafe { self.write_value(index, &value); - Ok(true) - }, - LookupResult::Full => Err(TableError::CapacityOverflow), - } - } - - /// Experimental low-limb allowance insert for benchmark values. - /// - /// # Safety - /// - /// The caller must ensure `value` fits in `u64` and the target slot has - /// zeroed high limbs when inserting a fresh key. - pub unsafe fn insert_allowance_u256_low64_bool_unchecked( - &self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result { - let tag = allowance_tag(owner, spender); - let value = value.low_u64(); - match self.lookup(tag)? { - LookupResult::Found(index) => unsafe { - self.write_value_low64(index, value); - Ok(true) - }, - LookupResult::Vacant(index) => unsafe { - self.write_tag(index, tag); - self.write_value_low64(index, value); - Ok(true) - }, - LookupResult::Full => Err(TableError::CapacityOverflow), - } - } - - /// Moves a nonzero amount between distinct actors and returns only success. - pub fn transfer_actor_u256_bool( - &self, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - let from_tag = actor_tag(from); - let to_tag = actor_tag(to); - if from_tag == to_tag || amount.is_zero() { - return Ok(false); - } - - let LookupResult::Found(from_index) = self.lookup(from_tag)? else { - return Ok(false); - }; - let Some(from_value) = self.visible_value(from_index).map(u256_from_value) else { - return Ok(false); - }; - if from_value < amount { - return Ok(false); - } - - let to_lookup = self.lookup(to_tag)?; - let to_value = match to_lookup { - LookupResult::Found(index) => self - .visible_value(index) - .map_or_else(U256::zero, u256_from_value), - LookupResult::Vacant(_) => U256::zero(), - LookupResult::Full => return Ok(false), - }; - let (to_value, overflow) = to_value.overflowing_add(amount); - if overflow { - return Ok(false); - } - - let from_value_bytes = u256_value(from_value - amount); - let to_value_bytes = u256_value(to_value); - unsafe { - self.write_value(from_index, &from_value_bytes); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, &to_value_bytes), - LookupResult::Vacant(index) => { - self.write_tag(index, to_tag); - self.write_value(index, &to_value_bytes); - } - LookupResult::Full => unreachable!("full table handled before writes"), - } - } - Ok(true) - } - - /// Moves a nonzero amount after checking allowance and returns only success. - pub fn transfer_actor_u256_from_bool( - &self, - allowances: &StaticAllowanceU256Map, - owner: ActorId, - spender: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - let owner_key = actor_key(owner); - let spender_key = actor_key(spender); - let LookupResult::Found(allowance_index) = - allowances.lookup(&owner_key, &spender_key)? - else { - return Ok(false); - }; - let Some(allowance) = allowances - .visible_value(allowance_index) - .map(u256_from_value) - else { - return Ok(false); - }; - if allowance < amount { - return Ok(false); - } - - let transferred = self.transfer_actor_u256_bool(owner, to, amount)?; - if !transferred { - return Ok(false); - } - - let allowance_bytes = u256_value(allowance - amount); - unsafe { - allowances.write_value(allowance_index, &allowance_bytes); - } - Ok(true) - } - - /// Moves a nonzero amount after checking a compact-tag allowance table. - pub fn transfer_actor_u256_from_tag_bool( - &self, - allowances: &StaticActorTagU256Table, - owner: ActorId, - spender: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - let allowance_tag = allowance_tag(owner, spender); - let LookupResult::Found(allowance_index) = allowances.lookup(allowance_tag)? else { - return Ok(false); - }; - let Some(allowance) = allowances - .visible_value(allowance_index) - .map(u256_from_value) - else { - return Ok(false); - }; - if allowance < amount { - return Ok(false); - } - - let from_tag = actor_tag(owner); - let to_tag = actor_tag(to); - if from_tag == to_tag || amount.is_zero() { - return Ok(false); - } - - let LookupResult::Found(from_index) = self.lookup(from_tag)? else { - return Ok(false); - }; - let Some(from_value) = self.visible_value(from_index).map(u256_from_value) else { - return Ok(false); - }; - if from_value < amount { - return Ok(false); - } - - let to_lookup = self.lookup(to_tag)?; - let to_value = match to_lookup { - LookupResult::Found(index) => self - .visible_value(index) - .map_or_else(U256::zero, u256_from_value), - LookupResult::Vacant(_) => U256::zero(), - LookupResult::Full => return Ok(false), - }; - let (to_value, overflow) = to_value.overflowing_add(amount); - if overflow { - return Ok(false); - } - - let allowance_bytes = u256_value(allowance - amount); - let from_value_bytes = u256_value(from_value - amount); - let to_value_bytes = u256_value(to_value); - unsafe { - allowances.write_value(allowance_index, &allowance_bytes); - self.write_value(from_index, &from_value_bytes); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, &to_value_bytes), - LookupResult::Vacant(index) => { - self.write_tag(index, to_tag); - self.write_value(index, &to_value_bytes); - } - LookupResult::Full => unreachable!("full table handled before writes"), } + Ok(previous.map(u256_from_value)) } - Ok(true) - } - - /// Experimental low-limb transfer fast path for benchmark values. - /// - /// # Safety - /// - /// The caller must ensure balances and `amount` fit in `u64` and high - /// limbs are zero. This is not a general `U256` transfer implementation. - pub unsafe fn transfer_actor_u256_low64_bool_unchecked( - &self, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - let amount = amount.low_u64(); - let from_tag = actor_tag(from); - let to_tag = actor_tag(to); - let LookupResult::Found(from_index) = self.lookup(from_tag)? else { - return Ok(false); - }; - let from_value = unsafe { self.read_value_low64(from_index) }; - if from_value < amount { - return Ok(false); - } - - let to_lookup = self.lookup(to_tag)?; - let to_value = match to_lookup { - LookupResult::Found(index) => unsafe { self.read_value_low64(index) }, - LookupResult::Vacant(_) => 0, - LookupResult::Full => return Ok(false), - }; - let Some(to_value) = to_value.checked_add(amount) else { - return Ok(false); - }; - - unsafe { - self.write_value_low64(from_index, from_value - amount); - match to_lookup { - LookupResult::Found(index) => self.write_value_low64(index, to_value), - LookupResult::Vacant(index) => { - self.write_tag(index, to_tag); - self.write_value_low64(index, to_value); - } - LookupResult::Full => unreachable!("full table handled before writes"), - } - } - Ok(true) - } - - /// Experimental low-limb transfer-from fast path for benchmark values. - /// - /// # Safety - /// - /// The caller must ensure balances, allowances, and `amount` fit in `u64` - /// and high limbs are zero. This is not a general `U256` implementation. - pub unsafe fn transfer_actor_u256_from_tag_low64_bool_unchecked( - &self, - allowances: &StaticActorTagU256Table, - owner: ActorId, - spender: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - let amount = amount.low_u64(); - let allowance_tag = allowance_tag(owner, spender); - let LookupResult::Found(allowance_index) = allowances.lookup(allowance_tag)? else { - return Ok(false); - }; - let allowance = unsafe { allowances.read_value_low64(allowance_index) }; - if allowance < amount { - return Ok(false); - } - - let from_tag = actor_tag(owner); - let to_tag = actor_tag(to); - let LookupResult::Found(from_index) = self.lookup(from_tag)? else { - return Ok(false); - }; - let from_value = unsafe { self.read_value_low64(from_index) }; - if from_value < amount { - return Ok(false); - } - - let to_lookup = self.lookup(to_tag)?; - let to_value = match to_lookup { - LookupResult::Found(index) => unsafe { self.read_value_low64(index) }, - LookupResult::Vacant(_) => 0, - LookupResult::Full => return Ok(false), - }; - let Some(to_value) = to_value.checked_add(amount) else { - return Ok(false); - }; - - unsafe { - allowances.write_value_low64(allowance_index, allowance - amount); - self.write_value_low64(from_index, from_value - amount); - match to_lookup { - LookupResult::Found(index) => self.write_value_low64(index, to_value), - LookupResult::Vacant(index) => { - self.write_tag(index, to_tag); - self.write_value_low64(index, to_value); - } - LookupResult::Full => unreachable!("full table handled before writes"), - } - } - Ok(true) - } - - /// Clears every slot to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - unsafe { - ptr::write_bytes(self.base as *mut u8, 0, Self::bytes_len(self.slots)?); - } - Ok(()) - } - - fn remove_tag(&self, tag: u64) -> Result, TableError> { - let LookupResult::Found(index) = self.lookup(tag)? else { - return Ok(None); - }; - let previous = self.visible_value(index); - if previous.is_some() { + StaticLookup::Vacant(index) => { unsafe { - self.write_value(index, &[0; 32]); + self.write_left(index, &left); + self.write_right(index, &right); + self.write_value(index, &value); } + Ok(None) } - Ok(previous) + StaticLookup::Full => Err(TableError::CapacityOverflow), } + } - fn lookup(&self, tag: u64) -> Result { - let mut index = (tag as usize) & self.mask; - for _ in 0..self.slots { - let stored = unsafe { self.read_tag(index) }; - if stored == tag { - return Ok(LookupResult::Found(index)); - } - if stored == 0 { - return Ok(LookupResult::Vacant(index)); - } - index += 1; - if index == self.slots { - index = 0; - } - } - - Ok(LookupResult::Full) - } + /// Removes the visible value for `(left, right)`. + pub fn remove_actor_pair_u256( + &self, + left: &ActorId, + right: &ActorId, + ) -> Result, TableError> { + let left = actor_key(*left); + let right = actor_key(*right); + reject_zero_key(&left)?; + reject_zero_key(&right)?; + self.remove_key(&left, &right) + .map(|previous| previous.map(u256_from_value)) + } - fn visible_value(&self, slot: usize) -> Option<[u8; 32]> { - let value = unsafe { self.read_value(slot) }; - if is_zero_32(&value) { - None - } else { - Some(value) - } + /// Clears every slot to the empty state. + pub fn clear(&self) -> Result<(), TableError> { + unsafe { + ptr::write_bytes(self.base as *mut u8, 0, self.bytes()?); } + Ok(()) + } - unsafe fn read_tag(&self, slot: usize) -> u64 { - let mut bytes = [0u8; Self::TAG_SIZE]; - unsafe { - ptr::copy_nonoverlapping(self.tag_ptr(slot), bytes.as_mut_ptr(), Self::TAG_SIZE); - } - u64::from_le_bytes(bytes) - } + fn remove_key( + &self, + left: &[u8; 32], + right: &[u8; 32], + ) -> Result, TableError> { + let StaticLookup::Found(index) = self.lookup(left, right) else { + return Ok(None); + }; - unsafe fn read_value(&self, slot: usize) -> [u8; 32] { - let mut bytes = [0u8; 32]; + let previous = self.visible_value(index); + if previous.is_some() { unsafe { - ptr::copy_nonoverlapping(self.value_ptr(slot), bytes.as_mut_ptr(), 32); + self.write_value(index, &[0; 32]); } - bytes - } - - unsafe fn read_value_low64(&self, slot: usize) -> u64 { - unsafe { ptr::read_unaligned(self.value_ptr(slot).cast::()) } } + Ok(previous) + } - unsafe fn write_tag(&self, slot: usize, tag: u64) { - let bytes = tag.to_le_bytes(); - unsafe { - ptr::copy_nonoverlapping(bytes.as_ptr(), self.tag_ptr(slot), Self::TAG_SIZE); - } - } + fn lookup(&self, left: &[u8; 32], right: &[u8; 32]) -> StaticLookup { + let left_words = words_32(left); + let right_words = words_32(right); + let mut index = static_map_index(hash_actor_pair_key(left, right), LOG2_SLOTS); - unsafe fn write_value(&self, slot: usize, value: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(value.as_ptr(), self.value_ptr(slot), 32); + for _ in 0..self.slots { + let stored_left = unsafe { read_words_32(self.left_ptr(index)) }; + if words_are_zero(stored_left) { + return StaticLookup::Vacant(index); } - } - - unsafe fn write_value_low64(&self, slot: usize, value: u64) { - unsafe { - ptr::write_unaligned(self.value_ptr(slot).cast::(), value); + if stored_left == left_words + && unsafe { read_words_32(self.right_ptr(index)) } == right_words + { + return StaticLookup::Found(index); } - } - - fn tag_ptr(&self, slot: usize) -> *mut u8 { - self.slot_ptr(slot) - } - - fn value_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(Self::VALUE_OFFSET) } - } - fn slot_ptr(&self, slot: usize) -> *mut u8 { - (self.base + slot * Self::slot_size()) as *mut u8 + index = (index + 1) & self.mask; } - } - /// Experimental compact static map keyed by a 64-bit actor tag and storing `u64`. - /// - /// Each slot is 16 bytes: 8 bytes of nonzero tag and 8 bytes of value. This - /// is only a benchmark architecture candidate for low-limb VFT values. - pub struct StaticActorTagU64Table { - base: usize, - slots: usize, - mask: usize, - _marker: PhantomData<*mut u8>, + StaticLookup::Full } - impl StaticActorTagU64Table { - const VALUE_OFFSET: usize = 8; - - /// Returns the byte length of one slot. - pub const fn slot_size() -> usize { - 16 - } - - /// Returns the byte length required for `slots`. - pub fn bytes_len(slots: usize) -> Result { - slots - .checked_mul(Self::slot_size()) - .ok_or(TableError::InvalidLayout) - } - - /// Creates a table over `slots * 16` bytes at `base`. - /// - /// # Safety - /// - /// The caller must ensure the memory interval is valid for reads and - /// writes for the whole lifetime of the table and does not overlap - /// other mutable state. - pub unsafe fn new(base: usize, slots: usize) -> Result { - if slots == 0 || !slots.is_power_of_two() { - return Err(TableError::InvalidLayout); - } - StaticRegion::new(base, Self::bytes_len(slots)?)?; - Ok(Self { - base, - slots, - mask: slots - 1, - _marker: PhantomData, - }) - } - - /// Returns a balance value by actor id. - pub fn get_actor_u256(&self, key: &ActorId) -> Result, TableError> { - let tag = actor_tag(*key); - let LookupResult::Found(index) = self.lookup(tag) else { - return Ok(None); - }; - Ok(Some(U256::from(unsafe { self.read_value(index) }))) - } - - /// Inserts or updates a low-limb balance value by actor id. - pub fn insert_actor_u256( - &self, - key: ActorId, - value: U256, - ) -> Result, TableError> { - let tag = actor_tag(key); - let value = value.low_u64(); - match self.lookup(tag) { - LookupResult::Found(index) => { - let previous = unsafe { self.read_value(index) }; - unsafe { - self.write_value(index, value); - } - Ok(Some(U256::from(previous))) - } - LookupResult::Vacant(index) => unsafe { - self.write_tag(index, tag); - self.write_value(index, value); - Ok(None) - }, - LookupResult::Full => Err(TableError::CapacityOverflow), - } - } - - /// Removes a value by actor id. - pub fn remove_actor_u256(&self, key: &ActorId) -> Result, TableError> { - let tag = actor_tag(*key); - let LookupResult::Found(index) = self.lookup(tag) else { - return Ok(None); - }; - let previous = unsafe { self.read_value(index) }; - unsafe { - self.write_value(index, 0); - } - Ok(Some(U256::from(previous))) - } - - /// Inserts or updates a low-limb allowance value and returns success. - pub fn insert_allowance_u256_bool( - &self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result { - let tag = allowance_tag(owner, spender); - let value = value.low_u64(); - match self.lookup(tag) { - LookupResult::Found(index) => unsafe { - self.write_value(index, value); - Ok(true) - }, - LookupResult::Vacant(index) => unsafe { - self.write_tag(index, tag); - self.write_value(index, value); - Ok(true) - }, - LookupResult::Full => Err(TableError::CapacityOverflow), - } + fn visible_value(&self, slot: usize) -> Option<[u8; 32]> { + if unsafe { words_at_zero(self.value_ptr(slot)) } { + None + } else { + Some(unsafe { self.read_value(slot) }) } + } - /// Inserts or updates a raw tag value and returns success. - pub fn insert_tag_value_bool(&self, tag: u64, value: u64) -> Result { - let tag = if tag == 0 { 1 } else { tag }; - match self.lookup(tag) { - LookupResult::Found(index) => unsafe { - self.write_value(index, value); - Ok(true) - }, - LookupResult::Vacant(index) => unsafe { - self.write_tag(index, tag); - self.write_value(index, value); - Ok(true) - }, - LookupResult::Full => Err(TableError::CapacityOverflow), - } + unsafe fn read_value(&self, slot: usize) -> [u8; 32] { + let mut bytes = [0u8; 32]; + unsafe { + ptr::copy_nonoverlapping(self.value_ptr(slot), bytes.as_mut_ptr(), 32); } + bytes + } - /// Inserts or updates a raw tag value when capacity is known to be available. - /// - /// # Safety - /// - /// The caller must ensure `tag` is unique to the logical key and the table - /// has at least one vacant slot if this is a new tag. - pub unsafe fn insert_tag_value_bool_capacity_unchecked( - &self, - tag: u64, - value: u64, - ) -> bool { - let tag = if tag == 0 { 1 } else { tag }; - match unsafe { self.lookup_capacity_unchecked(tag) } { - LookupResult::Found(index) => unsafe { - self.write_value(index, value); - true - }, - LookupResult::Vacant(index) => unsafe { - self.write_tag(index, tag); - self.write_value(index, value); - true - }, - LookupResult::Full => unsafe { core::hint::unreachable_unchecked() }, - } + unsafe fn write_left(&self, slot: usize, left: &[u8; 32]) { + unsafe { + ptr::copy_nonoverlapping(left.as_ptr(), self.left_ptr(slot), 32); } + } - /// Returns an allowance value by owner and spender. - pub fn get_allowance_u256( - &self, - owner: &ActorId, - spender: &ActorId, - ) -> Result, TableError> { - let tag = allowance_tag(*owner, *spender); - let LookupResult::Found(index) = self.lookup(tag) else { - return Ok(None); - }; - Ok(Some(U256::from(unsafe { self.read_value(index) }))) + unsafe fn write_right(&self, slot: usize, right: &[u8; 32]) { + unsafe { + ptr::copy_nonoverlapping(right.as_ptr(), self.right_ptr(slot), 32); } + } - /// Moves a low-limb amount and returns success. - pub fn transfer_actor_u256_bool( - &self, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - let amount = amount.low_u64(); - let from_tag = actor_tag(from); - let to_tag = actor_tag(to); - let LookupResult::Found(from_index) = self.lookup(from_tag) else { - return Ok(false); - }; - let from_value = unsafe { self.read_value(from_index) }; - if from_value < amount { - return Ok(false); - } - - let to_lookup = self.lookup(to_tag); - let to_value = match to_lookup { - LookupResult::Found(index) => unsafe { self.read_value(index) }, - LookupResult::Vacant(_) => 0, - LookupResult::Full => return Ok(false), - }; - let Some(to_value) = to_value.checked_add(amount) else { - return Ok(false); - }; - - unsafe { - self.write_value(from_index, from_value - amount); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, to_value), - LookupResult::Vacant(index) => { - self.write_tag(index, to_tag); - self.write_value(index, to_value); - } - LookupResult::Full => unreachable!("full table handled before writes"), - } - } - Ok(true) + unsafe fn write_value(&self, slot: usize, value: &[u8; 32]) { + unsafe { + ptr::copy_nonoverlapping(value.as_ptr(), self.value_ptr(slot), 32); } + } - /// Moves a low-limb amount between raw tags and returns success. - pub fn transfer_tags_bool( - &self, - from_tag: u64, - to_tag: u64, - amount: u64, - ) -> Result { - let from_tag = if from_tag == 0 { 1 } else { from_tag }; - let to_tag = if to_tag == 0 { 1 } else { to_tag }; - let LookupResult::Found(from_index) = self.lookup(from_tag) else { - return Ok(false); - }; - let from_value = unsafe { self.read_value(from_index) }; - if from_value < amount { - return Ok(false); - } - - let to_lookup = self.lookup(to_tag); - let to_value = match to_lookup { - LookupResult::Found(index) => unsafe { self.read_value(index) }, - LookupResult::Vacant(_) => 0, - LookupResult::Full => return Ok(false), - }; - let Some(to_value) = to_value.checked_add(amount) else { - return Ok(false); - }; - - unsafe { - self.write_value(from_index, from_value - amount); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, to_value), - LookupResult::Vacant(index) => { - self.write_tag(index, to_tag); - self.write_value(index, to_value); - } - LookupResult::Full => unreachable!("full table handled before writes"), - } - } - Ok(true) - } + fn left_ptr(&self, slot: usize) -> *mut u8 { + self.slot_ptr(slot) + } - /// Moves a low-limb amount between raw tags when capacity is known. - /// - /// # Safety - /// - /// The caller must ensure nonzero tags identify logical accounts, the table - /// has capacity for a missing recipient, and recipient overflow is impossible - /// for the token supply being represented. - pub unsafe fn transfer_tags_bool_capacity_unchecked( - &self, - from_tag: u64, - to_tag: u64, - amount: u64, - ) -> bool { - let from_tag = if from_tag == 0 { 1 } else { from_tag }; - let to_tag = if to_tag == 0 { 1 } else { to_tag }; - let LookupResult::Found(from_index) = - (unsafe { self.lookup_capacity_unchecked(from_tag) }) - else { - return false; - }; - let from_value = unsafe { self.read_value(from_index) }; - if from_value < amount { - return false; - } + fn right_ptr(&self, slot: usize) -> *mut u8 { + unsafe { self.slot_ptr(slot).add(32) } + } - let to_lookup = unsafe { self.lookup_capacity_unchecked(to_tag) }; - let to_value = match to_lookup { - LookupResult::Found(index) => unsafe { self.read_value(index) }, - LookupResult::Vacant(_) => 0, - LookupResult::Full => unsafe { core::hint::unreachable_unchecked() }, - }; + fn value_ptr(&self, slot: usize) -> *mut u8 { + unsafe { self.slot_ptr(slot).add(64) } + } - unsafe { - self.write_value(from_index, from_value - amount); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, to_value + amount), - LookupResult::Vacant(index) => { - self.write_tag(index, to_tag); - self.write_value(index, amount); - } - LookupResult::Full => core::hint::unreachable_unchecked(), - } - } - true - } + fn slot_ptr(&self, slot: usize) -> *mut u8 { + (self.base + slot * Self::slot_size()) as *mut u8 + } +} - /// Moves a low-limb amount after checking a low-limb allowance. - pub fn transfer_actor_u256_from_bool( - &self, - allowances: &StaticActorTagU64Table, - owner: ActorId, - spender: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - let amount = amount.low_u64(); - let allowance_tag = allowance_tag(owner, spender); - let LookupResult::Found(allowance_index) = allowances.lookup(allowance_tag) else { - return Ok(false); - }; - let allowance = unsafe { allowances.read_value(allowance_index) }; - if allowance < amount { - return Ok(false); - } +#[cfg(test)] +mod tests { + extern crate std; - let from_tag = actor_tag(owner); - let to_tag = actor_tag(to); - let LookupResult::Found(from_index) = self.lookup(from_tag) else { - return Ok(false); - }; - let from_value = unsafe { self.read_value(from_index) }; - if from_value < amount { - return Ok(false); - } + use super::*; + use gprimitives::{ActorId, U256}; + use std::{vec, vec::Vec}; - let to_lookup = self.lookup(to_tag); - let to_value = match to_lookup { - LookupResult::Found(index) => unsafe { self.read_value(index) }, - LookupResult::Vacant(_) => 0, - LookupResult::Full => return Ok(false), - }; - let Some(to_value) = to_value.checked_add(amount) else { - return Ok(false); - }; + type Fixed = FixedOpenAddressMap<1, 1, 2>; - unsafe { - allowances.write_value(allowance_index, allowance - amount); - self.write_value(from_index, from_value - amount); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, to_value), - LookupResult::Vacant(index) => { - self.write_tag(index, to_tag); - self.write_value(index, to_value); - } - LookupResult::Full => unreachable!("full table handled before writes"), + fn colliding_keys(slots: usize) -> ([u8; 1], [u8; 1]) { + for left in 0u8..=u8::MAX { + for right in left.wrapping_add(1)..=u8::MAX { + if hash_bytes(&[left]) % slots == hash_bytes(&[right]) % slots { + return ([left], [right]); } } - Ok(true) - } - - /// Moves a low-limb amount after checking a raw allowance tag. - pub fn transfer_from_tags_bool( - &self, - allowances: &StaticActorTagU64Table, - allowance_tag: u64, - from_tag: u64, - to_tag: u64, - amount: u64, - ) -> Result { - let allowance_tag = if allowance_tag == 0 { 1 } else { allowance_tag }; - let LookupResult::Found(allowance_index) = allowances.lookup(allowance_tag) else { - return Ok(false); - }; - let allowance = unsafe { allowances.read_value(allowance_index) }; - if allowance < amount { - return Ok(false); - } - - let from_tag = if from_tag == 0 { 1 } else { from_tag }; - let to_tag = if to_tag == 0 { 1 } else { to_tag }; - let LookupResult::Found(from_index) = self.lookup(from_tag) else { - return Ok(false); - }; - let from_value = unsafe { self.read_value(from_index) }; - if from_value < amount { - return Ok(false); - } - - let to_lookup = self.lookup(to_tag); - let to_value = match to_lookup { - LookupResult::Found(index) => unsafe { self.read_value(index) }, - LookupResult::Vacant(_) => 0, - LookupResult::Full => return Ok(false), - }; - let Some(to_value) = to_value.checked_add(amount) else { - return Ok(false); - }; - - unsafe { - allowances.write_value(allowance_index, allowance - amount); - self.write_value(from_index, from_value - amount); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, to_value), - LookupResult::Vacant(index) => { - self.write_tag(index, to_tag); - self.write_value(index, to_value); - } - LookupResult::Full => unreachable!("full table handled before writes"), - } - } - Ok(true) - } - - /// Moves a low-limb amount after checking a raw allowance tag when capacity is known. - /// - /// # Safety - /// - /// The caller must ensure nonzero tags identify logical accounts, both - /// tables have capacity for the operation, and recipient overflow is - /// impossible for the token supply being represented. - pub unsafe fn transfer_from_tags_bool_capacity_unchecked( - &self, - allowances: &StaticActorTagU64Table, - allowance_tag: u64, - from_tag: u64, - to_tag: u64, - amount: u64, - ) -> bool { - let allowance_tag = if allowance_tag == 0 { 1 } else { allowance_tag }; - let LookupResult::Found(allowance_index) = - (unsafe { allowances.lookup_capacity_unchecked(allowance_tag) }) - else { - return false; - }; - let allowance = unsafe { allowances.read_value(allowance_index) }; - if allowance < amount { - return false; - } - - let from_tag = if from_tag == 0 { 1 } else { from_tag }; - let to_tag = if to_tag == 0 { 1 } else { to_tag }; - let LookupResult::Found(from_index) = - (unsafe { self.lookup_capacity_unchecked(from_tag) }) - else { - return false; - }; - let from_value = unsafe { self.read_value(from_index) }; - if from_value < amount { - return false; - } - - let to_lookup = unsafe { self.lookup_capacity_unchecked(to_tag) }; - let to_value = match to_lookup { - LookupResult::Found(index) => unsafe { self.read_value(index) }, - LookupResult::Vacant(_) => 0, - LookupResult::Full => unsafe { core::hint::unreachable_unchecked() }, - }; - - unsafe { - allowances.write_value(allowance_index, allowance - amount); - self.write_value(from_index, from_value - amount); - match to_lookup { - LookupResult::Found(index) => self.write_value(index, to_value + amount), - LookupResult::Vacant(index) => { - self.write_tag(index, to_tag); - self.write_value(index, amount); - } - LookupResult::Full => core::hint::unreachable_unchecked(), - } - } - true - } - - /// Clears every slot to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - unsafe { - ptr::write_bytes(self.base as *mut u8, 0, Self::bytes_len(self.slots)?); - } - Ok(()) - } - - fn lookup(&self, tag: u64) -> LookupResult { - let mut index = (tag as usize) & self.mask; - for _ in 0..self.slots { - let stored = unsafe { self.read_tag(index) }; - if stored == tag { - return LookupResult::Found(index); - } - if stored == 0 { - return LookupResult::Vacant(index); - } - index = (index + 1) & self.mask; - } - LookupResult::Full - } - - unsafe fn lookup_capacity_unchecked(&self, tag: u64) -> LookupResult { - let mut index = (tag as usize) & self.mask; - loop { - let stored = unsafe { self.read_tag(index) }; - if stored == tag { - return LookupResult::Found(index); - } - if stored == 0 { - return LookupResult::Vacant(index); - } - index = (index + 1) & self.mask; - } - } - - unsafe fn read_tag(&self, slot: usize) -> u64 { - unsafe { ptr::read_unaligned(self.tag_ptr(slot).cast::()) } - } - - unsafe fn read_value(&self, slot: usize) -> u64 { - unsafe { ptr::read_unaligned(self.value_ptr(slot).cast::()) } - } - - unsafe fn write_tag(&self, slot: usize, tag: u64) { - unsafe { - ptr::write_unaligned(self.tag_ptr(slot).cast::(), tag); - } - } - - unsafe fn write_value(&self, slot: usize, value: u64) { - unsafe { - ptr::write_unaligned(self.value_ptr(slot).cast::(), value); - } - } - - fn tag_ptr(&self, slot: usize) -> *mut u8 { - self.slot_ptr(slot) - } - - fn value_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(Self::VALUE_OFFSET) } - } - - fn slot_ptr(&self, slot: usize) -> *mut u8 { - (self.base + slot * Self::slot_size()) as *mut u8 - } - } - - /// A static balance map with separate control bytes and 64-byte value slots. - /// - /// Each data slot is exactly 64 bytes: 32 bytes of key followed by 32 - /// bytes of value. The separate control region stores empty/deleted state - /// and a 7-bit hash fingerprint, so most probes can reject a slot without - /// loading its full 32-byte key. - pub struct StaticControlActorIdU256Map { - control_base: usize, - slots_base: usize, - slots: usize, - mask: usize, - _marker: PhantomData<*mut u8>, - } - - impl StaticControlActorIdU256Map { - const CONTROL_EMPTY: u8 = 0x00; - const CONTROL_DELETED: u8 = 0xff; - const CONTROL_FULL_MAX: u8 = 0x80; - const VALUE_OFFSET: usize = 32; - - /// Returns the byte length of one data slot. - pub const fn slot_size() -> usize { - ACTOR_ID_U256_SLOT_SIZE - } - - /// Returns the configured slot count. - pub fn slots() -> Result { - static_map_slots(LOG2_SLOTS) - } - - /// Returns the mask used for power-of-two probing. - pub fn mask() -> Result { - Ok(Self::slots()? - 1) - } - - /// Returns the byte length required for the control region. - pub fn control_bytes_len() -> Result { - Self::slots() - } - - /// Returns the byte length required for the data slot region. - pub fn slots_bytes_len() -> Result { - Self::slots()? - .checked_mul(Self::slot_size()) - .ok_or(TableError::InvalidLayout) - } - - /// Returns the logical byte length required by both regions. - pub fn bytes_len() -> Result { - Self::control_bytes_len()? - .checked_add(Self::slots_bytes_len()?) - .ok_or(TableError::InvalidLayout) - } - - /// Creates a control-byte table over separate control and data regions. - /// - /// # Safety - /// - /// The caller must ensure both memory intervals are valid for reads and - /// writes for the whole lifetime of the table and do not overlap other - /// mutable state. - pub unsafe fn new(control_base: usize, slots_base: usize) -> Result { - let slots = Self::slots()?; - let control_region = StaticRegion::new(control_base, slots)?; - let slots_bytes = slots - .checked_mul(Self::slot_size()) - .ok_or(TableError::InvalidLayout)?; - let slots_region = StaticRegion::new(slots_base, slots_bytes)?; - if regions_overlap(control_region, slots_region)? { - return Err(TableError::InvalidLayout); - } - - Ok(Self { - control_base, - slots_base, - slots, - mask: slots - 1, - _marker: PhantomData, - }) - } - - /// Returns the configured control region base address. - pub const fn control_base(&self) -> usize { - self.control_base - } - - /// Returns the configured data slot region base address. - pub const fn slots_base(&self) -> usize { - self.slots_base - } - - /// Returns the logical byte length occupied by both regions. - pub fn bytes(&self) -> Result { - Self::bytes_len() - } - - /// Returns a balance value by actor id. - pub fn get_actor_u256(&self, key: &ActorId) -> Result, TableError> { - let key = actor_key(*key); - if is_zero_32(&key) { - return Ok(None); - } - - let LookupResult::Found(index) = self.lookup(&key)? else { - return Ok(None); - }; - Ok(self.visible_value(index).map(u256_from_value)) - } - - /// Inserts or updates a balance value by actor id. - /// - /// A zero value removes the visible value without writing a new absent - /// key. Zero actor ids are rejected because zeroed control memory - /// already represents empty slots. - pub fn insert_actor_u256( - &self, - key: ActorId, - value: U256, - ) -> Result, TableError> { - let key = actor_key(key); - reject_zero_key(&key)?; - if value.is_zero() { - return self - .remove_key(&key) - .map(|previous| previous.map(u256_from_value)); - } - - let value = u256_value(value); - let hash = hash_actor_key(&key); - match self.lookup_with_hash(&key, hash)? { - LookupResult::Found(index) => { - let previous = self.visible_value(index); - unsafe { - self.write_value(index, &value); - } - Ok(previous.map(u256_from_value)) - } - LookupResult::Vacant(index) => { - unsafe { - self.write_key(index, &key); - self.write_value(index, &value); - self.write_control(index, control_fingerprint(hash)); - } - Ok(None) - } - LookupResult::Full => Err(TableError::CapacityOverflow), - } - } - - /// Removes a balance value by actor id. - pub fn remove_actor_u256(&self, key: &ActorId) -> Result, TableError> { - let key = actor_key(*key); - reject_zero_key(&key)?; - self.remove_key(&key) - .map(|previous| previous.map(u256_from_value)) - } - - /// Clears every slot to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - unsafe { - ptr::write_bytes(self.control_base as *mut u8, 0, Self::control_bytes_len()?); - ptr::write_bytes(self.slots_base as *mut u8, 0, Self::slots_bytes_len()?); - } - Ok(()) - } - - fn remove_key(&self, key: &[u8; 32]) -> Result, TableError> { - let LookupResult::Found(index) = self.lookup(key)? else { - return Ok(None); - }; - - let previous = self.visible_value(index); - unsafe { - self.write_control(index, Self::CONTROL_DELETED); - } - Ok(previous) - } - - fn lookup(&self, key: &[u8; 32]) -> Result { - self.lookup_with_hash(key, hash_actor_key(key)) - } - - fn lookup_with_hash(&self, key: &[u8; 32], hash: u32) -> Result { - let fingerprint = control_fingerprint(hash); - let key_words = words_32(key); - let mut first_deleted = None; - let mut index = static_map_index(hash, LOG2_SLOTS); - - for _ in 0..self.slots { - let control = unsafe { self.read_control(index) }; - match control { - Self::CONTROL_EMPTY => { - return Ok(LookupResult::Vacant(first_deleted.unwrap_or(index))); - } - Self::CONTROL_DELETED => { - if first_deleted.is_none() { - first_deleted = Some(index); - } - } - 1..=Self::CONTROL_FULL_MAX => { - if control == fingerprint - && unsafe { read_words_32(self.key_ptr(index)) } == key_words - { - return Ok(LookupResult::Found(index)); - } - } - _ => return Err(TableError::InvalidSlotState), - } - - index = (index + 1) & self.mask; - } - - Ok(first_deleted.map_or(LookupResult::Full, LookupResult::Vacant)) - } - - fn visible_value(&self, slot: usize) -> Option<[u8; 32]> { - let value = unsafe { self.read_value(slot) }; - if is_zero_32(&value) { - None - } else { - Some(value) - } - } - - unsafe fn read_control(&self, slot: usize) -> u8 { - unsafe { ptr::read(self.control_ptr(slot)) } - } - - unsafe fn read_value(&self, slot: usize) -> [u8; 32] { - let mut bytes = [0u8; 32]; - unsafe { - ptr::copy_nonoverlapping(self.value_ptr(slot), bytes.as_mut_ptr(), 32); - } - bytes - } - - unsafe fn write_control(&self, slot: usize, control: u8) { - unsafe { - ptr::write(self.control_ptr(slot), control); - } - } - - unsafe fn write_key(&self, slot: usize, key: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(key.as_ptr(), self.key_ptr(slot), 32); - } - } - - unsafe fn write_value(&self, slot: usize, value: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(value.as_ptr(), self.value_ptr(slot), 32); - } - } - - fn control_ptr(&self, slot: usize) -> *mut u8 { - (self.control_base + slot) as *mut u8 - } - - fn key_ptr(&self, slot: usize) -> *mut u8 { - self.slot_ptr(slot) - } - - fn value_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(Self::VALUE_OFFSET) } - } - - fn slot_ptr(&self, slot: usize) -> *mut u8 { - (self.slots_base + slot * Self::slot_size()) as *mut u8 - } - } - - /// A page-local static balance map keyed by `ActorId` and storing `U256`. - /// - /// Each 16 KiB tile stores its control bytes and data slots together: - /// 252 control bytes, 4 padding bytes, then 252 64-byte key/value slots. - /// This keeps the control byte, actor key, and value for a slot inside the - /// same Gear page while preserving control-byte rejection for missing keys. - pub struct StaticPageLocalActorIdU256Map { - base: usize, - tiles: usize, - tile_mask: usize, - slots: usize, - _marker: PhantomData<*mut u8>, - } - - impl StaticPageLocalActorIdU256Map { - const CONTROL_EMPTY: u8 = 0x00; - const CONTROL_DELETED: u8 = 0xff; - const CONTROL_FULL_MAX: u8 = 0x80; - const VALUE_OFFSET: usize = 32; - - /// Returns the byte length of one data slot. - pub const fn slot_size() -> usize { - ACTOR_ID_U256_SLOT_SIZE - } - - /// Returns the byte length of one page-local tile. - pub const fn tile_bytes() -> usize { - PAGE_LOCAL_ACTOR_U256_TILE_BYTES - } - - /// Returns the data offset inside each tile. - pub const fn data_offset() -> usize { - PAGE_LOCAL_ACTOR_U256_DATA_OFFSET - } - - /// Returns the number of actor/value slots inside each tile. - pub const fn slots_per_tile() -> usize { - PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE - } - - /// Returns the configured tile count. - pub fn tiles() -> Result { - static_map_slots(LOG2_TILES) - } - - /// Returns the configured slot count. - pub fn slots() -> Result { - Self::tiles()? - .checked_mul(Self::slots_per_tile()) - .ok_or(TableError::InvalidLayout) - } - - /// Returns the byte length required for this map. - pub fn bytes_len() -> Result { - Self::tiles()? - .checked_mul(Self::tile_bytes()) - .ok_or(TableError::InvalidLayout) - } - - /// Creates a page-local table over `2^LOG2_TILES * 16 KiB` bytes. - /// - /// # Safety - /// - /// The caller must ensure the memory interval is valid for reads and - /// writes for the whole lifetime of the table and does not overlap - /// other mutable state. - pub unsafe fn new(base: usize) -> Result { - let tiles = Self::tiles()?; - let bytes = tiles - .checked_mul(Self::tile_bytes()) - .ok_or(TableError::InvalidLayout)?; - StaticRegion::new(base, bytes)?; - let slots = tiles - .checked_mul(Self::slots_per_tile()) - .ok_or(TableError::InvalidLayout)?; - - Ok(Self { - base, - tiles, - tile_mask: tiles - 1, - slots, - _marker: PhantomData, - }) - } - - /// Returns the configured base address. - pub const fn base(&self) -> usize { - self.base - } - - /// Returns the configured tile count. - pub const fn tile_count(&self) -> usize { - self.tiles - } - - /// Returns the logical byte length occupied by all tiles. - pub fn bytes(&self) -> Result { - Self::bytes_len() - } - - /// Returns a balance value by actor id. - pub fn get_actor_u256(&self, key: &ActorId) -> Result, TableError> { - let key = actor_key(*key); - if is_zero_32(&key) { - return Ok(None); - } - - let LookupResult::Found(index) = self.lookup(&key)? else { - return Ok(None); - }; - Ok(self.visible_value(index).map(u256_from_value)) - } - - /// Inserts or updates a balance value by actor id. - /// - /// A zero value removes the visible value without writing a new absent - /// key. Zero actor ids are rejected because zeroed control memory - /// already represents empty slots. - pub fn insert_actor_u256( - &self, - key: ActorId, - value: U256, - ) -> Result, TableError> { - let key = actor_key(key); - reject_zero_key(&key)?; - if value.is_zero() { - return self - .remove_key(&key) - .map(|previous| previous.map(u256_from_value)); - } - - let value = u256_value(value); - let hash = hash_actor_key(&key); - match self.lookup_with_hash(&key, hash)? { - LookupResult::Found(index) => { - let previous = self.visible_value(index); - unsafe { - self.write_value(index, &value); - } - Ok(previous.map(u256_from_value)) - } - LookupResult::Vacant(index) => { - unsafe { - self.write_key(index, &key); - self.write_value(index, &value); - self.write_control(index, control_fingerprint(hash)); - } - Ok(None) - } - LookupResult::Full => Err(TableError::CapacityOverflow), - } - } - - /// Removes a balance value by actor id. - pub fn remove_actor_u256(&self, key: &ActorId) -> Result, TableError> { - let key = actor_key(*key); - reject_zero_key(&key)?; - self.remove_key(&key) - .map(|previous| previous.map(u256_from_value)) - } - - /// Clears every tile to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - unsafe { - ptr::write_bytes(self.base as *mut u8, 0, Self::bytes_len()?); - } - Ok(()) - } - - fn remove_key(&self, key: &[u8; 32]) -> Result, TableError> { - let LookupResult::Found(index) = self.lookup(key)? else { - return Ok(None); - }; - - let previous = self.visible_value(index); - unsafe { - self.write_control(index, Self::CONTROL_DELETED); - } - Ok(previous) - } - - fn lookup(&self, key: &[u8; 32]) -> Result { - self.lookup_with_hash(key, hash_actor_key(key)) - } - - fn lookup_with_hash(&self, key: &[u8; 32], hash: u32) -> Result { - let fingerprint = control_fingerprint(hash); - let key_words = words_32(key); - let mut first_deleted = None; - let mut tile = static_map_index(hash, LOG2_TILES); - let mut slot = (hash as usize) % Self::slots_per_tile(); - - for _ in 0..self.slots { - let index = tile * Self::slots_per_tile() + slot; - let control = unsafe { self.read_control(index) }; - match control { - Self::CONTROL_EMPTY => { - return Ok(LookupResult::Vacant(first_deleted.unwrap_or(index))); - } - Self::CONTROL_DELETED => { - if first_deleted.is_none() { - first_deleted = Some(index); - } - } - 1..=Self::CONTROL_FULL_MAX => { - if control == fingerprint - && unsafe { read_words_32(self.key_ptr(index)) } == key_words - { - return Ok(LookupResult::Found(index)); - } - } - _ => return Err(TableError::InvalidSlotState), - } - - slot += 1; - if slot == Self::slots_per_tile() { - slot = 0; - tile = (tile + 1) & self.tile_mask; - } - } - - Ok(first_deleted.map_or(LookupResult::Full, LookupResult::Vacant)) - } - - fn visible_value(&self, slot: usize) -> Option<[u8; 32]> { - let value = unsafe { self.read_value(slot) }; - if is_zero_32(&value) { - None - } else { - Some(value) - } - } - - unsafe fn read_control(&self, slot: usize) -> u8 { - unsafe { ptr::read(self.control_ptr(slot)) } - } - - unsafe fn read_value(&self, slot: usize) -> [u8; 32] { - let mut bytes = [0u8; 32]; - unsafe { - ptr::copy_nonoverlapping(self.value_ptr(slot), bytes.as_mut_ptr(), 32); - } - bytes - } - - unsafe fn write_control(&self, slot: usize, control: u8) { - unsafe { - ptr::write(self.control_ptr(slot), control); - } - } - - unsafe fn write_key(&self, slot: usize, key: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(key.as_ptr(), self.key_ptr(slot), 32); - } - } - - unsafe fn write_value(&self, slot: usize, value: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(value.as_ptr(), self.value_ptr(slot), 32); - } - } - - fn control_ptr(&self, slot: usize) -> *mut u8 { - let (tile, tile_slot) = self.tile_slot(slot); - (self.base + tile * Self::tile_bytes() + tile_slot) as *mut u8 - } - - fn key_ptr(&self, slot: usize) -> *mut u8 { - self.slot_ptr(slot) - } - - fn value_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(Self::VALUE_OFFSET) } - } - - fn slot_ptr(&self, slot: usize) -> *mut u8 { - let (tile, tile_slot) = self.tile_slot(slot); - (self.base - + tile * Self::tile_bytes() - + Self::data_offset() - + tile_slot * Self::slot_size()) as *mut u8 - } - - fn tile_slot(&self, slot: usize) -> (usize, usize) { - (slot / Self::slots_per_tile(), slot % Self::slots_per_tile()) - } - } - - /// A grouped-control static balance map keyed by `ActorId` and storing `U256`. - /// - /// Each group stores a compact control run followed by its 64-byte key/value - /// slots. Increasing `LOG2_GROUP_PAGES` makes control scanning denser for - /// misses while keeping hits closer to their data than a fully separated - /// global control region. - pub struct StaticGroupedControlActorIdU256Map { - base: usize, - groups: usize, - group_mask: usize, - group_bytes: usize, - slots_per_group: usize, - data_offset: usize, - slots: usize, - _marker: PhantomData<*mut u8>, - } - - impl - StaticGroupedControlActorIdU256Map - { - const CONTROL_EMPTY: u8 = 0x00; - const CONTROL_DELETED: u8 = 0xff; - const CONTROL_FULL_MAX: u8 = 0x80; - const VALUE_OFFSET: usize = 32; - - /// Returns the byte length of one data slot. - pub const fn slot_size() -> usize { - ACTOR_ID_U256_SLOT_SIZE - } - - /// Returns the configured group count. - pub fn groups() -> Result { - static_map_slots(LOG2_GROUPS) - } - - /// Returns the number of Gear pages inside each group. - pub fn group_pages() -> Result { - static_map_slots(LOG2_GROUP_PAGES) - } - - /// Returns the number of actor/value slots inside each group. - pub fn slots_per_group() -> Result { - Self::group_pages()? - .checked_mul(PAGE_LOCAL_ACTOR_U256_SLOTS_PER_TILE) - .ok_or(TableError::InvalidLayout) - } - - /// Returns the data offset inside each group. - pub fn data_offset() -> Result { - Self::group_pages()? - .checked_mul(PAGE_LOCAL_ACTOR_U256_DATA_OFFSET) - .ok_or(TableError::InvalidLayout) - } - - /// Returns the byte length of one group. - pub fn group_bytes() -> Result { - Self::group_pages()? - .checked_mul(PAGE_LOCAL_ACTOR_U256_TILE_BYTES) - .ok_or(TableError::InvalidLayout) - } - - /// Returns the configured slot count. - pub fn slots() -> Result { - Self::groups()? - .checked_mul(Self::slots_per_group()?) - .ok_or(TableError::InvalidLayout) - } - - /// Returns the byte length required for this map. - pub fn bytes_len() -> Result { - Self::groups()? - .checked_mul(Self::group_bytes()?) - .ok_or(TableError::InvalidLayout) - } - - /// Creates a grouped-control table over the configured static region. - /// - /// # Safety - /// - /// The caller must ensure the memory interval is valid for reads and - /// writes for the whole lifetime of the table and does not overlap - /// other mutable state. - pub unsafe fn new(base: usize) -> Result { - let groups = Self::groups()?; - let group_bytes = Self::group_bytes()?; - let slots_per_group = Self::slots_per_group()?; - let data_offset = Self::data_offset()?; - let bytes = groups - .checked_mul(group_bytes) - .ok_or(TableError::InvalidLayout)?; - StaticRegion::new(base, bytes)?; - let slots = groups - .checked_mul(slots_per_group) - .ok_or(TableError::InvalidLayout)?; - - Ok(Self { - base, - groups, - group_mask: groups - 1, - group_bytes, - slots_per_group, - data_offset, - slots, - _marker: PhantomData, - }) - } - - /// Returns the configured base address. - pub const fn base(&self) -> usize { - self.base - } - - /// Returns the configured group count. - pub const fn group_count(&self) -> usize { - self.groups - } - - /// Returns the byte length occupied by all groups. - pub fn bytes(&self) -> Result { - Self::bytes_len() - } - - /// Returns a balance value by actor id. - pub fn get_actor_u256(&self, key: &ActorId) -> Result, TableError> { - let key = actor_key(*key); - if is_zero_32(&key) { - return Ok(None); - } - - let LookupResult::Found(index) = self.lookup(&key)? else { - return Ok(None); - }; - Ok(self.visible_value(index).map(u256_from_value)) - } - - /// Inserts or updates a balance value by actor id. - /// - /// A zero value removes the visible value without writing a new absent - /// key. Zero actor ids are rejected because zeroed control memory - /// already represents empty slots. - pub fn insert_actor_u256( - &self, - key: ActorId, - value: U256, - ) -> Result, TableError> { - let key = actor_key(key); - reject_zero_key(&key)?; - if value.is_zero() { - return self - .remove_key(&key) - .map(|previous| previous.map(u256_from_value)); - } - - let value = u256_value(value); - let hash = hash_actor_key(&key); - match self.lookup_with_hash(&key, hash)? { - LookupResult::Found(index) => { - let previous = self.visible_value(index); - unsafe { - self.write_value(index, &value); - } - Ok(previous.map(u256_from_value)) - } - LookupResult::Vacant(index) => { - unsafe { - self.write_key(index, &key); - self.write_value(index, &value); - self.write_control(index, control_fingerprint(hash)); - } - Ok(None) - } - LookupResult::Full => Err(TableError::CapacityOverflow), - } - } - - /// Removes a balance value by actor id. - pub fn remove_actor_u256(&self, key: &ActorId) -> Result, TableError> { - let key = actor_key(*key); - reject_zero_key(&key)?; - self.remove_key(&key) - .map(|previous| previous.map(u256_from_value)) - } - - /// Clears every group to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - unsafe { - ptr::write_bytes(self.base as *mut u8, 0, Self::bytes_len()?); - } - Ok(()) - } - - fn remove_key(&self, key: &[u8; 32]) -> Result, TableError> { - let LookupResult::Found(index) = self.lookup(key)? else { - return Ok(None); - }; - - let previous = self.visible_value(index); - unsafe { - self.write_control(index, Self::CONTROL_DELETED); - } - Ok(previous) - } - - fn lookup(&self, key: &[u8; 32]) -> Result { - self.lookup_with_hash(key, hash_actor_key(key)) - } - - fn lookup_with_hash(&self, key: &[u8; 32], hash: u32) -> Result { - let fingerprint = control_fingerprint(hash); - let key_words = words_32(key); - let mut first_deleted = None; - let mut group = static_map_index(hash, LOG2_GROUPS); - let mut group_slot = (hash as usize) % self.slots_per_group; - - for _ in 0..self.slots { - let index = group * self.slots_per_group + group_slot; - let control = unsafe { self.read_control(index) }; - match control { - Self::CONTROL_EMPTY => { - return Ok(LookupResult::Vacant(first_deleted.unwrap_or(index))); - } - Self::CONTROL_DELETED => { - if first_deleted.is_none() { - first_deleted = Some(index); - } - } - 1..=Self::CONTROL_FULL_MAX => { - if control == fingerprint - && unsafe { read_words_32(self.key_ptr(index)) } == key_words - { - return Ok(LookupResult::Found(index)); - } - } - _ => return Err(TableError::InvalidSlotState), - } - - group_slot += 1; - if group_slot == self.slots_per_group { - group_slot = 0; - group = (group + 1) & self.group_mask; - } - } - - Ok(first_deleted.map_or(LookupResult::Full, LookupResult::Vacant)) - } - - fn visible_value(&self, slot: usize) -> Option<[u8; 32]> { - let value = unsafe { self.read_value(slot) }; - if is_zero_32(&value) { - None - } else { - Some(value) - } - } - - unsafe fn read_control(&self, slot: usize) -> u8 { - unsafe { ptr::read(self.control_ptr(slot)) } - } - - unsafe fn read_value(&self, slot: usize) -> [u8; 32] { - let mut bytes = [0u8; 32]; - unsafe { - ptr::copy_nonoverlapping(self.value_ptr(slot), bytes.as_mut_ptr(), 32); - } - bytes - } - - unsafe fn write_control(&self, slot: usize, control: u8) { - unsafe { - ptr::write(self.control_ptr(slot), control); - } - } - - unsafe fn write_key(&self, slot: usize, key: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(key.as_ptr(), self.key_ptr(slot), 32); - } - } - - unsafe fn write_value(&self, slot: usize, value: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(value.as_ptr(), self.value_ptr(slot), 32); - } - } - - fn control_ptr(&self, slot: usize) -> *mut u8 { - let (group, group_slot) = self.group_slot(slot); - (self.base + group * self.group_bytes + group_slot) as *mut u8 - } - - fn key_ptr(&self, slot: usize) -> *mut u8 { - self.slot_ptr(slot) - } - - fn value_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(Self::VALUE_OFFSET) } - } - - fn slot_ptr(&self, slot: usize) -> *mut u8 { - let (group, group_slot) = self.group_slot(slot); - (self.base - + group * self.group_bytes - + self.data_offset - + group_slot * Self::slot_size()) as *mut u8 - } - - fn group_slot(&self, slot: usize) -> (usize, usize) { - (slot / self.slots_per_group, slot % self.slots_per_group) - } - } - - /// A WAT-shaped static allowance map keyed by `(owner, spender)` and storing `U256`. - /// - /// Each slot is exactly 96 bytes: 32 bytes of owner, 32 bytes of spender, - /// and 32 bytes of value. A zero owner marks an empty slot, and a zero - /// value is treated as an absent/tombstoned entry for an existing key. - pub struct StaticAllowanceU256Map { - base: usize, - slots: usize, - mask: usize, - _marker: PhantomData<*mut u8>, - } - - impl StaticAllowanceU256Map { - const SPENDER_OFFSET: usize = 32; - const VALUE_OFFSET: usize = 64; - - /// Returns the byte length of one slot. - pub const fn slot_size() -> usize { - ACTOR_PAIR_U256_SLOT_SIZE - } - - /// Returns the configured slot count. - pub fn slots() -> Result { - static_map_slots(LOG2_SLOTS) - } - - /// Returns the mask used for power-of-two probing. - pub fn mask() -> Result { - Ok(Self::slots()? - 1) - } - - /// Returns the byte length required for this table. - pub fn bytes_len() -> Result { - Self::slots()? - .checked_mul(Self::slot_size()) - .ok_or(TableError::InvalidLayout) - } - - /// Creates a table over `2^LOG2_SLOTS * 96` bytes at `base`. - /// - /// # Safety - /// - /// The caller must ensure the memory interval is valid for reads and - /// writes for the whole lifetime of the table and does not overlap - /// other mutable state. - pub unsafe fn new(base: usize) -> Result { - let slots = Self::slots()?; - let bytes = slots - .checked_mul(Self::slot_size()) - .ok_or(TableError::InvalidLayout)?; - StaticRegion::new(base, bytes)?; - Ok(Self { - base, - slots, - mask: slots - 1, - _marker: PhantomData, - }) - } - - /// Returns the configured base address. - pub const fn base(&self) -> usize { - self.base - } - - /// Returns the total byte length occupied by this table. - pub fn bytes(&self) -> Result { - Self::bytes_len() - } - - /// Returns an allowance value by owner and spender. - pub fn get_allowance_u256( - &self, - owner: &ActorId, - spender: &ActorId, - ) -> Result, TableError> { - let owner = actor_key(*owner); - let spender = actor_key(*spender); - if is_zero_32(&owner) || is_zero_32(&spender) { - return Ok(None); - } - - let LookupResult::Found(index) = self.lookup(&owner, &spender)? else { - return Ok(None); - }; - Ok(self.visible_value(index).map(u256_from_value)) - } - - /// Inserts or updates an allowance value by owner and spender. - /// - /// A zero value removes the visible value without writing a new absent - /// key. Zero owner or spender ids are rejected because zero key bytes - /// are reserved by this layout. - pub fn insert_allowance_u256( - &self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result, TableError> { - let owner = actor_key(owner); - let spender = actor_key(spender); - reject_zero_key(&owner)?; - reject_zero_key(&spender)?; - let value = u256_value(value); - - if is_zero_32(&value) { - return self - .remove_key(&owner, &spender) - .map(|previous| previous.map(u256_from_value)); - } - - match self.lookup(&owner, &spender)? { - LookupResult::Found(index) => { - let previous = self.visible_value(index); - unsafe { - self.write_value(index, &value); - } - Ok(previous.map(u256_from_value)) - } - LookupResult::Vacant(index) => { - unsafe { - self.write_owner(index, &owner); - self.write_spender(index, &spender); - self.write_value(index, &value); - } - Ok(None) - } - LookupResult::Full => Err(TableError::CapacityOverflow), - } - } - - /// Removes an allowance value by owner and spender. - pub fn remove_allowance_u256( - &self, - owner: &ActorId, - spender: &ActorId, - ) -> Result, TableError> { - let owner = actor_key(*owner); - let spender = actor_key(*spender); - reject_zero_key(&owner)?; - reject_zero_key(&spender)?; - self.remove_key(&owner, &spender) - .map(|previous| previous.map(u256_from_value)) - } - - /// Decreases an allowance using one lookup. - /// - /// Returns `Ok(None)` without modifying storage when the allowance is - /// absent or smaller than `amount`. - pub fn decrease_allowance_u256( - &self, - owner: ActorId, - spender: ActorId, - amount: U256, - ) -> Result, TableError> { - let owner = actor_key(owner); - let spender = actor_key(spender); - reject_zero_key(&owner)?; - reject_zero_key(&spender)?; - if amount.is_zero() { - let LookupResult::Found(index) = self.lookup(&owner, &spender)? else { - return Ok(Some(U256::zero())); - }; - return Ok(Some( - self.visible_value(index) - .map_or_else(U256::zero, u256_from_value), - )); - } - let LookupResult::Found(index) = self.lookup(&owner, &spender)? else { - return Ok(None); - }; - let Some(value) = self.visible_value(index).map(u256_from_value) else { - return Ok(None); - }; - if value < amount { - return Ok(None); - } - - let value = value - amount; - let value_bytes = u256_value(value); - unsafe { - self.write_value(index, &value_bytes); - } - Ok(Some(value)) - } - - /// Clears every slot to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - let bytes = self.bytes()?; - unsafe { - ptr::write_bytes(self.base as *mut u8, 0, bytes); - } - Ok(()) - } - - fn remove_key( - &self, - owner: &[u8; 32], - spender: &[u8; 32], - ) -> Result, TableError> { - let LookupResult::Found(index) = self.lookup(owner, spender)? else { - return Ok(None); - }; - - let previous = self.visible_value(index); - if previous.is_some() { - unsafe { - self.write_value(index, &[0; 32]); - } - } - Ok(previous) - } - - fn lookup(&self, owner: &[u8; 32], spender: &[u8; 32]) -> Result { - let owner_words = words_32(owner); - let spender_words = words_32(spender); - let mut index = static_map_index(hash_allowance_key(owner, spender), LOG2_SLOTS); - - for _ in 0..self.slots { - let stored_owner = unsafe { read_words_32(self.owner_ptr(index)) }; - if words_are_zero(stored_owner) { - return Ok(LookupResult::Vacant(index)); - } - if stored_owner == owner_words - && unsafe { read_words_32(self.spender_ptr(index)) } == spender_words - { - return Ok(LookupResult::Found(index)); - } - - index = (index + 1) & self.mask; - } - - Ok(LookupResult::Full) - } - - fn visible_value(&self, slot: usize) -> Option<[u8; 32]> { - if unsafe { words_at_zero(self.value_ptr(slot)) } { - None - } else { - Some(unsafe { self.read_value(slot) }) - } - } - - unsafe fn read_value(&self, slot: usize) -> [u8; 32] { - let mut bytes = [0u8; 32]; - unsafe { - ptr::copy_nonoverlapping(self.value_ptr(slot), bytes.as_mut_ptr(), 32); - } - bytes - } - - unsafe fn write_owner(&self, slot: usize, owner: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(owner.as_ptr(), self.owner_ptr(slot), 32); - } - } - - unsafe fn write_spender(&self, slot: usize, spender: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(spender.as_ptr(), self.spender_ptr(slot), 32); - } - } - - unsafe fn write_value(&self, slot: usize, value: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(value.as_ptr(), self.value_ptr(slot), 32); - } - } - - fn owner_ptr(&self, slot: usize) -> *mut u8 { - self.slot_ptr(slot) - } - - fn spender_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(Self::SPENDER_OFFSET) } - } - - fn value_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(Self::VALUE_OFFSET) } - } - - fn slot_ptr(&self, slot: usize) -> *mut u8 { - (self.base + slot * Self::slot_size()) as *mut u8 - } - } - /// Core VFT storage over WAT-shaped static balance and allowance maps. - /// - /// This is intended for high-cardinality token hot paths where transfer, - /// transfer-from, and approve should avoid allocator-backed maps. It does - /// not track metadata, events, total supply, allowance expiry, or holder - /// listing indexes; keep those outside this transfer-shaped storage layer. - pub struct StaticVftStorage { - balances: VftBalances, - allowances: VftAllowances, - } - - impl - StaticVftStorage - { - /// Creates VFT storage over separately reserved static regions. - /// - /// # Safety - /// - /// The caller must ensure both memory intervals are valid for reads and - /// writes for the whole lifetime of the storage and do not overlap other - /// mutable state. - pub unsafe fn new(balance_base: usize, allowance_base: usize) -> Result { - let balance_region = StaticRegion::new( - balance_base, - VftBalances::::bytes_len()?, - )?; - let allowance_region = StaticRegion::new( - allowance_base, - VftAllowances::::bytes_len()?, - )?; - if regions_overlap(balance_region, allowance_region)? { - return Err(TableError::InvalidLayout); - } - - Ok(Self { - balances: unsafe { VftBalances::::new(balance_base)? }, - allowances: unsafe { VftAllowances::::new(allowance_base)? }, - }) - } - - /// Returns the balance map backing this storage. - pub fn balances(&self) -> &VftBalances { - &self.balances - } - - /// Returns the allowance map backing this storage. - pub fn allowances(&self) -> &VftAllowances { - &self.allowances - } - - /// Returns the visible balance of `account`. - pub fn balance_of(&self, account: ActorId) -> Result { - Ok(self - .balances - .get_actor_u256(&account)? - .unwrap_or_else(U256::zero)) - } - - /// Returns the visible allowance from `owner` to `spender`. - pub fn allowance(&self, owner: ActorId, spender: ActorId) -> Result { - Ok(self - .allowances - .get_allowance_u256(&owner, &spender)? - .unwrap_or_else(U256::zero)) - } - - /// Sets an allowance and returns whether the visible value changed. - pub fn approve( - &self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result { - if owner == spender { - return Ok(false); - } - - let previous = self - .allowances - .insert_allowance_u256(owner, spender, value)? - .unwrap_or_else(U256::zero); - Ok(previous != value) - } - - /// Adds `amount` to `to` and returns whether state changed. - pub fn mint(&self, to: ActorId, amount: U256) -> Result { - if amount.is_zero() { - return Ok(false); - } - - let previous = self.balance_of(to)?; - let (next, overflow) = previous.overflowing_add(amount); - if overflow { - return Ok(false); - } - - self.balances.insert_actor_u256(to, next)?; - Ok(true) - } - - /// Removes `amount` from `from` and returns whether state changed. - pub fn burn(&self, from: ActorId, amount: U256) -> Result { - if amount.is_zero() { - return Ok(false); - } - - let previous = self.balance_of(from)?; - if previous < amount { - return Ok(false); - } - - self.balances.insert_actor_u256(from, previous - amount)?; - Ok(true) - } - - /// Moves `amount` from `from` to `to` and returns whether state changed. - pub fn transfer( - &self, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - if from == to || amount.is_zero() { - return Ok(false); - } - - Ok(self - .balances - .transfer_actor_u256(from, to, amount)? - .is_some()) - } - - /// Moves `amount` after validating and decreasing `spender` allowance. - pub fn transfer_from( - &self, - spender: ActorId, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result { - if spender == from { - return self.transfer(from, to, amount); - } - if from == to || amount.is_zero() { - return Ok(false); - } - - Ok(self - .balances - .transfer_actor_u256_from(&self.allowances, from, spender, to, amount)? - .is_some()) - } - - /// Clears both balance and allowance maps. - pub fn clear(&self) -> Result<(), TableError> { - self.balances.clear()?; - self.allowances.clear() - } - } - - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - enum LookupResult { - Found(usize), - Vacant(usize), - Full, - } - - fn static_map_slots(log2_slots: u8) -> Result { - let shift = u32::from(log2_slots); - if shift > 32 || shift >= usize::BITS { - return Err(TableError::InvalidLayout); - } - - 1usize.checked_shl(shift).ok_or(TableError::InvalidLayout) - } - - fn static_map_index(hash: u32, log2_slots: u8) -> usize { - if log2_slots == 0 { - 0 - } else { - (hash >> (32 - u32::from(log2_slots))) as usize - } - } - - fn hash_actor_key(key: &[u8; 32]) -> u32 { - hash_words(key) - } - - fn actor_tag(actor: ActorId) -> u64 { - let key = actor_key(actor); - let lane = unsafe { ptr::read_unaligned(key.as_ptr().add(12).cast::()) }; - let tag = if lane != 0 { - lane - } else { - u64::from(fmix32(fold_words(&key))) - | (u64::from(fmix32(fold_words_rotated(&key))) << 32) - }; - if tag == 0 { 1 } else { tag } - } - - fn allowance_tag(owner: ActorId, spender: ActorId) -> u64 { - let owner = actor_tag(owner); - let spender = actor_tag(spender); - let tag = owner - .wrapping_mul(0x9E37_79B9_7F4A_7C15) - .wrapping_add(spender.wrapping_mul(0xBF58_476D_1CE4_E5B9)); - if tag == 0 { 1 } else { tag } - } - - fn hash_allowance_key(owner: &[u8; 32], spender: &[u8; 32]) -> u32 { - fold_words(owner) - .wrapping_mul(HASH_GOLDEN_RATIO) - .wrapping_add(fold_words(spender).wrapping_mul(HASH_PAIR_SPENDER)) - } - - const HASH_GOLDEN_RATIO: u32 = 0x9E37_79B9; - const HASH_PAIR_SPENDER: u32 = 0x85EB_CA6B; - - fn hash_words(bytes: &[u8; 32]) -> u32 { - fold_words(bytes).wrapping_mul(HASH_GOLDEN_RATIO) - } - - fn fmix32(mut hash: u32) -> u32 { - hash ^= hash >> 16; - hash = hash.wrapping_mul(0x85EB_CA6B); - hash ^= hash >> 13; - hash = hash.wrapping_mul(0xC2B2_AE35); - hash ^ (hash >> 16) - } - - fn fold_words(bytes: &[u8; 32]) -> u32 { - let ptr = bytes.as_ptr(); - unsafe { - ptr::read_unaligned(ptr.cast::()) - ^ ptr::read_unaligned(ptr.add(4).cast::()) - ^ ptr::read_unaligned(ptr.add(8).cast::()) - ^ ptr::read_unaligned(ptr.add(12).cast::()) - ^ ptr::read_unaligned(ptr.add(16).cast::()) - ^ ptr::read_unaligned(ptr.add(20).cast::()) - ^ ptr::read_unaligned(ptr.add(24).cast::()) - ^ ptr::read_unaligned(ptr.add(28).cast::()) - } - } - - fn fold_words_rotated(bytes: &[u8; 32]) -> u32 { - let ptr = bytes.as_ptr(); - unsafe { - ptr::read_unaligned(ptr.cast::()).rotate_left(5) - ^ ptr::read_unaligned(ptr.add(4).cast::()).rotate_left(9) - ^ ptr::read_unaligned(ptr.add(8).cast::()).rotate_left(13) - ^ ptr::read_unaligned(ptr.add(12).cast::()).rotate_left(17) - ^ ptr::read_unaligned(ptr.add(16).cast::()).rotate_left(21) - ^ ptr::read_unaligned(ptr.add(20).cast::()).rotate_left(25) - ^ ptr::read_unaligned(ptr.add(24).cast::()).rotate_left(29) - ^ ptr::read_unaligned(ptr.add(28).cast::()).rotate_left(3) - } - } - - fn reject_zero_key(bytes: &[u8; 32]) -> Result<(), TableError> { - if is_zero_32(bytes) { - Err(TableError::InvalidKey) - } else { - Ok(()) - } - } - - fn control_fingerprint(hash: u32) -> u8 { - ((hash as u8) & 0x7f) + 1 - } - - fn regions_overlap(left: StaticRegion, right: StaticRegion) -> Result { - Ok(left.base() < right.end()? && right.base() < left.end()?) - } - - fn words_32(bytes: &[u8; 32]) -> [u64; 4] { - [ - u64::from_le_bytes([ - bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], - ]), - u64::from_le_bytes([ - bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], - bytes[15], - ]), - u64::from_le_bytes([ - bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], - bytes[23], - ]), - u64::from_le_bytes([ - bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], - bytes[31], - ]), - ] - } - - unsafe fn read_words_32(ptr: *const u8) -> [u64; 4] { - unsafe { - [ - ptr::read_unaligned(ptr.cast::()), - ptr::read_unaligned(ptr.add(8).cast::()), - ptr::read_unaligned(ptr.add(16).cast::()), - ptr::read_unaligned(ptr.add(24).cast::()), - ] - } - } - - unsafe fn words_at_zero(ptr: *const u8) -> bool { - words_are_zero(unsafe { read_words_32(ptr) }) - } - - fn words_are_zero(words: [u64; 4]) -> bool { - (words[0] | words[1] | words[2] | words[3]) == 0 - } - - fn is_zero_32(bytes: &[u8; 32]) -> bool { - bytes.iter().all(|byte| *byte == 0) - } - - impl FixedBalanceMap { - /// Returns a balance value by actor id. - pub fn get_actor_u256(&self, key: &ActorId) -> Result, TableError> { - self.get(&actor_key(*key)) - .map(|value| value.map(u256_from_value)) - } - - /// Inserts or updates a balance value by actor id. - pub fn insert_actor_u256( - &mut self, - key: ActorId, - value: U256, - ) -> Result, TableError> { - self.insert(actor_key(key), u256_value(value)) - .map(|previous| previous.map(u256_from_value)) - } - - /// Removes a balance value by actor id. - pub fn remove_actor_u256(&mut self, key: &ActorId) -> Result, TableError> { - self.remove(&actor_key(*key)) - .map(|previous| previous.map(u256_from_value)) - } - } - - impl FixedAllowanceMap { - /// Returns an allowance value by owner and spender. - pub fn get_allowance_u256( - &self, - owner: &ActorId, - spender: &ActorId, - ) -> Result, TableError> { - self.get(&allowance_key(*owner, *spender)) - .map(|value| value.map(u256_from_value)) - } - - /// Inserts or updates an allowance value by owner and spender. - pub fn insert_allowance_u256( - &mut self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result, TableError> { - self.insert(allowance_key(owner, spender), u256_value(value)) - .map(|previous| previous.map(u256_from_value)) - } - - /// Removes an allowance value by owner and spender. - pub fn remove_allowance_u256( - &mut self, - owner: &ActorId, - spender: &ActorId, - ) -> Result, TableError> { - self.remove(&allowance_key(*owner, *spender)) - .map(|previous| previous.map(u256_from_value)) - } - } - - impl StaticBalanceTable { - /// Returns a balance value by actor id. - pub fn get_actor_u256(&self, key: &ActorId) -> Result, TableError> { - self.get(&actor_key(*key)) - .map(|value| value.map(u256_from_value)) - } - - /// Inserts or updates a balance value by actor id. - pub fn insert_actor_u256( - &self, - key: ActorId, - value: U256, - ) -> Result, TableError> { - self.insert(&actor_key(key), &u256_value(value)) - .map(|previous| previous.map(u256_from_value)) - } - - /// Removes a balance value by actor id. - pub fn remove_actor_u256(&self, key: &ActorId) -> Result, TableError> { - self.remove(&actor_key(*key)) - .map(|previous| previous.map(u256_from_value)) - } - - /// Moves `amount` from one actor balance to another using one lookup per key. - pub fn transfer_actor_u256( - &self, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result, TableError> { - let from_key = actor_key(from); - let to_key = actor_key(to); - if amount.is_zero() { - let from_balance = self.get_actor_u256(&from)?.unwrap_or_else(U256::zero); - let to_balance = if from_key == to_key { - from_balance - } else { - self.get_actor_u256(&to)?.unwrap_or_else(U256::zero) - }; - return Ok(Some(StaticActorU256Transfer { - from_balance, - to_balance, - inserted_to: false, - })); - } - - let Lookup::Found(from_index) = self.lookup(&from_key)? else { - return Ok(None); - }; - let from_value = u256_from_value(unsafe { self.read_value(from_index)? }); - if from_value < amount { - return Ok(None); - } - if from_key == to_key { - return Ok(Some(StaticActorU256Transfer { - from_balance: from_value, - to_balance: from_value, - inserted_to: false, - })); - } - - let to_lookup = self.lookup(&to_key)?; - let (to_value, inserted_to) = match to_lookup { - Lookup::Found(index) => { - (u256_from_value(unsafe { self.read_value(index)? }), false) - } - Lookup::Vacant(_) => (U256::zero(), true), - Lookup::Full => return Ok(None), - }; - let (to_value, overflow) = to_value.overflowing_add(amount); - if overflow { - return Ok(None); - } - - let from_value = from_value - amount; - let from_value_bytes = u256_value(from_value); - let to_value_bytes = u256_value(to_value); - unsafe { - self.write_value(from_index, &from_value_bytes); - match to_lookup { - Lookup::Found(index) => self.write_value(index, &to_value_bytes), - Lookup::Vacant(index) => { - self.write_key(index, &to_key); - self.write_value(index, &to_value_bytes); - self.write_state(index, SlotState::Full); - } - Lookup::Full => unreachable!("full table handled before writes"), - } - } - - Ok(Some(StaticActorU256Transfer { - from_balance: from_value, - to_balance: to_value, - inserted_to, - })) - } - - /// Moves `amount` after validating and decreasing a static-table allowance. - pub fn transfer_actor_u256_from( - &self, - allowances: &StaticAllowanceTable, - owner: ActorId, - spender: ActorId, - to: ActorId, - amount: U256, - ) -> Result, TableError> { - let owner_key = actor_key(owner); - let to_key = actor_key(to); - if amount.is_zero() { - let from_balance = self.get_actor_u256(&owner)?.unwrap_or_else(U256::zero); - let to_balance = if owner_key == to_key { - from_balance - } else { - self.get_actor_u256(&to)?.unwrap_or_else(U256::zero) - }; - let allowance = allowances - .get_allowance_u256(&owner, &spender)? - .unwrap_or_else(U256::zero); - return Ok(Some(StaticActorU256TransferFrom { - from_balance, - to_balance, - allowance, - inserted_to: false, - })); - } - - let allowance_key = allowance_key(owner, spender); - let Lookup::Found(allowance_index) = allowances.lookup(&allowance_key)? else { - return Ok(None); - }; - let allowance = u256_from_value(unsafe { allowances.read_value(allowance_index)? }); - if allowance < amount { - return Ok(None); - } - - let Lookup::Found(from_index) = self.lookup(&owner_key)? else { - return Ok(None); - }; - let from_balance = u256_from_value(unsafe { self.read_value(from_index)? }); - if from_balance < amount { - return Ok(None); - } - - let allowance = allowance - amount; - if owner_key == to_key { - let allowance_bytes = u256_value(allowance); - unsafe { - allowances.write_value(allowance_index, &allowance_bytes); - } - return Ok(Some(StaticActorU256TransferFrom { - from_balance, - to_balance: from_balance, - allowance, - inserted_to: false, - })); - } - - let to_lookup = self.lookup(&to_key)?; - let (to_balance, inserted_to) = match to_lookup { - Lookup::Found(index) => { - (u256_from_value(unsafe { self.read_value(index)? }), false) - } - Lookup::Vacant(_) => (U256::zero(), true), - Lookup::Full => return Ok(None), - }; - let (to_balance, overflow) = to_balance.overflowing_add(amount); - if overflow { - return Ok(None); - } - - let from_balance = from_balance - amount; - let from_balance_bytes = u256_value(from_balance); - let to_balance_bytes = u256_value(to_balance); - let allowance_bytes = u256_value(allowance); - unsafe { - self.write_value(from_index, &from_balance_bytes); - match to_lookup { - Lookup::Found(index) => self.write_value(index, &to_balance_bytes), - Lookup::Vacant(index) => { - self.write_key(index, &to_key); - self.write_value(index, &to_balance_bytes); - self.write_state(index, SlotState::Full); - } - Lookup::Full => unreachable!("full table handled before writes"), - } - allowances.write_value(allowance_index, &allowance_bytes); - } - - Ok(Some(StaticActorU256TransferFrom { - from_balance, - to_balance, - allowance, - inserted_to, - })) - } - - /// Moves a known nonzero amount between known distinct, nonzero actors. - /// - /// # Safety - /// - /// The caller must ensure `from` and `to` are nonzero actor ids, `from != to`, - /// and `amount != 0`. This skips validation intended for the safe public path. - pub unsafe fn transfer_actor_u256_nonzero_distinct_unchecked( - &self, - from: ActorId, - to: ActorId, - amount: U256, - ) -> Result, TableError> { - let from_key = actor_key(from); - let to_key = actor_key(to); - - let Lookup::Found(from_index) = self.lookup(&from_key)? else { - return Ok(None); - }; - let from_value = u256_from_value(unsafe { self.read_value(from_index)? }); - if from_value < amount { - return Ok(None); - } - - let to_lookup = self.lookup(&to_key)?; - let (to_value, inserted_to) = match to_lookup { - Lookup::Found(index) => { - (u256_from_value(unsafe { self.read_value(index)? }), false) - } - Lookup::Vacant(_) => (U256::zero(), true), - Lookup::Full => return Ok(None), - }; - let (to_value, overflow) = to_value.overflowing_add(amount); - if overflow { - return Ok(None); - } - - let from_value = from_value - amount; - let from_value_bytes = u256_value(from_value); - let to_value_bytes = u256_value(to_value); - unsafe { - self.write_value(from_index, &from_value_bytes); - match to_lookup { - Lookup::Found(index) => self.write_value(index, &to_value_bytes), - Lookup::Vacant(index) => { - self.write_key(index, &to_key); - self.write_value(index, &to_value_bytes); - self.write_state(index, SlotState::Full); - } - Lookup::Full => unreachable!("full table handled before writes"), - } - } - - Ok(Some(StaticActorU256Transfer { - from_balance: from_value, - to_balance: to_value, - inserted_to, - })) - } - - /// Moves a known nonzero amount after checking allowance for known distinct, nonzero actors. - /// - /// # Safety - /// - /// The caller must ensure `owner`, `spender`, and `to` are nonzero actor ids, - /// `owner != to`, and `amount != 0`. This skips validation intended for the - /// safe public path. - pub unsafe fn transfer_actor_u256_from_nonzero_distinct_unchecked( - &self, - allowances: &StaticAllowanceTable, - owner: ActorId, - spender: ActorId, - to: ActorId, - amount: U256, - ) -> Result, TableError> { - let owner_key = actor_key(owner); - let to_key = actor_key(to); - let allowance_key = allowance_key(owner, spender); - - let Lookup::Found(allowance_index) = allowances.lookup(&allowance_key)? else { - return Ok(None); - }; - let allowance = u256_from_value(unsafe { allowances.read_value(allowance_index)? }); - if allowance < amount { - return Ok(None); - } - - let Lookup::Found(from_index) = self.lookup(&owner_key)? else { - return Ok(None); - }; - let from_balance = u256_from_value(unsafe { self.read_value(from_index)? }); - if from_balance < amount { - return Ok(None); - } - - let to_lookup = self.lookup(&to_key)?; - let (to_balance, inserted_to) = match to_lookup { - Lookup::Found(index) => { - (u256_from_value(unsafe { self.read_value(index)? }), false) - } - Lookup::Vacant(_) => (U256::zero(), true), - Lookup::Full => return Ok(None), - }; - let (to_balance, overflow) = to_balance.overflowing_add(amount); - if overflow { - return Ok(None); - } - - let allowance = allowance - amount; - let from_balance = from_balance - amount; - let from_balance_bytes = u256_value(from_balance); - let to_balance_bytes = u256_value(to_balance); - let allowance_bytes = u256_value(allowance); - unsafe { - self.write_value(from_index, &from_balance_bytes); - match to_lookup { - Lookup::Found(index) => self.write_value(index, &to_balance_bytes), - Lookup::Vacant(index) => { - self.write_key(index, &to_key); - self.write_value(index, &to_balance_bytes); - self.write_state(index, SlotState::Full); - } - Lookup::Full => unreachable!("full table handled before writes"), - } - allowances.write_value(allowance_index, &allowance_bytes); - } - - Ok(Some(StaticActorU256TransferFrom { - from_balance, - to_balance, - allowance, - inserted_to, - })) - } - } - - impl StaticAllowanceTable { - /// Returns an allowance value by owner and spender. - pub fn get_allowance_u256( - &self, - owner: &ActorId, - spender: &ActorId, - ) -> Result, TableError> { - self.get(&allowance_key(*owner, *spender)) - .map(|value| value.map(u256_from_value)) - } - - /// Inserts or updates an allowance value by owner and spender. - pub fn insert_allowance_u256( - &self, - owner: ActorId, - spender: ActorId, - value: U256, - ) -> Result, TableError> { - self.insert(&allowance_key(owner, spender), &u256_value(value)) - .map(|previous| previous.map(u256_from_value)) - } - - /// Removes an allowance value by owner and spender. - pub fn remove_allowance_u256( - &self, - owner: &ActorId, - spender: &ActorId, - ) -> Result, TableError> { - self.remove(&allowance_key(*owner, *spender)) - .map(|previous| previous.map(u256_from_value)) - } - } -} - -/// A static-memory map keyed by `ActorId` and storing `U256`. -/// -/// The layout uses `2^LOG2_SLOTS` slots. Each slot is exactly 64 bytes: -/// a 32-byte actor id followed by a 32-byte little-endian `U256` value. -pub struct StaticActorIdU256Map { - inner: __experimental::StaticActorIdU256Map, -} - -impl StaticActorIdU256Map { - /// Returns the byte length of one slot. - pub const fn slot_size() -> usize { - __experimental::StaticActorIdU256Map::::slot_size() - } - - /// Returns the configured slot count. - pub fn slots() -> Result { - __experimental::StaticActorIdU256Map::::slots() - } - - /// Returns the mask used for power-of-two probing. - pub fn mask() -> Result { - __experimental::StaticActorIdU256Map::::mask() - } - - /// Returns the byte length required for this map. - pub fn bytes_len() -> Result { - __experimental::StaticActorIdU256Map::::bytes_len() - } - - /// Creates a map over `2^LOG2_SLOTS * 64` bytes at `base`. - /// - /// # Safety - /// - /// The caller must ensure the memory interval is valid for reads and writes - /// for the whole lifetime of the map and does not overlap other mutable - /// state. - pub unsafe fn new(base: usize) -> Result { - Ok(Self { - inner: unsafe { __experimental::StaticActorIdU256Map::::new(base)? }, - }) - } - - /// Returns the configured base address. - pub const fn base(&self) -> usize { - self.inner.base() - } - - /// Returns the total byte length occupied by this map. - pub fn bytes(&self) -> Result { - self.inner.bytes() - } - - /// Returns the visible value for `actor`. - pub fn get_actor_u256(&self, actor: &ActorId) -> Result, TableError> { - self.inner.get_actor_u256(actor) - } - - /// Inserts or updates `actor`, returning the previous visible value. - /// - /// A zero value removes the visible value. Zero actor ids are rejected - /// because zero key bytes mark empty slots in this layout. - pub fn insert_actor_u256( - &self, - actor: ActorId, - value: U256, - ) -> Result, TableError> { - self.inner.insert_actor_u256(actor, value) - } - - /// Removes the visible value for `actor`. - pub fn remove_actor_u256(&self, actor: &ActorId) -> Result, TableError> { - self.inner.remove_actor_u256(actor) - } - - /// Clears every slot to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - self.inner.clear() - } -} - -/// A static-memory map keyed by two `ActorId`s and storing `U256`. -/// -/// The layout uses `2^LOG2_SLOTS` slots. Each slot is exactly 96 bytes: -/// the two 32-byte actor ids followed by a 32-byte little-endian `U256` value. -pub struct StaticActorPairU256Map { - inner: __experimental::StaticAllowanceU256Map, -} - -impl StaticActorPairU256Map { - /// Returns the byte length of one slot. - pub const fn slot_size() -> usize { - __experimental::StaticAllowanceU256Map::::slot_size() - } - - /// Returns the configured slot count. - pub fn slots() -> Result { - __experimental::StaticAllowanceU256Map::::slots() - } - - /// Returns the mask used for power-of-two probing. - pub fn mask() -> Result { - __experimental::StaticAllowanceU256Map::::mask() - } - - /// Returns the byte length required for this map. - pub fn bytes_len() -> Result { - __experimental::StaticAllowanceU256Map::::bytes_len() - } - - /// Creates a map over `2^LOG2_SLOTS * 96` bytes at `base`. - /// - /// # Safety - /// - /// The caller must ensure the memory interval is valid for reads and writes - /// for the whole lifetime of the map and does not overlap other mutable - /// state. - pub unsafe fn new(base: usize) -> Result { - Ok(Self { - inner: unsafe { __experimental::StaticAllowanceU256Map::::new(base)? }, - }) - } - - /// Returns the configured base address. - pub const fn base(&self) -> usize { - self.inner.base() - } - - /// Returns the total byte length occupied by this map. - pub fn bytes(&self) -> Result { - self.inner.bytes() - } - - /// Returns the visible value for `(left, right)`. - pub fn get_actor_pair_u256( - &self, - left: &ActorId, - right: &ActorId, - ) -> Result, TableError> { - self.inner.get_allowance_u256(left, right) - } - - /// Inserts or updates `(left, right)`, returning the previous visible value. - /// - /// A zero value removes the visible value. Zero actor ids are rejected - /// because zero key bytes mark empty slots in this layout. - pub fn insert_actor_pair_u256( - &self, - left: ActorId, - right: ActorId, - value: U256, - ) -> Result, TableError> { - self.inner.insert_allowance_u256(left, right, value) - } - - /// Removes the visible value for `(left, right)`. - pub fn remove_actor_pair_u256( - &self, - left: &ActorId, - right: &ActorId, - ) -> Result, TableError> { - self.inner.remove_allowance_u256(left, right) - } - - /// Clears every slot to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - self.inner.clear() - } -} - -#[cfg(test)] -mod tests { - extern crate std; - - use super::*; - use crate::__experimental::{ - StaticActorIdU256Map, StaticActorU256Transfer, StaticActorU256TransferFrom, - StaticAllowanceTable, StaticAllowanceU256Map, StaticBalanceTable, - StaticControlActorIdU256Map, StaticGroupedControlActorIdU256Map, - StaticPageLocalActorIdU256Map, StaticVftStorage, VftAllowances, VftBalances, - }; - use gprimitives::{ActorId, U256}; - use std::{vec, vec::Vec}; - - type Fixed = FixedOpenAddressMap<1, 1, 2>; - - fn colliding_keys(slots: usize) -> ([u8; 1], [u8; 1]) { - for left in 0u8..=u8::MAX { - for right in left.wrapping_add(1)..=u8::MAX { - if hash_bytes(&[left]) % slots == hash_bytes(&[right]) % slots { - return ([left], [right]); - } - } - } - - unreachable!("single-byte key space must contain collisions") - } - - #[test] - fn fixed_map_updates_existing_key() { - let mut map = Fixed::new(); - - assert_eq!(map.insert([0], [0]), Ok(None)); - assert_eq!(map.insert([0], [7]), Ok(Some([0]))); - assert_eq!(map.get(&[0]), Ok(Some([7]))); - assert_eq!(map.len(), 1); - } - - #[test] - fn fixed_map_reuses_tombstone() { - let (first, second) = colliding_keys(1); - let mut map = FixedOpenAddressMap::<1, 1, 1>::new(); - - assert_eq!(map.insert(first, [1]), Ok(None)); - assert_eq!(map.remove(&first), Ok(Some([1]))); - assert_eq!(map.insert(second, [2]), Ok(None)); - assert_eq!(map.get(&second), Ok(Some([2]))); - assert_eq!(map.len(), 1); - } - - #[test] - fn fixed_map_reports_full_table() { - let mut map = FixedOpenAddressMap::<1, 1, 1>::new(); - - assert_eq!(map.insert([1], [1]), Ok(None)); - assert_eq!(map.insert([2], [2]), Err(TableError::CapacityOverflow)); - } - - #[test] - fn fixed_map_stores_zero_key_and_zero_value() { - let mut map = Fixed::new(); - - assert_eq!(map.insert([0], [0]), Ok(None)); - assert_eq!(map.get(&[0]), Ok(Some([0]))); - assert_eq!(map.remove(&[0]), Ok(Some([0]))); - assert_eq!(map.get(&[0]), Ok(None)); - } - - #[test] - fn static_table_updates_existing_key() { - let mut memory = [0u8; 8]; - let table = unsafe { - StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 2).unwrap() - }; - - assert_eq!(table.insert(&[0], &[0]), Ok(None)); - assert_eq!(table.insert(&[0], &[7]), Ok(Some([0]))); - assert_eq!(table.get(&[0]), Ok(Some([7]))); - } - - #[test] - fn static_table_reuses_tombstone() { - let (first, second) = colliding_keys(1); - let mut memory = [0u8; 3]; - let table = unsafe { - StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() - }; - - assert_eq!(table.insert(&first, &[1]), Ok(None)); - assert_eq!(table.remove(&first), Ok(Some([1]))); - assert_eq!(table.insert(&second, &[2]), Ok(None)); - assert_eq!(table.get(&second), Ok(Some([2]))); - } - - #[test] - fn static_table_reports_full_table() { - let mut memory = [0u8; 3]; - let table = unsafe { - StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() - }; - - assert_eq!(table.insert(&[1], &[1]), Ok(None)); - assert_eq!(table.insert(&[2], &[2]), Err(TableError::CapacityOverflow)); - } - - #[test] - fn static_table_reports_invalid_slot_state() { - let mut memory = [9u8, 0, 0]; - let table = unsafe { - StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() - }; - - assert_eq!(table.get(&[0]), Err(TableError::InvalidSlotState)); - } - - #[test] - fn static_table_clear_resets_deleted_slots() { - let mut memory = [0u8; 3]; - let table = unsafe { - StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() - }; - - table.insert(&[1], &[1]).unwrap(); - table.remove(&[1]).unwrap(); - table.clear().unwrap(); - assert_eq!(memory, [0, 0, 0]); - } - - #[test] - fn static_balance_table_transfers_without_result_rereads() { - let mut memory = vec![0u8; StaticBalanceTable::bytes_len(4).unwrap()]; - let table = unsafe { StaticBalanceTable::new(memory.as_mut_ptr() as usize, 4).unwrap() }; - let from = ActorId::from(1u64); - let to = ActorId::from(2u64); - - assert_eq!(table.insert_actor_u256(from, U256::from(10)), Ok(None)); - assert_eq!( - table.transfer_actor_u256(from, to, U256::from(3)), - Ok(Some(StaticActorU256Transfer { - from_balance: U256::from(7), - to_balance: U256::from(3), - inserted_to: true, - })) - ); - assert_eq!(table.get_actor_u256(&from), Ok(Some(U256::from(7)))); - assert_eq!(table.get_actor_u256(&to), Ok(Some(U256::from(3)))); - assert_eq!( - table.transfer_actor_u256(from, to, U256::from(2)), - Ok(Some(StaticActorU256Transfer { - from_balance: U256::from(5), - to_balance: U256::from(5), - inserted_to: false, - })) - ); - assert_eq!(table.transfer_actor_u256(from, to, U256::from(8)), Ok(None)); - assert_eq!(table.get_actor_u256(&from), Ok(Some(U256::from(5)))); - assert_eq!(table.get_actor_u256(&to), Ok(Some(U256::from(5)))); - } - - #[test] - fn static_balance_table_transfer_rejects_full_recipient_without_mutating() { - let mut memory = vec![0u8; StaticBalanceTable::bytes_len(1).unwrap()]; - let table = unsafe { StaticBalanceTable::new(memory.as_mut_ptr() as usize, 1).unwrap() }; - let from = ActorId::from(1u64); - let to = ActorId::from(2u64); - - assert_eq!(table.insert_actor_u256(from, U256::from(10)), Ok(None)); - assert_eq!(table.transfer_actor_u256(from, to, U256::from(3)), Ok(None)); - assert_eq!(table.get_actor_u256(&from), Ok(Some(U256::from(10)))); - assert_eq!(table.get_actor_u256(&to), Ok(None)); - } - - #[test] - fn static_balance_table_transfer_from_preserves_state_on_failure() { - let mut balance_memory = vec![0u8; StaticBalanceTable::bytes_len(4).unwrap()]; - let balances = - unsafe { StaticBalanceTable::new(balance_memory.as_mut_ptr() as usize, 4).unwrap() }; - let mut allowance_memory = vec![0u8; StaticAllowanceTable::bytes_len(4).unwrap()]; - let allowances = unsafe { - StaticAllowanceTable::new(allowance_memory.as_mut_ptr() as usize, 4).unwrap() - }; - let owner = ActorId::from(1u64); - let spender = ActorId::from(2u64); - let to = ActorId::from(3u64); - - assert_eq!(balances.insert_actor_u256(owner, U256::from(10)), Ok(None)); - assert_eq!( - allowances.insert_allowance_u256(owner, spender, U256::from(5)), - Ok(None) - ); - assert_eq!( - balances.transfer_actor_u256_from(&allowances, owner, spender, to, U256::from(4)), - Ok(Some(StaticActorU256TransferFrom { - from_balance: U256::from(6), - to_balance: U256::from(4), - allowance: U256::from(1), - inserted_to: true, - })) - ); - assert_eq!(balances.get_actor_u256(&owner), Ok(Some(U256::from(6)))); - assert_eq!(balances.get_actor_u256(&to), Ok(Some(U256::from(4)))); - assert_eq!( - allowances.get_allowance_u256(&owner, &spender), - Ok(Some(U256::from(1))) - ); - assert_eq!( - balances.transfer_actor_u256_from(&allowances, owner, spender, to, U256::from(2)), - Ok(None) - ); - assert_eq!(balances.get_actor_u256(&owner), Ok(Some(U256::from(6)))); - assert_eq!(balances.get_actor_u256(&to), Ok(Some(U256::from(4)))); - assert_eq!( - allowances.get_allowance_u256(&owner, &spender), - Ok(Some(U256::from(1))) - ); - } - - #[test] - fn static_balance_table_transfer_from_rejects_missing_allowance_without_mutating() { - let mut balance_memory = vec![0u8; StaticBalanceTable::bytes_len(2).unwrap()]; - let balances = - unsafe { StaticBalanceTable::new(balance_memory.as_mut_ptr() as usize, 2).unwrap() }; - let mut allowance_memory = vec![0u8; StaticAllowanceTable::bytes_len(1).unwrap()]; - let allowances = unsafe { - StaticAllowanceTable::new(allowance_memory.as_mut_ptr() as usize, 1).unwrap() - }; - let owner = ActorId::from(1u64); - let spender = ActorId::from(2u64); - let to = ActorId::from(3u64); - - assert_eq!(balances.insert_actor_u256(owner, U256::from(10)), Ok(None)); - assert_eq!( - balances.transfer_actor_u256_from(&allowances, owner, spender, to, U256::from(3)), - Ok(None) - ); - assert_eq!(balances.get_actor_u256(&owner), Ok(Some(U256::from(10)))); - assert_eq!(balances.get_actor_u256(&to), Ok(None)); - assert_eq!(allowances.get_allowance_u256(&owner, &spender), Ok(None)); - } - - #[test] - fn static_layout_reserves_non_overlapping_regions() { - let mut layout = StaticLayout::new(100, 16).unwrap(); - let table = layout.reserve_table::<1, 1>(2).unwrap(); - let value = layout.reserve_bytes(4).unwrap(); - - assert_eq!(table.base(), 100); - assert_eq!(table.region().bytes(), 6); - assert_eq!(value.base(), 106); - assert_eq!(value.bytes(), 4); - assert_eq!(layout.remaining(), 6); - } - - #[test] - fn static_layout_reserves_aligned_regions() { - let mut layout = StaticLayout::new(100, 32).unwrap(); - let value = layout.reserve_aligned_bytes(4, 16).unwrap(); - - assert_eq!(value.base(), 112); - assert_eq!(value.bytes(), 4); - assert_eq!(layout.cursor(), 116); - assert_eq!( - layout.reserve_aligned_bytes(1, 3).err(), - Some(TableError::InvalidLayout) - ); - } - - #[test] - fn static_layout_rejects_overflow_and_out_of_bounds() { - assert_eq!( - StaticLayout::new(usize::MAX, 1).err(), - Some(TableError::InvalidLayout) - ); - - let mut layout = StaticLayout::new(0, 2).unwrap(); - assert_eq!( - layout.reserve_table::<1, 1>(1).err(), - Some(TableError::InvalidLayout) - ); - } - - #[test] - fn fixed_and_static_tables_match_representative_ops() { - let mut fixed = FixedOpenAddressMap::<1, 1, 4>::new(); - let mut memory = [0u8; 12]; - let static_table = unsafe { - StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 4).unwrap() - }; - - for (key, value) in [([0], [0]), ([1], [2]), ([2], [3])] { - assert_eq!(fixed.insert(key, value), static_table.insert(&key, &value)); - } - - assert_eq!(fixed.remove(&[1]), static_table.remove(&[1])); - assert_eq!(fixed.insert([3], [4]), static_table.insert(&[3], &[4])); - - for key in [[0], [1], [2], [3]] { - assert_eq!(fixed.get(&key), static_table.get(&key)); - } - } - - #[test] - fn fixed_map_entries_return_visible_pairs_only() { - let mut map = FixedOpenAddressMap::<1, 1, 4>::new(); - - map.insert([1], [10]).unwrap(); - map.insert([2], [20]).unwrap(); - map.remove(&[1]).unwrap(); - map.insert([3], [30]).unwrap(); - - let mut entries = map.entries().collect::>(); - entries.sort_unstable_by_key(|(key, _)| *key); - - assert_eq!(entries, vec![([2], [20]), ([3], [30])]); - } -} - -#[cfg(test)] -mod experimental_tests { - extern crate std; - - use super::{__experimental::*, PAGE_LOCAL_ACTOR_U256_TILE_BYTES, TableError}; - use gprimitives::{ActorId, U256}; - use std::{vec, vec::Vec}; - - #[test] - fn static_actor_map_insert_update_and_remove() { - let mut memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; - let table = - unsafe { StaticActorIdU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - let actor = ActorId::from(1u64); - - assert_eq!(table.get_actor_u256(&actor), Ok(None)); - assert_eq!(table.insert_actor_u256(actor, U256::from(10)), Ok(None)); - assert_eq!(table.get_actor_u256(&actor), Ok(Some(U256::from(10)))); - assert_eq!( - table.insert_actor_u256(actor, U256::from(20)), - Ok(Some(U256::from(10))) - ); - assert_eq!(table.get_actor_u256(&actor), Ok(Some(U256::from(20)))); - assert_eq!(table.remove_actor_u256(&actor), Ok(Some(U256::from(20)))); - assert_eq!(table.get_actor_u256(&actor), Ok(None)); - } - - #[test] - fn static_actor_map_zero_value_is_tombstone() { - let mut memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; - let table = - unsafe { StaticActorIdU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - let actor = ActorId::from(2u64); - - assert_eq!(table.insert_actor_u256(actor, U256::from(7)), Ok(None)); - assert_eq!( - table.insert_actor_u256(actor, U256::zero()), - Ok(Some(U256::from(7))) - ); - assert_eq!(table.get_actor_u256(&actor), Ok(None)); - assert_eq!(table.insert_actor_u256(actor, U256::from(8)), Ok(None)); - assert_eq!(table.get_actor_u256(&actor), Ok(Some(U256::from(8)))); - } - - #[test] - fn static_actor_map_transfers_balance_in_one_operation() { - let mut memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; - let table = - unsafe { StaticActorIdU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - let from = ActorId::from(1u64); - let to = ActorId::from(2u64); - - assert_eq!(table.insert_actor_u256(from, U256::from(10)), Ok(None)); - assert_eq!( - table.transfer_actor_u256(from, to, U256::from(3)), - Ok(Some(ActorU256Transfer { - from_balance: U256::from(7), - to_balance: U256::from(3), - })) - ); - assert_eq!(table.get_actor_u256(&from), Ok(Some(U256::from(7)))); - assert_eq!(table.get_actor_u256(&to), Ok(Some(U256::from(3)))); - assert_eq!(table.transfer_actor_u256(from, to, U256::from(8)), Ok(None)); - assert_eq!(table.get_actor_u256(&from), Ok(Some(U256::from(7)))); - assert_eq!(table.get_actor_u256(&to), Ok(Some(U256::from(3)))); - } - - #[test] - fn static_actor_map_transfers_from_allowance_in_one_operation() { - let mut balance_memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; - let balances = unsafe { - StaticActorIdU256Map::<2>::new(balance_memory.as_mut_ptr() as usize).unwrap() - }; - let mut allowance_memory = vec![0u8; StaticAllowanceU256Map::<2>::bytes_len().unwrap()]; - let allowances = unsafe { - StaticAllowanceU256Map::<2>::new(allowance_memory.as_mut_ptr() as usize).unwrap() - }; - let owner = ActorId::from(1u64); - let spender = ActorId::from(2u64); - let to = ActorId::from(3u64); - - assert_eq!(balances.insert_actor_u256(owner, U256::from(10)), Ok(None)); - assert_eq!( - allowances.insert_allowance_u256(owner, spender, U256::from(5)), - Ok(None) - ); - assert_eq!( - balances.transfer_actor_u256_from(&allowances, owner, spender, to, U256::from(4)), - Ok(Some(ActorU256TransferFrom { - from_balance: U256::from(6), - to_balance: U256::from(4), - allowance: U256::from(1), - })) - ); - assert_eq!(balances.get_actor_u256(&owner), Ok(Some(U256::from(6)))); - assert_eq!(balances.get_actor_u256(&to), Ok(Some(U256::from(4)))); - assert_eq!( - allowances.get_allowance_u256(&owner, &spender), - Ok(Some(U256::from(1))) - ); - } - - #[test] - fn static_actor_map_transfer_from_preserves_state_on_failure() { - let mut balance_memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; - let balances = unsafe { - StaticActorIdU256Map::<2>::new(balance_memory.as_mut_ptr() as usize).unwrap() - }; - let mut allowance_memory = vec![0u8; StaticAllowanceU256Map::<2>::bytes_len().unwrap()]; - let allowances = unsafe { - StaticAllowanceU256Map::<2>::new(allowance_memory.as_mut_ptr() as usize).unwrap() - }; - let owner = ActorId::from(1u64); - let spender = ActorId::from(2u64); - let to = ActorId::from(3u64); - - assert_eq!(balances.insert_actor_u256(owner, U256::from(3)), Ok(None)); - assert_eq!( - allowances.insert_allowance_u256(owner, spender, U256::from(10)), - Ok(None) - ); - assert_eq!( - balances.transfer_actor_u256_from(&allowances, owner, spender, to, U256::from(4)), - Ok(None) - ); - assert_eq!(balances.get_actor_u256(&owner), Ok(Some(U256::from(3)))); - assert_eq!(balances.get_actor_u256(&to), Ok(None)); - assert_eq!( - allowances.get_allowance_u256(&owner, &spender), - Ok(Some(U256::from(10))) - ); - - assert_eq!( - allowances.insert_allowance_u256(owner, spender, U256::from(2)), - Ok(Some(U256::from(10))) - ); - assert_eq!( - balances.transfer_actor_u256_from(&allowances, owner, spender, to, U256::from(3)), - Ok(None) - ); - assert_eq!(balances.get_actor_u256(&owner), Ok(Some(U256::from(3)))); - assert_eq!(balances.get_actor_u256(&to), Ok(None)); - assert_eq!( - allowances.get_allowance_u256(&owner, &spender), - Ok(Some(U256::from(2))) - ); - } - - #[test] - fn static_vft_storage_handles_core_hot_path() { - let mut balance_memory = vec![0u8; VftBalances::<3>::bytes_len().unwrap()]; - let mut allowance_memory = vec![0u8; VftAllowances::<3>::bytes_len().unwrap()]; - let storage = unsafe { - StaticVftStorage::<3, 3>::new( - balance_memory.as_mut_ptr() as usize, - allowance_memory.as_mut_ptr() as usize, - ) - .unwrap() - }; - let owner = ActorId::from(1u64); - let spender = ActorId::from(2u64); - let to = ActorId::from(3u64); - - assert_eq!(storage.mint(owner, U256::from(10)), Ok(true)); - assert_eq!(storage.transfer(owner, to, U256::from(3)), Ok(true)); - assert_eq!(storage.balance_of(owner), Ok(U256::from(7))); - assert_eq!(storage.balance_of(to), Ok(U256::from(3))); - assert_eq!(storage.transfer(owner, owner, U256::from(1)), Ok(false)); - assert_eq!(storage.transfer(owner, to, U256::zero()), Ok(false)); - - assert_eq!(storage.approve(owner, spender, U256::from(5)), Ok(true)); - assert_eq!(storage.approve(owner, spender, U256::from(5)), Ok(false)); - assert_eq!(storage.allowance(owner, spender), Ok(U256::from(5))); - assert_eq!( - storage.transfer_from(spender, owner, to, U256::from(4)), - Ok(true) - ); - assert_eq!(storage.balance_of(owner), Ok(U256::from(3))); - assert_eq!(storage.balance_of(to), Ok(U256::from(7))); - assert_eq!(storage.allowance(owner, spender), Ok(U256::from(1))); - - assert_eq!(storage.burn(to, U256::from(7)), Ok(true)); - assert_eq!(storage.balance_of(to), Ok(U256::zero())); - } - - #[test] - fn static_vft_storage_preserves_state_on_failed_transfer_from() { - let mut balance_memory = vec![0u8; VftBalances::<3>::bytes_len().unwrap()]; - let mut allowance_memory = vec![0u8; VftAllowances::<3>::bytes_len().unwrap()]; - let storage = unsafe { - StaticVftStorage::<3, 3>::new( - balance_memory.as_mut_ptr() as usize, - allowance_memory.as_mut_ptr() as usize, - ) - .unwrap() - }; - let owner = ActorId::from(1u64); - let spender = ActorId::from(2u64); - let to = ActorId::from(3u64); - - assert_eq!(storage.mint(owner, U256::from(3)), Ok(true)); - assert_eq!(storage.approve(owner, spender, U256::from(10)), Ok(true)); - assert_eq!( - storage.transfer_from(spender, owner, to, U256::from(4)), - Ok(false) - ); - assert_eq!(storage.balance_of(owner), Ok(U256::from(3))); - assert_eq!(storage.balance_of(to), Ok(U256::zero())); - assert_eq!(storage.allowance(owner, spender), Ok(U256::from(10))); - - assert_eq!(storage.approve(owner, spender, U256::from(2)), Ok(true)); - assert_eq!( - storage.transfer_from(spender, owner, to, U256::from(3)), - Ok(false) - ); - assert_eq!(storage.balance_of(owner), Ok(U256::from(3))); - assert_eq!(storage.balance_of(to), Ok(U256::zero())); - assert_eq!(storage.allowance(owner, spender), Ok(U256::from(2))); - } - #[test] - fn static_actor_map_tombstone_preserves_probe_chain() { - let (first, second) = colliding_actor_pair::<1>(); - let mut memory = vec![0u8; StaticActorIdU256Map::<1>::bytes_len().unwrap()]; - let table = - unsafe { StaticActorIdU256Map::<1>::new(memory.as_mut_ptr() as usize).unwrap() }; - - assert_eq!(table.insert_actor_u256(first, U256::from(1)), Ok(None)); - assert_eq!(table.insert_actor_u256(second, U256::from(2)), Ok(None)); - assert_eq!(table.remove_actor_u256(&first), Ok(Some(U256::from(1)))); - assert_eq!(table.get_actor_u256(&second), Ok(Some(U256::from(2)))); - } - - #[test] - fn static_actor_map_rejects_zero_actor_mutation() { - let mut memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; - let table = - unsafe { StaticActorIdU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - let actor = ActorId::zero(); - - assert_eq!( - table.insert_actor_u256(actor, U256::from(1)), - Err(TableError::InvalidKey) - ); - assert_eq!(table.remove_actor_u256(&actor), Err(TableError::InvalidKey)); - assert_eq!(table.get_actor_u256(&actor), Ok(None)); - } - - #[test] - fn static_control_actor_map_insert_update_and_remove() { - let (mut control, mut slots) = control_actor_memory::<2>(); - let table = unsafe { - StaticControlActorIdU256Map::<2>::new( - control.as_mut_ptr() as usize, - slots.as_mut_ptr() as usize, - ) - .unwrap() - }; - let actor = ActorId::from(1u64); - - assert_eq!(table.get_actor_u256(&actor), Ok(None)); - assert_eq!(table.insert_actor_u256(actor, U256::from(10)), Ok(None)); - assert_eq!(table.get_actor_u256(&actor), Ok(Some(U256::from(10)))); - assert_eq!( - table.insert_actor_u256(actor, U256::from(20)), - Ok(Some(U256::from(10))) - ); - assert_eq!(table.get_actor_u256(&actor), Ok(Some(U256::from(20)))); - assert_eq!(table.remove_actor_u256(&actor), Ok(Some(U256::from(20)))); - assert_eq!(table.get_actor_u256(&actor), Ok(None)); + } + + unreachable!("single-byte key space must contain collisions") } #[test] - fn static_control_actor_map_zero_value_removes_and_reuses_deleted_slot() { - let (first, second) = colliding_actor_pair::<1>(); - let (mut control, mut slots) = control_actor_memory::<1>(); - let table = unsafe { - StaticControlActorIdU256Map::<1>::new( - control.as_mut_ptr() as usize, - slots.as_mut_ptr() as usize, - ) - .unwrap() - }; + fn fixed_map_updates_existing_key() { + let mut map = Fixed::new(); - assert_eq!(table.insert_actor_u256(first, U256::from(7)), Ok(None)); - assert_eq!( - table.insert_actor_u256(first, U256::zero()), - Ok(Some(U256::from(7))) - ); - assert_eq!(table.get_actor_u256(&first), Ok(None)); - assert_eq!(table.insert_actor_u256(second, U256::from(8)), Ok(None)); - assert_eq!(table.get_actor_u256(&second), Ok(Some(U256::from(8)))); + assert_eq!(map.insert([0], [0]), Ok(None)); + assert_eq!(map.insert([0], [7]), Ok(Some([0]))); + assert_eq!(map.get(&[0]), Ok(Some([7]))); + assert_eq!(map.len(), 1); } #[test] - fn static_control_actor_map_deletion_preserves_probe_chain() { - let (first, second) = colliding_actor_pair::<1>(); - let (mut control, mut slots) = control_actor_memory::<1>(); - let table = unsafe { - StaticControlActorIdU256Map::<1>::new( - control.as_mut_ptr() as usize, - slots.as_mut_ptr() as usize, - ) - .unwrap() - }; + fn fixed_map_reuses_tombstone() { + let (first, second) = colliding_keys(1); + let mut map = FixedOpenAddressMap::<1, 1, 1>::new(); - assert_eq!(table.insert_actor_u256(first, U256::from(1)), Ok(None)); - assert_eq!(table.insert_actor_u256(second, U256::from(2)), Ok(None)); - assert_eq!(table.remove_actor_u256(&first), Ok(Some(U256::from(1)))); - assert_eq!(table.get_actor_u256(&second), Ok(Some(U256::from(2)))); + assert_eq!(map.insert(first, [1]), Ok(None)); + assert_eq!(map.remove(&first), Ok(Some([1]))); + assert_eq!(map.insert(second, [2]), Ok(None)); + assert_eq!(map.get(&second), Ok(Some([2]))); + assert_eq!(map.len(), 1); } #[test] - fn static_control_actor_map_reports_full_table() { - let (mut control, mut slots) = control_actor_memory::<0>(); - let table = unsafe { - StaticControlActorIdU256Map::<0>::new( - control.as_mut_ptr() as usize, - slots.as_mut_ptr() as usize, - ) - .unwrap() - }; + fn fixed_map_reports_full_table() { + let mut map = FixedOpenAddressMap::<1, 1, 1>::new(); - assert_eq!( - table.insert_actor_u256(ActorId::from(1u64), U256::from(1)), - Ok(None) - ); - assert_eq!( - table.insert_actor_u256(ActorId::from(2u64), U256::from(2)), - Err(TableError::CapacityOverflow) - ); + assert_eq!(map.insert([1], [1]), Ok(None)); + assert_eq!(map.insert([2], [2]), Err(TableError::CapacityOverflow)); } #[test] - fn static_control_actor_map_rejects_zero_actor_mutation() { - let (mut control, mut slots) = control_actor_memory::<2>(); - let table = unsafe { - StaticControlActorIdU256Map::<2>::new( - control.as_mut_ptr() as usize, - slots.as_mut_ptr() as usize, - ) - .unwrap() - }; - let actor = ActorId::zero(); + fn fixed_map_stores_zero_key_and_zero_value() { + let mut map = Fixed::new(); - assert_eq!( - table.insert_actor_u256(actor, U256::from(1)), - Err(TableError::InvalidKey) - ); - assert_eq!(table.remove_actor_u256(&actor), Err(TableError::InvalidKey)); - assert_eq!(table.get_actor_u256(&actor), Ok(None)); + assert_eq!(map.insert([0], [0]), Ok(None)); + assert_eq!(map.get(&[0]), Ok(Some([0]))); + assert_eq!(map.remove(&[0]), Ok(Some([0]))); + assert_eq!(map.get(&[0]), Ok(None)); } #[test] - fn static_control_actor_map_clear_resets_control_and_data() { - let (mut control, mut slots) = control_actor_memory::<2>(); - let table = unsafe { - StaticControlActorIdU256Map::<2>::new( - control.as_mut_ptr() as usize, - slots.as_mut_ptr() as usize, - ) - .unwrap() - }; + fn fixed_map_entries_return_visible_pairs_only() { + let mut map = FixedOpenAddressMap::<1, 1, 4>::new(); - table - .insert_actor_u256(ActorId::from(1u64), U256::from(1)) - .unwrap(); - table.clear().unwrap(); + map.insert([1], [10]).unwrap(); + map.insert([2], [20]).unwrap(); + map.remove(&[1]).unwrap(); + map.insert([3], [30]).unwrap(); - assert!(control.iter().all(|byte| *byte == 0)); - assert!(slots.iter().all(|byte| *byte == 0)); - } + let mut entries = map.entries().collect::>(); + entries.sort_unstable_by_key(|(key, _)| *key); - #[test] - fn static_page_local_actor_map_layout_fits_one_gear_page_tile() { - assert_eq!( - StaticPageLocalActorIdU256Map::<0>::tile_bytes(), - PAGE_LOCAL_ACTOR_U256_TILE_BYTES - ); - assert_eq!(StaticPageLocalActorIdU256Map::<0>::slots_per_tile(), 252); - assert_eq!(StaticPageLocalActorIdU256Map::<0>::data_offset(), 256); - assert_eq!( - StaticPageLocalActorIdU256Map::<0>::data_offset() - + StaticPageLocalActorIdU256Map::<0>::slots_per_tile() - * StaticPageLocalActorIdU256Map::<0>::slot_size(), - StaticPageLocalActorIdU256Map::<0>::tile_bytes() - ); - assert_eq!( - StaticPageLocalActorIdU256Map::<1>::slots().unwrap(), - 2 * StaticPageLocalActorIdU256Map::<1>::slots_per_tile() - ); - assert_eq!( - StaticPageLocalActorIdU256Map::<1>::bytes_len().unwrap(), - 2 * PAGE_LOCAL_ACTOR_U256_TILE_BYTES - ); + assert_eq!(entries, vec![([2], [20]), ([3], [30])]); } #[test] - fn static_page_local_actor_map_insert_update_and_remove() { - let mut memory = page_local_actor_memory::<1>(); + fn static_table_updates_existing_key() { + let mut memory = [0u8; 8]; let table = unsafe { - StaticPageLocalActorIdU256Map::<1>::new(memory.as_mut_ptr() as usize).unwrap() + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 2).unwrap() }; - let actor = ActorId::from(1u64); - assert_eq!(table.tile_count(), 2); - assert_eq!(table.get_actor_u256(&actor), Ok(None)); - assert_eq!(table.insert_actor_u256(actor, U256::from(10)), Ok(None)); - assert_eq!(table.get_actor_u256(&actor), Ok(Some(U256::from(10)))); - assert_eq!( - table.insert_actor_u256(actor, U256::from(20)), - Ok(Some(U256::from(10))) - ); - assert_eq!(table.get_actor_u256(&actor), Ok(Some(U256::from(20)))); - assert_eq!(table.remove_actor_u256(&actor), Ok(Some(U256::from(20)))); - assert_eq!(table.get_actor_u256(&actor), Ok(None)); + assert_eq!(table.insert(&[0], &[0]), Ok(None)); + assert_eq!(table.insert(&[0], &[7]), Ok(Some([0]))); + assert_eq!(table.get(&[0]), Ok(Some([7]))); } #[test] - fn static_page_local_actor_map_zero_value_removes_and_reuses_deleted_slot() { - let (first, second) = colliding_page_local_actor_pair::<0>(); - let mut memory = page_local_actor_memory::<0>(); + fn static_table_reuses_tombstone() { + let (first, second) = colliding_keys(1); + let mut memory = [0u8; 3]; let table = unsafe { - StaticPageLocalActorIdU256Map::<0>::new(memory.as_mut_ptr() as usize).unwrap() + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() }; - assert_eq!(table.insert_actor_u256(first, U256::from(7)), Ok(None)); - assert_eq!( - table.insert_actor_u256(first, U256::zero()), - Ok(Some(U256::from(7))) - ); - assert_eq!(table.get_actor_u256(&first), Ok(None)); - assert_eq!(table.insert_actor_u256(second, U256::from(8)), Ok(None)); - assert_eq!(table.get_actor_u256(&second), Ok(Some(U256::from(8)))); + assert_eq!(table.insert(&first, &[1]), Ok(None)); + assert_eq!(table.remove(&first), Ok(Some([1]))); + assert_eq!(table.insert(&second, &[2]), Ok(None)); + assert_eq!(table.get(&second), Ok(Some([2]))); } #[test] - fn static_page_local_actor_map_deletion_preserves_probe_chain() { - let (first, second) = colliding_page_local_actor_pair::<0>(); - let mut memory = page_local_actor_memory::<0>(); + fn static_table_reports_full_table() { + let mut memory = [0u8; 3]; let table = unsafe { - StaticPageLocalActorIdU256Map::<0>::new(memory.as_mut_ptr() as usize).unwrap() + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() }; - assert_eq!(table.insert_actor_u256(first, U256::from(1)), Ok(None)); - assert_eq!(table.insert_actor_u256(second, U256::from(2)), Ok(None)); - assert_eq!(table.remove_actor_u256(&first), Ok(Some(U256::from(1)))); - assert_eq!(table.get_actor_u256(&second), Ok(Some(U256::from(2)))); + assert_eq!(table.insert(&[1], &[1]), Ok(None)); + assert_eq!(table.insert(&[2], &[2]), Err(TableError::CapacityOverflow)); } #[test] - fn static_page_local_actor_map_reports_full_table() { - let mut memory = page_local_actor_memory::<0>(); + fn static_table_reports_invalid_slot_state() { + let mut memory = [9u8, 0, 0]; let table = unsafe { - StaticPageLocalActorIdU256Map::<0>::new(memory.as_mut_ptr() as usize).unwrap() + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() }; - for seed in 1..=StaticPageLocalActorIdU256Map::<0>::slots_per_tile() as u64 { - assert_eq!( - table.insert_actor_u256(ActorId::from(seed), U256::from(seed)), - Ok(None) - ); - } - assert_eq!( - table.insert_actor_u256(ActorId::from(10_000u64), U256::from(1)), - Err(TableError::CapacityOverflow) - ); + assert_eq!(table.get(&[0]), Err(TableError::InvalidSlotState)); } #[test] - fn static_page_local_actor_map_rejects_zero_actor_mutation() { - let mut memory = page_local_actor_memory::<1>(); + fn static_table_clear_resets_deleted_slots() { + let mut memory = [0u8; 3]; let table = unsafe { - StaticPageLocalActorIdU256Map::<1>::new(memory.as_mut_ptr() as usize).unwrap() + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 1).unwrap() }; - let actor = ActorId::zero(); - assert_eq!( - table.insert_actor_u256(actor, U256::from(1)), - Err(TableError::InvalidKey) - ); - assert_eq!(table.remove_actor_u256(&actor), Err(TableError::InvalidKey)); - assert_eq!(table.get_actor_u256(&actor), Ok(None)); + table.insert(&[1], &[1]).unwrap(); + table.remove(&[1]).unwrap(); + table.clear().unwrap(); + assert_eq!(memory, [0, 0, 0]); } #[test] - fn static_page_local_actor_map_clear_resets_tile_memory() { - let mut memory = page_local_actor_memory::<1>(); - let table = unsafe { - StaticPageLocalActorIdU256Map::<1>::new(memory.as_mut_ptr() as usize).unwrap() - }; - - table - .insert_actor_u256(ActorId::from(1u64), U256::from(1)) - .unwrap(); - table.clear().unwrap(); + fn static_layout_reserves_non_overlapping_regions() { + let mut layout = StaticLayout::new(100, 16).unwrap(); + let table = layout.reserve_table::<1, 1>(2).unwrap(); + let value = layout.reserve_bytes(4).unwrap(); - assert!(memory.iter().all(|byte| *byte == 0)); + assert_eq!(table.base(), 100); + assert_eq!(table.region().bytes(), 6); + assert_eq!(value.base(), 106); + assert_eq!(value.bytes(), 4); + assert_eq!(layout.remaining(), 6); } #[test] - fn static_grouped_control_actor_map_layout_scales_by_group_pages() { - assert_eq!( - StaticGroupedControlActorIdU256Map::<2, 3>::groups().unwrap(), - 4 - ); - assert_eq!( - StaticGroupedControlActorIdU256Map::<2, 3>::group_pages().unwrap(), - 8 - ); - assert_eq!( - StaticGroupedControlActorIdU256Map::<2, 3>::slots_per_group().unwrap(), - 8 * StaticPageLocalActorIdU256Map::<0>::slots_per_tile() - ); - assert_eq!( - StaticGroupedControlActorIdU256Map::<2, 3>::data_offset().unwrap(), - 8 * StaticPageLocalActorIdU256Map::<0>::data_offset() - ); - assert_eq!( - StaticGroupedControlActorIdU256Map::<2, 3>::group_bytes().unwrap(), - 8 * PAGE_LOCAL_ACTOR_U256_TILE_BYTES - ); - assert_eq!( - StaticGroupedControlActorIdU256Map::<2, 3>::slots().unwrap(), - 4 * 8 * StaticPageLocalActorIdU256Map::<0>::slots_per_tile() - ); + fn static_layout_reserves_aligned_regions() { + let mut layout = StaticLayout::new(100, 32).unwrap(); + let value = layout.reserve_aligned_bytes(4, 16).unwrap(); + + assert_eq!(value.base(), 112); + assert_eq!(value.bytes(), 4); + assert_eq!(layout.cursor(), 116); assert_eq!( - StaticGroupedControlActorIdU256Map::<2, 3>::bytes_len().unwrap(), - 4 * 8 * PAGE_LOCAL_ACTOR_U256_TILE_BYTES + layout.reserve_aligned_bytes(1, 3).err(), + Some(TableError::InvalidLayout) ); } #[test] - fn static_grouped_control_actor_map_insert_update_and_remove() { - let mut memory = grouped_control_actor_memory::<1, 1>(); - let table = unsafe { - StaticGroupedControlActorIdU256Map::<1, 1>::new(memory.as_mut_ptr() as usize).unwrap() - }; - let actor = ActorId::from(1u64); - - assert_eq!(table.group_count(), 2); - assert_eq!(table.get_actor_u256(&actor), Ok(None)); - assert_eq!(table.insert_actor_u256(actor, U256::from(10)), Ok(None)); - assert_eq!(table.get_actor_u256(&actor), Ok(Some(U256::from(10)))); + fn static_layout_rejects_overflow_and_out_of_bounds() { assert_eq!( - table.insert_actor_u256(actor, U256::from(20)), - Ok(Some(U256::from(10))) + StaticLayout::new(usize::MAX, 1).err(), + Some(TableError::InvalidLayout) ); - assert_eq!(table.get_actor_u256(&actor), Ok(Some(U256::from(20)))); - assert_eq!(table.remove_actor_u256(&actor), Ok(Some(U256::from(20)))); - assert_eq!(table.get_actor_u256(&actor), Ok(None)); - } - - #[test] - fn static_grouped_control_actor_map_zero_value_removes_and_reuses_deleted_slot() { - let (first, second) = colliding_grouped_control_actor_pair::<0, 1>(); - let mut memory = grouped_control_actor_memory::<0, 1>(); - let table = unsafe { - StaticGroupedControlActorIdU256Map::<0, 1>::new(memory.as_mut_ptr() as usize).unwrap() - }; - assert_eq!(table.insert_actor_u256(first, U256::from(7)), Ok(None)); + let mut layout = StaticLayout::new(0, 2).unwrap(); assert_eq!( - table.insert_actor_u256(first, U256::zero()), - Ok(Some(U256::from(7))) + layout.reserve_table::<1, 1>(1).err(), + Some(TableError::InvalidLayout) ); - assert_eq!(table.get_actor_u256(&first), Ok(None)); - assert_eq!(table.insert_actor_u256(second, U256::from(8)), Ok(None)); - assert_eq!(table.get_actor_u256(&second), Ok(Some(U256::from(8)))); } #[test] - fn static_grouped_control_actor_map_deletion_preserves_probe_chain() { - let (first, second) = colliding_grouped_control_actor_pair::<0, 1>(); - let mut memory = grouped_control_actor_memory::<0, 1>(); - let table = unsafe { - StaticGroupedControlActorIdU256Map::<0, 1>::new(memory.as_mut_ptr() as usize).unwrap() + fn fixed_and_static_tables_match_representative_ops() { + let mut fixed = FixedOpenAddressMap::<1, 1, 4>::new(); + let mut memory = [0u8; 12]; + let static_table = unsafe { + StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 4).unwrap() }; - assert_eq!(table.insert_actor_u256(first, U256::from(1)), Ok(None)); - assert_eq!(table.insert_actor_u256(second, U256::from(2)), Ok(None)); - assert_eq!(table.remove_actor_u256(&first), Ok(Some(U256::from(1)))); - assert_eq!(table.get_actor_u256(&second), Ok(Some(U256::from(2)))); - } + for (key, value) in [([0], [0]), ([1], [2]), ([2], [3])] { + assert_eq!(fixed.insert(key, value), static_table.insert(&key, &value)); + } - #[test] - fn static_grouped_control_actor_map_reports_full_table() { - let mut memory = grouped_control_actor_memory::<0, 0>(); - let table = unsafe { - StaticGroupedControlActorIdU256Map::<0, 0>::new(memory.as_mut_ptr() as usize).unwrap() - }; + assert_eq!(fixed.remove(&[1]), static_table.remove(&[1])); + assert_eq!(fixed.insert([3], [4]), static_table.insert(&[3], &[4])); - for seed in - 1..=StaticGroupedControlActorIdU256Map::<0, 0>::slots_per_group().unwrap() as u64 - { - assert_eq!( - table.insert_actor_u256(ActorId::from(seed), U256::from(seed)), - Ok(None) - ); + for key in [[0], [1], [2], [3]] { + assert_eq!(fixed.get(&key), static_table.get(&key)); } - assert_eq!( - table.insert_actor_u256(ActorId::from(10_000u64), U256::from(1)), - Err(TableError::CapacityOverflow) - ); } #[test] - fn static_grouped_control_actor_map_rejects_zero_actor_mutation() { - let mut memory = grouped_control_actor_memory::<1, 1>(); - let table = unsafe { - StaticGroupedControlActorIdU256Map::<1, 1>::new(memory.as_mut_ptr() as usize).unwrap() - }; - let actor = ActorId::zero(); + fn actor_map_preserves_static_layout_and_tombstones_zero_values() { + assert_eq!(StaticActorIdU256Map::<2>::slot_size(), 64); + assert_eq!(StaticActorIdU256Map::<2>::slots(), Ok(4)); + assert_eq!(StaticActorIdU256Map::<2>::mask(), Ok(3)); + assert_eq!(StaticActorIdU256Map::<2>::bytes_len(), Ok(256)); + + let mut memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; + let map = unsafe { StaticActorIdU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; + let actor = ActorId::from(1u64); + assert_eq!(map.get_actor_u256(&actor), Ok(None)); + assert_eq!(map.insert_actor_u256(actor, U256::from(10)), Ok(None)); + assert_eq!(map.get_actor_u256(&actor), Ok(Some(U256::from(10)))); assert_eq!( - table.insert_actor_u256(actor, U256::from(1)), - Err(TableError::InvalidKey) + map.insert_actor_u256(actor, U256::zero()), + Ok(Some(U256::from(10))) ); - assert_eq!(table.remove_actor_u256(&actor), Err(TableError::InvalidKey)); - assert_eq!(table.get_actor_u256(&actor), Ok(None)); + assert_eq!(map.get_actor_u256(&actor), Ok(None)); } #[test] - fn static_grouped_control_actor_map_clear_resets_group_memory() { - let mut memory = grouped_control_actor_memory::<1, 1>(); - let table = unsafe { - StaticGroupedControlActorIdU256Map::<1, 1>::new(memory.as_mut_ptr() as usize).unwrap() - }; - - table - .insert_actor_u256(ActorId::from(1u64), U256::from(1)) - .unwrap(); - table.clear().unwrap(); - - assert!(memory.iter().all(|byte| *byte == 0)); - } + fn actor_map_rejects_zero_actor_mutation() { + let mut memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; + let map = unsafe { StaticActorIdU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - #[test] - fn static_allowance_map_insert_update_and_remove() { - let mut memory = vec![0u8; StaticAllowanceU256Map::<2>::bytes_len().unwrap()]; - let table = - unsafe { StaticAllowanceU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - let owner = ActorId::from(1u64); - let spender = ActorId::from(2u64); - - assert_eq!(table.get_allowance_u256(&owner, &spender), Ok(None)); - assert_eq!( - table.insert_allowance_u256(owner, spender, U256::from(10)), - Ok(None) - ); assert_eq!( - table.get_allowance_u256(&owner, &spender), - Ok(Some(U256::from(10))) + map.insert_actor_u256(ActorId::zero(), U256::from(1)), + Err(TableError::InvalidKey) ); assert_eq!( - table.insert_allowance_u256(owner, spender, U256::zero()), - Ok(Some(U256::from(10))) + map.remove_actor_u256(&ActorId::zero()), + Err(TableError::InvalidKey) ); - assert_eq!(table.get_allowance_u256(&owner, &spender), Ok(None)); } #[test] - fn static_allowance_map_decreases_allowance_in_one_operation() { - let mut memory = vec![0u8; StaticAllowanceU256Map::<2>::bytes_len().unwrap()]; - let table = - unsafe { StaticAllowanceU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - let owner = ActorId::from(1u64); - let spender = ActorId::from(2u64); - + fn actor_pair_map_preserves_static_layout_and_basic_ops() { + assert_eq!(StaticActorPairU256Map::<2>::slot_size(), 96); + assert_eq!(StaticActorPairU256Map::<2>::slots(), Ok(4)); + assert_eq!(StaticActorPairU256Map::<2>::mask(), Ok(3)); + assert_eq!(StaticActorPairU256Map::<2>::bytes_len(), Ok(384)); + + let mut memory = vec![0u8; StaticActorPairU256Map::<2>::bytes_len().unwrap()]; + let map = + unsafe { StaticActorPairU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; + let left = ActorId::from(1u64); + let right = ActorId::from(2u64); + + assert_eq!(map.get_actor_pair_u256(&left, &right), Ok(None)); assert_eq!( - table.insert_allowance_u256(owner, spender, U256::from(10)), + map.insert_actor_pair_u256(left, right, U256::from(20)), Ok(None) ); assert_eq!( - table.decrease_allowance_u256(owner, spender, U256::from(4)), - Ok(Some(U256::from(6))) - ); - assert_eq!( - table.get_allowance_u256(&owner, &spender), - Ok(Some(U256::from(6))) - ); - assert_eq!( - table.decrease_allowance_u256(owner, spender, U256::from(7)), - Ok(None) + map.get_actor_pair_u256(&left, &right), + Ok(Some(U256::from(20))) ); assert_eq!( - table.get_allowance_u256(&owner, &spender), - Ok(Some(U256::from(6))) + map.remove_actor_pair_u256(&left, &right), + Ok(Some(U256::from(20))) ); + assert_eq!(map.get_actor_pair_u256(&left, &right), Ok(None)); } #[test] - fn static_allowance_map_rejects_zero_actor_mutation() { - let mut memory = vec![0u8; StaticAllowanceU256Map::<2>::bytes_len().unwrap()]; - let table = - unsafe { StaticAllowanceU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - let owner = ActorId::zero(); - let spender = ActorId::from(2u64); + fn actor_pair_map_rejects_zero_actor_mutation() { + let mut memory = vec![0u8; StaticActorPairU256Map::<2>::bytes_len().unwrap()]; + let map = + unsafe { StaticActorPairU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; assert_eq!( - table.insert_allowance_u256(owner, spender, U256::from(1)), + map.insert_actor_pair_u256(ActorId::zero(), ActorId::from(1u64), U256::from(1)), Err(TableError::InvalidKey) ); - assert_eq!( - table.remove_allowance_u256(&owner, &spender), - Err(TableError::InvalidKey) - ); - assert_eq!(table.get_allowance_u256(&owner, &spender), Ok(None)); - } - - fn control_actor_memory() -> (Vec, Vec) { - ( - vec![0u8; StaticControlActorIdU256Map::::control_bytes_len().unwrap()], - vec![0u8; StaticControlActorIdU256Map::::slots_bytes_len().unwrap()], - ) - } - - fn page_local_actor_memory() -> Vec { - vec![0u8; StaticPageLocalActorIdU256Map::::bytes_len().unwrap()] - } - - fn grouped_control_actor_memory() -> Vec - { - vec![ - 0u8; - StaticGroupedControlActorIdU256Map::::bytes_len() - .unwrap() - ] - } - - fn colliding_actor_pair() -> (ActorId, ActorId) { - let mut seen = vec![None; 1usize << LOG2_SLOTS]; - for seed in 1u64..=4096 { - let actor = ActorId::from(seed); - let index = actor_index::(actor); - if let Some(first) = seen[index] { - return (first, actor); - } - seen[index] = Some(actor); - } - - unreachable!("small actor key space must contain collisions") - } - - fn colliding_page_local_actor_pair() -> (ActorId, ActorId) { - let slots = - (1usize << LOG2_TILES) * StaticPageLocalActorIdU256Map::::slots_per_tile(); - let mut seen = vec![None; slots]; - for seed in 1u64..=4096 { - let actor = ActorId::from(seed); - let index = page_local_actor_index::(actor); - if let Some(first) = seen[index] { - return (first, actor); - } - seen[index] = Some(actor); - } - - unreachable!("small actor key space must contain page-local collisions") - } - - fn colliding_grouped_control_actor_pair() - -> (ActorId, ActorId) { - let slots = - StaticGroupedControlActorIdU256Map::::slots().unwrap(); - let mut seen = vec![None; slots]; - for seed in 1u64..=4096 { - let actor = ActorId::from(seed); - let index = grouped_control_actor_index::(actor); - if let Some(first) = seen[index] { - return (first, actor); - } - seen[index] = Some(actor); - } - - unreachable!("small actor key space must contain grouped-control collisions") - } - - fn actor_index(actor: ActorId) -> usize { - let key = actor_key(actor); - let hash = hash_words_for_test(&key); - if LOG2_SLOTS == 0 { - 0 - } else { - (hash >> (32 - u32::from(LOG2_SLOTS))) as usize - } - } - - fn page_local_actor_index(actor: ActorId) -> usize { - let key = actor_key(actor); - let hash = hash_words_for_test(&key); - let tile = if LOG2_TILES == 0 { - 0 - } else { - (hash >> (32 - u32::from(LOG2_TILES))) as usize - }; - let tile_slot = - (hash as usize) % StaticPageLocalActorIdU256Map::::slots_per_tile(); - tile * StaticPageLocalActorIdU256Map::::slots_per_tile() + tile_slot - } - - fn grouped_control_actor_index( - actor: ActorId, - ) -> usize { - let key = actor_key(actor); - let hash = hash_words_for_test(&key); - let group = if LOG2_GROUPS == 0 { - 0 - } else { - (hash >> (32 - u32::from(LOG2_GROUPS))) as usize - }; - let group_slot = (hash as usize) - % StaticGroupedControlActorIdU256Map::::slots_per_group( - ) - .unwrap(); - group - * StaticGroupedControlActorIdU256Map::::slots_per_group() - .unwrap() - + group_slot - } - - fn hash_words_for_test(bytes: &[u8; 32]) -> u32 { - let mut hash = 0u32; - let mut offset = 0; - while offset < 32 { - hash ^= u32::from_le_bytes([ - bytes[offset], - bytes[offset + 1], - bytes[offset + 2], - bytes[offset + 3], - ]); - offset += 4; - } - - hash.wrapping_mul(0x9E37_79B9) } } From f2490eb41980e2ac28092b4458eb8d5bd16cb587 Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Wed, 20 May 2026 17:07:10 +0400 Subject: [PATCH 11/14] chore: simplify storage primitives --- rs/src/build.rs | 241 ++++++++++-------------------------------- rs/storage/src/lib.rs | 8 +- 2 files changed, 55 insertions(+), 194 deletions(-) diff --git a/rs/src/build.rs b/rs/src/build.rs index 3eb91431d..d2acb1403 100644 --- a/rs/src/build.rs +++ b/rs/src/build.rs @@ -36,18 +36,8 @@ pub struct StaticMemoryLayout { struct StaticTable { name: String, slots: StaticTableSlots, - kind: StaticTableKind, -} - -#[derive(Clone, Debug)] -enum StaticTableSlots { - Exact(usize), - Log2(u8), -} - -#[derive(Clone, Debug)] -enum StaticTableKind { - SingleRegion { slot_size: usize, align: usize }, + slot_size: usize, + align: usize, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -61,27 +51,18 @@ struct ResolvedStaticMemoryLayout { struct ResolvedStaticTable { name: String, base: usize, - control_base: Option, - slots_base: Option, slots: usize, log2_slots: Option, - tiles: Option, - log2_tiles: Option, - slots_per_tile: Option, - tile_bytes: Option, - groups: Option, - log2_groups: Option, - group_pages: Option, - log2_group_pages: Option, - slots_per_group: Option, - group_bytes: Option, - data_offset: Option, mask: Option, - control_bytes: Option, - slots_bytes: Option, bytes: usize, } +#[derive(Clone, Debug)] +enum StaticTableSlots { + Exact(usize), + Log2(u8), +} + /// Errors returned by static-memory build helpers. #[derive(Debug, thiserror::Error)] pub enum Error { @@ -167,35 +148,29 @@ impl StaticMemoryLayout { /// Reserves a named static open-addressed table. pub fn reserve_table( - mut self, + self, name: impl Into, slots: usize, ) -> Self { - self.tables.push(StaticTable { - name: name.into(), - slots: StaticTableSlots::Exact(slots), - kind: StaticTableKind::SingleRegion { - slot_size: StaticOpenAddressTable::::slot_size(), - align: 1, - }, - }); - self + self.reserve_single_region_table( + name, + StaticTableSlots::Exact(slots), + StaticOpenAddressTable::::slot_size(), + 1, + ) } /// Reserves a WAT-shaped `ActorId -> U256` static map. /// /// The reserved table uses `2^LOG2_SLOTS` slots. Each slot is exactly 64 /// bytes: a 32-byte actor id followed by a 32-byte `U256` value. - pub fn reserve_actor_u256_map(mut self, name: impl Into) -> Self { - self.tables.push(StaticTable { - name: name.into(), - slots: StaticTableSlots::Log2(LOG2_SLOTS), - kind: StaticTableKind::SingleRegion { - slot_size: ACTOR_ID_U256_SLOT_SIZE, - align: 64, - }, - }); - self + pub fn reserve_actor_u256_map(self, name: impl Into) -> Self { + self.reserve_single_region_table( + name, + StaticTableSlots::Log2(LOG2_SLOTS), + ACTOR_ID_U256_SLOT_SIZE, + 64, + ) } /// Reserves a `(ActorId, ActorId) -> U256` static map. @@ -203,19 +178,33 @@ impl StaticMemoryLayout { /// The reserved table uses `2^LOG2_SLOTS` slots. Each slot is exactly 96 /// bytes: owner actor id, spender actor id, and a `U256` value. pub fn reserve_actor_pair_u256_map( + self, + name: impl Into, + ) -> Self { + self.reserve_single_region_table( + name, + StaticTableSlots::Log2(LOG2_SLOTS), + ACTOR_PAIR_U256_SLOT_SIZE, + 32, + ) + } + + fn reserve_single_region_table( mut self, name: impl Into, + slots: StaticTableSlots, + slot_size: usize, + align: usize, ) -> Self { self.tables.push(StaticTable { name: name.into(), - slots: StaticTableSlots::Log2(LOG2_SLOTS), - kind: StaticTableKind::SingleRegion { - slot_size: ACTOR_PAIR_U256_SLOT_SIZE, - align: 32, - }, + slots, + slot_size, + align, }); self } + fn resolve(self) -> Result { let start_base = page_offset(self.start_page)?; let max_bytes = page_offset(MAX_STATIC_MEMORY_PAGES)?; @@ -234,36 +223,19 @@ impl StaticMemoryLayout { let log2_slots = table.slots.log2(); let slots = table.slots.resolve()?; - let resolved = match table.kind { - StaticTableKind::SingleRegion { slot_size, align } => { - let bytes = slots.checked_mul(slot_size).ok_or(Error::LayoutOverflow)?; - let region = layout - .reserve_aligned_bytes(bytes, align) - .map_err(Error::StorageLayout)?; - ResolvedStaticTable { - name: table.name, - base: region.base(), - control_base: None, - slots_base: None, - slots, - log2_slots, - tiles: None, - log2_tiles: None, - slots_per_tile: None, - tile_bytes: None, - groups: None, - log2_groups: None, - group_pages: None, - log2_group_pages: None, - slots_per_group: None, - group_bytes: None, - data_offset: None, - mask: log2_slots.map(|_| slots - 1), - control_bytes: None, - slots_bytes: None, - bytes: region.bytes(), - } - } + let bytes = slots + .checked_mul(table.slot_size) + .ok_or(Error::LayoutOverflow)?; + let region = layout + .reserve_aligned_bytes(bytes, table.align) + .map_err(Error::StorageLayout)?; + let resolved = ResolvedStaticTable { + name: table.name, + base: region.base(), + slots, + log2_slots, + mask: log2_slots.map(|_| slots - 1), + bytes: region.bytes(), }; tables.push(resolved); } @@ -356,20 +328,6 @@ fn emit_static_storage_to_dir( table.base ) .expect("writing to String cannot fail"); - if let Some(control_base) = table.control_base { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_CONTROL_BASE: usize = {control_base};" - ) - .expect("writing to String cannot fail"); - } - if let Some(slots_base) = table.slots_base { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_SLOTS_BASE: usize = {slots_base};" - ) - .expect("writing to String cannot fail"); - } writeln!( &mut source, "#[allow(dead_code)]\npub const {name}_SLOTS: usize = {};", @@ -383,83 +341,6 @@ fn emit_static_storage_to_dir( ) .expect("writing to String cannot fail"); } - if let Some(tiles) = table.tiles { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_TILES: usize = {tiles};" - ) - .expect("writing to String cannot fail"); - } - if let Some(log2_tiles) = table.log2_tiles { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_LOG2_TILES: u8 = {log2_tiles};" - ) - .expect("writing to String cannot fail"); - } - if let Some(slots_per_tile) = table.slots_per_tile { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_SLOTS_PER_TILE: usize = {slots_per_tile};" - ) - .expect("writing to String cannot fail"); - } - if let Some(tile_bytes) = table.tile_bytes { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_TILE_BYTES: usize = {tile_bytes};" - ) - .expect("writing to String cannot fail"); - } - if let Some(groups) = table.groups { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_GROUPS: usize = {groups};" - ) - .expect("writing to String cannot fail"); - } - if let Some(log2_groups) = table.log2_groups { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_LOG2_GROUPS: u8 = {log2_groups};" - ) - .expect("writing to String cannot fail"); - } - if let Some(group_pages) = table.group_pages { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_GROUP_PAGES: usize = {group_pages};" - ) - .expect("writing to String cannot fail"); - } - if let Some(log2_group_pages) = table.log2_group_pages { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_LOG2_GROUP_PAGES: u8 = {log2_group_pages};" - ) - .expect("writing to String cannot fail"); - } - if let Some(slots_per_group) = table.slots_per_group { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_SLOTS_PER_GROUP: usize = {slots_per_group};" - ) - .expect("writing to String cannot fail"); - } - if let Some(group_bytes) = table.group_bytes { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_GROUP_BYTES: usize = {group_bytes};" - ) - .expect("writing to String cannot fail"); - } - if let Some(data_offset) = table.data_offset { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_DATA_OFFSET: usize = {data_offset};" - ) - .expect("writing to String cannot fail"); - } if let Some(mask) = table.mask { writeln!( &mut source, @@ -467,20 +348,6 @@ fn emit_static_storage_to_dir( ) .expect("writing to String cannot fail"); } - if let Some(control_bytes) = table.control_bytes { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_CONTROL_BYTES: usize = {control_bytes};" - ) - .expect("writing to String cannot fail"); - } - if let Some(slots_bytes) = table.slots_bytes { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_SLOTS_BYTES: usize = {slots_bytes};" - ) - .expect("writing to String cannot fail"); - } writeln!( &mut source, "#[allow(dead_code)]\npub const {name}_BYTES: usize = {};", diff --git a/rs/storage/src/lib.rs b/rs/storage/src/lib.rs index 21fe1f6d5..e7325a429 100644 --- a/rs/storage/src/lib.rs +++ b/rs/storage/src/lib.rs @@ -186,11 +186,7 @@ impl match self.lookup(&key)? { Lookup::Found(index) => { let previous = self.slots[index].value; - self.slots[index] = FixedSlot { - state: SlotState::Full, - key, - value, - }; + self.slots[index].value = value; Ok(Some(previous)) } Lookup::Vacant(index) => { @@ -458,9 +454,7 @@ impl StaticOpenAddressTable { let previous = unsafe { self.read_value(index)? }; unsafe { - self.write_key(index, key); self.write_value(index, value); - self.write_state(index, SlotState::Full); } Ok(Some(previous)) } From de0c7c7641236efc582348d674cf6f0ccc2adf1a Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Wed, 20 May 2026 17:13:14 +0400 Subject: [PATCH 12/14] docs: document sails storage primitives --- rs/storage/src/lib.rs | 63 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 63 insertions(+) diff --git a/rs/storage/src/lib.rs b/rs/storage/src/lib.rs index e7325a429..d81697eb3 100644 --- a/rs/storage/src/lib.rs +++ b/rs/storage/src/lib.rs @@ -1,3 +1,66 @@ +//! Allocator-light storage primitives for Sails programs. +//! +//! This crate provides bounded maps for program state that should not grow +//! through the allocator at runtime. It has two layers: +//! +//! - [`FixedOpenAddressMap`] stores a fixed-capacity map directly inside a +//! Rust value. It is useful for small bounded state and tests. +//! - [`StaticOpenAddressTable`] and the actor-specific maps operate on a +//! caller-reserved memory region. They are useful when a Sails program wants +//! stable, lazy-page-friendly storage outside allocator-managed collections. +//! +//! The static tables are intentionally low level. Constructors are `unsafe` +//! because the caller must reserve a valid writable memory range and keep it +//! from overlapping other mutable state for the whole lifetime of the table. +//! In normal Sails builds, use `sails_rs::build::StaticMemoryLayout` from a +//! build script to reserve static memory and generate base/size constants. +//! +//! # Fixed map +//! +//! ``` +//! use sails_storage::FixedOpenAddressMap; +//! +//! let mut map = FixedOpenAddressMap::<1, 1, 2>::new(); +//! assert_eq!(map.insert([1], [10]), Ok(None)); +//! assert_eq!(map.get(&[1]), Ok(Some([10]))); +//! ``` +//! +//! # Generic static table +//! +//! ``` +//! use sails_storage::StaticOpenAddressTable; +//! +//! let mut memory = [0u8; StaticOpenAddressTable::<1, 1>::slot_size() * 2]; +//! let table = unsafe { +//! StaticOpenAddressTable::<1, 1>::new(memory.as_mut_ptr() as usize, 2).unwrap() +//! }; +//! +//! assert_eq!(table.insert(&[1], &[10]), Ok(None)); +//! assert_eq!(table.get(&[1]), Ok(Some([10]))); +//! ``` +//! +//! # Actor maps +//! +//! [`StaticActorIdU256Map`] and [`StaticActorPairU256Map`] are specialized +//! static-memory layouts for common token-like state. They store `ActorId` and +//! `U256` values without per-slot state bytes: an all-zero actor key marks an +//! empty slot, and writing a zero `U256` removes the visible value for an +//! existing key. Because zero actor ids are reserved by the layout, mutation +//! methods reject them with [`TableError::InvalidKey`]. +//! +//! ``` +//! use gprimitives::{ActorId, U256}; +//! use sails_storage::{ACTOR_ID_U256_SLOT_SIZE, StaticActorIdU256Map}; +//! +//! let mut memory = [0u8; 4 * ACTOR_ID_U256_SLOT_SIZE]; +//! let balances = +//! unsafe { StaticActorIdU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; +//! let account = ActorId::from(1u64); +//! +//! assert_eq!(balances.insert_actor_u256(account, U256::from(10)), Ok(None)); +//! assert_eq!(balances.get_actor_u256(&account), Ok(Some(U256::from(10)))); +//! ``` + #![no_std] use core::{fmt, marker::PhantomData, ptr}; From da6febfc12e901cb83c2627a18264fb6389fb376 Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Wed, 20 May 2026 17:46:29 +0400 Subject: [PATCH 13/14] chore: refresh lockfile for storage crate --- Cargo.lock | 1394 +++++++++++++++++++++++++++++++--------------------- 1 file changed, 840 insertions(+), 554 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c1d61830e..5cecab7ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -15,9 +15,19 @@ dependencies = [ [[package]] name = "actor-system-error" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8862bc579cb6407ed5ff150781881003f9a09b724fa70ddbde1572a0ff87fd0" dependencies = [ "derive_more 2.1.1", - "gear-workspace-hack", +] + +[[package]] +name = "addr2line" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a76fd60b23679b7d19bd066031410fb7e458ccc5e958eb5c325888ce4baedc97" +dependencies = [ + "gimli 0.27.3", ] [[package]] @@ -142,7 +152,7 @@ dependencies = [ "alloy-sol-macro-input", "const-hex", "heck", - "indexmap", + "indexmap 2.14.0", "proc-macro-error2", "proc-macro2", "quote", @@ -186,6 +196,15 @@ dependencies = [ "libc", ] +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anstream" version = "1.0.0" @@ -265,6 +284,15 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "ar_archive_writer" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eb93bbb63b9c227414f6eb3a0adfddca591a8ce1e9b60661bb08969b87e340b" +dependencies = [ + "object 0.37.3", +] + [[package]] name = "ark-bls12-377" version = "0.4.0" @@ -656,7 +684,7 @@ version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ - "addr2line", + "addr2line 0.25.1", "cfg-if", "libc", "miniz_oxide", @@ -716,6 +744,15 @@ dependencies = [ "tokio", ] +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.70.1" @@ -901,12 +938,13 @@ dependencies = [ [[package]] name = "builtins-common" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13a54b46c994eee08098e021332099c898037316c0776f9f09885a9dc37a3f5c" dependencies = [ "derive_more 2.1.1", "gbuiltin-bls381", "gbuiltin-eth-bridge", "gear-core", - "gear-workspace-hack", "gprimitives", "log", "parity-scale-codec", @@ -1411,6 +1449,24 @@ version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +[[package]] +name = "core-processor" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c6661d52b0cc6d2157640dffe0298e42a9babb38d4629198939b70b7a23aaa" +dependencies = [ + "actor-system-error", + "derive_more 2.1.1", + "gear-core", + "gear-core-backend", + "gear-core-errors", + "gear-lazy-pages-common", + "gear-wasm-instrument", + "gsys", + "log", + "parity-scale-codec", +] + [[package]] name = "corosensei" version = "0.3.3" @@ -1431,6 +1487,15 @@ dependencies = [ "sails-rs", ] +[[package]] +name = "cpp_demangle" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" +dependencies = [ + "cfg-if", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -1449,6 +1514,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cranelift-entity" +version = "0.95.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40099d38061b37e505e63f89bab52199037a72b931ad4868d9089ff7268660b0" +dependencies = [ + "serde", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -2251,6 +2325,12 @@ dependencies = [ "rand 0.8.6", ] +[[package]] +name = "fallible-iterator" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -2372,8 +2452,9 @@ dependencies = [ [[package]] name = "frame-benchmarking" -version = "38.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "38.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a0f983d69640f90a0ce87f107cff07f6f8f7f5ef9334ffb6f37a9c6e224ca1d" dependencies = [ "frame-support", "frame-support-procedural", @@ -2385,12 +2466,12 @@ dependencies = [ "scale-info", "serde", "sp-api", - "sp-application-crypto 38.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-io 38.0.0", - "sp-runtime 39.0.1", - "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-application-crypto", + "sp-core", + "sp-io", + "sp-runtime", + "sp-runtime-interface", + "sp-storage 21.0.0", "static_assertions", ] @@ -2406,7 +2487,7 @@ dependencies = [ "scale-encode", "scale-info", "scale-type-resolver", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-crypto-hashing", "thiserror 2.0.18", ] @@ -2436,8 +2517,9 @@ dependencies = [ [[package]] name = "frame-support" -version = "38.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "38.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7dd8b9f161a8289e3b9fe6c1068519358dbff2270d38097a923d3d1b4459dca" dependencies = [ "aquamarine", "array-bytes", @@ -2454,31 +2536,32 @@ dependencies = [ "paste", "scale-info", "serde", - "serde-json-wasm", + "serde_json", "smallvec", "sp-api", - "sp-arithmetic 26.0.0", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-arithmetic", + "sp-core", "sp-crypto-hashing-proc-macro", - "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-debug-derive 14.0.0", "sp-genesis-builder", "sp-inherents", - "sp-io 38.0.0", + "sp-io", "sp-metadata-ir", - "sp-runtime 39.0.1", + "sp-runtime", "sp-staking", - "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-tracing 17.0.1", - "sp-weights 31.0.0", + "sp-state-machine", + "sp-std 14.0.0", + "sp-tracing 17.1.0", + "sp-weights", "static_assertions", "tt-call", ] [[package]] name = "frame-support-procedural" -version = "30.0.3" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "30.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8da784d943f2a945be923ab081a7c0837355b38045c50945d7ec1a138e2f3c52" dependencies = [ "Inflector", "cfg-expr", @@ -2491,17 +2574,18 @@ dependencies = [ "proc-macro-warning", "proc-macro2", "quote", - "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-crypto-hashing", "syn 2.0.117", ] [[package]] name = "frame-support-procedural-tools" -version = "13.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "13.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81a088fd6fda5f53ff0c17fc7551ce8bd0ead14ba742228443c8196296a7369b" dependencies = [ "frame-support-procedural-tools-derive", - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -2510,7 +2594,8 @@ dependencies = [ [[package]] name = "frame-support-procedural-tools-derive" version = "12.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed971c6435503a099bdac99fe4c5bea08981709e5b5a0a8535a1856f48561191" dependencies = [ "proc-macro2", "quote", @@ -2520,7 +2605,8 @@ dependencies = [ [[package]] name = "frame-system" version = "38.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c7fa02f8c305496d2ae52edaecdb9d165f11afa965e05686d7d7dd1ce93611" dependencies = [ "cfg-if", "docify", @@ -2529,12 +2615,12 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-io 38.0.0", - "sp-runtime 39.0.1", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", "sp-version", - "sp-weights 31.0.0", + "sp-weights", ] [[package]] @@ -2556,6 +2642,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "fs4" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e871a4cfa68bb224863b53149d973df1ac8d1ed2fa1d1bfc37ac1bb65dd37207" +dependencies = [ + "rustix 0.38.44", + "windows-sys 0.52.0", +] + [[package]] name = "funty" version = "2.0.0" @@ -2672,21 +2768,23 @@ dependencies = [ [[package]] name = "galloc" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d95f9357f55c811d9b1f5c7bb1ef3b3f0094dee912a2bdb5155eb46611d8f87" dependencies = [ "gear-dlmalloc", - "gear-workspace-hack", ] [[package]] name = "gbuiltin-bls381" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "919a1d0d8397053524bd9785788afb5a4dedc5c50e72650dae6857df2409a3f1" dependencies = [ "ark-bls12-381", "ark-ec", "ark-ff", "ark-scale", "ark-serialize", - "gear-workspace-hack", "parity-scale-codec", "scale-info", ] @@ -2694,8 +2792,9 @@ dependencies = [ [[package]] name = "gbuiltin-eth-bridge" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ea714934fa347452f0b33afa8a9afcdaa4079ea2fc2d7ee46ecdd03681ed72" dependencies = [ - "gear-workspace-hack", "gprimitives", "parity-scale-codec", "scale-info", @@ -2712,11 +2811,11 @@ dependencies = [ "futures", "gear-core", "gear-node-wrapper", - "gear-utils 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "gear-utils", "gsdk", "hex", "parity-scale-codec", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core", "thiserror 2.0.18", "url", ] @@ -2724,11 +2823,12 @@ dependencies = [ [[package]] name = "gcore" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "015093eb24aa69f98f6474a9547a51a1a9230204e55a1778e9c8588cd8ee6047" dependencies = [ "arrayvec 0.7.6", "gear-core-errors", "gear-stack-buffer", - "gear-workspace-hack", "gprimitives", "gsys", "paste", @@ -2738,6 +2838,8 @@ dependencies = [ [[package]] name = "gear-common" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6802013b852310c7a9e1e4e48e6bf56a877c4d746783ab3b93e521c7d70225f3" dependencies = [ "enum-iterator 1.5.0", "fail", @@ -2747,24 +2849,24 @@ dependencies = [ "gear-common-codegen", "gear-core", "gear-wasm-instrument", - "gear-workspace-hack", "gsys", "log", "primitive-types 0.12.2", "scale-decode", "scale-encode", - "sp-arithmetic 26.0.0", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-io 38.0.0", - "sp-runtime 39.0.1", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-runtime", + "sp-std 14.0.0", ] [[package]] name = "gear-common-codegen" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67ec789cbf723ef6bcb29552cbbe72bd9a1f46a07feaab5a0647a2f42480cf8d" dependencies = [ - "gear-workspace-hack", "quote", "syn 2.0.117", ] @@ -2772,6 +2874,8 @@ dependencies = [ [[package]] name = "gear-core" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7172ef6fd588e780ed2d9f8e99f6cbc2c65a19e4fd61b4686cc5a74c04ab07b" dependencies = [ "blake2", "bytemuck", @@ -2780,7 +2884,6 @@ dependencies = [ "enum-iterator 1.5.0", "gear-core-errors", "gear-wasm-instrument", - "gear-workspace-hack", "gprimitives", "gsys", "hex", @@ -2800,6 +2903,8 @@ dependencies = [ [[package]] name = "gear-core-backend" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4af189ad011d19bf4a42acbfe946b267ef2224b74f9f87778387a2a545495c78" dependencies = [ "actor-system-error", "blake2", @@ -2811,7 +2916,6 @@ dependencies = [ "gear-sandbox", "gear-sandbox-env", "gear-wasm-instrument", - "gear-workspace-hack", "gsys", "log", "parity-scale-codec", @@ -2820,9 +2924,10 @@ dependencies = [ [[package]] name = "gear-core-errors" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a068b7e99afbedd710b959a20c8ac91b2cc77f8a0ce87af687b673bfbf26a809" dependencies = [ "enum-iterator 1.5.0", - "gear-workspace-hack", "parity-scale-codec", "scale-decode", "scale-encode", @@ -2831,23 +2936,6 @@ dependencies = [ "thiserror 2.0.18", ] -[[package]] -name = "gear-core-processor" -version = "1.10.0" -dependencies = [ - "actor-system-error", - "derive_more 2.1.1", - "gear-core", - "gear-core-backend", - "gear-core-errors", - "gear-lazy-pages-common", - "gear-wasm-instrument", - "gear-workspace-hack", - "gsys", - "log", - "parity-scale-codec", -] - [[package]] name = "gear-dlmalloc" version = "0.2.0" @@ -2864,6 +2952,8 @@ dependencies = [ [[package]] name = "gear-lazy-pages" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e94788c25494ddb5ca7a73b71b61429990caba31dea7e3b55e0dfe33ae3634c" dependencies = [ "cfg-if", "derive_more 2.1.1", @@ -2871,7 +2961,6 @@ dependencies = [ "gear-core", "gear-lazy-pages-common", "gear-sandbox-host", - "gear-workspace-hack", "libc", "log", "mach", @@ -2886,9 +2975,10 @@ dependencies = [ [[package]] name = "gear-lazy-pages-common" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e1bd7e4e3d11c037298b2908c6ea1f3061a90bc98dbca188adf147599a88a96" dependencies = [ "gear-core", - "gear-workspace-hack", "num_enum", "parity-scale-codec", ] @@ -2896,11 +2986,12 @@ dependencies = [ [[package]] name = "gear-lazy-pages-native-interface" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affb988d8baa32dced50d696046e9a3470602abf3effe0fe06bb546dd05581d0" dependencies = [ "gear-core", "gear-lazy-pages", "gear-lazy-pages-common", - "gear-workspace-hack", "log", ] @@ -2919,16 +3010,17 @@ dependencies = [ [[package]] name = "gear-sandbox" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4ca22d8209f2c3077c0c97063e0804bf4e1c5b60a4a2e6200da664311d1920c" dependencies = [ "gear-sandbox-env", "gear-sandbox-interface", "gear-wasmer-cache", - "gear-workspace-hack", "log", "parity-scale-codec", "pathdiff", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core", + "sp-std 14.0.0", "sp-wasm-interface-common", "wasmer", "wasmer-compiler", @@ -2939,28 +3031,30 @@ dependencies = [ [[package]] name = "gear-sandbox-env" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6570e31af2bd208e6ce48d6a5e86c62815e57465df3f812670f40021e6a6a38b" dependencies = [ - "gear-workspace-hack", "parity-scale-codec", - "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-debug-derive 14.0.0", + "sp-std 14.0.0", "sp-wasm-interface-common", ] [[package]] name = "gear-sandbox-host" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c08257ac1e9ba954883fd229254fa54abc150cdc0c62380905f22d335c46c44" dependencies = [ "atomic_enum", "defer", "environmental", "gear-sandbox-env", "gear-wasmer-cache", - "gear-workspace-hack", + "gp-allocator", "log", "parity-scale-codec", "region", - "sp-allocator", "sp-wasm-interface-common", "tempfile", "thiserror 2.0.18", @@ -2972,45 +3066,32 @@ dependencies = [ [[package]] name = "gear-sandbox-interface" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da9b75e5d27247deff3569a5cfa246e8e8289ea021fd16cc8a80b784eb85ff13" dependencies = [ "gear-sandbox-host", - "gear-workspace-hack", + "gp-runtime-interface", + "gp-wasm-interface", "log", - "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-wasm-interface 21.0.1 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "parity-scale-codec", ] [[package]] name = "gear-ss58" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94745919b16a98a5b8d7b7766bcb9127eb8bf013eff098ad828e452744bee6e6" dependencies = [ "blake2", "bs58", - "gear-workspace-hack", "hex", ] [[package]] name = "gear-stack-buffer" version = "1.10.0" -dependencies = [ - "gear-workspace-hack", -] - -[[package]] -name = "gear-utils" -version = "1.10.0" -dependencies = [ - "gear-core", - "gear-workspace-hack", - "hex", - "nonempty", - "parity-scale-codec", - "path-clean", - "serde", - "serde_json", - "tracing-subscriber", -] +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d30f39766a97c23c841c55e868d93e01e146ace585aa36b291b4c46b9d0c15" [[package]] name = "gear-utils" @@ -3025,12 +3106,14 @@ dependencies = [ "path-clean", "serde", "serde_json", - "tracing-subscriber", + "tracing-subscriber 0.3.23", ] [[package]] name = "gear-wasm-builder" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8bed5dd34648a27e7dc217dc3b00eea4282bd451f4cf76bf627a05c5dbcc84f9" dependencies = [ "anyhow", "cargo_metadata 0.20.0", @@ -3039,7 +3122,6 @@ dependencies = [ "gear-core", "gear-wasm-instrument", "gear-wasm-optimizer", - "gear-workspace-hack", "itertools 0.13.0", "log", "pathdiff", @@ -3051,10 +3133,11 @@ dependencies = [ [[package]] name = "gear-wasm-instrument" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf819c76e5002586f7778e4acd10b775bd797e2baece1c31fa1b95a630eec238" dependencies = [ "derive_more 2.1.1", "enum-iterator 1.5.0", - "gear-workspace-hack", "log", "wasm-encoder 0.230.0", "wasmparser 0.230.0", @@ -3063,11 +3146,12 @@ dependencies = [ [[package]] name = "gear-wasm-optimizer" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed336b47eecdcee1c91dbedb8e6f6f702734f405afae68fdca3f1ddea4c3669d" dependencies = [ "anyhow", "colored", "gear-wasm-instrument", - "gear-workspace-hack", "log", "regex", "rustc_version", @@ -3078,22 +3162,18 @@ dependencies = [ [[package]] name = "gear-wasmer-cache" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1715238463dc4a8436e81bef5faadf28d2ad9f7320ef15e0f752da9568a71c48" dependencies = [ "bytes", "derive_more 2.1.1", - "gear-workspace-hack", + "fs4", "log", "uluru", "wasmer", "wasmer-cache", ] -[[package]] -name = "gear-workspace-hack" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76ac67118eef8a153d4df06bd865d0a5c3ac51888c238673a203a5c424db0327" - [[package]] name = "genco" version = "0.19.0" @@ -3175,14 +3255,25 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "gimli" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c80984affa11d98d1b88b66ac8853f143217b399d3c74116778ff8fdb4ed2e" +dependencies = [ + "fallible-iterator 0.2.0", + "indexmap 1.9.3", + "stable_deref_trait", +] + [[package]] name = "gimli" version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" dependencies = [ - "fallible-iterator", - "indexmap", + "fallible-iterator 0.3.0", + "indexmap 2.14.0", "stable_deref_trait", ] @@ -3209,16 +3300,77 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "gp-allocator" +version = "4.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93cfb4525b5362966a1971125ff8f9825e6a511403635dccf883273d20c43bdf" +dependencies = [ + "log", + "parity-scale-codec", + "sp-wasm-interface-common", + "thiserror 1.0.69", +] + +[[package]] +name = "gp-runtime-interface" +version = "18.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8dfb3786bf8fe51099c67dfe7c9a925fe0bfb78ffe938aa07b598995ee48d0e3" +dependencies = [ + "bytes", + "gp-runtime-interface-proc-macro", + "gp-wasm-interface", + "impl-trait-for-tuples", + "parity-scale-codec", + "primitive-types 0.12.2", + "sp-externalities 0.20.0", + "sp-std 9.0.0", + "sp-storage 14.0.0", + "sp-tracing 10.0.0", + "static_assertions", +] + +[[package]] +name = "gp-runtime-interface-proc-macro" +version = "12.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1e4d907f86db64e8b9d9af0b5adfd59e62b4023fd88511e13d62028ed101f2c" +dependencies = [ + "Inflector", + "proc-macro-crate 1.3.1", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "gp-wasm-interface" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de334e40147f969276463ad04d0ddfe519aebb6254832ea0637a300c6d637c11" +dependencies = [ + "anyhow", + "gp-allocator", + "impl-trait-for-tuples", + "log", + "parity-scale-codec", + "sp-std 9.0.0", + "sp-wasm-interface-common", + "wasmtime", +] + [[package]] name = "gprimitives" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f79911d3caeb051ae07b0d4b57c196fdc6e105f7be7cc51365416d7248ca5ce" dependencies = [ "alloy-primitives", "alloy-sol-types", "bytemuck", "derive_more 2.1.1", "gear-ss58", - "gear-workspace-hack", "hex", "primitive-types 0.12.2", "scale-decode", @@ -3252,14 +3404,14 @@ dependencies = [ "gear-core-errors", "gsdk-codegen", "hex", - "indexmap", + "indexmap 2.14.0", "jsonrpsee", "log", "parity-scale-codec", "parking_lot", "serde", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime 39.0.5", + "sp-core", + "sp-runtime", "subxt", "thiserror 2.0.18", "url", @@ -3279,6 +3431,8 @@ dependencies = [ [[package]] name = "gstd" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "232fec5b812a76c016c11faceef031fbe7608bb4db07051744201a2dabff1072" dependencies = [ "arrayvec 0.7.6", "const_format", @@ -3286,7 +3440,6 @@ dependencies = [ "galloc", "gcore", "gear-core-errors", - "gear-workspace-hack", "gprimitives", "gstd-codegen", "hashbrown 0.14.5", @@ -3300,8 +3453,9 @@ dependencies = [ [[package]] name = "gstd-codegen" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b347d845d9fdc17b47288d082fd7001390d0652acb5d221c29a1b3f16e9a238" dependencies = [ - "gear-workspace-hack", "gprimitives", "proc-macro2", "quote", @@ -3311,28 +3465,30 @@ dependencies = [ [[package]] name = "gsys" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31b08a4ef9c0f6aed528b42117cea34f53af2a4a7f080c04b4356eb63adc9302" dependencies = [ "bytemuck", - "gear-workspace-hack", ] [[package]] name = "gtest" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cba01a0d56d5ff4a072ce3dfc142ecba271f097435985fe623a8b69918e785c" dependencies = [ "builtins-common", "cargo_toml", "colored", + "core-processor", "etc", "gear-common", "gear-core", "gear-core-errors", - "gear-core-processor", "gear-lazy-pages", "gear-lazy-pages-common", "gear-lazy-pages-native-interface", - "gear-utils 1.10.0", - "gear-workspace-hack", + "gear-utils", "gprimitives", "gsys", "log", @@ -3341,7 +3497,7 @@ dependencies = [ "rand 0.8.6", "scale-info", "thiserror 2.0.18", - "tracing-subscriber", + "tracing-subscriber 0.3.23", ] [[package]] @@ -3356,7 +3512,7 @@ dependencies = [ "futures-core", "futures-sink", "http", - "indexmap", + "indexmap 2.14.0", "slab", "tokio", "tokio-util", @@ -3394,6 +3550,12 @@ dependencies = [ "crunchy", ] +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" + [[package]] name = "hashbrown" version = "0.13.2" @@ -3453,6 +3615,12 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + [[package]] name = "hermit-abi" version = "0.5.2" @@ -3826,6 +3994,17 @@ dependencies = [ "quote", ] +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + [[package]] name = "indexmap" version = "2.14.0" @@ -3893,13 +4072,24 @@ dependencies = [ "num-traits", ] +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + [[package]] name = "is-terminal" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3640c1c38b8e4e43584d8df18be5fc6b0aa314ce6ebf51b53313d4306cca8e46" dependencies = [ - "hermit-abi", + "hermit-abi 0.5.2", "libc", "windows-sys 0.61.2", ] @@ -4185,7 +4375,7 @@ dependencies = [ "lalrpop-util", "petgraph", "regex", - "regex-syntax", + "regex-syntax 0.8.10", "string_cache", "term", "tiny-keccak", @@ -4199,7 +4389,7 @@ version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "507460a910eb7b32ee961886ff48539633b788a36b65692b95f225b844c82553" dependencies = [ - "regex-automata", + "regex-automata 0.4.14", ] [[package]] @@ -4326,6 +4516,12 @@ dependencies = [ "nalgebra", ] +[[package]] +name = "linux-raw-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f051f77a7c8e6957c0696eac88f26b0117e54f52d3fc682ab19397a8812846a4" + [[package]] name = "linux-raw-sys" version = "0.4.15" @@ -4377,8 +4573,8 @@ dependencies = [ "fnv", "proc-macro2", "quote", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.14", + "regex-syntax 0.8.10", "syn 2.0.117", ] @@ -4488,13 +4684,22 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "matchers" +version = "0.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1" +dependencies = [ + "regex-automata 0.1.10", +] + [[package]] name = "matchers" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata", + "regex-automata 0.4.14", ] [[package]] @@ -4513,6 +4718,15 @@ version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" +[[package]] +name = "memfd" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad38eb12aea514a0466ea40a80fd8cc83637065948eb4a426e4aa46261175227" +dependencies = [ + "rustix 1.1.4", +] + [[package]] name = "memmap2" version = "0.6.2" @@ -4540,6 +4754,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1" +dependencies = [ + "autocfg", +] + [[package]] name = "memoffset" version = "0.9.1" @@ -4860,13 +5083,26 @@ dependencies = [ [[package]] name = "numerated" version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb9cd51526a9244bccb59b31a21af8fabc6a70a23707ff0e9095abda2a5033a" dependencies = [ "derive_more 2.1.1", - "gear-workspace-hack", "num-traits", "scale-info", ] +[[package]] +name = "object" +version = "0.30.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03b4680b86d9cfafba8fc491dc9b6df26b68cf40e9e6cd73909194759a63c385" +dependencies = [ + "crc32fast", + "hashbrown 0.13.2", + "indexmap 1.9.3", + "memchr", +] + [[package]] name = "object" version = "0.32.2" @@ -4876,7 +5112,7 @@ dependencies = [ "crc32fast", "flate2", "hashbrown 0.14.5", - "indexmap", + "indexmap 2.14.0", "memchr", "ruzstd 0.5.0", ] @@ -4979,7 +5215,7 @@ version = "3.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34b4653168b563151153c9e4c08ebed57fb8262bebfa79711552fa983c623e7a" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -4991,11 +5227,6 @@ version = "0.45.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1ad0aff30c1da14b1254fcb2af73e1fa9a28670e584a626f53a369d0e157304" -[[package]] -name = "parity-wasm" -version = "0.45.0" -source = "git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext#bad3e1ec78f655f3eec5a0c9c12a3546c8c9d432" - [[package]] name = "parking" version = "2.2.1" @@ -5146,7 +5377,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", - "indexmap", + "indexmap 2.14.0", ] [[package]] @@ -5286,7 +5517,7 @@ checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", - "hermit-abi", + "hermit-abi 0.5.2", "pin-project-lite", "rustix 1.1.4", "windows-sys 0.61.2", @@ -5397,9 +5628,19 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.5.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ "toml_edit 0.25.11+spec-1.1.0", ] @@ -5493,6 +5734,16 @@ dependencies = [ "tokio", ] +[[package]] +name = "psm" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" +dependencies = [ + "ar_archive_writer", + "cc", +] + [[package]] name = "ptr_meta" version = "0.1.4" @@ -5760,8 +6011,17 @@ checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", - "regex-automata", - "regex-syntax", + "regex-automata 0.4.14", + "regex-syntax 0.8.10", +] + +[[package]] +name = "regex-automata" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +dependencies = [ + "regex-syntax 0.6.29", ] [[package]] @@ -5772,9 +6032,15 @@ checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", - "regex-syntax", + "regex-syntax 0.8.10", ] +[[package]] +name = "regex-syntax" +version = "0.6.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" + [[package]] name = "regex-syntax" version = "0.8.10" @@ -5841,7 +6107,7 @@ dependencies = [ "bytecheck 0.8.2", "bytes", "hashbrown 0.17.0", - "indexmap", + "indexmap 2.14.0", "munge", "ptr_meta 0.3.1", "rancor", @@ -5964,6 +6230,20 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "rustix" +version = "0.36.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "305efbd14fde4139eb501df5f136994bb520b033fa9fbdce287507dc23b8c7ed" +dependencies = [ + "bitflags 1.3.2", + "errno", + "io-lifetimes", + "libc", + "linux-raw-sys 0.1.4", + "windows-sys 0.45.0", +] + [[package]] name = "rustix" version = "0.38.44" @@ -6278,7 +6558,7 @@ dependencies = [ name = "sails-reflect-hash-derive" version = "1.0.0-beta.5" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -6363,7 +6643,7 @@ version = "1.0.0-beta.5" dependencies = [ "insta", "prettyplease", - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -6439,7 +6719,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17020f2d59baabf2ddcdc20a4e567f8210baf089b8a8d4785f5fd5e716f92038" dependencies = [ "darling 0.20.11", - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -6465,7 +6745,7 @@ version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -6649,15 +6929,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-json-wasm" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05da0d153dd4595bdffd5099dc0e9ce425b205ee648eb93437ff7302af8c9a5" -dependencies = [ - "serde", -] - [[package]] name = "serde-untagged" version = "0.1.9" @@ -7039,21 +7310,11 @@ dependencies = [ "sha1", ] -[[package]] -name = "sp-allocator" -version = "29.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "log", - "parity-scale-codec", - "sp-wasm-interface-common", - "thiserror 1.0.69", -] - [[package]] name = "sp-api" version = "34.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbce492e0482134128b7729ea36f5ef1a9f9b4de2d48ff8dde7b5e464e28ce75" dependencies = [ "docify", "hash-db", @@ -7061,26 +7322,27 @@ dependencies = [ "parity-scale-codec", "scale-info", "sp-api-proc-macro", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core", + "sp-externalities 0.29.0", "sp-metadata-ir", - "sp-runtime 39.0.1", - "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime", + "sp-runtime-interface", + "sp-state-machine", + "sp-trie", "sp-version", "thiserror 1.0.69", ] [[package]] name = "sp-api-proc-macro" -version = "20.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "20.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e3ec5a1a14307e21d2356e73e01573b6c82330a7e30eaceed59a90161b0c2d2" dependencies = [ "Inflector", "blake2", "expander", - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -7095,34 +7357,8 @@ dependencies = [ "parity-scale-codec", "scale-info", "serde", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-io 38.0.2", -] - -[[package]] -name = "sp-application-crypto" -version = "38.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "parity-scale-codec", - "scale-info", - "serde", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-io 38.0.0", -] - -[[package]] -name = "sp-arithmetic" -version = "26.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "docify", - "integer-sqrt", - "num-traits", - "parity-scale-codec", - "scale-info", - "serde", - "static_assertions", + "sp-core", + "sp-io", ] [[package]] @@ -7173,60 +7409,14 @@ dependencies = [ "secp256k1", "secrecy", "serde", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime-interface 28.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ss58-registry", - "substrate-bip39 0.6.1", - "thiserror 1.0.69", - "tracing", - "w3f-bls", - "zeroize", -] - -[[package]] -name = "sp-core" -version = "34.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "array-bytes", - "bitflags 1.3.2", - "blake2", - "bounded-collections", - "bs58", - "dyn-clonable", - "ed25519-zebra", - "futures", - "hash-db", - "hash256-std-hasher", - "impl-serde 0.4.0", - "itertools 0.11.0", - "k256", - "libsecp256k1", - "log", - "merlin", - "parity-bip39", - "parity-scale-codec", - "parking_lot", - "paste", - "primitive-types 0.12.2", - "rand 0.8.6", - "scale-info", - "schnorrkel", - "secp256k1", - "secrecy", - "serde", - "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-crypto-hashing", + "sp-debug-derive 14.0.0", + "sp-externalities 0.29.0", + "sp-runtime-interface", + "sp-std 14.0.0", + "sp-storage 21.0.0", "ss58-registry", - "substrate-bip39 0.6.0", + "substrate-bip39", "thiserror 1.0.69", "tracing", "w3f-bls", @@ -7247,34 +7437,22 @@ dependencies = [ "twox-hash 1.6.3", ] -[[package]] -name = "sp-crypto-hashing" -version = "0.1.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "blake2b_simd", - "byteorder", - "digest 0.10.7", - "sha2 0.10.9", - "sha3", - "twox-hash 1.6.3", -] - [[package]] name = "sp-crypto-hashing-proc-macro" version = "0.1.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b85d0f1f1e44bd8617eb2a48203ee854981229e3e79e6f468c7175d5fd37489b" dependencies = [ "quote", - "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-crypto-hashing", "syn 2.0.117", ] [[package]] name = "sp-debug-derive" -version = "14.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" +checksum = "f12dae7cf6c1e825d13ffd4ce16bd9309db7c539929d0302b4443ed451a9f4e5" dependencies = [ "proc-macro2", "quote", @@ -7284,7 +7462,8 @@ dependencies = [ [[package]] name = "sp-debug-derive" version = "14.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2", "quote", @@ -7293,76 +7472,54 @@ dependencies = [ [[package]] name = "sp-externalities" -version = "0.29.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a904407d61cb94228c71b55a9d3708e9d6558991f9e83bd42bd91df37a159d30" +checksum = "3313e2c5f2523b06062e541dff9961bde88ad5a28861621dc7b7b47a32bb0f7c" dependencies = [ "environmental", "parity-scale-codec", - "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-std 9.0.0", + "sp-storage 14.0.0", ] [[package]] name = "sp-externalities" version = "0.29.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a904407d61cb94228c71b55a9d3708e9d6558991f9e83bd42bd91df37a159d30" dependencies = [ "environmental", "parity-scale-codec", - "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-storage 21.0.0", ] [[package]] name = "sp-genesis-builder" version = "0.15.1" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a646ed222fd86d5680faa4a8967980eb32f644cae6c8523e1c689a6deda3e8" dependencies = [ "parity-scale-codec", "scale-info", "serde_json", "sp-api", - "sp-runtime 39.0.1", + "sp-runtime", ] [[package]] name = "sp-inherents" version = "34.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afffbddc380d99a90c459ba1554bbbc01d62e892de9f1485af6940b89c4c0d57" dependencies = [ "async-trait", "impl-trait-for-tuples", "parity-scale-codec", "scale-info", - "sp-runtime 39.0.1", + "sp-runtime", "thiserror 1.0.69", ] -[[package]] -name = "sp-io" -version = "38.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "bytes", - "docify", - "ed25519-dalek", - "libsecp256k1", - "log", - "parity-scale-codec", - "polkavm-derive", - "rustversion", - "secp256k1", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-crypto-hashing 0.1.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-keystore 0.40.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-runtime-interface 28.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-state-machine 0.43.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-tracing 17.0.1", - "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "tracing", - "tracing-core", -] - [[package]] name = "sp-io" version = "38.0.2" @@ -7378,14 +7535,14 @@ dependencies = [ "polkavm-derive", "rustversion", "secp256k1", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-keystore 0.40.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime-interface 28.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-state-machine 0.43.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core", + "sp-crypto-hashing", + "sp-externalities 0.29.0", + "sp-keystore", + "sp-runtime-interface", + "sp-state-machine", "sp-tracing 17.1.0", - "sp-trie 37.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-trie", "tracing", "tracing-core", ] @@ -7398,41 +7555,21 @@ checksum = "0248b4d784cb4a01472276928977121fa39d977a5bb24793b6b15e64b046df42" dependencies = [ "parity-scale-codec", "parking_lot", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "sp-keystore" -version = "0.40.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "parity-scale-codec", - "parking_lot", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core", + "sp-externalities 0.29.0", ] [[package]] name = "sp-metadata-ir" version = "0.7.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a616fa51350b35326682a472ee8e6ba742fdacb18babac38ecd46b3e05ead869" dependencies = [ "frame-metadata 16.0.0", "parity-scale-codec", "scale-info", ] -[[package]] -name = "sp-panic-handler" -version = "13.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "backtrace", - "lazy_static", - "regex", -] - [[package]] name = "sp-panic-handler" version = "13.0.2" @@ -7443,32 +7580,6 @@ dependencies = [ "regex", ] -[[package]] -name = "sp-runtime" -version = "39.0.1" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "docify", - "either", - "hash256-std-hasher", - "impl-trait-for-tuples", - "log", - "num-traits", - "parity-scale-codec", - "paste", - "rand 0.8.6", - "scale-info", - "serde", - "simple-mermaid", - "sp-application-crypto 38.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-arithmetic 26.0.0", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-io 38.0.0", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-weights 31.0.0", - "tracing", -] - [[package]] name = "sp-runtime" version = "39.0.5" @@ -7487,12 +7598,12 @@ dependencies = [ "scale-info", "serde", "simple-mermaid", - "sp-application-crypto 38.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-arithmetic 26.1.0", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-io 38.0.2", - "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-weights 31.1.0", + "sp-application-crypto", + "sp-arithmetic", + "sp-core", + "sp-io", + "sp-std 14.0.0", + "sp-weights", "tracing", ] @@ -7507,31 +7618,12 @@ dependencies = [ "parity-scale-codec", "polkavm-derive", "primitive-types 0.12.2", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-runtime-interface-proc-macro 18.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-std 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-storage 21.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-externalities 0.29.0", + "sp-runtime-interface-proc-macro", + "sp-std 14.0.0", + "sp-storage 21.0.0", "sp-tracing 17.1.0", - "sp-wasm-interface 21.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "static_assertions", -] - -[[package]] -name = "sp-runtime-interface" -version = "28.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "bytes", - "impl-trait-for-tuples", - "parity-scale-codec", - "polkavm-derive", - "primitive-types 0.12.2", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-runtime-interface-proc-macro 18.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-storage 21.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-tracing 17.0.1", - "sp-wasm-interface 21.0.1 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-wasm-interface", "static_assertions", ] @@ -7543,20 +7635,7 @@ checksum = "0195f32c628fee3ce1dfbbf2e7e52a30ea85f3589da9fe62a8b816d70fc06294" dependencies = [ "Inflector", "expander", - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "sp-runtime-interface-proc-macro" -version = "18.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "Inflector", - "expander", - "proc-macro-crate", + "proc-macro-crate 3.5.0", "proc-macro2", "quote", "syn 2.0.117", @@ -7565,14 +7644,15 @@ dependencies = [ [[package]] name = "sp-staking" version = "36.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a73eedb4b85f4cd420d31764827546aa22f82ce1646d0fd258993d051de7a90" dependencies = [ "impl-trait-for-tuples", "parity-scale-codec", "scale-info", "serde", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-runtime 39.0.1", + "sp-core", + "sp-runtime", ] [[package]] @@ -7587,80 +7667,71 @@ dependencies = [ "parking_lot", "rand 0.8.6", "smallvec", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-panic-handler 13.0.2", - "sp-trie 37.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-core", + "sp-externalities 0.29.0", + "sp-panic-handler", + "sp-trie", "thiserror 1.0.69", "tracing", "trie-db", ] [[package]] -name = "sp-state-machine" -version = "0.43.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "hash-db", - "log", - "parity-scale-codec", - "parking_lot", - "rand 0.8.6", - "smallvec", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-panic-handler 13.0.0", - "sp-trie 37.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "thiserror 1.0.69", - "tracing", - "trie-db", -] +name = "sp-std" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53458e3c57df53698b3401ec0934bea8e8cfce034816873c0b0abbd83d7bac0d" [[package]] name = "sp-std" -version = "14.0.0" +version = "9.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12f8ee986414b0a9ad741776762f4083cd3a5128449b982a3919c4df36874834" +checksum = "2d5bbc9339227d1b6a9b7ccd9b2920c818653d40eef1512f1e2e824d72e7a336" [[package]] name = "sp-std" version = "14.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8ee986414b0a9ad741776762f4083cd3a5128449b982a3919c4df36874834" [[package]] name = "sp-storage" -version = "21.0.0" +version = "14.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99c82989b3a4979a7e1ad848aad9f5d0b4388f1f454cc131766526601ab9e8f8" +checksum = "a21245c3a7799ff6d3f1f159b496f9ac72eb32cd6fe68c6f73013155289aa9f1" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-debug-derive 9.0.0", + "sp-std 9.0.0", ] [[package]] name = "sp-storage" version = "21.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99c82989b3a4979a7e1ad848aad9f5d0b4388f1f454cc131766526601ab9e8f8" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", "ref-cast", "serde", - "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-debug-derive 14.0.0", ] [[package]] name = "sp-tracing" -version = "17.0.1" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "10.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357f7591980dd58305956d32f8f6646d0a8ea9ea0e7e868e46f53b68ddf00cec" dependencies = [ "parity-scale-codec", + "sp-std 8.0.0", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.2.25", ] [[package]] @@ -7672,7 +7743,7 @@ dependencies = [ "parity-scale-codec", "tracing", "tracing-core", - "tracing-subscriber", + "tracing-subscriber 0.3.23", ] [[package]] @@ -7691,31 +7762,8 @@ dependencies = [ "rand 0.8.6", "scale-info", "schnellru", - "sp-core 34.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "sp-externalities 0.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "thiserror 1.0.69", - "tracing", - "trie-db", - "trie-root", -] - -[[package]] -name = "sp-trie" -version = "37.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "ahash", - "hash-db", - "lazy_static", - "memory-db", - "nohash-hasher", - "parity-scale-codec", - "parking_lot", - "rand 0.8.6", - "scale-info", - "schnellru", - "sp-core 34.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", - "sp-externalities 0.29.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-core", + "sp-externalities 0.29.0", "thiserror 1.0.69", "tracing", "trie-db", @@ -7725,16 +7773,17 @@ dependencies = [ [[package]] name = "sp-version" version = "37.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d521a405707b5be561367cd3d442ff67588993de24062ce3adefcf8437ee9fe1" dependencies = [ "impl-serde 0.4.0", "parity-scale-codec", - "parity-wasm 0.45.0 (registry+https://github.com/rust-lang/crates.io-index)", + "parity-wasm", "scale-info", "serde", "sp-crypto-hashing-proc-macro", - "sp-runtime 39.0.1", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-runtime", + "sp-std 14.0.0", "sp-version-proc-macro", "thiserror 1.0.69", ] @@ -7742,7 +7791,8 @@ dependencies = [ [[package]] name = "sp-version-proc-macro" version = "14.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aee8f6730641a65fcf0c8f9b1e448af4b3bb083d08058b47528188bccc7b7a7" dependencies = [ "parity-scale-codec", "proc-macro2", @@ -7762,43 +7812,17 @@ dependencies = [ "parity-scale-codec", ] -[[package]] -name = "sp-wasm-interface" -version = "21.0.1" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "anyhow", - "impl-trait-for-tuples", - "log", - "parity-scale-codec", - "sp-allocator", - "sp-wasm-interface-common", -] - [[package]] name = "sp-wasm-interface-common" -version = "7.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" +version = "15.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03223ee788e1490f6341eb5b76501f83abf931fa8b5d83b5f49a4fecaf83f4ae" dependencies = [ "parity-scale-codec", - "sp-std 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", + "sp-std 9.0.0", "wasmi 0.13.2", ] -[[package]] -name = "sp-weights" -version = "31.0.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "bounded-collections", - "parity-scale-codec", - "scale-info", - "serde", - "smallvec", - "sp-arithmetic 26.0.0", - "sp-debug-derive 14.0.0 (git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none)", -] - [[package]] name = "sp-weights" version = "31.1.0" @@ -7810,8 +7834,8 @@ dependencies = [ "scale-info", "serde", "smallvec", - "sp-arithmetic 26.1.0", - "sp-debug-derive 14.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-arithmetic", + "sp-debug-derive 14.0.0", ] [[package]] @@ -7898,18 +7922,6 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" -[[package]] -name = "substrate-bip39" -version = "0.6.0" -source = "git+https://github.com/gear-tech/polkadot-sdk.git?branch=gear-polkadot-stable2409-wasm32v1-none#1d1b394647eb26c094cf50c759b900dc5faa3b80" -dependencies = [ - "hmac 0.12.1", - "pbkdf2", - "schnorrkel", - "sha2 0.10.9", - "zeroize", -] - [[package]] name = "substrate-bip39" version = "0.6.1" @@ -7951,7 +7963,7 @@ dependencies = [ "scale-value", "serde", "serde_json", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-crypto-hashing", "subxt-core", "subxt-lightclient", "subxt-macro", @@ -8007,7 +8019,7 @@ dependencies = [ "scale-value", "serde", "serde_json", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-crypto-hashing", "subxt-metadata", "thiserror 2.0.18", "tracing", @@ -8058,7 +8070,7 @@ dependencies = [ "hashbrown 0.14.5", "parity-scale-codec", "scale-info", - "sp-crypto-hashing 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "sp-crypto-hashing", "thiserror 2.0.18", ] @@ -8405,7 +8417,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ - "indexmap", + "indexmap 2.14.0", "serde_core", "serde_spanned 1.1.1", "toml_datetime 1.1.1+spec-1.1.0", @@ -8432,13 +8444,24 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + [[package]] name = "toml_edit" version = "0.22.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" dependencies = [ - "indexmap", + "indexmap 2.14.0", "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", @@ -8452,7 +8475,7 @@ version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ - "indexmap", + "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", @@ -8540,6 +8563,17 @@ dependencies = [ "valuable", ] +[[package]] +name = "tracing-log" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f751112709b4e791d8ce53e32c4ed2d353565a795ce84da2285393f41557bdf2" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + [[package]] name = "tracing-log" version = "0.2.0" @@ -8551,23 +8585,55 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-serde" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc6b213177105856957181934e4920de57730fc69bf42c37ee5bb664d406d9e1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e0d2eaa99c3c2e41547cfa109e910a68ea03823cccad4a0525dcbc9b01e8c71" +dependencies = [ + "ansi_term", + "chrono", + "lazy_static", + "matchers 0.0.1", + "regex", + "serde", + "serde_json", + "sharded-slab", + "smallvec", + "thread_local", + "tracing", + "tracing-core", + "tracing-log 0.1.4", + "tracing-serde", +] + [[package]] name = "tracing-subscriber" version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ - "matchers", + "matchers 0.2.0", "nu-ansi-term", "once_cell", - "regex-automata", + "regex-automata 0.4.14", "sharded-slab", "smallvec", "thread_local", "time", "tracing", "tracing-core", - "tracing-log", + "tracing-log 0.2.0", ] [[package]] @@ -8935,7 +9001,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" dependencies = [ "anyhow", - "indexmap", + "indexmap 2.14.0", "wasm-encoder 0.244.0", "wasmparser 0.244.0", ] @@ -8951,7 +9017,7 @@ dependencies = [ "cfg-if", "cmake", "derive_more 2.1.1", - "indexmap", + "indexmap 2.14.0", "js-sys", "more-asserts", "paste", @@ -9056,7 +9122,7 @@ dependencies = [ "enumset", "getrandom 0.2.17", "hex", - "indexmap", + "indexmap 2.14.0", "more-asserts", "rkyv", "sha2 0.10.9", @@ -9079,7 +9145,7 @@ dependencies = [ "dashmap", "enum-iterator 0.7.0", "fnv", - "indexmap", + "indexmap 2.14.0", "libc", "libunwind", "mach2", @@ -9096,9 +9162,10 @@ dependencies = [ [[package]] name = "wasmi" version = "0.13.2" -source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06c326c93fbf86419608361a2c925a31754cf109da1b8b55737070b4d6669422" dependencies = [ - "parity-wasm 0.45.0 (git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext)", + "parity-wasm", "wasmi-validation", "wasmi_core 0.2.1", ] @@ -9138,9 +9205,10 @@ dependencies = [ [[package]] name = "wasmi-validation" version = "0.5.0" -source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ff416ad1ff0c42e5a926ed5d5fab74c0f098749aa0ad8b2a34b982ce0e867b" dependencies = [ - "parity-wasm 0.45.0 (git+https://github.com/gear-tech/parity-wasm?branch=v0.45.0-sign-ext)", + "parity-wasm", ] [[package]] @@ -9163,7 +9231,8 @@ checksum = "e80d6b275b1c922021939d561574bf376613493ae2b61c6963b15db0e8813562" [[package]] name = "wasmi_core" version = "0.2.1" -source = "git+https://github.com/gear-tech/wasmi?branch=v0.13.2-sign-ext#3a0b1022377919e62aadf4d78b762abd1c3e9a04" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d20cb3c59b788653d99541c646c561c9dd26506f25c0cebfe810659c54c6d7" dependencies = [ "downcast-rs", "libm", @@ -9212,6 +9281,16 @@ dependencies = [ "wasmi_core 0.40.0", ] +[[package]] +name = "wasmparser" +version = "0.102.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48134de3d7598219ab9eaf6b91b15d8e50d31da76b8519fe4ecfcec2cf35104b" +dependencies = [ + "indexmap 1.9.3", + "url", +] + [[package]] name = "wasmparser" version = "0.221.3" @@ -9237,7 +9316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "808198a69b5a0535583370a51d459baa14261dfab04800c4864ee9e1a14346ed" dependencies = [ "bitflags 2.11.1", - "indexmap", + "indexmap 2.14.0", ] [[package]] @@ -9248,7 +9327,7 @@ checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ "bitflags 2.11.1", "hashbrown 0.15.5", - "indexmap", + "indexmap 2.14.0", "semver", "serde", ] @@ -9262,6 +9341,138 @@ dependencies = [ "indexmap-nostd", ] +[[package]] +name = "wasmtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f907fdead3153cb9bfb7a93bbd5b62629472dc06dee83605358c64c52ed3dda9" +dependencies = [ + "anyhow", + "bincode", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "object 0.30.4", + "once_cell", + "paste", + "psm", + "serde", + "target-lexicon", + "wasmparser 0.102.0", + "wasmtime-environ", + "wasmtime-jit", + "wasmtime-runtime", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-asm-macros" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3b9daa7c14cd4fa3edbf69de994408d5f4b7b0959ac13fa69d465f6597f810d" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "wasmtime-environ" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a990198cee4197423045235bf89d3359e69bd2ea031005f4c2d901125955c949" +dependencies = [ + "anyhow", + "cranelift-entity", + "gimli 0.27.3", + "indexmap 1.9.3", + "log", + "object 0.30.4", + "serde", + "target-lexicon", + "thiserror 1.0.69", + "wasmparser 0.102.0", + "wasmtime-types", +] + +[[package]] +name = "wasmtime-jit" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de48df552cfca1c9b750002d3e07b45772dd033b0b206d5c0968496abf31244" +dependencies = [ + "addr2line 0.19.0", + "anyhow", + "bincode", + "cfg-if", + "cpp_demangle", + "gimli 0.27.3", + "log", + "object 0.30.4", + "rustc-demangle", + "serde", + "target-lexicon", + "wasmtime-environ", + "wasmtime-jit-icache-coherence", + "wasmtime-runtime", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-jit-debug" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0554b84c15a27d76281d06838aed94e13a77d7bf604bbbaf548aa20eb93846" +dependencies = [ + "once_cell", +] + +[[package]] +name = "wasmtime-jit-icache-coherence" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aecae978b13f7f67efb23bd827373ace4578f2137ec110bbf6a4a7cde4121bbd" +dependencies = [ + "cfg-if", + "libc", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-runtime" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "658cf6f325232b6760e202e5255d823da5e348fdea827eff0a2a22319000b441" +dependencies = [ + "anyhow", + "cc", + "cfg-if", + "indexmap 1.9.3", + "libc", + "log", + "mach", + "memfd", + "memoffset 0.8.0", + "paste", + "rand 0.8.6", + "rustix 0.36.17", + "wasmtime-asm-macros", + "wasmtime-environ", + "wasmtime-jit-debug", + "windows-sys 0.45.0", +] + +[[package]] +name = "wasmtime-types" +version = "8.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f6fffd2a1011887d57f07654dd112791e872e3ff4a2e626aee8059ee17f06f" +dependencies = [ + "cranelift-entity", + "serde", + "thiserror 1.0.69", + "wasmparser 0.102.0", +] + [[package]] name = "web-time" version = "1.1.0" @@ -9411,6 +9622,15 @@ dependencies = [ "windows-targets 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -9453,6 +9673,21 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -9475,6 +9710,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -9487,6 +9728,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -9499,6 +9746,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -9517,6 +9770,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -9529,6 +9788,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -9541,6 +9806,12 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -9553,12 +9824,27 @@ version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + [[package]] name = "winnow" version = "0.7.15" @@ -9611,7 +9897,7 @@ checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" dependencies = [ "anyhow", "heck", - "indexmap", + "indexmap 2.14.0", "prettyplease", "syn 2.0.117", "wasm-metadata", @@ -9642,7 +9928,7 @@ checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", "bitflags 2.11.1", - "indexmap", + "indexmap 2.14.0", "log", "serde", "serde_derive", @@ -9661,7 +9947,7 @@ checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" dependencies = [ "anyhow", "id-arena", - "indexmap", + "indexmap 2.14.0", "log", "semver", "serde", From 16f1e2115f7e2356216200bfba4bbba85ea63b36 Mon Sep 17 00:00:00 2001 From: Vadim Smirnov Date: Wed, 20 May 2026 21:15:08 +0400 Subject: [PATCH 14/14] chore: trim storage API to generic primitives --- Cargo.lock | 3 - rs/ethexe/Cargo.lock | 196 ++++++---- rs/src/build.rs | 322 ++++++--------- rs/storage/Cargo.toml | 3 - rs/storage/src/lib.rs | 693 ++------------------------------- rs/storage/tests/public_api.rs | 72 +--- 6 files changed, 265 insertions(+), 1024 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5cecab7ba..b4af93835 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6621,9 +6621,6 @@ dependencies = [ [[package]] name = "sails-storage" version = "1.0.0-beta.5" -dependencies = [ - "gprimitives", -] [[package]] name = "sails-type-registry" diff --git a/rs/ethexe/Cargo.lock b/rs/ethexe/Cargo.lock index a2dd46d39..dad4c68ff 100644 --- a/rs/ethexe/Cargo.lock +++ b/rs/ethexe/Cargo.lock @@ -51,7 +51,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common", + "crypto-common 0.1.6", "generic-array", ] @@ -85,9 +85,9 @@ checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "alloy-primitives" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3b431b4e72cd8bd0ec7a50b4be18e73dab74de0dba180eef171055e5d5926e" +checksum = "4885c1409b6936c4898e646ef58baf6ec54edaf6d8179f79df805a7b85b7cf3e" dependencies = [ "bytes", "cfg-if", @@ -97,14 +97,14 @@ dependencies = [ "paste", "ruint", "rustc-hash 2.1.2", - "sha3", + "sha3 0.11.0", ] [[package]] name = "alloy-sol-macro" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab81bab693da9bb79f7a95b64b394718259fdd7e41dceeced4cad57cb71c4f6a" +checksum = "840128ed2b2971d6d4668a553fe403a82683d3acc646c73e75887e7157408033" dependencies = [ "alloy-sol-macro-expander", "alloy-sol-macro-input", @@ -116,9 +116,9 @@ dependencies = [ [[package]] name = "alloy-sol-macro-expander" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "489f1620bb7e2483fb5819ed01ab6edc1d2f93939dce35a5695085a1afd1d699" +checksum = "63ec265e5d65d725175f6ca7711c970824c90ef9c0d1f1973711d4150ee612dd" dependencies = [ "alloy-sol-macro-input", "const-hex", @@ -127,16 +127,16 @@ dependencies = [ "proc-macro-error2", "proc-macro2", "quote", - "sha3", + "sha3 0.11.0", "syn 2.0.117", "syn-solidity", ] [[package]] name = "alloy-sol-macro-input" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56cef806ad22d4392c5fc83cf8f2089f988eb99c7067b4e0c6f1971fc1cca318" +checksum = "89bf01077f18650876cfa682eb1f949967b5cde03f1a51c955c469d2c9b4aa67" dependencies = [ "const-hex", "dunce", @@ -150,9 +150,9 @@ dependencies = [ [[package]] name = "alloy-sol-types" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64612d29379782a5dde6f4b6570d9c756d734d760c0c94c254d361e678a6591f" +checksum = "384cf252de0db2dec52821eac037a7f57e2aa33fe5b900ce6fe39973402341f1" dependencies = [ "alloy-primitives", "alloy-sol-macro", @@ -419,7 +419,7 @@ checksum = "7db09fde9143e7ac4513358fb32ee32847125b63b18ea715afd487956da715da" dependencies = [ "rustc-hash 2.1.2", "unicode-ident", - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -597,6 +597,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bounded-collections" version = "0.2.4" @@ -633,7 +642,7 @@ dependencies = [ "parity-scale-codec", "scale-info", "sha2 0.10.9", - "sha3", + "sha3 0.10.9", ] [[package]] @@ -786,9 +795,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.61" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "shlex", @@ -1106,6 +1115,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "crypto-mac" version = "0.8.0" @@ -1214,9 +1232,9 @@ dependencies = [ [[package]] name = "dashmap" -version = "6.1.0" +version = "6.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" dependencies = [ "cfg-if", "crossbeam-utils", @@ -1345,10 +1363,20 @@ checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ "block-buffer 0.10.4", "const-oid", - "crypto-common", + "crypto-common 0.1.6", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "crypto-common 0.2.2", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -1582,9 +1610,9 @@ dependencies = [ [[package]] name = "enumset" -version = "1.1.12" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f96a4a12fe60ac746ae295a1a4ecb5bb02debc20856506c8635288065f142de" +checksum = "839c4174b41e75c8f7306110b2c51996a293b8d1d850edd529011841d9fede7d" dependencies = [ "enumset_derive", ] @@ -1729,13 +1757,12 @@ dependencies = [ [[package]] name = "filetime" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" dependencies = [ "cfg-if", "libc", - "libredox", ] [[package]] @@ -2731,6 +2758,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "foldhash 0.1.5", + "serde", ] [[package]] @@ -2746,9 +2774,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" dependencies = [ "allocator-api2", "equivalent", @@ -2818,6 +2846,15 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + [[package]] name = "iana-time-zone" version = "0.1.65" @@ -3023,7 +3060,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -3134,6 +3171,16 @@ dependencies = [ "cpufeatures 0.2.17", ] +[[package]] +name = "keccak" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", +] + [[package]] name = "keccak-const" version = "0.2.0" @@ -3204,18 +3251,6 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" -[[package]] -name = "libredox" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" -dependencies = [ - "bitflags 2.11.1", - "libc", - "plain", - "redox_syscall 0.7.5", -] - [[package]] name = "libsecp256k1" version = "0.7.2" @@ -3349,9 +3384,9 @@ dependencies = [ [[package]] name = "macro-string" -version = "0.1.4" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b27834086c65ec3f9387b096d66e99f221cf081c2b738042aa252bcd41204e3" +checksum = "59a9dbbfc75d2688ed057456ce8a3ee3f48d12eec09229f560f3643b9f275653" dependencies = [ "proc-macro2", "quote", @@ -3531,7 +3566,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "58c38e2799fc0978b65dfff8023ec7843e2330bb462f19198840b34b6582397d" dependencies = [ "byteorder", - "keccak", + "keccak 0.1.6", "rand_core 0.6.4", "zeroize", ] @@ -3675,9 +3710,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-format" @@ -3882,7 +3917,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.5.18", + "redox_syscall", "smallvec", "windows-link", ] @@ -3963,12 +3998,6 @@ dependencies = [ "spki", ] -[[package]] -name = "plain" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" - [[package]] name = "polkavm-common" version = "0.9.0" @@ -4347,15 +4376,6 @@ dependencies = [ "bitflags 2.11.1", ] -[[package]] -name = "redox_syscall" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" -dependencies = [ - "bitflags 2.11.1", -] - [[package]] name = "ref-cast" version = "1.0.25" @@ -4459,7 +4479,7 @@ checksum = "73389e0c99e664f919275ab5b5b0471391fe9a8de61e1dff9b1eaf56a90f16e3" dependencies = [ "bytecheck 0.8.2", "bytes", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "indexmap 2.14.0", "munge", "ptr_meta 0.3.1", @@ -4689,7 +4709,7 @@ dependencies = [ "gstd", "gsys", "gtest", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "hex", "keccak-const", "log", @@ -4700,12 +4720,19 @@ dependencies = [ "sails-idl-meta", "sails-macros", "sails-reflect-hash", + "sails-storage", "sails-type-registry", "spin 0.10.0", "thiserror 2.0.18", "tokio-stream", + "wasm-encoder 0.244.0", + "wasmparser 0.244.0", ] +[[package]] +name = "sails-storage" +version = "1.0.0-beta.5" + [[package]] name = "sails-type-registry" version = "1.0.0-beta.5" @@ -5074,7 +5101,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77fd7028345d415a4034cf8777cd4f8ab1851274233b45f84e3d955502d93874" dependencies = [ "digest 0.10.7", - "keccak", + "keccak 0.1.6", +] + +[[package]] +name = "sha3" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1" +dependencies = [ + "digest 0.11.3", + "keccak 0.2.0", ] [[package]] @@ -5304,7 +5341,7 @@ dependencies = [ "byteorder", "digest 0.10.7", "sha2 0.10.9", - "sha3", + "sha3 0.10.9", "twox-hash", ] @@ -5824,9 +5861,9 @@ dependencies = [ [[package]] name = "syn-solidity" -version = "1.5.7" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53f425ae0b12e2f5ae65542e00898d500d4d318b4baf09f40fd0d410454e9947" +checksum = "ec005042c7d952febc1a3ef5b0f6674e9054aa836877a31c90b20e25b3d31744" dependencies = [ "paste", "proc-macro2", @@ -5853,9 +5890,9 @@ checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" [[package]] name = "tar" -version = "0.4.45" +version = "0.4.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" dependencies = [ "filetime", "libc", @@ -6012,9 +6049,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.52.2" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "110a78583f19d5cdb2c5ccf321d1290344e71313c6c37d43520d386027d18386" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -6073,7 +6110,7 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -6128,7 +6165,7 @@ dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -6137,7 +6174,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.2", + "winnow 1.0.3", ] [[package]] @@ -6448,7 +6485,7 @@ dependencies = [ "rand_chacha 0.3.1", "rand_core 0.6.4", "sha2 0.10.9", - "sha3", + "sha3 0.10.9", "zeroize", ] @@ -6842,6 +6879,7 @@ dependencies = [ "hashbrown 0.15.5", "indexmap 2.14.0", "semver", + "serde", ] [[package]] @@ -7340,9 +7378,9 @@ dependencies = [ [[package]] name = "winnow" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -7517,9 +7555,9 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] diff --git a/rs/src/build.rs b/rs/src/build.rs index d2acb1403..3d45d6ab3 100644 --- a/rs/src/build.rs +++ b/rs/src/build.rs @@ -3,9 +3,7 @@ pub use gwasm_builder::build as build_wasm; use gwasm_builder::{PreProcessor, PreProcessorTarget, WasmBuilder}; -use sails_storage::{ - ACTOR_ID_U256_SLOT_SIZE, ACTOR_PAIR_U256_SLOT_SIZE, StaticLayout, StaticOpenAddressTable, -}; +use sails_storage::{StaticLayout, StaticOpenAddressTable}; use std::{ boxed::Box, collections::BTreeSet, @@ -18,6 +16,7 @@ use std::{ vec, vec::Vec, }; +use wasm_encoder::reencode::{self, Reencode, RoundtripReencoder}; const GENERATED_STATIC_STORAGE: &str = "sails_static_storage.rs"; const WASM_PAGE_SIZE: usize = 64 * 1024; @@ -35,7 +34,7 @@ pub struct StaticMemoryLayout { #[derive(Clone, Debug)] struct StaticTable { name: String, - slots: StaticTableSlots, + slots: usize, slot_size: usize, align: usize, } @@ -52,17 +51,9 @@ struct ResolvedStaticTable { name: String, base: usize, slots: usize, - log2_slots: Option, - mask: Option, bytes: usize, } -#[derive(Clone, Debug)] -enum StaticTableSlots { - Exact(usize), - Log2(u8), -} - /// Errors returned by static-memory build helpers. #[derive(Debug, thiserror::Error)] pub enum Error { @@ -148,59 +139,15 @@ impl StaticMemoryLayout { /// Reserves a named static open-addressed table. pub fn reserve_table( - self, - name: impl Into, - slots: usize, - ) -> Self { - self.reserve_single_region_table( - name, - StaticTableSlots::Exact(slots), - StaticOpenAddressTable::::slot_size(), - 1, - ) - } - - /// Reserves a WAT-shaped `ActorId -> U256` static map. - /// - /// The reserved table uses `2^LOG2_SLOTS` slots. Each slot is exactly 64 - /// bytes: a 32-byte actor id followed by a 32-byte `U256` value. - pub fn reserve_actor_u256_map(self, name: impl Into) -> Self { - self.reserve_single_region_table( - name, - StaticTableSlots::Log2(LOG2_SLOTS), - ACTOR_ID_U256_SLOT_SIZE, - 64, - ) - } - - /// Reserves a `(ActorId, ActorId) -> U256` static map. - /// - /// The reserved table uses `2^LOG2_SLOTS` slots. Each slot is exactly 96 - /// bytes: owner actor id, spender actor id, and a `U256` value. - pub fn reserve_actor_pair_u256_map( - self, - name: impl Into, - ) -> Self { - self.reserve_single_region_table( - name, - StaticTableSlots::Log2(LOG2_SLOTS), - ACTOR_PAIR_U256_SLOT_SIZE, - 32, - ) - } - - fn reserve_single_region_table( mut self, name: impl Into, - slots: StaticTableSlots, - slot_size: usize, - align: usize, + slots: usize, ) -> Self { self.tables.push(StaticTable { name: name.into(), slots, - slot_size, - align, + slot_size: StaticOpenAddressTable::::slot_size(), + align: 1, }); self } @@ -221,9 +168,8 @@ impl StaticMemoryLayout { return Err(Error::DuplicateTableName(table.name)); } - let log2_slots = table.slots.log2(); - let slots = table.slots.resolve()?; - let bytes = slots + let bytes = table + .slots .checked_mul(table.slot_size) .ok_or(Error::LayoutOverflow)?; let region = layout @@ -232,9 +178,7 @@ impl StaticMemoryLayout { let resolved = ResolvedStaticTable { name: table.name, base: region.base(), - slots, - log2_slots, - mask: log2_slots.map(|_| slots - 1), + slots: table.slots, bytes: region.bytes(), }; tables.push(resolved); @@ -249,29 +193,6 @@ impl StaticMemoryLayout { } } -impl StaticTableSlots { - fn log2(&self) -> Option { - match *self { - Self::Exact(_) => None, - Self::Log2(log2_slots) => Some(log2_slots), - } - } - - fn resolve(&self) -> Result { - match *self { - Self::Exact(slots) => Ok(slots), - Self::Log2(log2_slots) => { - let shift = u32::from(log2_slots); - if shift > 32 || shift >= usize::BITS { - return Err(Error::LayoutOverflow); - } - - 1usize.checked_shl(shift).ok_or(Error::LayoutOverflow) - } - } - } -} - struct StaticMemoryPreProcessor { layout: ResolvedStaticMemoryLayout, } @@ -334,20 +255,6 @@ fn emit_static_storage_to_dir( table.slots ) .expect("writing to String cannot fail"); - if let Some(log2_slots) = table.log2_slots { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_LOG2_SLOTS: u8 = {log2_slots};" - ) - .expect("writing to String cannot fail"); - } - if let Some(mask) = table.mask { - writeln!( - &mut source, - "#[allow(dead_code)]\npub const {name}_MASK: usize = {mask};" - ) - .expect("writing to String cannot fail"); - } writeln!( &mut source, "#[allow(dead_code)]\npub const {name}_BYTES: usize = {};", @@ -367,108 +274,90 @@ fn patch_imported_memory( ) -> Result, Error> { let parser = wasmparser::Parser::new(0); let mut module = wasm_encoder::Module::new(); - let mut memory_import_seen = false; - - for payload in parser.parse_all(wasm) { - match payload? { - wasmparser::Payload::Version { .. } => {} - wasmparser::Payload::ImportSection(imports) => { - let mut import_section = wasm_encoder::ImportSection::new(); - - for imports in imports { - match imports? { - wasmparser::Imports::Single(_, import) => append_import( - &mut import_section, - import.module, - import.name, - import.ty, - &mut memory_import_seen, - start_page, - required_pages, - )?, - wasmparser::Imports::Compact1 { module, items } => { - for item in items { - let item = item?; - append_import( - &mut import_section, - module, - item.name, - item.ty, - &mut memory_import_seen, - start_page, - required_pages, - )?; - } - } - wasmparser::Imports::Compact2 { module, ty, names } => { - for name in names { - append_import( - &mut import_section, - module, - name?, - ty, - &mut memory_import_seen, - start_page, - required_pages, - )?; - } - } - } - } + let mut reencoder = StaticMemoryReencoder { + memory_import_seen: false, + start_page, + required_pages, + }; - module.section(&import_section); - } - wasmparser::Payload::End(_) => break, - other => { - if let Some((id, range)) = other.as_section() { - module.section(&wasm_encoder::RawSection { - id, - data: &wasm[range], - }); - } else if !matches!( - other, - wasmparser::Payload::CodeSectionStart { .. } - | wasmparser::Payload::CodeSectionEntry { .. } - ) { - return Err(Error::UnsupportedWasmPayload); - } - } - } - } + reencoder + .parse_core_module(&mut module, parser, wasm) + .map_err(map_reencode_error)?; - if !memory_import_seen { + if !reencoder.memory_import_seen { return Err(Error::MemoryImportNotFound); } Ok(module.finish()) } -fn append_import( - imports: &mut wasm_encoder::ImportSection, - module: &str, - name: &str, - ty: wasmparser::TypeRef, - memory_import_seen: &mut bool, +fn map_reencode_error(error: reencode::Error) -> Error { + match error { + reencode::Error::ParseError(error) => Error::WasmParse(error), + reencode::Error::UserError(error) => error, + error => Error::UnsupportedImport(error.to_string()), + } +} + +struct StaticMemoryReencoder { + memory_import_seen: bool, start_page: u32, required_pages: u32, -) -> Result<(), Error> { - let entity = match ty { - wasmparser::TypeRef::Memory(memory) => { - if *memory_import_seen { - return Err(Error::MultipleMemoryImports); +} + +impl Reencode for StaticMemoryReencoder { + type Error = Error; + + fn parse_imports( + &mut self, + imports: &mut wasm_encoder::ImportSection, + import_group: wasmparser::Imports<'_>, + ) -> Result<(), reencode::Error> { + match import_group { + wasmparser::Imports::Single(_, import) => { + self.append_import(imports, import.module, import.name, import.ty)?; + } + wasmparser::Imports::Compact1 { module, items } => { + for item in items { + let item = item?; + self.append_import(imports, module, item.name, item.ty)?; + } + } + wasmparser::Imports::Compact2 { module, ty, names } => { + for name in names { + self.append_import(imports, module, name?, ty)?; + } } - *memory_import_seen = true; - patched_memory_type(memory, start_page, required_pages)? } - other => other - .try_into() - .map_err(|error: wasm_encoder::reencode::Error| { - Error::UnsupportedImport(error.to_string()) + Ok(()) + } +} + +impl StaticMemoryReencoder { + fn append_import( + &mut self, + imports: &mut wasm_encoder::ImportSection, + module: &str, + name: &str, + ty: wasmparser::TypeRef, + ) -> Result<(), reencode::Error> { + let entity = match ty { + wasmparser::TypeRef::Memory(memory) => { + if self.memory_import_seen { + return Err(reencode::Error::UserError(Error::MultipleMemoryImports)); + } + self.memory_import_seen = true; + patched_memory_type(memory, self.start_page, self.required_pages) + .map_err(reencode::Error::UserError)? + } + other => RoundtripReencoder.entity_type(other).map_err(|error| { + reencode::Error::UserError(Error::UnsupportedImport(error.to_string())) })?, - }; + }; - imports.import(module, name, entity); - Ok(()) + imports.import(module, name, entity); + Ok(()) + } } fn patched_memory_type( @@ -557,6 +446,7 @@ fn is_rust_keyword(name: &str) -> bool { | "false" | "fn" | "for" + | "gen" | "if" | "impl" | "in" @@ -577,10 +467,24 @@ fn is_rust_keyword(name: &str) -> bool { | "trait" | "true" | "type" + | "union" | "unsafe" | "use" | "where" | "while" + | "abstract" + | "become" + | "box" + | "do" + | "final" + | "macro" + | "override" + | "priv" + | "try" + | "typeof" + | "unsized" + | "virtual" + | "yield" ) } @@ -662,32 +566,32 @@ mod tests { } #[test] - fn resolves_specialized_static_maps_with_alignment_and_metadata() { + fn resolves_multiple_static_tables_with_metadata() { let layout = StaticMemoryLayout::new(1024) .reserve_table::<1, 1>("prefix", 1) - .reserve_actor_u256_map::<2>("fast_balances") - .reserve_actor_pair_u256_map::<1>("fast_allowances") + .reserve_table::<32, 32>("fast_balances", 4) + .reserve_table::<64, 32>("fast_allowances", 2) .resolve() .unwrap(); - assert_eq!(layout.tables[1].base % 64, 0); assert_eq!(layout.tables[1].slots, 4); - assert_eq!(layout.tables[1].log2_slots, Some(2)); - assert_eq!(layout.tables[1].mask, Some(3)); - assert_eq!(layout.tables[1].bytes, 4 * ACTOR_ID_U256_SLOT_SIZE); - assert_eq!(layout.tables[2].base % 32, 0); + assert_eq!( + layout.tables[1].bytes, + 4 * StaticOpenAddressTable::<32, 32>::slot_size() + ); assert_eq!(layout.tables[2].slots, 2); - assert_eq!(layout.tables[2].log2_slots, Some(1)); - assert_eq!(layout.tables[2].mask, Some(1)); - assert_eq!(layout.tables[2].bytes, 2 * ACTOR_PAIR_U256_SLOT_SIZE); + assert_eq!( + layout.tables[2].bytes, + 2 * StaticOpenAddressTable::<64, 32>::slot_size() + ); let dir = tempfile::tempdir().unwrap(); emit_static_storage_to_dir(&layout, dir.path()).unwrap(); let generated = fs::read_to_string(dir.path().join(GENERATED_STATIC_STORAGE)).unwrap(); - assert!(generated.contains("pub const FAST_BALANCES_LOG2_SLOTS: u8 = 2;")); - assert!(generated.contains("pub const FAST_BALANCES_MASK: usize = 3;")); - assert!(generated.contains("pub const FAST_ALLOWANCES_LOG2_SLOTS: u8 = 1;")); - assert!(generated.contains("pub const FAST_ALLOWANCES_MASK: usize = 1;")); + assert!(generated.contains("pub const FAST_BALANCES_SLOTS: usize = 4;")); + assert!(generated.contains("pub const FAST_BALANCES_BYTES: usize = 260;")); + assert!(generated.contains("pub const FAST_ALLOWANCES_SLOTS: usize = 2;")); + assert!(generated.contains("pub const FAST_ALLOWANCES_BYTES: usize = 194;")); } #[test] @@ -711,6 +615,14 @@ mod tests { .resolve(), Err(Error::ReservedTableName(_)) )); + for name in ["async", "dyn", "gen", "try", "union", "yield"] { + assert!(matches!( + StaticMemoryLayout::new(1) + .reserve_table::<1, 1>(name, 1) + .resolve(), + Err(Error::ReservedTableName(_)) + )); + } } #[test] diff --git a/rs/storage/Cargo.toml b/rs/storage/Cargo.toml index 192c8c745..8406e0234 100644 --- a/rs/storage/Cargo.toml +++ b/rs/storage/Cargo.toml @@ -9,6 +9,3 @@ license.workspace = true repository.workspace = true rust-version.workspace = true -[dependencies] -gprimitives.workspace = true - diff --git a/rs/storage/src/lib.rs b/rs/storage/src/lib.rs index d81697eb3..c3aa0d558 100644 --- a/rs/storage/src/lib.rs +++ b/rs/storage/src/lib.rs @@ -5,9 +5,9 @@ //! //! - [`FixedOpenAddressMap`] stores a fixed-capacity map directly inside a //! Rust value. It is useful for small bounded state and tests. -//! - [`StaticOpenAddressTable`] and the actor-specific maps operate on a -//! caller-reserved memory region. They are useful when a Sails program wants -//! stable, lazy-page-friendly storage outside allocator-managed collections. +//! - [`StaticOpenAddressTable`] operates on a caller-reserved memory region. +//! It is useful when a Sails program wants stable, lazy-page-friendly storage +//! outside allocator-managed collections. //! //! The static tables are intentionally low level. Constructors are `unsafe` //! because the caller must reserve a valid writable memory range and keep it @@ -39,40 +39,16 @@ //! assert_eq!(table.get(&[1]), Ok(Some([10]))); //! ``` //! -//! # Actor maps -//! -//! [`StaticActorIdU256Map`] and [`StaticActorPairU256Map`] are specialized -//! static-memory layouts for common token-like state. They store `ActorId` and -//! `U256` values without per-slot state bytes: an all-zero actor key marks an -//! empty slot, and writing a zero `U256` removes the visible value for an -//! existing key. Because zero actor ids are reserved by the layout, mutation -//! methods reject them with [`TableError::InvalidKey`]. -//! -//! ``` -//! use gprimitives::{ActorId, U256}; -//! use sails_storage::{ACTOR_ID_U256_SLOT_SIZE, StaticActorIdU256Map}; -//! -//! let mut memory = [0u8; 4 * ACTOR_ID_U256_SLOT_SIZE]; -//! let balances = -//! unsafe { StaticActorIdU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; -//! let account = ActorId::from(1u64); -//! -//! assert_eq!(balances.insert_actor_u256(account, U256::from(10)), Ok(None)); -//! assert_eq!(balances.get_actor_u256(&account), Ok(Some(U256::from(10)))); -//! ``` #![no_std] use core::{fmt, marker::PhantomData, ptr}; -use gprimitives::{ActorId, U256}; /// Errors returned by fixed and static storage tables. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum TableError { /// The table has no reusable slot for a new key. CapacityOverflow, - /// The key cannot be represented by this table layout. - InvalidKey, /// The provided memory layout overflows or does not fit the requested region. InvalidLayout, /// A static-memory slot state byte is not one of the supported values. @@ -83,7 +59,6 @@ impl fmt::Display for TableError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::CapacityOverflow => f.write_str("capacity overflow"), - Self::InvalidKey => f.write_str("invalid key"), Self::InvalidLayout => f.write_str("invalid storage layout"), Self::InvalidSlotState => f.write_str("invalid slot state"), } @@ -126,7 +101,7 @@ enum Lookup { fn find_slot( slots: usize, - hash: usize, + hash: u32, mut classify: impl FnMut(usize) -> Result, ) -> Result { if slots == 0 { @@ -134,7 +109,7 @@ fn find_slot( } let mut first_deleted = None; - let mut index = hash % slots; + let mut index = start_index(slots, hash); for _ in 0..slots { match classify(index)? { @@ -157,7 +132,16 @@ fn find_slot( Ok(first_deleted.map_or(Lookup::Full, Lookup::Vacant)) } -fn hash_bytes(bytes: &[u8]) -> usize { +fn start_index(slots: usize, hash: u32) -> usize { + let hash = hash as usize; + if slots.is_power_of_two() { + hash & (slots - 1) + } else { + hash % slots + } +} + +fn hash_bytes(bytes: &[u8]) -> u32 { let mut hash = 0x811c_9dc5u32; for byte in bytes { @@ -165,15 +149,9 @@ fn hash_bytes(bytes: &[u8]) -> usize { hash = hash.wrapping_mul(0x0100_0193); } - hash as usize + hash } -/// Slot size used by the specialized actor-id static map. -pub const ACTOR_ID_U256_SLOT_SIZE: usize = 64; - -/// Slot size used by the specialized actor-pair static map. -pub const ACTOR_PAIR_U256_SLOT_SIZE: usize = 96; - #[derive(Clone, Copy, Debug, PartialEq, Eq)] struct FixedSlot { state: SlotState, @@ -619,568 +597,11 @@ impl StaticOpenAddressTable Result { - let shift = u32::from(log2_slots); - if shift > 32 || shift >= usize::BITS { - return Err(TableError::InvalidLayout); - } - - 1usize.checked_shl(shift).ok_or(TableError::InvalidLayout) -} - -fn static_map_index(hash: u32, log2_slots: u8) -> usize { - if log2_slots == 0 { - 0 - } else { - (hash >> (32 - u32::from(log2_slots))) as usize - } -} - -fn actor_key(actor: ActorId) -> [u8; 32] { - let mut key = [0u8; 32]; - key.copy_from_slice(actor.as_ref()); - key -} - -fn u256_value(value: U256) -> [u8; 32] { - let mut bytes = [0u8; 32]; - value.to_little_endian(&mut bytes); - bytes -} - -fn u256_from_value(bytes: [u8; 32]) -> U256 { - U256::from_little_endian(&bytes) -} - -fn reject_zero_key(bytes: &[u8; 32]) -> Result<(), TableError> { - if is_zero_32(bytes) { - Err(TableError::InvalidKey) - } else { - Ok(()) - } -} - -fn is_zero_32(bytes: &[u8; 32]) -> bool { - bytes.iter().all(|byte| *byte == 0) -} - -fn words_32(bytes: &[u8; 32]) -> [u64; 4] { - [ - u64::from_le_bytes([ - bytes[0], bytes[1], bytes[2], bytes[3], bytes[4], bytes[5], bytes[6], bytes[7], - ]), - u64::from_le_bytes([ - bytes[8], bytes[9], bytes[10], bytes[11], bytes[12], bytes[13], bytes[14], bytes[15], - ]), - u64::from_le_bytes([ - bytes[16], bytes[17], bytes[18], bytes[19], bytes[20], bytes[21], bytes[22], bytes[23], - ]), - u64::from_le_bytes([ - bytes[24], bytes[25], bytes[26], bytes[27], bytes[28], bytes[29], bytes[30], bytes[31], - ]), - ] -} - -unsafe fn read_words_32(ptr: *const u8) -> [u64; 4] { - unsafe { - [ - ptr::read_unaligned(ptr.cast::()), - ptr::read_unaligned(ptr.add(8).cast::()), - ptr::read_unaligned(ptr.add(16).cast::()), - ptr::read_unaligned(ptr.add(24).cast::()), - ] - } -} - -unsafe fn words_at_zero(ptr: *const u8) -> bool { - words_are_zero(unsafe { read_words_32(ptr) }) -} - -fn words_are_zero(words: [u64; 4]) -> bool { - (words[0] | words[1] | words[2] | words[3]) == 0 -} - -const HASH_GOLDEN_RATIO: u32 = 0x9E37_79B9; -const HASH_PAIR_SPENDER: u32 = 0x85EB_CA6B; - -fn hash_words(bytes: &[u8; 32]) -> u32 { - fold_words(bytes).wrapping_mul(HASH_GOLDEN_RATIO) -} - -fn hash_actor_key(key: &[u8; 32]) -> u32 { - hash_words(key) -} - -fn hash_actor_pair_key(left: &[u8; 32], right: &[u8; 32]) -> u32 { - fold_words(left) - .wrapping_mul(HASH_GOLDEN_RATIO) - .wrapping_add(fold_words(right).wrapping_mul(HASH_PAIR_SPENDER)) -} - -fn fold_words(bytes: &[u8; 32]) -> u32 { - let ptr = bytes.as_ptr(); - unsafe { - ptr::read_unaligned(ptr.cast::()) - ^ ptr::read_unaligned(ptr.add(4).cast::()) - ^ ptr::read_unaligned(ptr.add(8).cast::()) - ^ ptr::read_unaligned(ptr.add(12).cast::()) - ^ ptr::read_unaligned(ptr.add(16).cast::()) - ^ ptr::read_unaligned(ptr.add(20).cast::()) - ^ ptr::read_unaligned(ptr.add(24).cast::()) - ^ ptr::read_unaligned(ptr.add(28).cast::()) - } -} - -/// A static-memory map keyed by `ActorId` and storing `U256`. -/// -/// The layout uses `2^LOG2_SLOTS` slots. Each slot is exactly 64 bytes: -/// a 32-byte actor id followed by a 32-byte little-endian `U256` value. -pub struct StaticActorIdU256Map { - base: usize, - slots: usize, - mask: usize, - _marker: PhantomData<*mut u8>, -} - -impl StaticActorIdU256Map { - /// Returns the byte length of one slot. - pub const fn slot_size() -> usize { - ACTOR_ID_U256_SLOT_SIZE - } - - /// Returns the configured slot count. - pub fn slots() -> Result { - static_map_slots(LOG2_SLOTS) - } - - /// Returns the mask used for power-of-two probing. - pub fn mask() -> Result { - Ok(Self::slots()? - 1) - } - - /// Returns the byte length required for this map. - pub fn bytes_len() -> Result { - Self::slots()? - .checked_mul(Self::slot_size()) - .ok_or(TableError::InvalidLayout) - } - - /// Creates a map over `2^LOG2_SLOTS * 64` bytes at `base`. - /// - /// # Safety - /// - /// The caller must ensure the memory interval is valid for reads and writes - /// for the whole lifetime of the map and does not overlap other mutable - /// state. - pub unsafe fn new(base: usize) -> Result { - let slots = Self::slots()?; - StaticRegion::new(base, Self::bytes_len()?)?; - Ok(Self { - base, - slots, - mask: slots - 1, - _marker: PhantomData, - }) - } - - /// Returns the configured base address. - pub const fn base(&self) -> usize { - self.base - } - - /// Returns the total byte length occupied by this map. - pub fn bytes(&self) -> Result { - Self::bytes_len() - } - - /// Returns the visible value for `actor`. - pub fn get_actor_u256(&self, actor: &ActorId) -> Result, TableError> { - let key = actor_key(*actor); - if is_zero_32(&key) { - return Ok(None); - } - - let StaticLookup::Found(index) = self.lookup(&key) else { - return Ok(None); - }; - Ok(self.visible_value(index).map(u256_from_value)) - } - - /// Inserts or updates `actor`, returning the previous visible value. - /// - /// A zero value removes the visible value. Zero actor ids are rejected - /// because zero key bytes mark empty slots in this layout. - pub fn insert_actor_u256( - &self, - actor: ActorId, - value: U256, - ) -> Result, TableError> { - let key = actor_key(actor); - reject_zero_key(&key)?; - if value.is_zero() { - return self - .remove_key(&key) - .map(|previous| previous.map(u256_from_value)); - } - - let value = u256_value(value); - match self.lookup(&key) { - StaticLookup::Found(index) => { - let previous = self.visible_value(index); - unsafe { - self.write_value(index, &value); - } - Ok(previous.map(u256_from_value)) - } - StaticLookup::Vacant(index) => { - unsafe { - self.write_key(index, &key); - self.write_value(index, &value); - } - Ok(None) - } - StaticLookup::Full => Err(TableError::CapacityOverflow), - } - } - - /// Removes the visible value for `actor`. - pub fn remove_actor_u256(&self, actor: &ActorId) -> Result, TableError> { - let key = actor_key(*actor); - reject_zero_key(&key)?; - self.remove_key(&key) - .map(|previous| previous.map(u256_from_value)) - } - - /// Clears every slot to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - unsafe { - ptr::write_bytes(self.base as *mut u8, 0, self.bytes()?); - } - Ok(()) - } - - fn remove_key(&self, key: &[u8; 32]) -> Result, TableError> { - let StaticLookup::Found(index) = self.lookup(key) else { - return Ok(None); - }; - - let previous = self.visible_value(index); - if previous.is_some() { - unsafe { - self.write_value(index, &[0; 32]); - } - } - Ok(previous) - } - - fn lookup(&self, key: &[u8; 32]) -> StaticLookup { - let key_words = words_32(key); - let mut index = static_map_index(hash_actor_key(key), LOG2_SLOTS); - - for _ in 0..self.slots { - let stored_key = unsafe { read_words_32(self.key_ptr(index)) }; - if stored_key == key_words { - return StaticLookup::Found(index); - } - if words_are_zero(stored_key) { - return StaticLookup::Vacant(index); - } - - index = (index + 1) & self.mask; - } - - StaticLookup::Full - } - - fn visible_value(&self, slot: usize) -> Option<[u8; 32]> { - let value = unsafe { self.read_value(slot) }; - if is_zero_32(&value) { - None - } else { - Some(value) - } - } - - unsafe fn read_value(&self, slot: usize) -> [u8; 32] { - let mut bytes = [0u8; 32]; - unsafe { - ptr::copy_nonoverlapping(self.value_ptr(slot), bytes.as_mut_ptr(), 32); - } - bytes - } - - unsafe fn write_key(&self, slot: usize, key: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(key.as_ptr(), self.key_ptr(slot), 32); - } - } - - unsafe fn write_value(&self, slot: usize, value: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(value.as_ptr(), self.value_ptr(slot), 32); - } - } - - fn key_ptr(&self, slot: usize) -> *mut u8 { - self.slot_ptr(slot) - } - - fn value_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(32) } - } - - fn slot_ptr(&self, slot: usize) -> *mut u8 { - (self.base + slot * Self::slot_size()) as *mut u8 - } -} - -/// A static-memory map keyed by two `ActorId`s and storing `U256`. -/// -/// The layout uses `2^LOG2_SLOTS` slots. Each slot is exactly 96 bytes: -/// the two 32-byte actor ids followed by a 32-byte little-endian `U256` value. -pub struct StaticActorPairU256Map { - base: usize, - slots: usize, - mask: usize, - _marker: PhantomData<*mut u8>, -} - -impl StaticActorPairU256Map { - /// Returns the byte length of one slot. - pub const fn slot_size() -> usize { - ACTOR_PAIR_U256_SLOT_SIZE - } - - /// Returns the configured slot count. - pub fn slots() -> Result { - static_map_slots(LOG2_SLOTS) - } - - /// Returns the mask used for power-of-two probing. - pub fn mask() -> Result { - Ok(Self::slots()? - 1) - } - - /// Returns the byte length required for this map. - pub fn bytes_len() -> Result { - Self::slots()? - .checked_mul(Self::slot_size()) - .ok_or(TableError::InvalidLayout) - } - - /// Creates a map over `2^LOG2_SLOTS * 96` bytes at `base`. - /// - /// # Safety - /// - /// The caller must ensure the memory interval is valid for reads and writes - /// for the whole lifetime of the map and does not overlap other mutable - /// state. - pub unsafe fn new(base: usize) -> Result { - let slots = Self::slots()?; - StaticRegion::new(base, Self::bytes_len()?)?; - Ok(Self { - base, - slots, - mask: slots - 1, - _marker: PhantomData, - }) - } - - /// Returns the configured base address. - pub const fn base(&self) -> usize { - self.base - } - - /// Returns the total byte length occupied by this map. - pub fn bytes(&self) -> Result { - Self::bytes_len() - } - - /// Returns the visible value for `(left, right)`. - pub fn get_actor_pair_u256( - &self, - left: &ActorId, - right: &ActorId, - ) -> Result, TableError> { - let left = actor_key(*left); - let right = actor_key(*right); - if is_zero_32(&left) || is_zero_32(&right) { - return Ok(None); - } - - let StaticLookup::Found(index) = self.lookup(&left, &right) else { - return Ok(None); - }; - Ok(self.visible_value(index).map(u256_from_value)) - } - - /// Inserts or updates `(left, right)`, returning the previous visible value. - /// - /// A zero value removes the visible value. Zero actor ids are rejected - /// because zero key bytes mark empty slots in this layout. - pub fn insert_actor_pair_u256( - &self, - left: ActorId, - right: ActorId, - value: U256, - ) -> Result, TableError> { - let left = actor_key(left); - let right = actor_key(right); - reject_zero_key(&left)?; - reject_zero_key(&right)?; - if value.is_zero() { - return self - .remove_key(&left, &right) - .map(|previous| previous.map(u256_from_value)); - } - - let value = u256_value(value); - match self.lookup(&left, &right) { - StaticLookup::Found(index) => { - let previous = self.visible_value(index); - unsafe { - self.write_value(index, &value); - } - Ok(previous.map(u256_from_value)) - } - StaticLookup::Vacant(index) => { - unsafe { - self.write_left(index, &left); - self.write_right(index, &right); - self.write_value(index, &value); - } - Ok(None) - } - StaticLookup::Full => Err(TableError::CapacityOverflow), - } - } - - /// Removes the visible value for `(left, right)`. - pub fn remove_actor_pair_u256( - &self, - left: &ActorId, - right: &ActorId, - ) -> Result, TableError> { - let left = actor_key(*left); - let right = actor_key(*right); - reject_zero_key(&left)?; - reject_zero_key(&right)?; - self.remove_key(&left, &right) - .map(|previous| previous.map(u256_from_value)) - } - - /// Clears every slot to the empty state. - pub fn clear(&self) -> Result<(), TableError> { - unsafe { - ptr::write_bytes(self.base as *mut u8, 0, self.bytes()?); - } - Ok(()) - } - - fn remove_key( - &self, - left: &[u8; 32], - right: &[u8; 32], - ) -> Result, TableError> { - let StaticLookup::Found(index) = self.lookup(left, right) else { - return Ok(None); - }; - - let previous = self.visible_value(index); - if previous.is_some() { - unsafe { - self.write_value(index, &[0; 32]); - } - } - Ok(previous) - } - - fn lookup(&self, left: &[u8; 32], right: &[u8; 32]) -> StaticLookup { - let left_words = words_32(left); - let right_words = words_32(right); - let mut index = static_map_index(hash_actor_pair_key(left, right), LOG2_SLOTS); - - for _ in 0..self.slots { - let stored_left = unsafe { read_words_32(self.left_ptr(index)) }; - if words_are_zero(stored_left) { - return StaticLookup::Vacant(index); - } - if stored_left == left_words - && unsafe { read_words_32(self.right_ptr(index)) } == right_words - { - return StaticLookup::Found(index); - } - - index = (index + 1) & self.mask; - } - - StaticLookup::Full - } - - fn visible_value(&self, slot: usize) -> Option<[u8; 32]> { - if unsafe { words_at_zero(self.value_ptr(slot)) } { - None - } else { - Some(unsafe { self.read_value(slot) }) - } - } - - unsafe fn read_value(&self, slot: usize) -> [u8; 32] { - let mut bytes = [0u8; 32]; - unsafe { - ptr::copy_nonoverlapping(self.value_ptr(slot), bytes.as_mut_ptr(), 32); - } - bytes - } - - unsafe fn write_left(&self, slot: usize, left: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(left.as_ptr(), self.left_ptr(slot), 32); - } - } - - unsafe fn write_right(&self, slot: usize, right: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(right.as_ptr(), self.right_ptr(slot), 32); - } - } - - unsafe fn write_value(&self, slot: usize, value: &[u8; 32]) { - unsafe { - ptr::copy_nonoverlapping(value.as_ptr(), self.value_ptr(slot), 32); - } - } - - fn left_ptr(&self, slot: usize) -> *mut u8 { - self.slot_ptr(slot) - } - - fn right_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(32) } - } - - fn value_ptr(&self, slot: usize) -> *mut u8 { - unsafe { self.slot_ptr(slot).add(64) } - } - - fn slot_ptr(&self, slot: usize) -> *mut u8 { - (self.base + slot * Self::slot_size()) as *mut u8 - } -} - #[cfg(test)] mod tests { extern crate std; use super::*; - use gprimitives::{ActorId, U256}; use std::{vec, vec::Vec}; type Fixed = FixedOpenAddressMap<1, 1, 2>; @@ -1188,7 +609,9 @@ mod tests { fn colliding_keys(slots: usize) -> ([u8; 1], [u8; 1]) { for left in 0u8..=u8::MAX { for right in left.wrapping_add(1)..=u8::MAX { - if hash_bytes(&[left]) % slots == hash_bytes(&[right]) % slots { + if start_index(slots, hash_bytes(&[left])) + == start_index(slots, hash_bytes(&[right])) + { return ([left], [right]); } } @@ -1374,79 +797,9 @@ mod tests { } #[test] - fn actor_map_preserves_static_layout_and_tombstones_zero_values() { - assert_eq!(StaticActorIdU256Map::<2>::slot_size(), 64); - assert_eq!(StaticActorIdU256Map::<2>::slots(), Ok(4)); - assert_eq!(StaticActorIdU256Map::<2>::mask(), Ok(3)); - assert_eq!(StaticActorIdU256Map::<2>::bytes_len(), Ok(256)); - - let mut memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; - let map = unsafe { StaticActorIdU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - let actor = ActorId::from(1u64); - - assert_eq!(map.get_actor_u256(&actor), Ok(None)); - assert_eq!(map.insert_actor_u256(actor, U256::from(10)), Ok(None)); - assert_eq!(map.get_actor_u256(&actor), Ok(Some(U256::from(10)))); - assert_eq!( - map.insert_actor_u256(actor, U256::zero()), - Ok(Some(U256::from(10))) - ); - assert_eq!(map.get_actor_u256(&actor), Ok(None)); - } - - #[test] - fn actor_map_rejects_zero_actor_mutation() { - let mut memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; - let map = unsafe { StaticActorIdU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - - assert_eq!( - map.insert_actor_u256(ActorId::zero(), U256::from(1)), - Err(TableError::InvalidKey) - ); - assert_eq!( - map.remove_actor_u256(&ActorId::zero()), - Err(TableError::InvalidKey) - ); - } - - #[test] - fn actor_pair_map_preserves_static_layout_and_basic_ops() { - assert_eq!(StaticActorPairU256Map::<2>::slot_size(), 96); - assert_eq!(StaticActorPairU256Map::<2>::slots(), Ok(4)); - assert_eq!(StaticActorPairU256Map::<2>::mask(), Ok(3)); - assert_eq!(StaticActorPairU256Map::<2>::bytes_len(), Ok(384)); - - let mut memory = vec![0u8; StaticActorPairU256Map::<2>::bytes_len().unwrap()]; - let map = - unsafe { StaticActorPairU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - let left = ActorId::from(1u64); - let right = ActorId::from(2u64); - - assert_eq!(map.get_actor_pair_u256(&left, &right), Ok(None)); - assert_eq!( - map.insert_actor_pair_u256(left, right, U256::from(20)), - Ok(None) - ); - assert_eq!( - map.get_actor_pair_u256(&left, &right), - Ok(Some(U256::from(20))) - ); - assert_eq!( - map.remove_actor_pair_u256(&left, &right), - Ok(Some(U256::from(20))) - ); - assert_eq!(map.get_actor_pair_u256(&left, &right), Ok(None)); - } - - #[test] - fn actor_pair_map_rejects_zero_actor_mutation() { - let mut memory = vec![0u8; StaticActorPairU256Map::<2>::bytes_len().unwrap()]; - let map = - unsafe { StaticActorPairU256Map::<2>::new(memory.as_mut_ptr() as usize).unwrap() }; - - assert_eq!( - map.insert_actor_pair_u256(ActorId::zero(), ActorId::from(1u64), U256::from(1)), - Err(TableError::InvalidKey) - ); + fn hash_bytes_is_arch_stable() { + assert_eq!(hash_bytes(&[]), 0x811c_9dc5); + assert_eq!(hash_bytes(&[0]), 0x050c_5d1f); + assert_eq!(hash_bytes(&[1, 2, 3, 4]), 0x5734_a87d); } } diff --git a/rs/storage/tests/public_api.rs b/rs/storage/tests/public_api.rs index 3d04e9e73..b2c2098e6 100644 --- a/rs/storage/tests/public_api.rs +++ b/rs/storage/tests/public_api.rs @@ -1,7 +1,5 @@ -use gprimitives::{ActorId, U256}; use sails_storage::{ - ACTOR_ID_U256_SLOT_SIZE, ACTOR_PAIR_U256_SLOT_SIZE, FixedOpenAddressMap, StaticActorIdU256Map, - StaticActorPairU256Map, StaticLayout, StaticOpenAddressTable, TableError, + FixedOpenAddressMap, StaticLayout, StaticOpenAddressTable, StaticRegion, TableError, }; #[test] @@ -24,68 +22,14 @@ fn root_fixed_and_static_table_api_is_usable_by_consumers() { } #[test] -fn root_actor_static_maps_preserve_layout_and_basic_operations() { - assert_eq!( - StaticActorIdU256Map::<2>::slot_size(), - ACTOR_ID_U256_SLOT_SIZE - ); - assert_eq!( - StaticActorPairU256Map::<2>::slot_size(), - ACTOR_PAIR_U256_SLOT_SIZE - ); - assert_eq!(StaticActorIdU256Map::<2>::slots(), Ok(4)); - assert_eq!(StaticActorPairU256Map::<2>::slots(), Ok(4)); - - let mut actor_memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; - let actors = - unsafe { StaticActorIdU256Map::<2>::new(actor_memory.as_mut_ptr() as usize).unwrap() }; - let account = ActorId::from(1u64); - - assert_eq!(actors.get_actor_u256(&account), Ok(None)); - assert_eq!(actors.insert_actor_u256(account, U256::from(10)), Ok(None)); - assert_eq!(actors.get_actor_u256(&account), Ok(Some(U256::from(10)))); - assert_eq!( - actors.insert_actor_u256(account, U256::zero()), - Ok(Some(U256::from(10))) - ); - assert_eq!(actors.get_actor_u256(&account), Ok(None)); - - let mut pair_memory = vec![0u8; StaticActorPairU256Map::<2>::bytes_len().unwrap()]; - let pairs = - unsafe { StaticActorPairU256Map::<2>::new(pair_memory.as_mut_ptr() as usize).unwrap() }; - let left = ActorId::from(2u64); - let right = ActorId::from(3u64); - - assert_eq!(pairs.get_actor_pair_u256(&left, &right), Ok(None)); - assert_eq!( - pairs.insert_actor_pair_u256(left, right, U256::from(20)), - Ok(None) - ); - assert_eq!( - pairs.get_actor_pair_u256(&left, &right), - Ok(Some(U256::from(20))) - ); - assert_eq!( - pairs.remove_actor_pair_u256(&left, &right), - Ok(Some(U256::from(20))) - ); -} - -#[test] -fn root_actor_maps_reject_zero_actor_mutation() { - let mut actor_memory = vec![0u8; StaticActorIdU256Map::<2>::bytes_len().unwrap()]; - let actors = - unsafe { StaticActorIdU256Map::<2>::new(actor_memory.as_mut_ptr() as usize).unwrap() }; - assert_eq!( - actors.insert_actor_u256(ActorId::zero(), U256::from(1)), - Err(TableError::InvalidKey) - ); +fn root_static_region_api_is_usable_by_consumers() { + let region = StaticRegion::new(10, 4).unwrap(); - let mut pair_memory = vec![0u8; StaticActorPairU256Map::<2>::bytes_len().unwrap()]; - let pairs = - unsafe { StaticActorPairU256Map::<2>::new(pair_memory.as_mut_ptr() as usize).unwrap() }; + assert_eq!(region.base(), 10); + assert_eq!(region.bytes(), 4); + assert_eq!(region.end(), Ok(14)); assert_eq!( - pairs.insert_actor_pair_u256(ActorId::zero(), ActorId::from(1u64), U256::from(1)), - Err(TableError::InvalidKey) + StaticRegion::new(usize::MAX, 1), + Err(TableError::InvalidLayout) ); }