diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ecc27d016a811..ae25a2c5953f5 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -229,19 +229,21 @@ variables: # this job runs only on nightly pipeline with the mentioned variable, against `master` branch - if: $CI_COMMIT_REF_NAME == "master" && $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "nightly" -.crate-publishing-pipeline: - rules: - - if: $CI_COMMIT_REF_NAME != "master" - when: never +.crates-publishing-variables: + variables: + CRATESIO_CRATES_OWNER: parity-crate-owner + REPO: substrate + REPO_OWNER: paritytech -.scheduled-crate-publishing-pipeline: +.crates-publishing-pipeline: + extends: .crates-publishing-variables rules: - - !reference [.crate-publishing-pipeline, rules] - - if: $CI_PIPELINE_SOURCE == "schedule" && $PIPELINE == "automatic-crate-publishing" + - if: $CI_PIPELINE_SOURCE == "schedule" && $CI_COMMIT_REF_NAME == "master" && $PIPELINE == "automatic-crate-publishing" .crates-publishing-template: - stage: test - extends: .docker-env + extends: + - .docker-env + - .crates-publishing-variables # collect artifacts even on failure so that we know how the crates were generated (they'll be # generated to the artifacts folder according to SPUB_TMP further down) artifacts: @@ -251,12 +253,7 @@ variables: paths: - artifacts/ variables: - CRATESIO_API: https://crates.io/api - CRATESIO_CRATES_OWNER: parity-crate-owner - GH_API: https://api.github.com - REPO: substrate - REPO_OWNER: paritytech - SPUB_TMP: artifacts + SPUB_TMP: artifacts #### stage: .pre @@ -274,6 +271,18 @@ skip-if-draft: - ./scripts/ci/gitlab/skip_if_draft.sh allow_failure: true +check-crates-publishing-pipeline: + stage: .pre + extends: + - .kubernetes-env + - .crates-publishing-pipeline + script: + - git clone + --depth 1 + --branch "$RELENG_SCRIPTS_BRANCH" + https://github.com/paritytech/releng-scripts.git + - ONLY_CHECK_PIPELINE=true ./releng-scripts/publish-crates + include: # check jobs - scripts/ci/gitlab/pipeline/check.yml diff --git a/Cargo.lock b/Cargo.lock index 68c144b3f177e..08948ffbd970b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -137,6 +137,18 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf6ccdb167abbf410dcb915cabd428929d7f6a04980b54a11f26a39f1c7f7107" +dependencies = [ + "cfg-if", + "getrandom 0.2.8", + "once_cell", + "version_check", +] + [[package]] name = "aho-corasick" version = "0.7.20" @@ -1602,6 +1614,17 @@ dependencies = [ "rusticata-macros", ] +[[package]] +name = "derive-syn-parse" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79116f119dd1dba1abf1f3405f03b9b0e79a27a3883864bfebded8a3dc768cd" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "derive_builder" version = "0.11.2" @@ -1836,7 +1859,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c24f403d068ad0b359e577a77f92392118be3f3c927538f2bb544a5ecd828c6" dependencies = [ "curve25519-dalek 3.2.0", - "hashbrown", + "hashbrown 0.12.3", "hex", "rand_core 0.6.4", "sha2 0.9.9", @@ -2314,6 +2337,7 @@ dependencies = [ "sp-std", "sp-tracing", "sp-weights", + "static_assertions", "tt-call", ] @@ -2323,6 +2347,7 @@ version = "4.0.0-dev" dependencies = [ "Inflector", "cfg-expr", + "derive-syn-parse", "frame-support-procedural-tools", "itertools", "proc-macro2", @@ -2354,6 +2379,7 @@ dependencies = [ name = "frame-support-test" version = "3.0.0" dependencies = [ + "frame-benchmarking", "frame-support", "frame-support-test-pallet", "frame-system", @@ -2806,9 +2832,15 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.6", ] +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + [[package]] name = "heck" version = "0.4.0" @@ -3122,7 +3154,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1885e79c1fc4b10f0e172c475f458b7f7b93061064d98c3293e98c5ba0c8b399" dependencies = [ "autocfg", - "hashbrown", + "hashbrown 0.12.3", "serde", ] @@ -4172,7 +4204,7 @@ version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6e8aaa3f231bb4bd57b84b2d5dc3ae7f350265df8aa96492e0bc394a1571909" dependencies = [ - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -4310,7 +4342,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e0c7cba9ce19ac7ffd2053ac9f49843bbd3f4318feedfd74e85c19d5fb0ba66" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.12.3", ] [[package]] @@ -5085,7 +5117,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21158b2c33aa6d4561f1c0a6ea283ca92bc54802a93b263e910746d679a7eb53" dependencies = [ "crc32fast", - "hashbrown", + "hashbrown 0.12.3", "indexmap", "memchr", ] @@ -6079,6 +6111,7 @@ dependencies = [ "frame-election-provider-support", "frame-support", "frame-system", + "log", "pallet-babe", "pallet-balances", "pallet-grandpa", @@ -6660,9 +6693,9 @@ dependencies = [ [[package]] name = "parity-scale-codec" -version = "3.2.1" +version = "3.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366e44391a8af4cfd6002ef6ba072bae071a96aafca98d7d448a34c5dca38b6a" +checksum = "e7ab01d0f889e957861bc65888d5ccbe82c158d0270136ba46820d43837cdf72" dependencies = [ "arrayvec 0.7.2", "bitvec", @@ -6675,9 +6708,9 @@ dependencies = [ [[package]] name = "parity-scale-codec-derive" -version = "3.1.3" +version = "3.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9299338969a3d2f491d65f140b00ddec470858402f888af98e8642fb5e8965cd" +checksum = "86b26a931f824dd4eca30b3e43bb4f31cd5f0d3a403c5f5ff27106b805bfde7b" dependencies = [ "proc-macro-crate", "proc-macro2", @@ -7109,9 +7142,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.49" +version = "1.0.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57a8eca9f9c4ffde41714334dee777596264c7825420f521abc92b5b5deb63a5" +checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2" dependencies = [ "unicode-ident", ] @@ -7990,6 +8023,7 @@ dependencies = [ "rand 0.8.5", "sc-client-api", "sc-state-db", + "schnellru", "sp-arithmetic", "sp-blockchain", "sp-core", @@ -8250,6 +8284,7 @@ name = "sc-executor" version = "0.10.0-dev" dependencies = [ "array-bytes", + "assert_matches", "criterion", "env_logger", "lru", @@ -8333,7 +8368,7 @@ dependencies = [ name = "sc-finality-grandpa" version = "0.10.0-dev" dependencies = [ - "ahash", + "ahash 0.7.6", "array-bytes", "assert_matches", "async-trait", @@ -8533,7 +8568,7 @@ dependencies = [ name = "sc-network-gossip" version = "0.10.0-dev" dependencies = [ - "ahash", + "ahash 0.7.6", "futures", "futures-timer", "libp2p", @@ -8822,6 +8857,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", + "sp-runtime-interface", "sp-std", "substrate-wasm-builder", ] @@ -9045,6 +9081,7 @@ dependencies = [ "futures-timer", "linked-hash-map", "log", + "num-traits", "parity-scale-codec", "parking_lot 0.12.1", "sc-block-builder", @@ -9130,6 +9167,17 @@ dependencies = [ "windows-sys 0.36.1", ] +[[package]] +name = "schnellru" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772575a524feeb803e5b0fcbc6dd9f367e579488197c94c6e4023aad2305774d" +dependencies = [ + "ahash 0.8.2", + "cfg-if", + "hashbrown 0.13.2", +] + [[package]] name = "schnorrkel" version = "0.9.1" @@ -10273,11 +10321,11 @@ dependencies = [ name = "sp-trie" version = "7.0.0" dependencies = [ - "ahash", + "ahash 0.7.6", "array-bytes", "criterion", "hash-db", - "hashbrown", + "hashbrown 0.12.3", "lazy_static", "lru", "memory-db", @@ -11277,7 +11325,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "004e1e8f92535694b4cb1444dc5a8073ecf0815e3357f729638b9f8fc4062908" dependencies = [ "hash-db", - "hashbrown", + "hashbrown 0.12.3", "log", "rustc-hex", "smallvec", diff --git a/HEADER-APACHE2 b/HEADER-APACHE2 index 58baa53894ca7..403497a86907f 100644 --- a/HEADER-APACHE2 +++ b/HEADER-APACHE2 @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); diff --git a/HEADER-GPL3 b/HEADER-GPL3 index 9412b5a70bb35..ed7daba723272 100644 --- a/HEADER-GPL3 +++ b/HEADER-GPL3 @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 // This program is free software: you can redistribute it and/or modify diff --git a/bin/node-template/pallets/template/Cargo.toml b/bin/node-template/pallets/template/Cargo.toml index 7c04838cae319..6dec5c8da6e5d 100644 --- a/bin/node-template/pallets/template/Cargo.toml +++ b/bin/node-template/pallets/template/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/substrate-developer-hub/substrate-node-template targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/bin/node-template/runtime/Cargo.toml b/bin/node-template/runtime/Cargo.toml index 5dc7b03d39a81..5560eb6721377 100644 --- a/bin/node-template/runtime/Cargo.toml +++ b/bin/node-template/runtime/Cargo.toml @@ -13,7 +13,7 @@ repository = "https://github.com/substrate-developer-hub/substrate-node-template targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } pallet-aura = { version = "4.0.0-dev", default-features = false, path = "../../../frame/aura" } diff --git a/bin/node/cli/Cargo.toml b/bin/node/cli/Cargo.toml index 6b50115fd9a00..4d9279b857eed 100644 --- a/bin/node/cli/Cargo.toml +++ b/bin/node/cli/Cargo.toml @@ -37,7 +37,7 @@ crate-type = ["cdylib", "rlib"] # third-party dependencies array-bytes = "4.1" clap = { version = "4.0.9", features = ["derive"], optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } serde = { version = "1.0.136", features = ["derive"] } jsonrpsee = { version = "0.16.2", features = ["server"] } futures = "0.3.21" diff --git a/bin/node/cli/src/service.rs b/bin/node/cli/src/service.rs index cdee61af3f500..d77a333dfa220 100644 --- a/bin/node/cli/src/service.rs +++ b/bin/node/cli/src/service.rs @@ -21,6 +21,7 @@ //! Service implementation. Specialized wrapper over substrate service. use codec::Encode; +use frame_benchmarking_cli::SUBSTRATE_REFERENCE_HARDWARE; use frame_system_rpc_runtime_api::AccountNonceApi; use futures::prelude::*; use kitchensink_runtime::RuntimeApi; @@ -317,14 +318,12 @@ pub fn new_full_base( &sc_consensus_babe::BabeLink, ), ) -> Result { - let hwbench = if !disable_hardware_benchmarks { - config.database.path().map(|database_path| { + let hwbench = (!disable_hardware_benchmarks) + .then_some(config.database.path().map(|database_path| { let _ = std::fs::create_dir_all(&database_path); sc_sysinfo::gather_hwbench(Some(database_path)) - }) - } else { - None - }; + })) + .flatten(); let sc_service::PartialComponents { client, @@ -398,6 +397,11 @@ pub fn new_full_base( if let Some(hwbench) = hwbench { sc_sysinfo::print_hwbench(&hwbench); + if !SUBSTRATE_REFERENCE_HARDWARE.check_hardware(&hwbench) && role.is_authority() { + log::warn!( + "⚠️ The hardware does not meet the minimal requirements for role 'Authority'." + ); + } if let Some(ref mut telemetry) = telemetry { let telemetry_handle = telemetry.handle(); diff --git a/bin/node/executor/Cargo.toml b/bin/node/executor/Cargo.toml index 8b3add78a9a26..edcc4df9c8176 100644 --- a/bin/node/executor/Cargo.toml +++ b/bin/node/executor/Cargo.toml @@ -13,7 +13,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } scale-info = { version = "2.1.1", features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", path = "../../../frame/benchmarking" } node-primitives = { version = "2.0.0", path = "../primitives" } diff --git a/bin/node/inspect/Cargo.toml b/bin/node/inspect/Cargo.toml index a7343b3ca827f..d6a8755179f3e 100644 --- a/bin/node/inspect/Cargo.toml +++ b/bin/node/inspect/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] clap = { version = "4.0.9", features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } thiserror = "1.0" sc-cli = { version = "0.10.0-dev", path = "../../../client/cli" } sc-client-api = { version = "4.0.0-dev", path = "../../../client/api" } diff --git a/bin/node/primitives/Cargo.toml b/bin/node/primitives/Cargo.toml index 1aa0a8f0e27a3..1f515f3e3a5b5 100644 --- a/bin/node/primitives/Cargo.toml +++ b/bin/node/primitives/Cargo.toml @@ -13,7 +13,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index 7037be9d7c54d..76fca730ef7d0 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # third-party dependencies -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index a9e93a16f0713..0ac6ade9127e8 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1206,7 +1206,7 @@ impl pallet_contracts::Config for Runtime { type CallFilter = Nothing; type DepositPerItem = DepositPerItem; type DepositPerByte = DepositPerByte; - type CallStack = [pallet_contracts::Frame; 31]; + type CallStack = [pallet_contracts::Frame; 5]; type WeightPrice = pallet_transaction_payment::Pallet; type WeightInfo = pallet_contracts::weights::SubstrateWeight; type ChainExtension = (); @@ -1214,7 +1214,7 @@ impl pallet_contracts::Config for Runtime { type DeletionWeightLimit = DeletionWeightLimit; type Schedule = Schedule; type AddressGenerator = pallet_contracts::DefaultAddressGenerator; - type MaxCodeLen = ConstU32<{ 128 * 1024 }>; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = ConstBool; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; @@ -1499,6 +1499,7 @@ parameter_types! { pub const ThawThrottle: (Perquintill, BlockNumber) = (Perquintill::from_percent(25), 5); pub Target: Perquintill = Perquintill::zero(); pub const NisPalletId: PalletId = PalletId(*b"py/nis "); + pub const NisReserveId: [u8; 8] = *b"py/nis "; } impl pallet_nis::Config for Runtime { @@ -1522,6 +1523,7 @@ impl pallet_nis::Config for Runtime { type IntakePeriod = IntakePeriod; type MaxIntakeWeight = MaxIntakeWeight; type ThawThrottle = ThawThrottle; + type ReserveId = NisReserveId; } parameter_types! { @@ -1821,13 +1823,9 @@ mod mmr { pub type Hashing = ::Hashing; } -#[cfg(feature = "runtime-benchmarks")] -#[macro_use] -extern crate frame_benchmarking; - #[cfg(feature = "runtime-benchmarks")] mod benches { - define_benchmarks!( + frame_benchmarking::define_benchmarks!( [frame_benchmarking, BaselineBench::] [pallet_alliance, Alliance] [pallet_assets, Assets] diff --git a/bin/node/testing/Cargo.toml b/bin/node/testing/Cargo.toml index 0154a778457fc..a43b2b9ba13e5 100644 --- a/bin/node/testing/Cargo.toml +++ b/bin/node/testing/Cargo.toml @@ -13,7 +13,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } fs_extra = "1" futures = "0.3.21" log = "0.4.17" diff --git a/client/api/Cargo.toml b/client/api/Cargo.toml index bd1f6f34b0eff..f494200852729 100644 --- a/client/api/Cargo.toml +++ b/client/api/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } fnv = "1.0.6" diff --git a/client/api/src/backend.rs b/client/api/src/backend.rs index 79cc0d7a16bcc..4ef609bdd4525 100644 --- a/client/api/src/backend.rs +++ b/client/api/src/backend.rs @@ -303,17 +303,17 @@ pub trait AuxStore { } /// An `Iterator` that iterates keys in a given block under a prefix. -pub struct KeyIterator<'a, State, Block> { +pub struct KeyIterator { state: State, child_storage: Option, - prefix: Option<&'a StorageKey>, + prefix: Option, current_key: Vec, _phantom: PhantomData, } -impl<'a, State, Block> KeyIterator<'a, State, Block> { +impl KeyIterator { /// create a KeyIterator instance - pub fn new(state: State, prefix: Option<&'a StorageKey>, current_key: Vec) -> Self { + pub fn new(state: State, prefix: Option, current_key: Vec) -> Self { Self { state, child_storage: None, prefix, current_key, _phantom: PhantomData } } @@ -321,14 +321,14 @@ impl<'a, State, Block> KeyIterator<'a, State, Block> { pub fn new_child( state: State, child_info: ChildInfo, - prefix: Option<&'a StorageKey>, + prefix: Option, current_key: Vec, ) -> Self { Self { state, child_storage: Some(child_info), prefix, current_key, _phantom: PhantomData } } } -impl<'a, State, Block> Iterator for KeyIterator<'a, State, Block> +impl Iterator for KeyIterator where Block: BlockT, State: StateBackend>, @@ -344,7 +344,7 @@ where .ok() .flatten()?; // this terminates the iterator the first time it fails. - if let Some(prefix) = self.prefix { + if let Some(ref prefix) = self.prefix { if !next_key.starts_with(&prefix.0[..]) { return None } @@ -387,12 +387,12 @@ pub trait StorageProvider> { /// Given a block's `Hash` and a key prefix, return a `KeyIterator` iterates matching storage /// keys in that block. - fn storage_keys_iter<'a>( + fn storage_keys_iter( &self, hash: Block::Hash, - prefix: Option<&'a StorageKey>, + prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result>; + ) -> sp_blockchain::Result>; /// Given a block's `Hash`, a key and a child storage key, return the value under the key in /// that block. @@ -414,13 +414,13 @@ pub trait StorageProvider> { /// Given a block's `Hash` and a key `prefix` and a child storage key, /// return a `KeyIterator` that iterates matching storage keys in that block. - fn child_storage_keys_iter<'a>( + fn child_storage_keys_iter( &self, hash: Block::Hash, child_info: ChildInfo, - prefix: Option<&'a StorageKey>, + prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result>; + ) -> sp_blockchain::Result>; /// Given a block's `Hash`, a key and a child storage key, return the hash under the key in that /// block. @@ -436,12 +436,24 @@ pub trait StorageProvider> { /// /// Manages the data layer. /// -/// Note on state pruning: while an object from `state_at` is alive, the state +/// # State Pruning +/// +/// While an object from `state_at` is alive, the state /// should not be pruned. The backend should internally reference-count /// its state objects. /// /// The same applies for live `BlockImportOperation`s: while an import operation building on a /// parent `P` is alive, the state for `P` should not be pruned. +/// +/// # Block Pruning +/// +/// Users can pin blocks in memory by calling `pin_block`. When +/// a block would be pruned, its value is kept in an in-memory cache +/// until it is unpinned via `unpin_block`. +/// +/// While a block is pinned, its state is also preserved. +/// +/// The backend should internally reference count the number of pin / unpin calls. pub trait Backend: AuxStore + Send + Sync { /// Associated block insertion operation type. type BlockImportOperation: BlockImportOperation; @@ -502,6 +514,14 @@ pub trait Backend: AuxStore + Send + Sync { /// Returns a handle to offchain storage. fn offchain_storage(&self) -> Option; + /// Pin the block to keep body, justification and state available after pruning. + /// Number of pins are reference counted. Users need to make sure to perform + /// one call to [`Self::unpin_block`] per call to [`Self::pin_block`]. + fn pin_block(&self, hash: Block::Hash) -> sp_blockchain::Result<()>; + + /// Unpin the block to allow pruning. + fn unpin_block(&self, hash: Block::Hash); + /// Returns true if state for given block is available. fn have_state_at(&self, hash: Block::Hash, _number: NumberFor) -> bool { self.state_at(hash).is_ok() diff --git a/client/api/src/client.rs b/client/api/src/client.rs index 0d00257fa7b06..8e7ceb68704b2 100644 --- a/client/api/src/client.rs +++ b/client/api/src/client.rs @@ -30,7 +30,7 @@ use std::{collections::HashSet, fmt, sync::Arc}; use crate::{blockchain::Info, notifications::StorageEventStream, FinalizeSummary, ImportSummary}; use sc_transaction_pool_api::ChainEvent; -use sc_utils::mpsc::TracingUnboundedReceiver; +use sc_utils::mpsc::{TracingUnboundedReceiver, TracingUnboundedSender}; use sp_blockchain; /// Type that implements `futures::Stream` of block import events. @@ -264,6 +264,53 @@ impl fmt::Display for UsageInfo { } } +/// Sends a message to the pinning-worker once dropped to unpin a block in the backend. +#[derive(Debug)] +pub struct UnpinHandleInner { + /// Hash of the block pinned by this handle + hash: Block::Hash, + unpin_worker_sender: TracingUnboundedSender, +} + +impl UnpinHandleInner { + /// Create a new [`UnpinHandleInner`] + pub fn new( + hash: Block::Hash, + unpin_worker_sender: TracingUnboundedSender, + ) -> Self { + Self { hash, unpin_worker_sender } + } +} + +impl Drop for UnpinHandleInner { + fn drop(&mut self) { + if let Err(err) = self.unpin_worker_sender.unbounded_send(self.hash) { + log::debug!(target: "db", "Unable to unpin block with hash: {}, error: {:?}", self.hash, err); + }; + } +} + +/// Keeps a specific block pinned while the handle is alive. +/// Once the last handle instance for a given block is dropped, the +/// block is unpinned in the [`Backend`](crate::backend::Backend::unpin_block). +#[derive(Debug, Clone)] +pub struct UnpinHandle(Arc>); + +impl UnpinHandle { + /// Create a new [`UnpinHandle`] + pub fn new( + hash: Block::Hash, + unpin_worker_sender: TracingUnboundedSender, + ) -> UnpinHandle { + UnpinHandle(Arc::new(UnpinHandleInner::new(hash, unpin_worker_sender))) + } + + /// Hash of the block this handle is unpinning on drop + pub fn hash(&self) -> Block::Hash { + self.0.hash + } +} + /// Summary of an imported block #[derive(Clone, Debug)] pub struct BlockImportNotification { @@ -279,6 +326,36 @@ pub struct BlockImportNotification { /// /// If `None`, there was no re-org while importing. pub tree_route: Option>>, + /// Handle to unpin the block this notification is for + unpin_handle: UnpinHandle, +} + +impl BlockImportNotification { + /// Create new notification + pub fn new( + hash: Block::Hash, + origin: BlockOrigin, + header: Block::Header, + is_new_best: bool, + tree_route: Option>>, + unpin_worker_sender: TracingUnboundedSender, + ) -> Self { + Self { + hash, + origin, + header, + is_new_best, + tree_route, + unpin_handle: UnpinHandle::new(hash, unpin_worker_sender), + } + } + + /// Consume this notification and extract the unpin handle. + /// + /// Note: Only use this if you want to keep the block pinned in the backend. + pub fn into_unpin_handle(self) -> UnpinHandle { + self.unpin_handle + } } /// Summary of a finalized block. @@ -294,6 +371,8 @@ pub struct FinalityNotification { pub tree_route: Arc<[Block::Hash]>, /// Stale branches heads. pub stale_heads: Arc<[Block::Hash]>, + /// Handle to unpin the block this notification is for + unpin_handle: UnpinHandle, } impl TryFrom> for ChainEvent { @@ -314,26 +393,44 @@ impl From> for ChainEvent { } } -impl From> for FinalityNotification { - fn from(mut summary: FinalizeSummary) -> Self { +impl FinalityNotification { + /// Create finality notification from finality summary. + pub fn from_summary( + mut summary: FinalizeSummary, + unpin_worker_sender: TracingUnboundedSender, + ) -> FinalityNotification { let hash = summary.finalized.pop().unwrap_or_default(); FinalityNotification { hash, header: summary.header, tree_route: Arc::from(summary.finalized), stale_heads: Arc::from(summary.stale_heads), + unpin_handle: UnpinHandle::new(hash, unpin_worker_sender), } } + + /// Consume this notification and extract the unpin handle. + /// + /// Note: Only use this if you want to keep the block pinned in the backend. + pub fn into_unpin_handle(self) -> UnpinHandle { + self.unpin_handle + } } -impl From> for BlockImportNotification { - fn from(summary: ImportSummary) -> Self { +impl BlockImportNotification { + /// Create finality notification from finality summary. + pub fn from_summary( + summary: ImportSummary, + unpin_worker_sender: TracingUnboundedSender, + ) -> BlockImportNotification { + let hash = summary.hash; BlockImportNotification { - hash: summary.hash, + hash, origin: summary.origin, header: summary.header, is_new_best: summary.is_new_best, tree_route: summary.tree_route.map(Arc::new), + unpin_handle: UnpinHandle::new(hash, unpin_worker_sender), } } } diff --git a/client/api/src/in_mem.rs b/client/api/src/in_mem.rs index 144aa352f5533..5e82757e7d9cd 100644 --- a/client/api/src/in_mem.rs +++ b/client/api/src/in_mem.rs @@ -788,6 +788,12 @@ where fn requires_full_sync(&self) -> bool { false } + + fn pin_block(&self, _: ::Hash) -> blockchain::Result<()> { + Ok(()) + } + + fn unpin_block(&self, _: ::Hash) {} } impl backend::LocalBackend for Backend where Block::Hash: Ord {} diff --git a/client/authority-discovery/Cargo.toml b/client/authority-discovery/Cargo.toml index a24002517cca8..b37507662bfeb 100644 --- a/client/authority-discovery/Cargo.toml +++ b/client/authority-discovery/Cargo.toml @@ -17,7 +17,7 @@ targets = ["x86_64-unknown-linux-gnu"] prost-build = "0.11" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } futures = "0.3.21" futures-timer = "3.0.1" ip_network = "0.4.1" diff --git a/client/basic-authorship/Cargo.toml b/client/basic-authorship/Cargo.toml index 09b5c47394491..c4f1d2e245f92 100644 --- a/client/basic-authorship/Cargo.toml +++ b/client/basic-authorship/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" futures-timer = "3.0.1" log = "0.4.17" diff --git a/client/beefy/Cargo.toml b/client/beefy/Cargo.toml index b039faa2d095d..d81ff2587cf2c 100644 --- a/client/beefy/Cargo.toml +++ b/client/beefy/Cargo.toml @@ -11,7 +11,7 @@ homepage = "https://substrate.io" [dependencies] array-bytes = "4.1" async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } fnv = "1.0.6" futures = "0.3" log = "0.4" diff --git a/client/beefy/rpc/Cargo.toml b/client/beefy/rpc/Cargo.toml index f853af8a81c74..ab3e6921f3cfe 100644 --- a/client/beefy/rpc/Cargo.toml +++ b/client/beefy/rpc/Cargo.toml @@ -9,7 +9,7 @@ description = "RPC for the BEEFY Client gadget for substrate" homepage = "https://substrate.io" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } log = "0.4" diff --git a/client/beefy/src/keystore.rs b/client/beefy/src/keystore.rs index 886c00fc5d817..8d77410746e52 100644 --- a/client/beefy/src/keystore.rs +++ b/client/beefy/src/keystore.rs @@ -25,7 +25,7 @@ use log::warn; use beefy_primitives::{ crypto::{Public, Signature}, - BeefyVerify, KEY_TYPE, + BeefyAuthorityId, KEY_TYPE, }; use crate::error; @@ -99,7 +99,7 @@ impl BeefyKeystore { /// /// Return `true` if the signature is authentic, `false` otherwise. pub fn verify(public: &Public, sig: &Signature, message: &[u8]) -> bool { - BeefyVerify::::verify(sig, message, public) + BeefyAuthorityId::::verify(public, sig, message) } } diff --git a/client/block-builder/Cargo.toml b/client/block-builder/Cargo.toml index 2516374864bc1..3b4bea9818c9e 100644 --- a/client/block-builder/Cargo.toml +++ b/client/block-builder/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } sc-client-api = { version = "4.0.0-dev", path = "../api" } diff --git a/client/cli/Cargo.toml b/client/cli/Cargo.toml index 5e1f2bab4be9f..c0da53ad129d5 100644 --- a/client/cli/Cargo.toml +++ b/client/cli/Cargo.toml @@ -21,7 +21,7 @@ futures = "0.3.21" libp2p = "0.50.0" log = "0.4.17" names = { version = "0.13.0", default-features = false } -parity-scale-codec = "3.0.0" +parity-scale-codec = "3.2.2" rand = "0.8.5" regex = "1.6.0" rpassword = "7.0.0" diff --git a/client/cli/src/params/network_params.rs b/client/cli/src/params/network_params.rs index 5580dea45bde6..984789da752d1 100644 --- a/client/cli/src/params/network_params.rs +++ b/client/cli/src/params/network_params.rs @@ -68,18 +68,18 @@ pub struct NetworkParams { #[arg(long, value_name = "PORT", conflicts_with_all = &[ "listen_addr" ])] pub port: Option, - /// Always forbid connecting to private IPv4 addresses (as specified in + /// Always forbid connecting to private IPv4/IPv6 addresses (as specified in /// [RFC1918](https://tools.ietf.org/html/rfc1918)), unless the address was passed with /// `--reserved-nodes` or `--bootnodes`. Enabled by default for chains marked as "live" in /// their chain specifications. - #[arg(long, conflicts_with_all = &["allow_private_ipv4"])] - pub no_private_ipv4: bool, + #[arg(long, alias = "no-private-ipv4", conflicts_with_all = &["allow_private_ip"])] + pub no_private_ip: bool, - /// Always accept connecting to private IPv4 addresses (as specified in + /// Always accept connecting to private IPv4/IPv6 addresses (as specified in /// [RFC1918](https://tools.ietf.org/html/rfc1918)). Enabled by default for chains marked as /// "local" in their chain specifications, or when `--dev` is passed. - #[arg(long, conflicts_with_all = &["no_private_ipv4"])] - pub allow_private_ipv4: bool, + #[arg(long, alias = "allow-private-ipv4", conflicts_with_all = &["no_private_ip"])] + pub allow_private_ip: bool, /// Specify the number of outgoing connections we're trying to maintain. #[arg(long, value_name = "COUNT", default_value_t = 15)] @@ -200,8 +200,8 @@ impl NetworkParams { self.discover_local || is_dev || matches!(chain_type, ChainType::Local | ChainType::Development); - let allow_private_ipv4 = match (self.allow_private_ipv4, self.no_private_ipv4) { - (true, true) => unreachable!("`*_private_ipv4` flags are mutually exclusive; qed"), + let allow_private_ip = match (self.allow_private_ip, self.no_private_ip) { + (true, true) => unreachable!("`*_private_ip` flags are mutually exclusive; qed"), (true, false) => true, (false, true) => false, (false, false) => @@ -231,7 +231,7 @@ impl NetworkParams { client_version: client_id.to_string(), transport: TransportConfig::Normal { enable_mdns: !is_dev && !self.no_mdns, - allow_private_ipv4, + allow_private_ip, }, max_parallel_downloads: self.max_parallel_downloads, enable_dht_random_walk: !self.reserved_only, diff --git a/client/consensus/aura/Cargo.toml b/client/consensus/aura/Cargo.toml index 47aee0ec084eb..4c0305e9f66e7 100644 --- a/client/consensus/aura/Cargo.toml +++ b/client/consensus/aura/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" log = "0.4.17" thiserror = "1.0" diff --git a/client/consensus/babe/Cargo.toml b/client/consensus/babe/Cargo.toml index 312cc25dd0b59..f0ff0080fd610 100644 --- a/client/consensus/babe/Cargo.toml +++ b/client/consensus/babe/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" log = "0.4.17" merlin = "2.0" diff --git a/client/consensus/babe/src/tests.rs b/client/consensus/babe/src/tests.rs index be5f076c742d4..0dd0b59dd69c0 100644 --- a/client/consensus/babe/src/tests.rs +++ b/client/consensus/babe/src/tests.rs @@ -432,6 +432,7 @@ async fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + ' .for_each(|_| future::ready(())), ); + let client_clone = client.clone(); babe_futures.push( start_babe(BabeParams { block_import: data.block_import.lock().take().expect("import set up during init"), @@ -439,12 +440,19 @@ async fn run_one_test(mutator: impl Fn(&mut TestHeader, Stage) + Send + Sync + ' client, env: environ, sync_oracle: DummyOracle, - create_inherent_data_providers: Box::new(|_, _| async { - let slot = InherentDataProvider::from_timestamp_and_slot_duration( - Timestamp::current(), - SlotDuration::from_millis(SLOT_DURATION_MS), + create_inherent_data_providers: Box::new(move |parent, _| { + // Get the slot of the parent header and just increase this slot. + // + // Below we will running everything in one big future. If we would use + // time based slot, it can happen that on babe instance imports a block from + // another babe instance and then tries to build a block in the same slot making + // this test fail. + let parent_header = client_clone.header(parent).ok().flatten().unwrap(); + let slot = Slot::from( + find_pre_digest::(&parent_header).unwrap().slot() + 1, ); - Ok((slot,)) + + async move { Ok((InherentDataProvider::new(slot),)) } }), force_authoring: false, backoff_authoring_blocks: Some(BackoffAuthoringOnFinalizedHeadLagging::default()), diff --git a/client/consensus/epochs/Cargo.toml b/client/consensus/epochs/Cargo.toml index c88b5c52ba18e..89588cc7d4c5c 100644 --- a/client/consensus/epochs/Cargo.toml +++ b/client/consensus/epochs/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } fork-tree = { version = "3.0.0", path = "../../../utils/fork-tree" } sc-client-api = { version = "4.0.0-dev", path = "../../api" } sc-consensus = { version = "0.10.0-dev", path = "../common" } diff --git a/client/consensus/manual-seal/Cargo.toml b/client/consensus/manual-seal/Cargo.toml index fb89445a97002..19c4b22247e0f 100644 --- a/client/consensus/manual-seal/Cargo.toml +++ b/client/consensus/manual-seal/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } assert_matches = "1.3.0" async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" log = "0.4.17" serde = { version = "1.0", features = ["derive"] } diff --git a/client/consensus/pow/Cargo.toml b/client/consensus/pow/Cargo.toml index 480d9b23b06a3..b5454e35f994e 100644 --- a/client/consensus/pow/Cargo.toml +++ b/client/consensus/pow/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" futures-timer = "3.0.1" log = "0.4.17" diff --git a/client/consensus/slots/Cargo.toml b/client/consensus/slots/Cargo.toml index 6acd656d21a06..5cacf4f476281 100644 --- a/client/consensus/slots/Cargo.toml +++ b/client/consensus/slots/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" futures-timer = "3.0.1" log = "0.4.17" diff --git a/client/consensus/slots/src/lib.rs b/client/consensus/slots/src/lib.rs index 6126647e6190d..30d2c1761c712 100644 --- a/client/consensus/slots/src/lib.rs +++ b/client/consensus/slots/src/lib.rs @@ -524,13 +524,7 @@ pub async fn start_slot_worker( let mut slots = Slots::new(slot_duration.as_duration(), create_inherent_data_providers, client); loop { - let slot_info = match slots.next_slot().await { - Ok(r) => r, - Err(e) => { - warn!(target: LOG_TARGET, "Error while polling for next slot: {}", e); - return - }, - }; + let slot_info = slots.next_slot().await; if sync_oracle.is_major_syncing() { debug!(target: LOG_TARGET, "Skipping proposal slot due to sync."); diff --git a/client/consensus/slots/src/slots.rs b/client/consensus/slots/src/slots.rs index 9bb5650b313d5..7bb263b2bdba7 100644 --- a/client/consensus/slots/src/slots.rs +++ b/client/consensus/slots/src/slots.rs @@ -21,7 +21,7 @@ //! This is used instead of `futures_timer::Interval` because it was unreliable. use super::{InherentDataProviderExt, Slot, LOG_TARGET}; -use sp_consensus::{Error, SelectChain}; +use sp_consensus::SelectChain; use sp_inherents::{CreateInherentDataProviders, InherentDataProvider}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; @@ -90,7 +90,7 @@ impl SlotInfo { pub(crate) struct Slots { last_slot: Slot, slot_duration: Duration, - inner_delay: Option, + until_next_slot: Option, create_inherent_data_providers: IDP, select_chain: SC, _phantom: std::marker::PhantomData, @@ -106,7 +106,7 @@ impl Slots { Slots { last_slot: 0.into(), slot_duration, - inner_delay: None, + until_next_slot: None, create_inherent_data_providers, select_chain, _phantom: Default::default(), @@ -122,26 +122,22 @@ where IDP::InherentDataProviders: crate::InherentDataProviderExt, { /// Returns a future that fires when the next slot starts. - pub async fn next_slot(&mut self) -> Result, Error> { + pub async fn next_slot(&mut self) -> SlotInfo { loop { - self.inner_delay = match self.inner_delay.take() { - None => { - // schedule wait. + // Wait for slot timeout + self.until_next_slot + .take() + .unwrap_or_else(|| { + // Schedule first timeout. let wait_dur = time_until_next_slot(self.slot_duration); - Some(Delay::new(wait_dur)) - }, - Some(d) => Some(d), - }; - - if let Some(inner_delay) = self.inner_delay.take() { - inner_delay.await; - } - // timeout has fired. + Delay::new(wait_dur) + }) + .await; - let ends_in = time_until_next_slot(self.slot_duration); + // Schedule delay for next slot. + let wait_dur = time_until_next_slot(self.slot_duration); + self.until_next_slot = Some(Delay::new(wait_dur)); - // reschedule delay for next slot. - self.inner_delay = Some(Delay::new(ends_in)); let chain_head = match self.select_chain.best_chain().await { Ok(x) => x, Err(e) => { @@ -150,30 +146,41 @@ where "Unable to author block in slot. No best block header: {}", e, ); - // Let's try at the next slot.. - self.inner_delay.take(); + // Let's retry at the next slot. continue }, }; - let inherent_data_providers = self + let inherent_data_providers = match self .create_inherent_data_providers .create_inherent_data_providers(chain_head.hash(), ()) - .await?; + .await + { + Ok(x) => x, + Err(e) => { + log::warn!( + target: LOG_TARGET, + "Unable to author block in slot. Failure creating inherent data provider: {}", + e, + ); + // Let's retry at the next slot. + continue + }, + }; let slot = inherent_data_providers.slot(); - // never yield the same slot twice. + // Never yield the same slot twice. if slot > self.last_slot { self.last_slot = slot; - break Ok(SlotInfo::new( + break SlotInfo::new( slot, Box::new(inherent_data_providers), self.slot_duration, chain_head, None, - )) + ) } } } diff --git a/client/db/Cargo.toml b/client/db/Cargo.toml index 562a94f1900d6..028af5d6e45c9 100644 --- a/client/db/Cargo.toml +++ b/client/db/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } hash-db = "0.15.2" @@ -26,6 +26,7 @@ parity-db = "0.4.2" parking_lot = "0.12.1" sc-client-api = { version = "4.0.0-dev", path = "../api" } sc-state-db = { version = "0.10.0-dev", path = "../state-db" } +schnellru = "0.2.1" sp-arithmetic = { version = "6.0.0", path = "../../primitives/arithmetic" } sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } sp-core = { version = "7.0.0", path = "../../primitives/core" } diff --git a/client/db/src/lib.rs b/client/db/src/lib.rs index 99fa786e04036..09ccfef1cc28b 100644 --- a/client/db/src/lib.rs +++ b/client/db/src/lib.rs @@ -34,6 +34,7 @@ pub mod bench; mod children; mod parity_db; +mod pinned_blocks_cache; mod record_stats_state; mod stats; #[cfg(any(feature = "rocksdb", test))] @@ -51,6 +52,7 @@ use std::{ }; use crate::{ + pinned_blocks_cache::PinnedBlocksCache, record_stats_state::RecordStatsState, stats::StateUsageStats, utils::{meta_keys, read_db, read_meta, DatabaseType, Meta}, @@ -481,6 +483,7 @@ pub struct BlockchainDb { leaves: RwLock>>, header_metadata_cache: Arc>, header_cache: Mutex>>, + pinned_blocks_cache: Arc>>, } impl BlockchainDb { @@ -493,6 +496,7 @@ impl BlockchainDb { meta: Arc::new(RwLock::new(meta)), header_metadata_cache: Arc::new(HeaderMetadataCache::default()), header_cache: Default::default(), + pinned_blocks_cache: Arc::new(RwLock::new(PinnedBlocksCache::new())), }) } @@ -521,62 +525,83 @@ impl BlockchainDb { let mut meta = self.meta.write(); meta.block_gap = gap; } -} -impl sc_client_api::blockchain::HeaderBackend for BlockchainDb { - fn header(&self, hash: Block::Hash) -> ClientResult> { - let mut cache = self.header_cache.lock(); - if let Some(result) = cache.get_refresh(&hash) { - return Ok(result.clone()) + /// Empty the cache of pinned items. + fn clear_pinning_cache(&self) { + self.pinned_blocks_cache.write().clear(); + } + + /// Load a justification into the cache of pinned items. + /// Reference count of the item will not be increased. Use this + /// to load values for items into the cache which have already been pinned. + fn insert_justifications_if_pinned(&self, hash: Block::Hash, justification: Justification) { + let mut cache = self.pinned_blocks_cache.write(); + if !cache.contains(hash) { + return } - let header = utils::read_header( - &*self.db, - columns::KEY_LOOKUP, - columns::HEADER, - BlockId::::Hash(hash), - )?; - cache_header(&mut cache, hash, header.clone()); - Ok(header) + + let justifications = Justifications::from(justification); + cache.insert_justifications(hash, Some(justifications)); } - fn info(&self) -> sc_client_api::blockchain::Info { - let meta = self.meta.read(); - sc_client_api::blockchain::Info { - best_hash: meta.best_hash, - best_number: meta.best_number, - genesis_hash: meta.genesis_hash, - finalized_hash: meta.finalized_hash, - finalized_number: meta.finalized_number, - finalized_state: meta.finalized_state, - number_leaves: self.leaves.read().count(), - block_gap: meta.block_gap, + /// Load a justification from the db into the cache of pinned items. + /// Reference count of the item will not be increased. Use this + /// to load values for items into the cache which have already been pinned. + fn insert_persisted_justifications_if_pinned(&self, hash: Block::Hash) -> ClientResult<()> { + let mut cache = self.pinned_blocks_cache.write(); + if !cache.contains(hash) { + return Ok(()) } + + let justifications = self.justifications_uncached(hash)?; + cache.insert_justifications(hash, justifications); + Ok(()) } - fn status(&self, hash: Block::Hash) -> ClientResult { - match self.header(hash)?.is_some() { - true => Ok(sc_client_api::blockchain::BlockStatus::InChain), - false => Ok(sc_client_api::blockchain::BlockStatus::Unknown), + /// Load a block body from the db into the cache of pinned items. + /// Reference count of the item will not be increased. Use this + /// to load values for items items into the cache which have already been pinned. + fn insert_persisted_body_if_pinned(&self, hash: Block::Hash) -> ClientResult<()> { + let mut cache = self.pinned_blocks_cache.write(); + if !cache.contains(hash) { + return Ok(()) } + + let body = self.body_uncached(hash)?; + cache.insert_body(hash, body); + Ok(()) } - fn number(&self, hash: Block::Hash) -> ClientResult>> { - Ok(self.header_metadata(hash).ok().map(|header_metadata| header_metadata.number)) + /// Bump reference count for pinned item. + fn bump_ref(&self, hash: Block::Hash) { + self.pinned_blocks_cache.write().pin(hash); } - fn hash(&self, number: NumberFor) -> ClientResult> { - Ok(utils::read_header::( + /// Decrease reference count for pinned item and remove if reference count is 0. + fn unpin(&self, hash: Block::Hash) { + self.pinned_blocks_cache.write().unpin(hash); + } + + fn justifications_uncached(&self, hash: Block::Hash) -> ClientResult> { + match read_db( &*self.db, columns::KEY_LOOKUP, - columns::HEADER, - BlockId::Number(number), - )? - .map(|header| header.hash())) + columns::JUSTIFICATIONS, + BlockId::::Hash(hash), + )? { + Some(justifications) => match Decode::decode(&mut &justifications[..]) { + Ok(justifications) => Ok(Some(justifications)), + Err(err) => + return Err(sp_blockchain::Error::Backend(format!( + "Error decoding justifications: {}", + err + ))), + }, + None => Ok(None), + } } -} -impl sc_client_api::blockchain::Backend for BlockchainDb { - fn body(&self, hash: Block::Hash) -> ClientResult>> { + fn body_uncached(&self, hash: Block::Hash) -> ClientResult>> { if let Some(body) = read_db(&*self.db, columns::KEY_LOOKUP, columns::BODY, BlockId::Hash::(hash))? { @@ -640,24 +665,77 @@ impl sc_client_api::blockchain::Backend for BlockchainDb ClientResult> { - match read_db( +impl sc_client_api::blockchain::HeaderBackend for BlockchainDb { + fn header(&self, hash: Block::Hash) -> ClientResult> { + let mut cache = self.header_cache.lock(); + if let Some(result) = cache.get_refresh(&hash) { + return Ok(result.clone()) + } + let header = utils::read_header( &*self.db, columns::KEY_LOOKUP, - columns::JUSTIFICATIONS, + columns::HEADER, BlockId::::Hash(hash), - )? { - Some(justifications) => match Decode::decode(&mut &justifications[..]) { - Ok(justifications) => Ok(Some(justifications)), - Err(err) => - return Err(sp_blockchain::Error::Backend(format!( - "Error decoding justifications: {}", - err - ))), - }, - None => Ok(None), + )?; + cache_header(&mut cache, hash, header.clone()); + Ok(header) + } + + fn info(&self) -> sc_client_api::blockchain::Info { + let meta = self.meta.read(); + sc_client_api::blockchain::Info { + best_hash: meta.best_hash, + best_number: meta.best_number, + genesis_hash: meta.genesis_hash, + finalized_hash: meta.finalized_hash, + finalized_number: meta.finalized_number, + finalized_state: meta.finalized_state, + number_leaves: self.leaves.read().count(), + block_gap: meta.block_gap, + } + } + + fn status(&self, hash: Block::Hash) -> ClientResult { + match self.header(hash)?.is_some() { + true => Ok(sc_client_api::blockchain::BlockStatus::InChain), + false => Ok(sc_client_api::blockchain::BlockStatus::Unknown), + } + } + + fn number(&self, hash: Block::Hash) -> ClientResult>> { + Ok(self.header_metadata(hash).ok().map(|header_metadata| header_metadata.number)) + } + + fn hash(&self, number: NumberFor) -> ClientResult> { + Ok(utils::read_header::( + &*self.db, + columns::KEY_LOOKUP, + columns::HEADER, + BlockId::Number(number), + )? + .map(|header| header.hash())) + } +} + +impl sc_client_api::blockchain::Backend for BlockchainDb { + fn body(&self, hash: Block::Hash) -> ClientResult>> { + let cache = self.pinned_blocks_cache.read(); + if let Some(result) = cache.body(&hash) { + return Ok(result.clone()) + } + + self.body_uncached(hash) + } + + fn justifications(&self, hash: Block::Hash) -> ClientResult> { + let cache = self.pinned_blocks_cache.read(); + if let Some(result) = cache.justifications(&hash) { + return Ok(result.clone()) } + + self.justifications_uncached(hash) } fn last_finalized(&self) -> ClientResult { @@ -1291,20 +1369,28 @@ impl Backend { header: &Block::Header, last_finalized: Option, justification: Option, + current_transaction_justifications: &mut HashMap, ) -> ClientResult> { // TODO: ensure best chain contains this block. let number = *header.number(); self.ensure_sequential_finalization(header, last_finalized)?; let with_state = sc_client_api::Backend::have_state_at(self, hash, number); - self.note_finalized(transaction, header, hash, with_state)?; + self.note_finalized( + transaction, + header, + hash, + with_state, + current_transaction_justifications, + )?; if let Some(justification) = justification { transaction.set_from_vec( columns::JUSTIFICATIONS, &utils::number_and_hash_to_lookup_key(number, hash)?, - Justifications::from(justification).encode(), + Justifications::from(justification.clone()).encode(), ); + current_transaction_justifications.insert(hash, justification); } Ok(MetaUpdate { hash, number, is_best: false, is_finalized: true, with_state }) } @@ -1371,6 +1457,8 @@ impl Backend { (meta.best_number, meta.finalized_hash, meta.finalized_number, meta.block_gap) }; + let mut current_transaction_justifications: HashMap = + HashMap::new(); for (block_hash, justification) in operation.finalized_blocks { let block_header = self.blockchain.expect_header(block_hash)?; meta_updates.push(self.finalize_block_with_transaction( @@ -1379,6 +1467,7 @@ impl Backend { &block_header, Some(last_finalized_hash), justification, + &mut current_transaction_justifications, )?); last_finalized_hash = block_hash; last_finalized_num = *block_header.number(); @@ -1551,7 +1640,14 @@ impl Backend { if finalized { // TODO: ensure best chain contains this block. self.ensure_sequential_finalization(header, Some(last_finalized_hash))?; - self.note_finalized(&mut transaction, header, hash, operation.commit_state)?; + let mut current_transaction_justifications = HashMap::new(); + self.note_finalized( + &mut transaction, + header, + hash, + operation.commit_state, + &mut current_transaction_justifications, + )?; } else { // canonicalize blocks which are old enough, regardless of finality. self.force_delayed_canonicalize(&mut transaction)? @@ -1684,6 +1780,7 @@ impl Backend { f_header: &Block::Header, f_hash: Block::Hash, with_state: bool, + current_transaction_justifications: &mut HashMap, ) -> ClientResult<()> { let f_num = *f_header.number(); @@ -1709,7 +1806,7 @@ impl Backend { } let new_displaced = self.blockchain.leaves.write().finalize_height(f_num); - self.prune_blocks(transaction, f_num, &new_displaced)?; + self.prune_blocks(transaction, f_num, &new_displaced, current_transaction_justifications)?; Ok(()) } @@ -1717,22 +1814,39 @@ impl Backend { fn prune_blocks( &self, transaction: &mut Transaction, - finalized: NumberFor, + finalized_number: NumberFor, displaced: &FinalizationOutcome>, + current_transaction_justifications: &mut HashMap, ) -> ClientResult<()> { match self.blocks_pruning { BlocksPruning::KeepAll => {}, BlocksPruning::Some(blocks_pruning) => { // Always keep the last finalized block let keep = std::cmp::max(blocks_pruning, 1); - if finalized >= keep.into() { - let number = finalized.saturating_sub(keep.into()); + if finalized_number >= keep.into() { + let number = finalized_number.saturating_sub(keep.into()); + + // Before we prune a block, check if it is pinned + if let Some(hash) = self.blockchain.hash(number)? { + self.blockchain.insert_persisted_body_if_pinned(hash)?; + + // If the block was finalized in this transaction, it will not be in the db + // yet. + if let Some(justification) = + current_transaction_justifications.remove(&hash) + { + self.blockchain.insert_justifications_if_pinned(hash, justification); + } else { + self.blockchain.insert_persisted_justifications_if_pinned(hash)?; + } + }; + self.prune_block(transaction, BlockId::::number(number))?; } - self.prune_displaced_branches(transaction, finalized, displaced)?; + self.prune_displaced_branches(transaction, finalized_number, displaced)?; }, BlocksPruning::KeepFinalized => { - self.prune_displaced_branches(transaction, finalized, displaced)?; + self.prune_displaced_branches(transaction, finalized_number, displaced)?; }, } Ok(()) @@ -1755,6 +1869,8 @@ impl Backend { while self.blockchain.hash(number)? != Some(hash) { match self.blockchain.header(hash)? { Some(header) => { + self.blockchain.insert_persisted_body_if_pinned(hash)?; + self.prune_block(transaction, BlockId::::hash(hash))?; number = header.number().saturating_sub(One::one()); hash = *header.parent_hash(); @@ -1985,6 +2101,7 @@ impl sc_client_api::backend::Backend for Backend { .state_db .reset(state_meta_db) .map_err(sp_blockchain::Error::from_state_db)?; + self.blockchain.clear_pinning_cache(); Err(e) } else { self.storage.state_db.sync(); @@ -2000,12 +2117,14 @@ impl sc_client_api::backend::Backend for Backend { let mut transaction = Transaction::new(); let header = self.blockchain.expect_header(hash)?; + let mut current_transaction_justifications = HashMap::new(); let m = self.finalize_block_with_transaction( &mut transaction, hash, &header, None, justification, + &mut current_transaction_justifications, )?; self.storage.db.commit(transaction)?; @@ -2382,6 +2501,49 @@ impl sc_client_api::backend::Backend for Backend { PruningMode::ArchiveAll | PruningMode::ArchiveCanonical ) } + + fn pin_block(&self, hash: ::Hash) -> sp_blockchain::Result<()> { + let hint = || { + let header_metadata = self.blockchain.header_metadata(hash); + header_metadata + .map(|hdr| { + sc_state_db::NodeDb::get(self.storage.as_ref(), hdr.state_root.as_ref()) + .unwrap_or(None) + .is_some() + }) + .unwrap_or(false) + }; + + if let Some(number) = self.blockchain.number(hash)? { + self.storage.state_db.pin(&hash, number.saturated_into::(), hint).map_err( + |_| { + sp_blockchain::Error::UnknownBlock(format!( + "State already discarded for `{:?}`", + hash + )) + }, + )?; + } else { + return Err(ClientError::UnknownBlock(format!( + "Can not pin block with hash `{:?}`. Block not found.", + hash + ))) + } + + if self.blocks_pruning != BlocksPruning::KeepAll { + // Only increase reference count for this hash. Value is loaded once we prune. + self.blockchain.bump_ref(hash); + } + Ok(()) + } + + fn unpin_block(&self, hash: ::Hash) { + self.storage.state_db.unpin(&hash); + + if self.blocks_pruning != BlocksPruning::KeepAll { + self.blockchain.unpin(hash); + } + } } impl sc_client_api::backend::LocalBackend for Backend {} @@ -4009,4 +4171,249 @@ pub(crate) mod tests { assert_eq!(block4, backend.blockchain().hash(4).unwrap().unwrap()); } } + + #[test] + fn test_pinned_blocks_on_finalize() { + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10); + let mut blocks = Vec::new(); + let mut prev_hash = Default::default(); + + let build_justification = |i: u64| ([0, 0, 0, 0], vec![i.try_into().unwrap()]); + // Block tree: + // 0 -> 1 -> 2 -> 3 -> 4 + for i in 0..5 { + let hash = insert_block( + &backend, + i, + prev_hash, + None, + Default::default(), + vec![i.into()], + None, + ) + .unwrap(); + blocks.push(hash); + // Avoid block pruning. + backend.pin_block(blocks[i as usize]).unwrap(); + + prev_hash = hash; + } + + let bc = backend.blockchain(); + + // Check that we can properly access values when there is reference count + // but no value. + assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); + + // Block 1 gets pinned three times + backend.pin_block(blocks[1]).unwrap(); + backend.pin_block(blocks[1]).unwrap(); + + // Finalize all blocks. This will trigger pruning. + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[4]).unwrap(); + for i in 1..5 { + op.mark_finalized(blocks[i], Some(build_justification(i.try_into().unwrap()))) + .unwrap(); + } + backend.commit_operation(op).unwrap(); + + // Block 0, 1, 2, 3 are pinned, so all values should be cached. + // Block 4 is inside the pruning window, its value is in db. + assert_eq!(Some(vec![0.into()]), bc.body(blocks[0]).unwrap()); + + assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(1))), + bc.justifications(blocks[1]).unwrap() + ); + + assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(2))), + bc.justifications(blocks[2]).unwrap() + ); + + assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(3))), + bc.justifications(blocks[3]).unwrap() + ); + + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(4))), + bc.justifications(blocks[4]).unwrap() + ); + + // Unpin all blocks. Values should be removed from cache. + for block in &blocks { + backend.unpin_block(*block); + } + + assert!(bc.body(blocks[0]).unwrap().is_none()); + // Block 1 was pinned twice, we expect it to be still cached + assert!(bc.body(blocks[1]).unwrap().is_some()); + assert!(bc.justifications(blocks[1]).unwrap().is_some()); + // Headers should also be available while pinned + assert!(bc.header(blocks[1]).ok().flatten().is_some()); + assert!(bc.body(blocks[2]).unwrap().is_none()); + assert!(bc.justifications(blocks[2]).unwrap().is_none()); + assert!(bc.body(blocks[3]).unwrap().is_none()); + assert!(bc.justifications(blocks[3]).unwrap().is_none()); + + // After these unpins, block 1 should also be removed + backend.unpin_block(blocks[1]); + assert!(bc.body(blocks[1]).unwrap().is_some()); + assert!(bc.justifications(blocks[1]).unwrap().is_some()); + backend.unpin_block(blocks[1]); + assert!(bc.body(blocks[1]).unwrap().is_none()); + assert!(bc.justifications(blocks[1]).unwrap().is_none()); + + // Block 4 is inside the pruning window and still kept + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(4))), + bc.justifications(blocks[4]).unwrap() + ); + + // Block tree: + // 0 -> 1 -> 2 -> 3 -> 4 -> 5 + let hash = + insert_block(&backend, 5, prev_hash, None, Default::default(), vec![5.into()], None) + .unwrap(); + blocks.push(hash); + + backend.pin_block(blocks[4]).unwrap(); + // Mark block 5 as finalized. + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[5]).unwrap(); + op.mark_finalized(blocks[5], Some(build_justification(5))).unwrap(); + backend.commit_operation(op).unwrap(); + + assert!(bc.body(blocks[0]).unwrap().is_none()); + assert!(bc.body(blocks[1]).unwrap().is_none()); + assert!(bc.body(blocks[2]).unwrap().is_none()); + assert!(bc.body(blocks[3]).unwrap().is_none()); + + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + assert_eq!( + Some(Justifications::from(build_justification(4))), + bc.justifications(blocks[4]).unwrap() + ); + assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert!(bc.header(blocks[5]).ok().flatten().is_some()); + + backend.unpin_block(blocks[4]); + assert!(bc.body(blocks[4]).unwrap().is_none()); + assert!(bc.justifications(blocks[4]).unwrap().is_none()); + + // Append a justification to block 5. + backend.append_justification(blocks[5], ([0, 0, 0, 1], vec![42])).unwrap(); + + let hash = + insert_block(&backend, 6, blocks[5], None, Default::default(), vec![6.into()], None) + .unwrap(); + blocks.push(hash); + + // Pin block 5 so it gets loaded into the cache on prune + backend.pin_block(blocks[5]).unwrap(); + + // Finalize block 6 so block 5 gets pruned. Since it is pinned both justifications should be + // in memory. + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[6]).unwrap(); + op.mark_finalized(blocks[6], None).unwrap(); + backend.commit_operation(op).unwrap(); + + assert_eq!(Some(vec![5.into()]), bc.body(blocks[5]).unwrap()); + assert!(bc.header(blocks[5]).ok().flatten().is_some()); + let mut expected = Justifications::from(build_justification(5)); + expected.append(([0, 0, 0, 1], vec![42])); + assert_eq!(Some(expected), bc.justifications(blocks[5]).unwrap()); + } + + #[test] + fn test_pinned_blocks_on_finalize_with_fork() { + let backend = Backend::::new_test_with_tx_storage(BlocksPruning::Some(1), 10); + let mut blocks = Vec::new(); + let mut prev_hash = Default::default(); + + // Block tree: + // 0 -> 1 -> 2 -> 3 -> 4 + for i in 0..5 { + let hash = insert_block( + &backend, + i, + prev_hash, + None, + Default::default(), + vec![i.into()], + None, + ) + .unwrap(); + blocks.push(hash); + + // Avoid block pruning. + backend.pin_block(blocks[i as usize]).unwrap(); + + prev_hash = hash; + } + + // Insert a fork at the second block. + // Block tree: + // 0 -> 1 -> 2 -> 3 -> 4 + // \ -> 2 -> 3 + let fork_hash_root = + insert_block(&backend, 2, blocks[1], None, H256::random(), vec![2.into()], None) + .unwrap(); + let fork_hash_3 = insert_block( + &backend, + 3, + fork_hash_root, + None, + H256::random(), + vec![3.into(), 11.into()], + None, + ) + .unwrap(); + + // Do not prune the fork hash. + backend.pin_block(fork_hash_3).unwrap(); + + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[4]).unwrap(); + op.mark_head(blocks[4]).unwrap(); + backend.commit_operation(op).unwrap(); + + for i in 1..5 { + let mut op = backend.begin_operation().unwrap(); + backend.begin_state_operation(&mut op, blocks[4]).unwrap(); + op.mark_finalized(blocks[i], None).unwrap(); + backend.commit_operation(op).unwrap(); + } + + let bc = backend.blockchain(); + assert_eq!(Some(vec![0.into()]), bc.body(blocks[0]).unwrap()); + assert_eq!(Some(vec![1.into()]), bc.body(blocks[1]).unwrap()); + assert_eq!(Some(vec![2.into()]), bc.body(blocks[2]).unwrap()); + assert_eq!(Some(vec![3.into()]), bc.body(blocks[3]).unwrap()); + assert_eq!(Some(vec![4.into()]), bc.body(blocks[4]).unwrap()); + // Check the fork hashes. + assert_eq!(None, bc.body(fork_hash_root).unwrap()); + assert_eq!(Some(vec![3.into(), 11.into()]), bc.body(fork_hash_3).unwrap()); + + // Unpin all blocks, except the forked one. + for block in &blocks { + backend.unpin_block(*block); + } + assert!(bc.body(blocks[0]).unwrap().is_none()); + assert!(bc.body(blocks[1]).unwrap().is_none()); + assert!(bc.body(blocks[2]).unwrap().is_none()); + assert!(bc.body(blocks[3]).unwrap().is_none()); + + assert!(bc.body(fork_hash_3).unwrap().is_some()); + backend.unpin_block(fork_hash_3); + assert!(bc.body(fork_hash_3).unwrap().is_none()); + } } diff --git a/client/db/src/pinned_blocks_cache.rs b/client/db/src/pinned_blocks_cache.rs new file mode 100644 index 0000000000000..39ff1c5277871 --- /dev/null +++ b/client/db/src/pinned_blocks_cache.rs @@ -0,0 +1,231 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use schnellru::{Limiter, LruMap}; +use sp_runtime::{traits::Block as BlockT, Justifications}; + +const LOG_TARGET: &str = "db::pin"; +const PINNING_CACHE_SIZE: usize = 1024; + +/// Entry for pinned blocks cache. +struct PinnedBlockCacheEntry { + /// How many times this item has been pinned + ref_count: u32, + + /// Cached justifications for this block + pub justifications: Option>, + + /// Cached body for this block + pub body: Option>>, +} + +impl Default for PinnedBlockCacheEntry { + fn default() -> Self { + Self { ref_count: 0, justifications: None, body: None } + } +} + +impl PinnedBlockCacheEntry { + pub fn decrease_ref(&mut self) { + self.ref_count = self.ref_count.saturating_sub(1); + } + + pub fn increase_ref(&mut self) { + self.ref_count = self.ref_count.saturating_add(1); + } + + pub fn has_no_references(&self) -> bool { + self.ref_count == 0 + } +} + +/// A limiter for a map which is limited by the number of elements. +#[derive(Copy, Clone, Debug)] +struct LoggingByLengthLimiter { + max_length: usize, +} + +impl LoggingByLengthLimiter { + /// Creates a new length limiter with a given `max_length`. + pub const fn new(max_length: usize) -> LoggingByLengthLimiter { + LoggingByLengthLimiter { max_length } + } +} + +impl Limiter> for LoggingByLengthLimiter { + type KeyToInsert<'a> = Block::Hash; + type LinkType = usize; + + fn is_over_the_limit(&self, length: usize) -> bool { + length > self.max_length + } + + fn on_insert( + &mut self, + _length: usize, + key: Self::KeyToInsert<'_>, + value: PinnedBlockCacheEntry, + ) -> Option<(Block::Hash, PinnedBlockCacheEntry)> { + if self.max_length > 0 { + Some((key, value)) + } else { + None + } + } + + fn on_replace( + &mut self, + _length: usize, + _old_key: &mut Block::Hash, + _new_key: Block::Hash, + _old_value: &mut PinnedBlockCacheEntry, + _new_value: &mut PinnedBlockCacheEntry, + ) -> bool { + true + } + + fn on_removed(&mut self, key: &mut Block::Hash, value: &mut PinnedBlockCacheEntry) { + // If reference count was larger than 0 on removal, + // the item was removed due to capacity limitations. + // Since the cache should be large enough for pinned items, + // we want to know about these evictions. + if value.ref_count > 0 { + log::warn!( + target: LOG_TARGET, + "Pinned block cache limit reached. Evicting value. hash = {}", + key + ); + } else { + log::trace!( + target: LOG_TARGET, + "Evicting value from pinned block cache. hash = {}", + key + ) + } + } + + fn on_cleared(&mut self) {} + + fn on_grow(&mut self, _new_memory_usage: usize) -> bool { + true + } +} + +/// Reference counted cache for pinned block bodies and justifications. +pub struct PinnedBlocksCache { + cache: LruMap, LoggingByLengthLimiter>, +} + +impl PinnedBlocksCache { + pub fn new() -> Self { + Self { cache: LruMap::new(LoggingByLengthLimiter::new(PINNING_CACHE_SIZE)) } + } + + /// Increase reference count of an item. + /// Create an entry with empty value in the cache if necessary. + pub fn pin(&mut self, hash: Block::Hash) { + match self.cache.get_or_insert(hash, Default::default) { + Some(entry) => { + entry.increase_ref(); + log::trace!( + target: LOG_TARGET, + "Bumped cache refcount. hash = {}, num_entries = {}", + hash, + self.cache.len() + ); + }, + None => + log::warn!(target: LOG_TARGET, "Unable to bump reference count. hash = {}", hash), + }; + } + + /// Clear the cache + pub fn clear(&mut self) { + self.cache.clear(); + } + + /// Check if item is contained in the cache + pub fn contains(&self, hash: Block::Hash) -> bool { + self.cache.peek(&hash).is_some() + } + + /// Attach body to an existing cache item + pub fn insert_body(&mut self, hash: Block::Hash, extrinsics: Option>) { + match self.cache.peek_mut(&hash) { + Some(mut entry) => { + entry.body = Some(extrinsics); + log::trace!( + target: LOG_TARGET, + "Cached body. hash = {}, num_entries = {}", + hash, + self.cache.len() + ); + }, + None => log::warn!( + target: LOG_TARGET, + "Unable to insert body for uncached item. hash = {}", + hash + ), + } + } + + /// Attach justification to an existing cache item + pub fn insert_justifications( + &mut self, + hash: Block::Hash, + justifications: Option, + ) { + match self.cache.peek_mut(&hash) { + Some(mut entry) => { + entry.justifications = Some(justifications); + log::trace!( + target: LOG_TARGET, + "Cached justification. hash = {}, num_entries = {}", + hash, + self.cache.len() + ); + }, + None => log::warn!( + target: LOG_TARGET, + "Unable to insert justifications for uncached item. hash = {}", + hash + ), + } + } + + /// Decreases reference count of an item. + /// If the count hits 0, the item is removed. + pub fn unpin(&mut self, hash: Block::Hash) { + if let Some(entry) = self.cache.peek_mut(&hash) { + entry.decrease_ref(); + if entry.has_no_references() { + self.cache.remove(&hash); + } + } + } + + /// Get justifications for cached block + pub fn justifications(&self, hash: &Block::Hash) -> Option<&Option> { + self.cache.peek(hash).and_then(|entry| entry.justifications.as_ref()) + } + + /// Get body for cached block + pub fn body(&self, hash: &Block::Hash) -> Option<&Option>> { + self.cache.peek(hash).and_then(|entry| entry.body.as_ref()) + } +} diff --git a/client/db/src/storage_cache.rs b/client/db/src/storage_cache.rs deleted file mode 100644 index 474599e5d74e8..0000000000000 --- a/client/db/src/storage_cache.rs +++ /dev/null @@ -1,1975 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Global state cache. Maintains recently queried/committed state values -//! Tracks changes over the span of a few recent blocks and handles forks -//! by tracking/removing cache entries for conflicting changes. - -use crate::{stats::StateUsageStats, utils::Meta}; -use hash_db::Hasher; -use linked_hash_map::{Entry, LinkedHashMap}; -use log::trace; -use parking_lot::{RwLock, RwLockUpgradableReadGuard}; -use sp_core::{hexdisplay::HexDisplay, storage::ChildInfo}; -use sp_runtime::{ - traits::{Block as BlockT, HashFor, Header, NumberFor}, - StateVersion, -}; -use sp_state_machine::{ - backend::Backend as StateBackend, ChildStorageCollection, StorageCollection, StorageKey, - StorageValue, TrieBackend, -}; -use std::{ - collections::{HashMap, HashSet, VecDeque}, - hash::Hash as StdHash, - sync::Arc, -}; - -const STATE_CACHE_BLOCKS: usize = 12; - -type ChildStorageKey = (Vec, Vec); - -/// Shared canonical state cache. -pub struct Cache { - /// Storage cache. `None` indicates that key is known to be missing. - lru_storage: LRUMap>, - /// Storage hashes cache. `None` indicates that key is known to be missing. - lru_hashes: LRUMap>, - /// Storage cache for child trie. `None` indicates that key is known to be missing. - lru_child_storage: LRUMap>, - /// Information on the modifications in recently committed blocks; specifically which keys - /// changed in which block. Ordered by block number. - modifications: VecDeque>, -} - -struct LRUMap(LinkedHashMap, usize, usize); - -/// Internal trait similar to `heapsize` but using a simple estimation. -/// -/// This should not be made public, it is an implementation detail trait. -trait EstimateSize { - /// Return a size estimation of the additional size needed to cache this struct (in bytes). - fn estimate_size(&self) -> usize; -} - -impl EstimateSize for Vec { - fn estimate_size(&self) -> usize { - self.capacity() - } -} - -impl EstimateSize for Option> { - fn estimate_size(&self) -> usize { - self.as_ref().map(|v| v.capacity()).unwrap_or(0) - } -} - -struct OptionHOut>(Option); - -impl> EstimateSize for OptionHOut { - fn estimate_size(&self) -> usize { - // capacity would be better - self.0.as_ref().map(|v| v.as_ref().len()).unwrap_or(0) - } -} - -impl EstimateSize for (T, T) { - fn estimate_size(&self) -> usize { - self.0.estimate_size() + self.1.estimate_size() - } -} - -impl LRUMap { - fn remove(&mut self, k: &K) { - let map = &mut self.0; - let storage_used_size = &mut self.1; - if let Some(v) = map.remove(k) { - *storage_used_size -= k.estimate_size(); - *storage_used_size -= v.estimate_size(); - } - } - - fn add(&mut self, k: K, v: V) { - let lmap = &mut self.0; - let storage_used_size = &mut self.1; - let limit = self.2; - let klen = k.estimate_size(); - *storage_used_size += v.estimate_size(); - // TODO assert k v size fit into limit?? to avoid insert remove? - match lmap.entry(k) { - Entry::Occupied(mut entry) => { - // note that in this case we are not running pure lru as - // it would require to remove first - *storage_used_size -= entry.get().estimate_size(); - entry.insert(v); - }, - Entry::Vacant(entry) => { - *storage_used_size += klen; - entry.insert(v); - }, - }; - - while *storage_used_size > limit { - if let Some((k, v)) = lmap.pop_front() { - *storage_used_size -= k.estimate_size(); - *storage_used_size -= v.estimate_size(); - } else { - // can happen fairly often as we get value from multiple lru - // and only remove from a single lru - break - } - } - } - - fn get(&mut self, k: &Q) -> Option<&mut V> - where - K: std::borrow::Borrow, - Q: StdHash + Eq, - { - self.0.get_refresh(k) - } - - fn used_size(&self) -> usize { - self.1 - } - fn clear(&mut self) { - self.0.clear(); - self.1 = 0; - } -} - -impl Cache { - /// Returns the used memory size of the storage cache in bytes. - pub fn used_storage_cache_size(&self) -> usize { - self.lru_storage.used_size() + self.lru_child_storage.used_size() - // ignore small hashes storage and self.lru_hashes.used_size() - } - - /// Synchronize the shared cache with the best block state. - /// - /// This function updates the shared cache by removing entries - /// that are invalidated by chain reorganization. It should be called - /// externally when chain reorg happens without importing a new block. - pub fn sync(&mut self, enacted: &[B::Hash], retracted: &[B::Hash]) { - trace!("Syncing shared cache, enacted = {:?}, retracted = {:?}", enacted, retracted); - - // Purge changes from re-enacted and retracted blocks. - let mut clear = false; - for block in enacted { - clear = clear || { - if let Some(m) = self.modifications.iter_mut().find(|m| &m.hash == block) { - trace!("Reverting enacted block {:?}", block); - m.is_canon = true; - for a in &m.storage { - trace!("Reverting enacted key {:?}", HexDisplay::from(a)); - self.lru_storage.remove(a); - self.lru_hashes.remove(a); - } - for a in &m.child_storage { - trace!("Reverting enacted child key {:?}", a); - self.lru_child_storage.remove(a); - } - false - } else { - true - } - }; - } - - for block in retracted { - clear = clear || { - if let Some(m) = self.modifications.iter_mut().find(|m| &m.hash == block) { - trace!("Retracting block {:?}", block); - m.is_canon = false; - for a in &m.storage { - trace!("Retracted key {:?}", HexDisplay::from(a)); - self.lru_storage.remove(a); - self.lru_hashes.remove(a); - } - for a in &m.child_storage { - trace!("Retracted child key {:?}", a); - self.lru_child_storage.remove(a); - } - false - } else { - true - } - }; - } - if clear { - // We don't know anything about the block; clear everything - trace!("Wiping cache"); - self.lru_storage.clear(); - self.lru_child_storage.clear(); - self.lru_hashes.clear(); - self.modifications.clear(); - } - } -} - -pub type SharedCache = Arc>>; - -/// Fix lru storage size for hash (small 64ko). -const FIX_LRU_HASH_SIZE: usize = 65_536; - -/// Create a new shared cache instance with given max memory usage. -pub fn new_shared_cache( - shared_cache_size: usize, - child_ratio: (usize, usize), -) -> SharedCache { - let top = child_ratio.1.saturating_sub(child_ratio.0); - Arc::new(RwLock::new(Cache { - lru_storage: LRUMap(LinkedHashMap::new(), 0, shared_cache_size * top / child_ratio.1), - lru_hashes: LRUMap(LinkedHashMap::new(), 0, FIX_LRU_HASH_SIZE), - lru_child_storage: LRUMap( - LinkedHashMap::new(), - 0, - shared_cache_size * child_ratio.0 / child_ratio.1, - ), - modifications: VecDeque::new(), - })) -} - -#[derive(Debug)] -/// Accumulates a list of storage changed in a block. -struct BlockChanges { - /// Block number. - number: B::Number, - /// Block hash. - hash: B::Hash, - /// Parent block hash. - parent: B::Hash, - /// A set of modified storage keys. - storage: HashSet, - /// A set of modified child storage keys. - child_storage: HashSet, - /// Block is part of the canonical chain. - is_canon: bool, -} - -/// Cached values specific to a state. -struct LocalCache { - /// Storage cache. - /// - /// `None` indicates that key is known to be missing. - storage: HashMap>, - /// Storage hashes cache. - /// - /// `None` indicates that key is known to be missing. - hashes: HashMap>, - /// Child storage cache. - /// - /// `None` indicates that key is known to be missing. - child_storage: HashMap>, -} - -/// Cache changes. -pub struct CacheChanges { - /// Shared canonical state cache. - shared_cache: SharedCache, - /// Local cache of values for this state. - local_cache: RwLock>>, - /// Hash of the block on top of which this instance was created or - /// `None` if cache is disabled - pub parent_hash: Option, -} - -/// State cache abstraction. -/// -/// Manages shared global state cache which reflects the canonical -/// state as it is on the disk. -/// -/// A instance of `CachingState` may be created as canonical or not. -/// For canonical instances local cache is accumulated and applied -/// in `sync_cache` along with the change overlay. -/// For non-canonical clones local cache and changes are dropped. -pub struct CachingState { - /// Usage statistics - usage: StateUsageStats, - /// State machine registered stats - overlay_stats: sp_state_machine::StateMachineStats, - /// Backing state. - state: S, - /// Cache data. - cache: CacheChanges, -} - -impl std::fmt::Debug for CachingState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "Block {:?}", self.cache.parent_hash) - } -} - -impl CacheChanges { - /// Propagate local cache into the shared cache and synchronize - /// the shared cache with the best block state. - /// - /// This function updates the shared cache by removing entries - /// that are invalidated by chain reorganization. `sync_cache` - /// should be called after the block has been committed and the - /// blockchain route has been calculated. - pub fn sync_cache( - &mut self, - enacted: &[B::Hash], - retracted: &[B::Hash], - changes: StorageCollection, - child_changes: ChildStorageCollection, - commit_hash: Option, - commit_number: Option>, - is_best: bool, - ) { - let mut cache = self.shared_cache.write(); - trace!( - "Syncing cache, id = (#{:?}, {:?}), parent={:?}, best={}", - commit_number, - commit_hash, - self.parent_hash, - is_best, - ); - let cache = &mut *cache; - // Filter out committing block if any. - let mut enacted: Vec<_> = enacted - .iter() - .filter(|h| commit_hash.as_ref().map_or(true, |p| *h != p)) - .cloned() - .collect(); - - let mut retracted = std::borrow::Cow::Borrowed(retracted); - if let Some(commit_hash) = &commit_hash { - if let Some(m) = cache.modifications.iter_mut().find(|m| &m.hash == commit_hash) { - if m.is_canon != is_best { - // Same block comitted twice with different state changes. - // Treat it as reenacted/retracted. - if is_best { - enacted.push(*commit_hash); - } else { - retracted.to_mut().push(*commit_hash); - } - } - } - } - cache.sync(&enacted, &retracted); - // Propagate cache only if committing on top of the latest canonical state - // blocks are ordered by number and only one block with a given number is marked as - // canonical (contributed to canonical state cache) - if self.parent_hash.is_some() { - let mut local_cache = self.local_cache.write(); - if is_best { - trace!( - "Committing {} local, {} hashes, {} modified root entries, {} modified child entries", - local_cache.storage.len(), - local_cache.hashes.len(), - changes.len(), - child_changes.iter().map(|v|v.1.len()).sum::(), - ); - for (k, v) in local_cache.storage.drain() { - cache.lru_storage.add(k, v); - } - for (k, v) in local_cache.child_storage.drain() { - cache.lru_child_storage.add(k, v); - } - for (k, v) in local_cache.hashes.drain() { - cache.lru_hashes.add(k, OptionHOut(v)); - } - } - } - - if let (Some(ref number), Some(ref hash), Some(ref parent)) = - (commit_number, commit_hash, self.parent_hash) - { - if cache.modifications.len() == STATE_CACHE_BLOCKS { - cache.modifications.pop_back(); - } - let mut modifications = HashSet::new(); - let mut child_modifications = HashSet::new(); - child_changes.into_iter().for_each(|(sk, changes)| { - for (k, v) in changes.into_iter() { - let k = (sk.clone(), k); - if is_best { - cache.lru_child_storage.add(k.clone(), v); - } - child_modifications.insert(k); - } - }); - for (k, v) in changes.into_iter() { - if is_best { - cache.lru_hashes.remove(&k); - cache.lru_storage.add(k.clone(), v); - } - modifications.insert(k); - } - - // Save modified storage. These are ordered by the block number in reverse. - let block_changes = BlockChanges { - storage: modifications, - child_storage: child_modifications, - number: *number, - hash: *hash, - is_canon: is_best, - parent: *parent, - }; - let insert_at = cache - .modifications - .iter() - .enumerate() - .find(|(_, m)| m.number < *number) - .map(|(i, _)| i); - trace!("Inserting modifications at {:?}", insert_at); - if let Some(insert_at) = insert_at { - cache.modifications.insert(insert_at, block_changes); - } else { - cache.modifications.push_back(block_changes); - } - } - } -} - -impl>, B: BlockT> CachingState { - /// Create a new instance wrapping generic State and shared cache. - pub(crate) fn new( - state: S, - shared_cache: SharedCache, - parent_hash: Option, - ) -> Self { - CachingState { - usage: StateUsageStats::new(), - overlay_stats: sp_state_machine::StateMachineStats::default(), - state, - cache: CacheChanges { - shared_cache, - local_cache: RwLock::new(LocalCache { - storage: Default::default(), - hashes: Default::default(), - child_storage: Default::default(), - }), - parent_hash, - }, - } - } - - /// Check if the key can be returned from cache by matching current block parent hash against - /// canonical state and filtering out entries modified in later blocks. - fn is_allowed( - key: Option<&[u8]>, - child_key: Option<&ChildStorageKey>, - parent_hash: &Option, - modifications: &VecDeque>, - ) -> bool { - let mut parent = match *parent_hash { - None => { - trace!( - "Cache lookup skipped for {:?}: no parent hash", - key.as_ref().map(HexDisplay::from) - ); - return false - }, - Some(ref parent) => parent, - }; - // Ignore all storage entries modified in later blocks. - // Modifications contains block ordered by the number - // We search for our parent in that list first and then for - // all its parents until we hit the canonical block, - // checking against all the intermediate modifications. - for m in modifications { - if &m.hash == parent { - if m.is_canon { - return true - } - parent = &m.parent; - } - if let Some(key) = key { - if m.storage.contains(key) { - trace!( - "Cache lookup skipped for {:?}: modified in a later block", - HexDisplay::from(&key) - ); - return false - } - } - if let Some(child_key) = child_key { - if m.child_storage.contains(child_key) { - trace!("Cache lookup skipped for {:?}: modified in a later block", child_key); - return false - } - } - } - trace!( - "Cache lookup skipped for {:?}: parent hash is unknown", - key.as_ref().map(HexDisplay::from), - ); - false - } -} - -impl>, B: BlockT> StateBackend> for CachingState { - type Error = S::Error; - type Transaction = S::Transaction; - type TrieBackendStorage = S::TrieBackendStorage; - - fn storage(&self, key: &[u8]) -> Result>, Self::Error> { - let local_cache = self.cache.local_cache.upgradable_read(); - // Note that local cache makes that lru is not refreshed - if let Some(entry) = local_cache.storage.get(key).cloned() { - trace!("Found in local cache: {:?}", HexDisplay::from(&key)); - self.usage.tally_key_read(key, entry.as_ref(), true); - - return Ok(entry) - } - { - let cache = self.cache.shared_cache.upgradable_read(); - if Self::is_allowed(Some(key), None, &self.cache.parent_hash, &cache.modifications) { - let mut cache = RwLockUpgradableReadGuard::upgrade(cache); - if let Some(entry) = cache.lru_storage.get(key).map(|a| a.clone()) { - trace!("Found in shared cache: {:?}", HexDisplay::from(&key)); - self.usage.tally_key_read(key, entry.as_ref(), true); - return Ok(entry) - } - } - } - trace!("Cache miss: {:?}", HexDisplay::from(&key)); - let value = self.state.storage(key)?; - RwLockUpgradableReadGuard::upgrade(local_cache) - .storage - .insert(key.to_vec(), value.clone()); - self.usage.tally_key_read(key, value.as_ref(), false); - Ok(value) - } - - fn storage_hash(&self, key: &[u8]) -> Result, Self::Error> { - let local_cache = self.cache.local_cache.upgradable_read(); - if let Some(entry) = local_cache.hashes.get(key).cloned() { - trace!("Found hash in local cache: {:?}", HexDisplay::from(&key)); - return Ok(entry) - } - { - let cache = self.cache.shared_cache.upgradable_read(); - if Self::is_allowed(Some(key), None, &self.cache.parent_hash, &cache.modifications) { - let mut cache = RwLockUpgradableReadGuard::upgrade(cache); - if let Some(entry) = cache.lru_hashes.get(key).map(|a| a.0) { - trace!("Found hash in shared cache: {:?}", HexDisplay::from(&key)); - return Ok(entry) - } - } - } - trace!("Cache hash miss: {:?}", HexDisplay::from(&key)); - let hash = self.state.storage_hash(key)?; - RwLockUpgradableReadGuard::upgrade(local_cache) - .hashes - .insert(key.to_vec(), hash); - Ok(hash) - } - - fn child_storage( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result>, Self::Error> { - let key = (child_info.storage_key().to_vec(), key.to_vec()); - let local_cache = self.cache.local_cache.upgradable_read(); - if let Some(entry) = local_cache.child_storage.get(&key).cloned() { - trace!("Found in local cache: {:?}", key); - return Ok(self.usage.tally_child_key_read(&key, entry, true)) - } - { - let cache = self.cache.shared_cache.upgradable_read(); - if Self::is_allowed(None, Some(&key), &self.cache.parent_hash, &cache.modifications) { - let mut cache = RwLockUpgradableReadGuard::upgrade(cache); - if let Some(entry) = cache.lru_child_storage.get(&key).map(|a| a.clone()) { - trace!("Found in shared cache: {:?}", key); - return Ok(self.usage.tally_child_key_read(&key, entry, true)) - } - } - } - trace!("Cache miss: {:?}", key); - let value = self.state.child_storage(child_info, &key.1[..])?; - - // just pass it through the usage counter - let value = self.usage.tally_child_key_read(&key, value, false); - - RwLockUpgradableReadGuard::upgrade(local_cache) - .child_storage - .insert(key, value.clone()); - Ok(value) - } - - fn exists_storage(&self, key: &[u8]) -> Result { - Ok(self.storage(key)?.is_some()) - } - - fn exists_child_storage( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result { - self.state.exists_child_storage(child_info, key) - } - - fn apply_to_key_values_while, Vec) -> bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - allow_missing: bool, - ) -> Result { - self.state - .apply_to_key_values_while(child_info, prefix, start_at, f, allow_missing) - } - - fn apply_to_keys_while bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - ) { - self.state.apply_to_keys_while(child_info, prefix, start_at, f) - } - - fn next_storage_key(&self, key: &[u8]) -> Result>, Self::Error> { - self.state.next_storage_key(key) - } - - fn next_child_storage_key( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result>, Self::Error> { - self.state.next_child_storage_key(child_info, key) - } - - fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.state.for_keys_with_prefix(prefix, f) - } - - fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { - self.state.for_key_values_with_prefix(prefix, f) - } - - fn for_child_keys_with_prefix( - &self, - child_info: &ChildInfo, - prefix: &[u8], - f: F, - ) { - self.state.for_child_keys_with_prefix(child_info, prefix, f) - } - - fn storage_root<'a>( - &self, - delta: impl Iterator)>, - state_version: StateVersion, - ) -> (B::Hash, Self::Transaction) - where - B::Hash: Ord, - { - self.state.storage_root(delta, state_version) - } - - fn child_storage_root<'a>( - &self, - child_info: &ChildInfo, - delta: impl Iterator)>, - state_version: StateVersion, - ) -> (B::Hash, bool, Self::Transaction) - where - B::Hash: Ord, - { - self.state.child_storage_root(child_info, delta, state_version) - } - - fn pairs(&self) -> Vec<(Vec, Vec)> { - self.state.pairs() - } - - fn keys(&self, prefix: &[u8]) -> Vec> { - self.state.keys(prefix) - } - - fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec> { - self.state.child_keys(child_info, prefix) - } - - fn as_trie_backend(&self) -> Option<&TrieBackend>> { - self.state.as_trie_backend() - } - - fn register_overlay_stats(&self, stats: &sp_state_machine::StateMachineStats) { - self.overlay_stats.add(stats); - } - - fn usage_info(&self) -> sp_state_machine::UsageInfo { - let mut info = self.usage.take(); - info.include_state_machine_states(&self.overlay_stats); - info - } -} - -/// Extended [`CachingState`] that will sync the caches on drop. -pub struct SyncingCachingState { - /// The usage statistics of the backend. These will be updated on drop. - state_usage: Arc, - /// Reference to the meta db. - meta: Arc, Block::Hash>>>, - /// Mutex to lock get exlusive access to the backend. - lock: Arc>, - /// The wrapped caching state. - /// - /// This is required to be a `Option`, because sometimes we want to extract - /// the cache changes and Rust does not allow to move fields from types that - /// implement `Drop`. - caching_state: Option>, - /// Disable syncing of the cache. This is by default always `false`. However, - /// we need to disable syncing when this is a state in a - /// [`BlockImportOperation`](crate::BlockImportOperation). The import operation - /// takes care to sync the cache and more importantly we want to prevent a dead - /// lock. - disable_syncing: bool, -} - -impl SyncingCachingState { - /// Create new automatic syncing state. - pub fn new( - caching_state: CachingState, - state_usage: Arc, - meta: Arc, B::Hash>>>, - lock: Arc>, - ) -> Self { - Self { caching_state: Some(caching_state), state_usage, meta, lock, disable_syncing: false } - } - - /// Returns the reference to the internal [`CachingState`]. - fn caching_state(&self) -> &CachingState { - self.caching_state - .as_ref() - .expect("`caching_state` is always valid for the lifetime of the object; qed") - } - - /// Convert `Self` into the cache changes. - pub fn into_cache_changes(mut self) -> CacheChanges { - self.caching_state - .take() - .expect("`caching_state` is always valid for the lifetime of the object; qed") - .cache - } - - /// Disable syncing the cache on drop. - pub fn disable_syncing(&mut self) { - self.disable_syncing = true; - } -} - -impl std::fmt::Debug for SyncingCachingState { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.caching_state().fmt(f) - } -} - -impl>, B: BlockT> StateBackend> - for SyncingCachingState -{ - type Error = S::Error; - type Transaction = S::Transaction; - type TrieBackendStorage = S::TrieBackendStorage; - - fn storage(&self, key: &[u8]) -> Result>, Self::Error> { - self.caching_state().storage(key) - } - - fn storage_hash(&self, key: &[u8]) -> Result, Self::Error> { - self.caching_state().storage_hash(key) - } - - fn child_storage( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result>, Self::Error> { - self.caching_state().child_storage(child_info, key) - } - - fn exists_storage(&self, key: &[u8]) -> Result { - self.caching_state().exists_storage(key) - } - - fn exists_child_storage( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result { - self.caching_state().exists_child_storage(child_info, key) - } - - fn apply_to_key_values_while, Vec) -> bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - allow_missing: bool, - ) -> Result { - self.caching_state().apply_to_key_values_while( - child_info, - prefix, - start_at, - f, - allow_missing, - ) - } - - fn apply_to_keys_while bool>( - &self, - child_info: Option<&ChildInfo>, - prefix: Option<&[u8]>, - start_at: Option<&[u8]>, - f: F, - ) { - self.caching_state().apply_to_keys_while(child_info, prefix, start_at, f) - } - - fn next_storage_key(&self, key: &[u8]) -> Result>, Self::Error> { - self.caching_state().next_storage_key(key) - } - - fn next_child_storage_key( - &self, - child_info: &ChildInfo, - key: &[u8], - ) -> Result>, Self::Error> { - self.caching_state().next_child_storage_key(child_info, key) - } - - fn for_keys_with_prefix(&self, prefix: &[u8], f: F) { - self.caching_state().for_keys_with_prefix(prefix, f) - } - - fn for_key_values_with_prefix(&self, prefix: &[u8], f: F) { - self.caching_state().for_key_values_with_prefix(prefix, f) - } - - fn for_child_keys_with_prefix( - &self, - child_info: &ChildInfo, - prefix: &[u8], - f: F, - ) { - self.caching_state().for_child_keys_with_prefix(child_info, prefix, f) - } - - fn storage_root<'a>( - &self, - delta: impl Iterator)>, - state_version: StateVersion, - ) -> (B::Hash, Self::Transaction) - where - B::Hash: Ord, - { - self.caching_state().storage_root(delta, state_version) - } - - fn child_storage_root<'a>( - &self, - child_info: &ChildInfo, - delta: impl Iterator)>, - state_version: StateVersion, - ) -> (B::Hash, bool, Self::Transaction) - where - B::Hash: Ord, - { - self.caching_state().child_storage_root(child_info, delta, state_version) - } - - fn pairs(&self) -> Vec<(Vec, Vec)> { - self.caching_state().pairs() - } - - fn keys(&self, prefix: &[u8]) -> Vec> { - self.caching_state().keys(prefix) - } - - fn child_keys(&self, child_info: &ChildInfo, prefix: &[u8]) -> Vec> { - self.caching_state().child_keys(child_info, prefix) - } - - fn as_trie_backend(&self) -> Option<&TrieBackend>> { - self.caching_state - .as_ref() - .expect("`caching_state` is valid for the lifetime of the object; qed") - .as_trie_backend() - } - - fn register_overlay_stats(&self, stats: &sp_state_machine::StateMachineStats) { - self.caching_state().register_overlay_stats(stats); - } - - fn usage_info(&self) -> sp_state_machine::UsageInfo { - self.caching_state().usage_info() - } -} - -impl Drop for SyncingCachingState { - fn drop(&mut self) { - if self.disable_syncing { - return - } - - if let Some(mut caching_state) = self.caching_state.take() { - let _lock = self.lock.read(); - - self.state_usage.merge_sm(caching_state.usage.take()); - if let Some(hash) = caching_state.cache.parent_hash { - let is_best = self.meta.read().best_hash == hash; - caching_state.cache.sync_cache(&[], &[], vec![], vec![], None, None, is_best); - } - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - use sp_runtime::{ - testing::{Block as RawBlock, ExtrinsicWrapper, H256}, - traits::BlakeTwo256, - }; - use sp_state_machine::InMemoryBackend; - - type Block = RawBlock>; - - #[test] - fn smoke() { - // init_log(); - let root_parent = H256::random(); - let key = H256::random()[..].to_vec(); - let h0 = H256::random(); - let h1a = H256::random(); - let h1b = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3a = H256::random(); - let h3b = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - - // blocks [ 3a(c) 2a(c) 2b 1b 1a(c) 0 ] - // state [ 5 5 4 3 2 2 ] - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h0), - Some(0), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h0)); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h1a), Some(1), true); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h0)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![3]))], - vec![], - Some(h1b), - Some(1), - false, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1b)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![4]))], - vec![], - Some(h2b), - Some(2), - false, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1a)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![5]))], - vec![], - Some(h2a), - Some(2), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2a)); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h3a), Some(3), true); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h3a)); - assert_eq!(s.storage(&key).unwrap().unwrap(), vec![5]); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1a)); - assert!(s.storage(&key).unwrap().is_none()); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2b)); - assert!(s.storage(&key).unwrap().is_none()); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1b)); - assert!(s.storage(&key).unwrap().is_none()); - - // reorg to 3b - // blocks [ 3b(c) 3a 2a 2b(c) 1b 1a 0 ] - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2b)); - s.cache.sync_cache( - &[h1b, h2b, h3b], - &[h1a, h2a, h3a], - vec![], - vec![], - Some(h3b), - Some(3), - true, - ); - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h3a)); - assert!(s.storage(&key).unwrap().is_none()); - } - - #[test] - fn simple_fork() { - sp_tracing::try_init_simple(); - - let root_parent = H256::random(); - let key = H256::random()[..].to_vec(); - let h1 = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3b = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h1), - Some(1), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h2a), Some(2), true); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![3]))], - vec![], - Some(h2b), - Some(2), - false, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2b)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![3]))], - vec![], - Some(h3b), - Some(2), - false, - ); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2a)); - assert_eq!(s.storage(&key).unwrap().unwrap(), vec![2]); - } - - #[test] - fn double_fork() { - let root_parent = H256::random(); - let key = H256::random()[..].to_vec(); - let h1 = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3a = H256::random(); - let h3b = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h1), Some(1), true); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h2a), Some(2), true); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2a)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h3a), - Some(3), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h2b), Some(2), false); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h2b)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![3]))], - vec![], - Some(h3b), - Some(3), - false, - ); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h3a)); - assert_eq!(s.storage(&key).unwrap().unwrap(), vec![2]); - } - - #[test] - fn reverts_storage_hash() { - let root_parent = H256::random(); - let key = H256::random()[..].to_vec(); - let h1a = H256::random(); - let h1b = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - let mut backend = InMemoryBackend::::default(); - backend.insert( - std::iter::once((None, vec![(key.clone(), Some(vec![1]))])), - Default::default(), - ); - - let mut s = CachingState::new(backend.clone(), shared.clone(), Some(root_parent)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h1a), - Some(1), - true, - ); - - let mut s = CachingState::new(backend.clone(), shared.clone(), Some(root_parent)); - s.cache.sync_cache(&[], &[h1a], vec![], vec![], Some(h1b), Some(1), true); - - let s = CachingState::new(backend.clone(), shared.clone(), Some(h1b)); - assert_eq!(s.storage_hash(&key).unwrap().unwrap(), BlakeTwo256::hash(&vec![1])); - } - - #[test] - fn should_track_used_size_correctly() { - let root_parent = H256::random(); - let shared = new_shared_cache::(109, ((109 - 36), 109)); - let h0 = H256::random(); - - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - - let key = H256::random()[..].to_vec(); - let s_key = H256::random()[..].to_vec(); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![1, 2, 3]))], - vec![], - Some(h0), - Some(0), - true, - ); - // 32 key, 3 byte size - assert_eq!(shared.read().used_storage_cache_size(), 35 /* bytes */); - - let key = H256::random()[..].to_vec(); - s.cache.sync_cache( - &[], - &[], - vec![], - vec![(s_key.clone(), vec![(key.clone(), Some(vec![1, 2]))])], - Some(h0), - Some(0), - true, - ); - // 35 + (2 * 32) key, 2 byte size - assert_eq!(shared.read().used_storage_cache_size(), 101 /* bytes */); - } - - #[test] - fn should_remove_lru_items_based_on_tracking_used_size() { - let root_parent = H256::random(); - let shared = new_shared_cache::(36 * 3, (2, 3)); - let h0 = H256::random(); - - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - - let key = H256::random()[..].to_vec(); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![1, 2, 3, 4]))], - vec![], - Some(h0), - Some(0), - true, - ); - // 32 key, 4 byte size - assert_eq!(shared.read().used_storage_cache_size(), 36 /* bytes */); - - let key = H256::random()[..].to_vec(); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![1, 2]))], - vec![], - Some(h0), - Some(0), - true, - ); - // 32 key, 2 byte size - assert_eq!(shared.read().used_storage_cache_size(), 34 /* bytes */); - } - - #[test] - fn fix_storage_mismatch_issue() { - sp_tracing::try_init_simple(); - let root_parent = H256::random(); - - let key = H256::random()[..].to_vec(); - - let h0 = H256::random(); - let h1 = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h0), - Some(0), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h0)); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![3]))], - vec![], - Some(h1), - Some(1), - true, - ); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - assert_eq!(s.storage(&key).unwrap(), Some(vec![3])); - - // Restart (or unknown block?), clear caches. - { - let mut cache = s.cache.shared_cache.write(); - let cache = &mut *cache; - cache.lru_storage.clear(); - cache.lru_hashes.clear(); - cache.lru_child_storage.clear(); - cache.modifications.clear(); - } - - // New value is written because of cache miss. - s.cache.local_cache.write().storage.insert(key.clone(), Some(vec![42])); - - // New value is propagated. - s.cache.sync_cache(&[], &[], vec![], vec![], None, None, true); - - let s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - assert_eq!(s.storage(&key).unwrap(), None); - } - - #[test] - fn same_block_no_changes() { - sp_tracing::try_init_simple(); - - let root_parent = H256::random(); - let key = H256::random()[..].to_vec(); - let h1 = H256::random(); - let h2 = H256::random(); - - let shared = new_shared_cache::(256 * 1024, (0, 1)); - - let mut s = CachingState::new( - InMemoryBackend::::default(), - shared.clone(), - Some(root_parent), - ); - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![1]))], - vec![], - Some(h1), - Some(1), - true, - ); - assert_eq!(shared.write().lru_storage.get(&key).unwrap(), &Some(vec![1])); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - - // commit as non-best - s.cache.sync_cache( - &[], - &[], - vec![(key.clone(), Some(vec![2]))], - vec![], - Some(h2), - Some(2), - false, - ); - - assert_eq!(shared.write().lru_storage.get(&key).unwrap(), &Some(vec![1])); - - let mut s = - CachingState::new(InMemoryBackend::::default(), shared.clone(), Some(h1)); - - // commit again as best with no changes - s.cache.sync_cache(&[], &[], vec![], vec![], Some(h2), Some(2), true); - assert_eq!(s.storage(&key).unwrap(), None); - } -} - -#[cfg(test)] -mod qc { - use std::collections::{hash_map::Entry, HashMap}; - - use quickcheck::{quickcheck, Arbitrary, TestResult}; - - use super::*; - use sp_runtime::{ - testing::{Block as RawBlock, ExtrinsicWrapper, H256}, - traits::BlakeTwo256, - }; - use sp_state_machine::InMemoryBackend; - - type Block = RawBlock>; - - type KeySet = Vec<(Vec, Option>)>; - - type KeyMap = HashMap, Option>>; - - #[derive(Debug, Clone)] - struct Node { - hash: H256, - #[allow(unused)] - parent: H256, - state: KeyMap, - changes: KeySet, - } - - impl Node { - fn new_next(&self, hash: H256, changes: KeySet) -> Self { - let mut state = self.state.clone(); - - for (k, v) in self.state.iter() { - state.insert(k.clone(), v.clone()); - } - for (k, v) in changes.clone().into_iter() { - state.insert(k, v); - } - - Self { hash, parent: self.hash, changes, state } - } - - fn new(hash: H256, parent: H256, changes: KeySet) -> Self { - let mut state = KeyMap::new(); - - for (k, v) in changes.clone().into_iter() { - state.insert(k, v); - } - - Self { hash, parent, state, changes } - } - - fn purge(&mut self, other_changes: &KeySet) { - for (k, _) in other_changes.iter() { - self.state.remove(k); - } - } - } - - #[derive(Debug, Clone)] - enum Action { - Next { hash: H256, changes: KeySet }, - Fork { depth: usize, hash: H256, changes: KeySet }, - ReorgWithImport { depth: usize, hash: H256 }, - FinalizationReorg { fork_depth: usize, depth: usize }, - } - - impl Arbitrary for Action { - fn arbitrary(gen: &mut quickcheck::Gen) -> Self { - let path = u8::arbitrary(gen); - let buf = (0..32).map(|_| u8::arbitrary(gen)).collect::>(); - - match path { - 0..=175 => Action::Next { - hash: H256::from_slice(&buf[..]), - changes: { - let mut set = Vec::new(); - for _ in 0..::arbitrary(gen) / (64 * 256 * 256 * 256) { - set.push((vec![u8::arbitrary(gen)], Some(vec![u8::arbitrary(gen)]))); - } - set - }, - }, - 176..=220 => Action::Fork { - hash: H256::from_slice(&buf[..]), - depth: ((u8::arbitrary(gen)) / 32) as usize, - changes: { - let mut set = Vec::new(); - for _ in 0..::arbitrary(gen) / (64 * 256 * 256 * 256) { - set.push((vec![u8::arbitrary(gen)], Some(vec![u8::arbitrary(gen)]))); - } - set - }, - }, - 221..=240 => { - Action::ReorgWithImport { - hash: H256::from_slice(&buf[..]), - depth: ((u8::arbitrary(gen)) / 32) as usize, // 0-7 - } - }, - _ => { - Action::FinalizationReorg { - fork_depth: ((u8::arbitrary(gen)) / 32) as usize, // 0-7 - depth: ((u8::arbitrary(gen)) / 64) as usize, // 0-3 - } - }, - } - } - } - - struct Mutator { - shared: SharedCache, - canon: Vec, - forks: HashMap>, - } - - impl Mutator { - fn new_empty() -> Self { - let shared = new_shared_cache::(256 * 1024, (0, 1)); - - Self { shared, canon: vec![], forks: HashMap::new() } - } - - fn head_state(&self, hash: H256) -> CachingState, Block> { - CachingState::new( - InMemoryBackend::::default(), - self.shared.clone(), - Some(hash), - ) - } - - fn canon_head_state(&self) -> CachingState, Block> { - self.head_state(self.canon.last().expect("Expected to be one commit").hash) - } - - fn mutate_static( - &mut self, - action: Action, - ) -> CachingState, Block> { - self.mutate(action) - .expect("Expected to provide only valid actions to the mutate_static") - } - - fn canon_len(&self) -> usize { - return self.canon.len() - } - - fn head_storage_ref(&self) -> &KeyMap { - &self.canon.last().expect("Expected to be one commit").state - } - - fn key_permutations() -> Vec> { - (0u8..255).map(|x| vec![x]).collect() - } - - fn mutate( - &mut self, - action: Action, - ) -> Result, Block>, ()> { - let state = match action { - Action::Fork { depth, hash, changes } => { - let pos = self.canon.len() as isize - depth as isize; - if pos < 0 || self.canon.len() == 0 || pos >= (self.canon.len() - 1) as isize - // no fork on top also, thus len-1 - { - return Err(()) - } - - let pos = pos as usize; - - let fork_at = self.canon[pos].hash; - - let (total_h, parent) = match self.forks.entry(fork_at) { - Entry::Occupied(occupied) => { - let chain = occupied.into_mut(); - let parent = - chain.last().expect("No empty forks are ever created").clone(); - let mut node = parent.new_next(hash, changes.clone()); - - for earlier in chain.iter() { - node.purge(&earlier.changes.clone()); - } - - chain.push(node); - - (pos + chain.len(), parent.hash) - }, - Entry::Vacant(vacant) => { - let canon_parent = &self.canon[pos]; - vacant.insert(vec![canon_parent.new_next(hash, changes.clone())]); - - (pos + 1, fork_at) - }, - }; - - let mut state = CachingState::new( - InMemoryBackend::::default(), - self.shared.clone(), - Some(parent), - ); - - state.cache.sync_cache( - &[], - &[], - changes, - vec![], - Some(hash), - Some(total_h as u64), - false, - ); - - state - }, - Action::Next { hash, changes } => { - let (next, parent_hash) = match self.canon.last() { - None => { - let parent_hash = H256::from(&[0u8; 32]); - (Node::new(hash, parent_hash, changes.clone()), parent_hash) - }, - Some(parent) => (parent.new_next(hash, changes.clone()), parent.hash), - }; - - // delete cache entries for earlier - for node in self.canon.iter_mut() { - node.purge(&next.changes); - if let Some(fork) = self.forks.get_mut(&node.hash) { - for node in fork.iter_mut() { - node.purge(&next.changes); - } - } - } - - let mut state = CachingState::new( - InMemoryBackend::::default(), - self.shared.clone(), - Some(parent_hash), - ); - - state.cache.sync_cache( - &[], - &[], - next.changes.clone(), - vec![], - Some(hash), - Some(self.canon.len() as u64 + 1), - true, - ); - - self.canon.push(next); - - state - }, - Action::ReorgWithImport { depth, hash } => { - let pos = self.canon.len() as isize - depth as isize; - if pos < 0 || pos + 1 >= self.canon.len() as isize { - return Err(()) - } - let fork_at = self.canon[pos as usize].hash; - let pos = pos as usize; - - match self.forks.get_mut(&fork_at) { - Some(chain) => { - let mut new_fork = self.canon.drain(pos + 1..).collect::>(); - - let retracted: Vec = - new_fork.iter().map(|node| node.hash).collect(); - let enacted: Vec = chain.iter().map(|node| node.hash).collect(); - - std::mem::swap(chain, &mut new_fork); - - let mut node = new_fork - .last() - .map(|node| node.new_next(hash, vec![])) - .expect("No empty fork ever created!"); - - for invalidators in chain.iter().chain(new_fork.iter()) { - node.purge(&invalidators.changes); - } - - self.canon.extend(new_fork.into_iter()); - - self.canon.push(node); - - let mut state = CachingState::new( - InMemoryBackend::::default(), - self.shared.clone(), - Some(fork_at), - ); - - let height = pos as u64 + enacted.len() as u64 + 2; - state.cache.sync_cache( - &enacted[..], - &retracted[..], - vec![], - vec![], - Some(hash), - Some(height), - true, - ); - - state - }, - None => { - return Err(()) // no reorg without a fork atm! - }, - } - }, - Action::FinalizationReorg { fork_depth, depth } => { - let pos = self.canon.len() as isize - fork_depth as isize; - if pos < 0 || pos + 1 >= self.canon.len() as isize { - return Err(()) - } - let fork_at = self.canon[pos as usize].hash; - let pos = pos as usize; - - match self.forks.get_mut(&fork_at) { - Some(fork_chain) => { - let sync_pos = fork_chain.len() as isize - - fork_chain.len() as isize - depth as isize; - if sync_pos < 0 || sync_pos >= fork_chain.len() as isize { - return Err(()) - } - let sync_pos = sync_pos as usize; - - let mut new_fork = self.canon.drain(pos + 1..).collect::>(); - - let retracted: Vec = - new_fork.iter().map(|node| node.hash).collect(); - let enacted: Vec = fork_chain - .iter() - .take(sync_pos + 1) - .map(|node| node.hash) - .collect(); - - std::mem::swap(fork_chain, &mut new_fork); - - self.shared.write().sync(&retracted, &enacted); - - self.head_state( - self.canon - .last() - .expect("wasn't forking to emptiness so there should be one!") - .hash, - ) - }, - None => { - return Err(()) // no reorg to nothing pls! - }, - } - }, - }; - - Ok(state) - } - } - - #[test] - fn smoke() { - let key = H256::random()[..].to_vec(); - let h0 = H256::random(); - let h1a = H256::random(); - let h1b = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3a = H256::random(); - let h3b = H256::random(); - - let mut mutator = Mutator::new_empty(); - mutator - .mutate_static(Action::Next { hash: h0, changes: vec![(key.clone(), Some(vec![2]))] }); - mutator.mutate_static(Action::Next { hash: h1a, changes: vec![] }); - mutator.mutate_static(Action::Fork { - depth: 2, - hash: h1b, - changes: vec![(key.clone(), Some(vec![3]))], - }); - mutator.mutate_static(Action::Fork { - depth: 2, - hash: h2b, - changes: vec![(key.clone(), Some(vec![4]))], - }); - mutator - .mutate_static(Action::Next { hash: h2a, changes: vec![(key.clone(), Some(vec![5]))] }); - mutator.mutate_static(Action::Next { hash: h3a, changes: vec![] }); - - assert_eq!( - mutator.head_state(h3a).storage(&key).unwrap().expect("there should be a value"), - vec![5] - ); - assert!(mutator.head_state(h1a).storage(&key).unwrap().is_none()); - assert!(mutator.head_state(h2b).storage(&key).unwrap().is_none()); - assert!(mutator.head_state(h1b).storage(&key).unwrap().is_none()); - - mutator.mutate_static(Action::ReorgWithImport { depth: 4, hash: h3b }); - assert!(mutator.head_state(h3a).storage(&key).unwrap().is_none()); - } - - fn is_head_match(mutator: &Mutator) -> bool { - let head_state = mutator.canon_head_state(); - - for key in Mutator::key_permutations() { - match (head_state.storage(&key).unwrap(), mutator.head_storage_ref().get(&key)) { - (Some(x), Some(y)) => - if Some(&x) != y.as_ref() { - eprintln!("{:?} != {:?}", x, y); - return false - }, - (None, Some(_y)) => { - // TODO: cache miss is not tracked atm - }, - (Some(x), None) => { - eprintln!("{:?} != ", x); - return false - }, - _ => continue, - } - } - true - } - - fn is_canon_match(mutator: &Mutator) -> bool { - for node in mutator.canon.iter() { - let head_state = mutator.head_state(node.hash); - for key in Mutator::key_permutations() { - match (head_state.storage(&key).unwrap(), node.state.get(&key)) { - (Some(x), Some(y)) => - if Some(&x) != y.as_ref() { - eprintln!("at [{}]: {:?} != {:?}", node.hash, x, y); - return false - }, - (None, Some(_y)) => { - // cache miss is not tracked atm - }, - (Some(x), None) => { - eprintln!("at [{}]: {:?} != ", node.hash, x); - return false - }, - _ => continue, - } - } - } - true - } - - #[test] - fn reorg() { - let key = H256::random()[..].to_vec(); - let h0 = H256::random(); - let h1 = H256::random(); - let h2 = H256::random(); - let h1b = H256::random(); - let h2b = H256::random(); - - let mut mutator = Mutator::new_empty(); - mutator.mutate_static(Action::Next { hash: h0, changes: vec![] }); - mutator.mutate_static(Action::Next { hash: h1, changes: vec![] }); - mutator - .mutate_static(Action::Next { hash: h2, changes: vec![(key.clone(), Some(vec![2]))] }); - mutator.mutate_static(Action::Fork { - depth: 2, - hash: h1b, - changes: vec![(key.clone(), Some(vec![3]))], - }); - mutator.mutate_static(Action::ReorgWithImport { depth: 2, hash: h2b }); - - assert!(is_head_match(&mutator)) - } - - fn key(k: u8) -> Vec { - vec![k] - } - fn val(v: u8) -> Option> { - Some(vec![v]) - } - fn keyval(k: u8, v: u8) -> KeySet { - vec![(key(k), val(v))] - } - - #[test] - fn reorg2() { - let h0 = H256::random(); - let h1a = H256::random(); - let h1b = H256::random(); - let h2b = H256::random(); - let h2a = H256::random(); - let h3a = H256::random(); - - let mut mutator = Mutator::new_empty(); - mutator.mutate_static(Action::Next { hash: h0, changes: keyval(1, 1) }); - mutator.mutate_static(Action::Next { hash: h1a, changes: keyval(1, 1) }); - mutator.mutate_static(Action::Fork { depth: 2, hash: h1b, changes: keyval(2, 2) }); - - mutator.mutate_static(Action::Next { hash: h2a, changes: keyval(3, 3) }); - mutator.mutate_static(Action::Next { hash: h3a, changes: keyval(4, 4) }); - mutator.mutate_static(Action::ReorgWithImport { depth: 4, hash: h2b }); - - assert!(is_head_match(&mutator)) - } - - #[test] - fn fork2() { - let h1 = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3a = H256::random(); - let h3b = H256::random(); - - let mut mutator = Mutator::new_empty(); - mutator.mutate_static(Action::Next { hash: h1, changes: vec![] }); - mutator.mutate_static(Action::Next { hash: h2a, changes: vec![] }); - mutator.mutate_static(Action::Next { hash: h3a, changes: keyval(1, 1) }); - - mutator.mutate_static(Action::Fork { depth: 2, hash: h2b, changes: vec![] }); - mutator.mutate_static(Action::Fork { depth: 2, hash: h3b, changes: keyval(1, 2) }); - - assert!(is_head_match(&mutator)) - } - - #[test] - fn fork3() { - let h1 = H256::random(); - let h2a = H256::random(); - let h2b = H256::random(); - let h3a = H256::random(); - - let mut mutator = Mutator::new_empty(); - mutator.mutate_static(Action::Next { hash: h1, changes: keyval(1, 1) }); - mutator.mutate_static(Action::Next { hash: h2a, changes: keyval(2, 2) }); - mutator.mutate_static(Action::Next { hash: h3a, changes: keyval(3, 3) }); - - mutator.mutate_static(Action::Fork { depth: 2, hash: h2b, changes: keyval(1, 3) }); - - assert!(is_canon_match(&mutator)) - } - - quickcheck! { - fn head_complete(actions: Vec) -> TestResult { - let mut mutator = Mutator::new_empty(); - - for action in actions.into_iter() { - if let Err(_) = mutator.mutate(action) { - return TestResult::discard(); - } - } - - if mutator.canon_len() == 0 { - return TestResult::discard(); - } - - TestResult::from_bool(is_head_match(&mutator)) - } - - fn canon_complete(actions: Vec) -> TestResult { - let mut mutator = Mutator::new_empty(); - - for action in actions.into_iter() { - if let Err(_) = mutator.mutate(action) { - return TestResult::discard(); - } - } - - if mutator.canon_len() == 0 { - return TestResult::discard(); - } - - TestResult::from_bool(is_canon_match(&mutator)) - } - } -} diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index 83d4801d1c92b..8bedcd3edcff3 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -19,7 +19,7 @@ parking_lot = "0.12.1" tracing = "0.1.29" wasmi = "0.13" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } sc-executor-common = { version = "0.10.0-dev", path = "common" } sc-executor-wasmi = { version = "0.10.0-dev", path = "wasmi" } sc-executor-wasmtime = { version = "0.10.0-dev", path = "wasmtime" } @@ -35,6 +35,7 @@ sp-wasm-interface = { version = "7.0.0", path = "../../primitives/wasm-interface [dev-dependencies] array-bytes = "4.1" +assert_matches = "1.3.0" wat = "1.0" sc-runtime-test = { version = "2.0.0", path = "runtime-test" } substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" } diff --git a/client/executor/runtime-test/Cargo.toml b/client/executor/runtime-test/Cargo.toml index e99f3caa9447e..07626c2343361 100644 --- a/client/executor/runtime-test/Cargo.toml +++ b/client/executor/runtime-test/Cargo.toml @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] sp-core = { version = "7.0.0", default-features = false, path = "../../../primitives/core" } sp-io = { version = "7.0.0", default-features = false, features = ["improved_panic_error_reporting"], path = "../../../primitives/io" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } +sp-runtime-interface = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime-interface" } sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } [build-dependencies] diff --git a/client/executor/runtime-test/src/lib.rs b/client/executor/runtime-test/src/lib.rs index fc98d1909d00d..0aa30e4bc9675 100644 --- a/client/executor/runtime-test/src/lib.rs +++ b/client/executor/runtime-test/src/lib.rs @@ -29,6 +29,8 @@ use sp_runtime::{ print, traits::{BlakeTwo256, Hash}, }; +#[cfg(not(feature = "std"))] +use sp_runtime_interface::pack_ptr_and_len; extern "C" { #[allow(dead_code)] @@ -38,6 +40,10 @@ extern "C" { fn yet_another_missing_external(); } +#[cfg(not(feature = "std"))] +/// The size of a WASM page in bytes. +const WASM_PAGE_SIZE: usize = 65536; + #[cfg(not(feature = "std"))] /// Mutable static variables should be always observed to have /// the initialized value at the start of a runtime call. @@ -92,7 +98,7 @@ sp_core::wasm_export_functions! { let heap_ptr = heap_base as usize; // Find the next wasm page boundary. - let heap_ptr = round_up_to(heap_ptr, 65536); + let heap_ptr = round_up_to(heap_ptr, WASM_PAGE_SIZE); // Make it an actual pointer let heap_ptr = heap_ptr as *mut u8; @@ -337,3 +343,32 @@ sp_core::wasm_export_functions! { return 1234; } } + +// Returns a huge len. It should result in an error, and not an allocation. +#[no_mangle] +#[cfg(not(feature = "std"))] +pub extern "C" fn test_return_huge_len(_params: *const u8, _len: usize) -> u64 { + pack_ptr_and_len(0, u32::MAX) +} + +// Returns an offset right at the edge of the wasm memory boundary. With length 0, it should +// succeed. +#[no_mangle] +#[cfg(not(feature = "std"))] +pub extern "C" fn test_return_max_memory_offset(_params: *const u8, _len: usize) -> u64 { + pack_ptr_and_len((core::arch::wasm32::memory_size(0) * WASM_PAGE_SIZE) as u32, 0) +} + +// Returns an offset right at the edge of the wasm memory boundary. With length 1, it should fail. +#[no_mangle] +#[cfg(not(feature = "std"))] +pub extern "C" fn test_return_max_memory_offset_plus_one(_params: *const u8, _len: usize) -> u64 { + pack_ptr_and_len((core::arch::wasm32::memory_size(0) * WASM_PAGE_SIZE) as u32, 1) +} + +// Returns an output that overflows the u32 range. It should result in an error. +#[no_mangle] +#[cfg(not(feature = "std"))] +pub extern "C" fn test_return_overflow(_params: *const u8, _len: usize) -> u64 { + pack_ptr_and_len(u32::MAX, 1) +} diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index 25b999f115363..22e651abe98e2 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -19,8 +19,13 @@ #[cfg(target_os = "linux")] mod linux; +use assert_matches::assert_matches; use codec::{Decode, Encode}; -use sc_executor_common::{error::Error, runtime_blob::RuntimeBlob, wasm_runtime::WasmModule}; +use sc_executor_common::{ + error::{Error, WasmError}, + runtime_blob::RuntimeBlob, + wasm_runtime::WasmModule, +}; use sc_runtime_test::wasm_binary_unwrap; use sp_core::{ blake2_128, blake2_256, ed25519, map, @@ -781,3 +786,67 @@ fn return_value(wasm_method: WasmExecutionMethod) { (1234u64).encode() ); } + +test_wasm_execution!(return_huge_len); +fn return_huge_len(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + + match call_in_wasm("test_return_huge_len", &[], wasm_method, &mut ext).unwrap_err() { + Error::Runtime => { + assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); + }, + Error::RuntimeConstruction(WasmError::Other(error)) => { + assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); + assert_eq!(error, "output exceeds bounds of wasm memory"); + }, + error => panic!("unexpected error: {:?}", error), + } +} + +test_wasm_execution!(return_max_memory_offset); +fn return_max_memory_offset(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + + assert_eq!( + call_in_wasm("test_return_max_memory_offset", &[], wasm_method, &mut ext).unwrap(), + ().encode() + ); +} + +test_wasm_execution!(return_max_memory_offset_plus_one); +fn return_max_memory_offset_plus_one(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + + match call_in_wasm("test_return_max_memory_offset_plus_one", &[], wasm_method, &mut ext) + .unwrap_err() + { + Error::Runtime => { + assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); + }, + Error::RuntimeConstruction(WasmError::Other(error)) => { + assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); + assert_eq!(error, "output exceeds bounds of wasm memory"); + }, + error => panic!("unexpected error: {:?}", error), + } +} + +test_wasm_execution!(return_overflow); +fn return_overflow(wasm_method: WasmExecutionMethod) { + let mut ext = TestExternalities::default(); + let mut ext = ext.ext(); + + match call_in_wasm("test_return_overflow", &[], wasm_method, &mut ext).unwrap_err() { + Error::Runtime => { + assert_matches!(wasm_method, WasmExecutionMethod::Interpreted); + }, + Error::RuntimeConstruction(WasmError::Other(error)) => { + assert_matches!(wasm_method, WasmExecutionMethod::Compiled { .. }); + assert_eq!(error, "output exceeds bounds of wasm memory"); + }, + error => panic!("unexpected error: {:?}", error), + } +} diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index 7e38929f05a13..983ca3c44fba0 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -47,4 +47,4 @@ sc-runtime-test = { version = "2.0.0", path = "../runtime-test" } sp-io = { version = "7.0.0", path = "../../../primitives/io" } tempfile = "3.3.0" paste = "1.0" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index b124fd627dc69..a45dfa59b954a 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -30,6 +30,7 @@ use sc_executor_common::{ runtime_blob::{ self, DataSegmentsSnapshot, ExposedMutableGlobalsSet, GlobalsSnapshot, RuntimeBlob, }, + util::checked_range, wasm_runtime::{InvokeMethod, WasmInstance, WasmModule}, }; use sp_runtime_interface::unpack_ptr_and_len; @@ -41,7 +42,7 @@ use std::{ Arc, }, }; -use wasmtime::{Engine, Memory, StoreLimits, Table}; +use wasmtime::{AsContext, Engine, Memory, StoreLimits, Table}; pub(crate) struct StoreData { /// The limits we apply to the store. We need to store it here to return a reference to this @@ -793,7 +794,19 @@ fn extract_output_data( output_ptr: u32, output_len: u32, ) -> Result> { + let ctx = instance.store(); + + // Do a length check before allocating. The returned output should not be bigger than the + // available WASM memory. Otherwise, a malicious parachain can trigger a large allocation, + // potentially causing memory exhaustion. + // + // Get the size of the WASM memory in bytes. + let memory_size = ctx.as_context().data().memory().data_size(ctx); + if checked_range(output_ptr as usize, output_len as usize, memory_size).is_none() { + Err(WasmError::Other("output exceeds bounds of wasm memory".into()))? + } let mut output = vec![0; output_len as usize]; - util::read_memory_into(instance.store(), Pointer::new(output_ptr), &mut output)?; + + util::read_memory_into(ctx, Pointer::new(output_ptr), &mut output)?; Ok(output) } diff --git a/client/finality-grandpa/Cargo.toml b/client/finality-grandpa/Cargo.toml index 95195bfc2d946..9a31a1d4bf6a7 100644 --- a/client/finality-grandpa/Cargo.toml +++ b/client/finality-grandpa/Cargo.toml @@ -22,7 +22,7 @@ finality-grandpa = { version = "0.16.1", features = ["derive-codec"] } futures = "0.3.21" futures-timer = "3.0.1" log = "0.4.17" -parity-scale-codec = { version = "3.0.0", features = ["derive"] } +parity-scale-codec = { version = "3.2.2", features = ["derive"] } parking_lot = "0.12.1" rand = "0.8.5" serde_json = "1.0.85" diff --git a/client/finality-grandpa/rpc/Cargo.toml b/client/finality-grandpa/rpc/Cargo.toml index 429331e96a65f..e20ff174d41c5 100644 --- a/client/finality-grandpa/rpc/Cargo.toml +++ b/client/finality-grandpa/rpc/Cargo.toml @@ -14,7 +14,7 @@ finality-grandpa = { version = "0.16.1", features = ["derive-codec"] } futures = "0.3.16" jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } log = "0.4.8" -parity-scale-codec = { version = "3.0.0", features = ["derive"] } +parity-scale-codec = { version = "3.2.2", features = ["derive"] } serde = { version = "1.0.105", features = ["derive"] } thiserror = "1.0" sc-client-api = { version = "4.0.0-dev", path = "../../api" } diff --git a/client/finality-grandpa/src/until_imported.rs b/client/finality-grandpa/src/until_imported.rs index 776411f8fb493..3bca77ae8393f 100644 --- a/client/finality-grandpa/src/until_imported.rs +++ b/client/finality-grandpa/src/until_imported.rs @@ -593,16 +593,17 @@ mod tests { fn import_header(&self, header: Header) { let hash = header.hash(); let number = *header.number(); - + let (tx, _rx) = tracing_unbounded("unpin-worker-channel", 10_000); self.known_blocks.lock().insert(hash, number); self.sender - .unbounded_send(BlockImportNotification { + .unbounded_send(BlockImportNotification::::new( hash, - origin: BlockOrigin::File, + BlockOrigin::File, header, - is_new_best: false, - tree_route: None, - }) + false, + None, + tx, + )) .unwrap(); } } diff --git a/client/merkle-mountain-range/Cargo.toml b/client/merkle-mountain-range/Cargo.toml index cd7d9442492e1..90fb65453ba12 100644 --- a/client/merkle-mountain-range/Cargo.toml +++ b/client/merkle-mountain-range/Cargo.toml @@ -11,7 +11,7 @@ homepage = "https://substrate.io" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3" log = "0.4" beefy-primitives = { version = "4.0.0-dev", path = "../../primitives/beefy", package = "sp-beefy" } diff --git a/client/merkle-mountain-range/rpc/Cargo.toml b/client/merkle-mountain-range/rpc/Cargo.toml index dcc5e49c52051..ce71158808e1e 100644 --- a/client/merkle-mountain-range/rpc/Cargo.toml +++ b/client/merkle-mountain-range/rpc/Cargo.toml @@ -12,7 +12,7 @@ description = "Node-specific RPC methods for interaction with Merkle Mountain Ra targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } serde = { version = "1.0.136", features = ["derive"] } sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } diff --git a/client/network/Cargo.toml b/client/network/Cargo.toml index 4356cc5df1b13..44a7973b33659 100644 --- a/client/network/Cargo.toml +++ b/client/network/Cargo.toml @@ -19,7 +19,7 @@ async-trait = "0.1" asynchronous-codec = "0.6" backtrace = "0.3.67" bytes = "1" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } either = "1.5.3" fnv = "1.0.6" futures = "0.3.21" diff --git a/client/network/common/Cargo.toml b/client/network/common/Cargo.toml index e2dd7d0894eb0..2aaada021f0fa 100644 --- a/client/network/common/Cargo.toml +++ b/client/network/common/Cargo.toml @@ -19,7 +19,7 @@ prost-build = "0.11" async-trait = "0.1.57" bitflags = "1.3.2" bytes = "1" -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } futures = "0.3.21" diff --git a/client/network/common/src/config.rs b/client/network/common/src/config.rs index 96c7c11ec2696..c35d8a42a8c0c 100644 --- a/client/network/common/src/config.rs +++ b/client/network/common/src/config.rs @@ -301,10 +301,10 @@ pub enum TransportConfig { /// and connect to them if they support the same chain. enable_mdns: bool, - /// If true, allow connecting to private IPv4 addresses (as defined in + /// If true, allow connecting to private IPv4/IPv6 addresses (as defined in /// [RFC1918](https://tools.ietf.org/html/rfc1918)). Irrelevant for addresses that have /// been passed in `::sc_network::config::NetworkConfiguration::boot_nodes`. - allow_private_ipv4: bool, + allow_private_ip: bool, }, /// Only allow connections within the same process. diff --git a/client/network/light/Cargo.toml b/client/network/light/Cargo.toml index 8a3e9037729a8..f3e94790df61e 100644 --- a/client/network/light/Cargo.toml +++ b/client/network/light/Cargo.toml @@ -17,7 +17,7 @@ prost-build = "0.11" [dependencies] array-bytes = "4.1" -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } futures = "0.3.21" diff --git a/client/network/src/config.rs b/client/network/src/config.rs index 52993e2519400..8ca8d75f1681c 100644 --- a/client/network/src/config.rs +++ b/client/network/src/config.rs @@ -223,7 +223,7 @@ impl NetworkConfiguration { extra_sets: Vec::new(), client_version: client_version.into(), node_name: node_name.into(), - transport: TransportConfig::Normal { enable_mdns: false, allow_private_ipv4: true }, + transport: TransportConfig::Normal { enable_mdns: false, allow_private_ip: true }, max_parallel_downloads: 5, sync_mode: SyncMode::Full, enable_dht_random_walk: true, diff --git a/client/network/src/discovery.rs b/client/network/src/discovery.rs index 5421db8194456..7329ebf15cf0d 100644 --- a/client/network/src/discovery.rs +++ b/client/network/src/discovery.rs @@ -96,7 +96,7 @@ pub struct DiscoveryConfig { local_peer_id: PeerId, permanent_addresses: Vec<(PeerId, Multiaddr)>, dht_random_walk: bool, - allow_private_ipv4: bool, + allow_private_ip: bool, allow_non_globals_in_dht: bool, discovery_only_if_under_num: u64, enable_mdns: bool, @@ -111,7 +111,7 @@ impl DiscoveryConfig { local_peer_id: local_public_key.to_peer_id(), permanent_addresses: Vec::new(), dht_random_walk: true, - allow_private_ipv4: true, + allow_private_ip: true, allow_non_globals_in_dht: false, discovery_only_if_under_num: std::u64::MAX, enable_mdns: false, @@ -142,9 +142,9 @@ impl DiscoveryConfig { self } - /// Should private IPv4 addresses be reported? - pub fn allow_private_ipv4(&mut self, value: bool) -> &mut Self { - self.allow_private_ipv4 = value; + /// Should private IPv4/IPv6 addresses be reported? + pub fn allow_private_ip(&mut self, value: bool) -> &mut Self { + self.allow_private_ip = value; self } @@ -189,7 +189,7 @@ impl DiscoveryConfig { local_peer_id, permanent_addresses, dht_random_walk, - allow_private_ipv4, + allow_private_ip, allow_non_globals_in_dht, discovery_only_if_under_num, enable_mdns, @@ -231,7 +231,7 @@ impl DiscoveryConfig { pending_events: VecDeque::new(), local_peer_id, num_connections: 0, - allow_private_ipv4, + allow_private_ip, discovery_only_if_under_num, mdns: if enable_mdns { match TokioMdns::new(mdns::Config::default()) { @@ -278,9 +278,9 @@ pub struct DiscoveryBehaviour { local_peer_id: PeerId, /// Number of nodes we're currently connected to. num_connections: u64, - /// If false, `addresses_of_peer` won't return any private IPv4 address, except for the ones - /// stored in `permanent_addresses` or `ephemeral_addresses`. - allow_private_ipv4: bool, + /// If false, `addresses_of_peer` won't return any private IPv4/IPv6 address, except for the + /// ones stored in `permanent_addresses` or `ephemeral_addresses`. + allow_private_ip: bool, /// Number of active connections over which we interrupt the discovery process. discovery_only_if_under_num: u64, /// Should non-global addresses be added to the DHT? @@ -506,7 +506,7 @@ impl NetworkBehaviour for DiscoveryBehaviour { list_to_filter.extend(mdns.addresses_of_peer(peer_id)); } - if !self.allow_private_ipv4 { + if !self.allow_private_ip { list_to_filter.retain(|addr| match addr.iter().next() { Some(Protocol::Ip4(addr)) if !IpNetwork::from(addr).is_global() => false, Some(Protocol::Ip6(addr)) if !IpNetwork::from(addr).is_global() => false, @@ -947,7 +947,7 @@ mod tests { let mut config = DiscoveryConfig::new(keypair.public()); config .with_permanent_addresses(first_swarm_peer_id_and_addr.clone()) - .allow_private_ipv4(true) + .allow_private_ip(true) .allow_non_globals_in_dht(true) .discovery_limit(50) .with_kademlia(genesis_hash, fork_id, &protocol_id); @@ -1065,7 +1065,7 @@ mod tests { let keypair = Keypair::generate_ed25519(); let mut config = DiscoveryConfig::new(keypair.public()); config - .allow_private_ipv4(true) + .allow_private_ip(true) .allow_non_globals_in_dht(true) .discovery_limit(50) .with_kademlia(supported_genesis_hash, None, &supported_protocol_id); diff --git a/client/network/src/peer_info.rs b/client/network/src/peer_info.rs index 97604a82c35b0..f3402c0af110c 100644 --- a/client/network/src/peer_info.rs +++ b/client/network/src/peer_info.rs @@ -89,7 +89,9 @@ impl PeerInfoBehaviour { pub fn new(user_agent: String, local_public_key: PublicKey) -> Self { let identify = { let cfg = IdentifyConfig::new("/substrate/1.0".to_string(), local_public_key) - .with_agent_version(user_agent); + .with_agent_version(user_agent) + // We don't need any peer information cached. + .with_cache_size(0); Identify::new(cfg) }; @@ -182,10 +184,10 @@ impl NetworkBehaviour for PeerInfoBehaviour { IntoConnectionHandler::select(self.ping.new_handler(), self.identify.new_handler()) } - fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - let mut list = self.ping.addresses_of_peer(peer_id); - list.extend_from_slice(&self.identify.addresses_of_peer(peer_id)); - list + fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { + // Only `Discovery::addresses_of_peer` must be returning addresses to ensure that we + // don't return unwanted addresses. + Vec::new() } fn on_swarm_event(&mut self, event: FromSwarm) { diff --git a/client/network/src/protocol.rs b/client/network/src/protocol.rs index fd25c6526ff12..cd232334e3b6a 100644 --- a/client/network/src/protocol.rs +++ b/client/network/src/protocol.rs @@ -965,8 +965,10 @@ where self.behaviour.new_handler() } - fn addresses_of_peer(&mut self, peer_id: &PeerId) -> Vec { - self.behaviour.addresses_of_peer(peer_id) + fn addresses_of_peer(&mut self, _: &PeerId) -> Vec { + // Only `Discovery::addresses_of_peer` must be returning addresses to ensure that we + // don't return unwanted addresses. + Vec::new() } fn on_swarm_event(&mut self, event: FromSwarm) { diff --git a/client/network/src/service.rs b/client/network/src/service.rs index f943a03f50b38..f3afb58e8e0e0 100644 --- a/client/network/src/service.rs +++ b/client/network/src/service.rs @@ -291,11 +291,15 @@ where match params.network_config.transport { TransportConfig::MemoryOnly => { config.with_mdns(false); - config.allow_private_ipv4(false); + config.allow_private_ip(false); }, - TransportConfig::Normal { enable_mdns, allow_private_ipv4, .. } => { + TransportConfig::Normal { + enable_mdns, + allow_private_ip: allow_private_ipv4, + .. + } => { config.with_mdns(enable_mdns); - config.allow_private_ipv4(allow_private_ipv4); + config.allow_private_ip(allow_private_ipv4); }, } diff --git a/client/network/src/transport.rs b/client/network/src/transport.rs index 6c792a0c38042..51a8483e0e4e6 100644 --- a/client/network/src/transport.rs +++ b/client/network/src/transport.rs @@ -54,18 +54,27 @@ pub fn build_transport( ) -> (Boxed<(PeerId, StreamMuxerBox)>, Arc) { // Build the base layer of the transport. let transport = if !memory_only { + // Main transport: DNS(TCP) let tcp_config = tcp::Config::new().nodelay(true); - let desktop_trans = tcp::tokio::Transport::new(tcp_config.clone()); - let desktop_trans = websocket::WsConfig::new(desktop_trans) - .or_transport(tcp::tokio::Transport::new(tcp_config.clone())); - let dns_init = dns::TokioDnsConfig::system(desktop_trans); + let tcp_trans = tcp::tokio::Transport::new(tcp_config.clone()); + let dns_init = dns::TokioDnsConfig::system(tcp_trans); + EitherTransport::Left(if let Ok(dns) = dns_init { - EitherTransport::Left(dns) + // WS + WSS transport + // + // Main transport can't be used for `/wss` addresses because WSS transport needs + // unresolved addresses (BUT WSS transport itself needs an instance of DNS transport to + // resolve and dial addresses). + let tcp_trans = tcp::tokio::Transport::new(tcp_config); + let dns_for_wss = dns::TokioDnsConfig::system(tcp_trans) + .expect("same system_conf & resolver to work"); + EitherTransport::Left(websocket::WsConfig::new(dns_for_wss).or_transport(dns)) } else { - let desktop_trans = tcp::tokio::Transport::new(tcp_config.clone()); - let desktop_trans = websocket::WsConfig::new(desktop_trans) + // In case DNS can't be constructed, fallback to TCP + WS (WSS won't work) + let tcp_trans = tcp::tokio::Transport::new(tcp_config.clone()); + let desktop_trans = websocket::WsConfig::new(tcp_trans) .or_transport(tcp::tokio::Transport::new(tcp_config)); - EitherTransport::Right(desktop_trans.map_err(dns::DnsErr::Transport)) + EitherTransport::Right(desktop_trans) }) } else { EitherTransport::Right(OptionalTransport::some( diff --git a/client/network/sync/Cargo.toml b/client/network/sync/Cargo.toml index d2cbeb5ec1f1d..ff72b4e993026 100644 --- a/client/network/sync/Cargo.toml +++ b/client/network/sync/Cargo.toml @@ -18,7 +18,7 @@ prost-build = "0.11" [dependencies] array-bytes = "4.1" async-trait = "0.1.58" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" libp2p = "0.50.0" log = "0.4.17" diff --git a/client/network/transactions/Cargo.toml b/client/network/transactions/Cargo.toml index df13cdcb820d2..cab702fbd7a6c 100644 --- a/client/network/transactions/Cargo.toml +++ b/client/network/transactions/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "4.1" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } futures = "0.3.21" libp2p = "0.50.0" log = "0.4.17" diff --git a/client/offchain/Cargo.toml b/client/offchain/Cargo.toml index 09df9187fb2d5..dd6e2e44caf55 100644 --- a/client/offchain/Cargo.toml +++ b/client/offchain/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "4.1" bytes = "1.1" -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } fnv = "1.0.6" futures = "0.3.21" futures-timer = "3.0.2" diff --git a/client/rpc-api/Cargo.toml b/client/rpc-api/Cargo.toml index f697f53e98283..261339f131888 100644 --- a/client/rpc-api/Cargo.toml +++ b/client/rpc-api/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.85" diff --git a/client/rpc-api/src/state/mod.rs b/client/rpc-api/src/state/mod.rs index 40e208c2eba8d..323e6ad1d41a3 100644 --- a/client/rpc-api/src/state/mod.rs +++ b/client/rpc-api/src/state/mod.rs @@ -71,8 +71,8 @@ pub trait StateApi { fn storage_hash(&self, key: StorageKey, hash: Option) -> RpcResult>; /// Returns the size of a storage entry at a block's state. - #[method(name = "state_getStorageSize", aliases = ["state_getStorageSizeAt"], blocking)] - fn storage_size(&self, key: StorageKey, hash: Option) -> RpcResult>; + #[method(name = "state_getStorageSize", aliases = ["state_getStorageSizeAt"])] + async fn storage_size(&self, key: StorageKey, hash: Option) -> RpcResult>; /// Returns the runtime metadata as an opaque blob. #[method(name = "state_getMetadata", blocking)] diff --git a/client/rpc-spec-v2/Cargo.toml b/client/rpc-spec-v2/Cargo.toml index f9598b3c1abc9..43fb189081bae 100644 --- a/client/rpc-spec-v2/Cargo.toml +++ b/client/rpc-spec-v2/Cargo.toml @@ -24,7 +24,7 @@ sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } sp-blockchain = { version = "4.0.0-dev", path = "../../primitives/blockchain" } sp-version = { version = "5.0.0", path = "../../primitives/version" } sc-client-api = { version = "4.0.0-dev", path = "../api" } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } thiserror = "1.0" serde = "1.0" hex = "0.4" diff --git a/client/rpc/Cargo.toml b/client/rpc/Cargo.toml index d97170ddf42e6..a22f657878812 100644 --- a/client/rpc/Cargo.toml +++ b/client/rpc/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" jsonrpsee = { version = "0.16.2", features = ["server"] } log = "0.4.17" @@ -36,7 +36,7 @@ sp-runtime = { version = "7.0.0", path = "../../primitives/runtime" } sp-session = { version = "4.0.0-dev", path = "../../primitives/session" } sp-version = { version = "5.0.0", path = "../../primitives/version" } -tokio = { version = "1.22.0", optional = true } +tokio = "1.22.0" [dev-dependencies] env_logger = "0.9" @@ -51,4 +51,4 @@ sp-io = { version = "7.0.0", path = "../../primitives/io" } substrate-test-runtime-client = { version = "2.0.0", path = "../../test-utils/runtime/client" } [features] -test-helpers = ["tokio"] +test-helpers = [] diff --git a/client/rpc/src/state/mod.rs b/client/rpc/src/state/mod.rs index fd802e5a80391..9ba4c8218dd79 100644 --- a/client/rpc/src/state/mod.rs +++ b/client/rpc/src/state/mod.rs @@ -19,6 +19,7 @@ //! Substrate state API. mod state_full; +mod utils; #[cfg(test)] mod tests; @@ -28,7 +29,7 @@ use std::sync::Arc; use crate::SubscriptionTaskExecutor; use jsonrpsee::{ - core::{server::rpc_module::SubscriptionSink, Error as JsonRpseeError, RpcResult}, + core::{async_trait, server::rpc_module::SubscriptionSink, Error as JsonRpseeError, RpcResult}, types::SubscriptionResult, }; @@ -53,6 +54,7 @@ use sp_blockchain::{HeaderBackend, HeaderMetadata}; const STORAGE_KEYS_PAGED_MAX_COUNT: u32 = 1000; /// State backend API. +#[async_trait] pub trait StateBackend: Send + Sync + 'static where Block: BlockT + 'static, @@ -107,10 +109,11 @@ where /// /// If data is available at `key`, it is returned. Else, the sum of values who's key has `key` /// prefix is returned, i.e. all the storage (double) maps that have this prefix. - fn storage_size( + async fn storage_size( &self, block: Option, key: StorageKey, + deny_unsafe: DenyUnsafe, ) -> Result, Error>; /// Returns the runtime metadata as an opaque blob. @@ -202,6 +205,7 @@ pub struct State { deny_unsafe: DenyUnsafe, } +#[async_trait] impl StateApiServer for State where Block: BlockT + 'static, @@ -262,8 +266,15 @@ where self.backend.storage_hash(block, key).map_err(Into::into) } - fn storage_size(&self, key: StorageKey, block: Option) -> RpcResult> { - self.backend.storage_size(block, key).map_err(Into::into) + async fn storage_size( + &self, + key: StorageKey, + block: Option, + ) -> RpcResult> { + self.backend + .storage_size(block, key, self.deny_unsafe) + .await + .map_err(Into::into) } fn metadata(&self, block: Option) -> RpcResult { diff --git a/client/rpc/src/state/state_full.rs b/client/rpc/src/state/state_full.rs index 58aeac66e5c79..d8fe39030aa82 100644 --- a/client/rpc/src/state/state_full.rs +++ b/client/rpc/src/state/state_full.rs @@ -18,17 +18,20 @@ //! State API backend for full nodes. -use std::{collections::HashMap, marker::PhantomData, sync::Arc}; +use std::{collections::HashMap, marker::PhantomData, sync::Arc, time::Duration}; use super::{ client_err, error::{Error, Result}, ChildStateBackend, StateBackend, }; -use crate::SubscriptionTaskExecutor; +use crate::{DenyUnsafe, SubscriptionTaskExecutor}; use futures::{future, stream, FutureExt, StreamExt}; -use jsonrpsee::{core::Error as JsonRpseeError, SubscriptionSink}; +use jsonrpsee::{ + core::{async_trait, Error as JsonRpseeError}, + SubscriptionSink, +}; use sc_client_api::{ Backend, BlockBackend, BlockchainEvents, CallExecutor, ExecutorProvider, ProofProvider, StorageProvider, @@ -48,6 +51,9 @@ use sp_core::{ use sp_runtime::{generic::BlockId, traits::Block as BlockT}; use sp_version::RuntimeVersion; +/// The maximum time allowed for an RPC call when running without unsafe RPC enabled. +const MAXIMUM_SAFE_RPC_CALL_TIMEOUT: Duration = Duration::from_secs(30); + /// Ranges to query in state_queryStorage. struct QueryStorageRange { /// Hashes of all the blocks in the range. @@ -166,6 +172,7 @@ where } } +#[async_trait] impl StateBackend for FullState where Block: BlockT + 'static, @@ -251,33 +258,53 @@ where .map_err(client_err) } - fn storage_size( + async fn storage_size( &self, block: Option, key: StorageKey, + deny_unsafe: DenyUnsafe, ) -> std::result::Result, Error> { let block = match self.block_or_best(block) { Ok(b) => b, Err(e) => return Err(client_err(e)), }; - match self.client.storage(block, &key) { - Ok(Some(d)) => return Ok(Some(d.0.len() as u64)), - Err(e) => return Err(client_err(e)), - Ok(None) => {}, - } + let client = self.client.clone(); + let timeout = match deny_unsafe { + DenyUnsafe::Yes => Some(MAXIMUM_SAFE_RPC_CALL_TIMEOUT), + DenyUnsafe::No => None, + }; - self.client - .storage_pairs(block, &key) - .map(|kv| { - let item_sum = kv.iter().map(|(_, v)| v.0.len() as u64).sum::(); - if item_sum > 0 { - Some(item_sum) - } else { - None - } - }) - .map_err(client_err) + super::utils::spawn_blocking_with_timeout(timeout, move |is_timed_out| { + // Does the key point to a concrete entry in the database? + match client.storage(block, &key) { + Ok(Some(d)) => return Ok(Ok(Some(d.0.len() as u64))), + Err(e) => return Ok(Err(client_err(e))), + Ok(None) => {}, + } + + // The key doesn't point to anything, so it's probably a prefix. + let iter = match client.storage_keys_iter(block, Some(&key), None).map_err(client_err) { + Ok(iter) => iter, + Err(e) => return Ok(Err(e)), + }; + + let mut sum = 0; + for storage_key in iter { + let value = client.storage(block, &storage_key).ok().flatten().unwrap_or_default(); + sum += value.0.len() as u64; + + is_timed_out.check_if_timed_out()?; + } + + if sum > 0 { + Ok(Ok(Some(sum))) + } else { + Ok(Ok(None)) + } + }) + .await + .map_err(|error| Error::Client(Box::new(error)))? } fn storage_hash( diff --git a/client/rpc/src/state/tests.rs b/client/rpc/src/state/tests.rs index 3ef59e5ca9a7c..fe8bdf0ac2da8 100644 --- a/client/rpc/src/state/tests.rs +++ b/client/rpc/src/state/tests.rs @@ -70,9 +70,12 @@ async fn should_return_storage() { client.storage_hash(key.clone(), Some(genesis_hash).into()).map(|x| x.is_some()), Ok(true) ); - assert_eq!(client.storage_size(key.clone(), None).unwrap().unwrap() as usize, VALUE.len(),); assert_eq!( - client.storage_size(StorageKey(b":map".to_vec()), None).unwrap().unwrap() as usize, + client.storage_size(key.clone(), None).await.unwrap().unwrap() as usize, + VALUE.len(), + ); + assert_eq!( + client.storage_size(StorageKey(b":map".to_vec()), None).await.unwrap().unwrap() as usize, 2 + 3, ); assert_eq!( diff --git a/client/rpc/src/state/utils.rs b/client/rpc/src/state/utils.rs new file mode 100644 index 0000000000000..81476cdd34262 --- /dev/null +++ b/client/rpc/src/state/utils.rs @@ -0,0 +1,140 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use std::{ + sync::{ + atomic::{AtomicBool, Ordering}, + Arc, + }, + time::Duration, +}; + +/// An error signifying that a task has been cancelled due to a timeout. +#[derive(Debug)] +pub struct Timeout; + +impl std::error::Error for Timeout {} +impl std::fmt::Display for Timeout { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + fmt.write_str("task has been running too long") + } +} + +/// A handle which can be used to check whether the task has been cancelled due to a timeout. +#[repr(transparent)] +pub struct IsTimedOut(Arc); + +impl IsTimedOut { + #[must_use] + pub fn check_if_timed_out(&self) -> std::result::Result<(), Timeout> { + if self.0.load(Ordering::Relaxed) { + Err(Timeout) + } else { + Ok(()) + } + } +} + +/// An error for a task which either panicked, or has been cancelled due to a timeout. +#[derive(Debug)] +pub enum SpawnWithTimeoutError { + JoinError(tokio::task::JoinError), + Timeout, +} + +impl std::error::Error for SpawnWithTimeoutError {} +impl std::fmt::Display for SpawnWithTimeoutError { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + match self { + SpawnWithTimeoutError::JoinError(error) => error.fmt(fmt), + SpawnWithTimeoutError::Timeout => Timeout.fmt(fmt), + } + } +} + +struct CancelOnDrop(Arc); +impl Drop for CancelOnDrop { + fn drop(&mut self) { + self.0.store(true, Ordering::Relaxed) + } +} + +/// Spawns a new blocking task with a given `timeout`. +/// +/// The `callback` should continuously call [`IsTimedOut::check_if_timed_out`], +/// which will return an error once the task runs for longer than `timeout`. +/// +/// If `timeout` is `None` then this works just as a regular `spawn_blocking`. +pub async fn spawn_blocking_with_timeout( + timeout: Option, + callback: impl FnOnce(IsTimedOut) -> std::result::Result + Send + 'static, +) -> Result +where + R: Send + 'static, +{ + let is_timed_out_arc = Arc::new(AtomicBool::new(false)); + let is_timed_out = IsTimedOut(is_timed_out_arc.clone()); + let _cancel_on_drop = CancelOnDrop(is_timed_out_arc); + let task = tokio::task::spawn_blocking(move || callback(is_timed_out)); + + let result = if let Some(timeout) = timeout { + tokio::select! { + // Shouldn't really matter, but make sure the task is polled before the timeout, + // in case the task finishes after the timeout and the timeout is really short. + biased; + + task_result = task => task_result, + _ = tokio::time::sleep(timeout) => Ok(Err(Timeout)) + } + } else { + task.await + }; + + match result { + Ok(Ok(result)) => Ok(result), + Ok(Err(Timeout)) => Err(SpawnWithTimeoutError::Timeout), + Err(error) => Err(SpawnWithTimeoutError::JoinError(error)), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn spawn_blocking_with_timeout_works() { + let task: Result<(), SpawnWithTimeoutError> = + spawn_blocking_with_timeout(Some(Duration::from_millis(100)), |is_timed_out| { + std::thread::sleep(Duration::from_millis(200)); + is_timed_out.check_if_timed_out()?; + unreachable!(); + }) + .await; + + assert_matches::assert_matches!(task, Err(SpawnWithTimeoutError::Timeout)); + + let task = spawn_blocking_with_timeout(Some(Duration::from_millis(100)), |is_timed_out| { + std::thread::sleep(Duration::from_millis(20)); + is_timed_out.check_if_timed_out()?; + Ok(()) + }) + .await; + + assert_matches::assert_matches!(task, Ok(())); + } +} diff --git a/client/service/Cargo.toml b/client/service/Cargo.toml index 45b1c02620fb2..6122895d2300c 100644 --- a/client/service/Cargo.toml +++ b/client/service/Cargo.toml @@ -57,7 +57,7 @@ sc-chain-spec = { version = "4.0.0-dev", path = "../chain-spec" } sc-client-api = { version = "4.0.0-dev", path = "../api" } sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } sc-client-db = { version = "0.10.0-dev", default-features = false, path = "../db" } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } sc-executor = { version = "0.10.0-dev", path = "../executor" } sc-transaction-pool = { version = "4.0.0-dev", path = "../transaction-pool" } sp-transaction-pool = { version = "4.0.0-dev", path = "../../primitives/transaction-pool" } diff --git a/client/service/src/builder.rs b/client/service/src/builder.rs index 1f94f96fae89e..0b09f550ce338 100644 --- a/client/service/src/builder.rs +++ b/client/service/src/builder.rs @@ -329,13 +329,15 @@ where let executor = crate::client::LocalCallExecutor::new( backend.clone(), executor, - spawn_handle, + spawn_handle.clone(), config.clone(), execution_extensions, )?; + crate::client::Client::new( backend, executor, + spawn_handle, genesis_block_builder, fork_blocks, bad_blocks, diff --git a/client/service/src/client/client.rs b/client/service/src/client/client.rs index 18012fc1931fe..d32baa671f54c 100644 --- a/client/service/src/client/client.rs +++ b/client/service/src/client/client.rs @@ -22,7 +22,8 @@ use super::{ block_rules::{BlockRules, LookupResult as BlockLookupResult}, genesis::BuildGenesisBlock, }; -use log::{info, trace, warn}; +use futures::{FutureExt, StreamExt}; +use log::{error, info, trace, warn}; use parking_lot::{Mutex, RwLock}; use prometheus_endpoint::Registry; use rand::Rng; @@ -58,9 +59,12 @@ use sp_blockchain::{ use sp_consensus::{BlockOrigin, BlockStatus, Error as ConsensusError}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedSender}; -use sp_core::storage::{ - well_known_keys, ChildInfo, ChildType, PrefixedStorageKey, Storage, StorageChild, StorageData, - StorageKey, +use sp_core::{ + storage::{ + well_known_keys, ChildInfo, ChildType, PrefixedStorageKey, Storage, StorageChild, + StorageData, StorageKey, + }, + traits::SpawnNamed, }; #[cfg(feature = "test-helpers")] use sp_keystore::SyncCryptoStorePtr; @@ -88,9 +92,7 @@ use std::{ #[cfg(feature = "test-helpers")] use { - super::call_executor::LocalCallExecutor, - sc_client_api::in_mem, - sp_core::traits::{CodeExecutor, SpawnNamed}, + super::call_executor::LocalCallExecutor, sc_client_api::in_mem, sp_core::traits::CodeExecutor, }; type NotificationSinks = Mutex>>; @@ -116,6 +118,7 @@ where block_rules: BlockRules, config: ClientConfig, telemetry: Option, + unpin_worker_sender: TracingUnboundedSender, _phantom: PhantomData, } @@ -246,7 +249,7 @@ where let call_executor = LocalCallExecutor::new( backend.clone(), executor, - spawn_handle, + spawn_handle.clone(), config.clone(), extensions, )?; @@ -254,6 +257,7 @@ where Client::new( backend, call_executor, + spawn_handle, genesis_block_builder, Default::default(), Default::default(), @@ -296,11 +300,20 @@ where let ClientImportOperation { mut op, notify_imported, notify_finalized } = op; - let finality_notification = notify_finalized.map(|summary| summary.into()); + let finality_notification = notify_finalized.map(|summary| { + FinalityNotification::from_summary(summary, self.unpin_worker_sender.clone()) + }); + let (import_notification, storage_changes) = match notify_imported { Some(mut summary) => { let storage_changes = summary.storage_changes.take(); - (Some(summary.into()), storage_changes) + ( + Some(BlockImportNotification::from_summary( + summary, + self.unpin_worker_sender.clone(), + )), + storage_changes, + ) }, None => (None, None), }; @@ -318,6 +331,27 @@ where self.backend.commit_operation(op)?; + // We need to pin the block in the backend once + // for each notification. Once all notifications are + // dropped, the block will be unpinned automatically. + if let Some(ref notification) = finality_notification { + if let Err(err) = self.backend.pin_block(notification.hash) { + error!( + "Unable to pin block for finality notification. hash: {}, Error: {}", + notification.hash, err + ); + }; + } + + if let Some(ref notification) = import_notification { + if let Err(err) = self.backend.pin_block(notification.hash) { + error!( + "Unable to pin block for import notification. hash: {}, Error: {}", + notification.hash, err + ); + }; + } + self.notify_finalized(finality_notification)?; self.notify_imported(import_notification, storage_changes)?; @@ -357,6 +391,7 @@ where pub fn new( backend: Arc, executor: E, + spawn_handle: Box, genesis_block_builder: G, fork_blocks: ForkBlocks, bad_blocks: BadBlocks, @@ -369,6 +404,7 @@ where Block, BlockImportOperation = >::BlockImportOperation, >, + B: 'static, { let info = backend.blockchain().info(); if info.finalized_state.is_none() { @@ -390,6 +426,26 @@ where backend.commit_operation(op)?; } + let (unpin_worker_sender, mut rx) = + tracing_unbounded::("unpin-worker-channel", 10_000); + let task_backend = Arc::downgrade(&backend); + spawn_handle.spawn( + "unpin-worker", + None, + async move { + while let Some(message) = rx.next().await { + if let Some(backend) = task_backend.upgrade() { + backend.unpin_block(message); + } else { + log::debug!("Terminating unpin-worker, backend reference was dropped."); + return + } + } + log::debug!("Terminating unpin-worker, stream terminated.") + } + .boxed(), + ); + Ok(Client { backend, executor, @@ -402,6 +458,7 @@ where block_rules: BlockRules::new(fork_blocks, bad_blocks), config, telemetry, + unpin_worker_sender, _phantom: Default::default(), }) } @@ -1432,27 +1489,27 @@ where Ok(keys) } - fn storage_keys_iter<'a>( + fn storage_keys_iter( &self, hash: ::Hash, - prefix: Option<&'a StorageKey>, + prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result> { + ) -> sp_blockchain::Result> { let state = self.state_at(hash)?; let start_key = start_key.or(prefix).map(|key| key.0.clone()).unwrap_or_else(Vec::new); - Ok(KeyIterator::new(state, prefix, start_key)) + Ok(KeyIterator::new(state, prefix.cloned(), start_key)) } - fn child_storage_keys_iter<'a>( + fn child_storage_keys_iter( &self, hash: ::Hash, child_info: ChildInfo, - prefix: Option<&'a StorageKey>, + prefix: Option<&StorageKey>, start_key: Option<&StorageKey>, - ) -> sp_blockchain::Result> { + ) -> sp_blockchain::Result> { let state = self.state_at(hash)?; let start_key = start_key.or(prefix).map(|key| key.0.clone()).unwrap_or_else(Vec::new); - Ok(KeyIterator::new_child(state, child_info, prefix, start_key)) + Ok(KeyIterator::new_child(state, child_info, prefix.cloned(), start_key)) } fn storage( diff --git a/client/service/test/Cargo.toml b/client/service/test/Cargo.toml index d3bc39aa3486c..488fe76c333bc 100644 --- a/client/service/test/Cargo.toml +++ b/client/service/test/Cargo.toml @@ -16,7 +16,7 @@ array-bytes = "4.1" fdlimit = "0.2.1" futures = "0.3.21" log = "0.4.17" -parity-scale-codec = "3.0.0" +parity-scale-codec = "3.2.2" parking_lot = "0.12.1" tempfile = "3.1.0" tokio = { version = "1.22.0", features = ["time"] } diff --git a/client/service/test/src/lib.rs b/client/service/test/src/lib.rs index 5f75e3521e235..6533fe13961b5 100644 --- a/client/service/test/src/lib.rs +++ b/client/service/test/src/lib.rs @@ -220,7 +220,7 @@ fn node_config< ); network_config.transport = - TransportConfig::Normal { enable_mdns: false, allow_private_ipv4: true }; + TransportConfig::Normal { enable_mdns: false, allow_private_ip: true }; Configuration { impl_name: String::from("network-test-impl"), diff --git a/client/state-db/Cargo.toml b/client/state-db/Cargo.toml index fb70f5fc6a8ba..13439de110155 100644 --- a/client/state-db/Cargo.toml +++ b/client/state-db/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } log = "0.4.17" parking_lot = "0.12.1" sp-core = { version = "7.0.0", path = "../../primitives/core" } diff --git a/client/sync-state-rpc/Cargo.toml b/client/sync-state-rpc/Cargo.toml index a72b4106ba873..3a964521c505b 100644 --- a/client/sync-state-rpc/Cargo.toml +++ b/client/sync-state-rpc/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.85" diff --git a/client/sysinfo/src/lib.rs b/client/sysinfo/src/lib.rs index cef5a4d210df1..f623bdae53e66 100644 --- a/client/sysinfo/src/lib.rs +++ b/client/sysinfo/src/lib.rs @@ -29,7 +29,8 @@ mod sysinfo_linux; pub use sysinfo::{ benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes, benchmark_memory, benchmark_sr25519_verify, gather_hwbench, gather_sysinfo, - serialize_throughput, serialize_throughput_option, Throughput, + serialize_throughput, serialize_throughput_option, Metric, Requirement, Requirements, + Throughput, }; /// The operating system part of the current target triplet. diff --git a/client/sysinfo/src/sysinfo.rs b/client/sysinfo/src/sysinfo.rs index c66a6f6a62aed..1fbca7d37efb7 100644 --- a/client/sysinfo/src/sysinfo.rs +++ b/client/sysinfo/src/sysinfo.rs @@ -21,10 +21,10 @@ use crate::{ExecutionLimit, HwBench}; use sc_telemetry::SysInfo; use sp_core::{sr25519, Pair}; use sp_io::crypto::sr25519_verify; -use sp_std::{fmt, prelude::*}; +use sp_std::{fmt, fmt::Formatter, prelude::*}; use rand::{seq::SliceRandom, Rng, RngCore}; -use serde::Serializer; +use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; use std::{ fs::File, io::{Seek, SeekFrom, Write}, @@ -33,6 +33,43 @@ use std::{ time::{Duration, Instant}, }; +/// A single hardware metric. +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] +pub enum Metric { + /// SR25519 signature verification. + Sr25519Verify, + /// Blake2-256 hashing algorithm. + Blake2256, + /// Copying data in RAM. + MemCopy, + /// Disk sequential write. + DiskSeqWrite, + /// Disk random write. + DiskRndWrite, +} + +impl Metric { + /// The category of the metric. + pub fn category(&self) -> &'static str { + match self { + Self::Sr25519Verify | Self::Blake2256 => "CPU", + Self::MemCopy => "Memory", + Self::DiskSeqWrite | Self::DiskRndWrite => "Disk", + } + } + + /// The name of the metric. It is always prefixed by the [`self.category()`]. + pub fn name(&self) -> &'static str { + match self { + Self::Sr25519Verify => "SR25519-Verify", + Self::Blake2256 => "BLAKE2-256", + Self::MemCopy => "Copy", + Self::DiskSeqWrite => "Seq Write", + Self::DiskRndWrite => "Rnd Write", + } + } +} + /// The unit in which the [`Throughput`] (bytes per second) is denoted. pub enum Unit { GiBs, @@ -137,6 +174,54 @@ where serializer.serialize_none() } +/// Serializes throughput into MiBs and represents it as `f64`. +fn serialize_throughput_as_f64(throughput: &Throughput, serializer: S) -> Result +where + S: Serializer, +{ + serializer.serialize_f64(throughput.as_mibs()) +} + +struct ThroughputVisitor; +impl<'de> Visitor<'de> for ThroughputVisitor { + type Value = Throughput; + + fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { + formatter.write_str("A value that is a f64.") + } + + fn visit_f64(self, value: f64) -> Result + where + E: serde::de::Error, + { + Ok(Throughput::from_mibs(value)) + } +} + +fn deserialize_throughput<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + Ok(deserializer.deserialize_f64(ThroughputVisitor))? +} + +/// Multiple requirements for the hardware. +#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] +pub struct Requirements(pub Vec); + +/// A single requirement for the hardware. +#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] +pub struct Requirement { + /// The metric to measure. + pub metric: Metric, + /// The minimal throughput that needs to be archived for this requirement. + #[serde( + serialize_with = "serialize_throughput_as_f64", + deserialize_with = "deserialize_throughput" + )] + pub minimum: Throughput, +} + #[inline(always)] pub(crate) fn benchmark( name: &str, @@ -503,7 +588,9 @@ pub fn benchmark_sr25519_verify(limit: ExecutionLimit) -> Throughput { /// Benchmarks the hardware and returns the results of those benchmarks. /// -/// Optionally accepts a path to a `scratch_directory` to use to benchmark the disk. +/// Optionally accepts a path to a `scratch_directory` to use to benchmark the +/// disk. Also accepts the `requirements` for the hardware benchmark and a +/// boolean to specify if the node is an authority. pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { #[allow(unused_mut)] let mut hwbench = HwBench { @@ -537,6 +624,38 @@ pub fn gather_hwbench(scratch_directory: Option<&Path>) -> HwBench { hwbench } +impl Requirements { + /// Whether the hardware requirements are met by the provided benchmark results. + pub fn check_hardware(&self, hwbench: &HwBench) -> bool { + for requirement in self.0.iter() { + match requirement.metric { + Metric::Blake2256 => + if requirement.minimum > hwbench.cpu_hashrate_score { + return false + }, + Metric::MemCopy => + if requirement.minimum > hwbench.memory_memcpy_score { + return false + }, + Metric::DiskSeqWrite => + if let Some(score) = hwbench.disk_sequential_write_score { + if requirement.minimum > score { + return false + } + }, + Metric::DiskRndWrite => + if let Some(score) = hwbench.disk_random_write_score { + if requirement.minimum > score { + return false + } + }, + Metric::Sr25519Verify => {}, + } + } + true + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/client/transaction-pool/Cargo.toml b/client/transaction-pool/Cargo.toml index 7a3ab042d5a13..bbb5ebae440a0 100644 --- a/client/transaction-pool/Cargo.toml +++ b/client/transaction-pool/Cargo.toml @@ -14,11 +14,12 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" futures-timer = "3.0.2" linked-hash-map = "0.5.4" log = "0.4.17" +num-traits = "0.2.8" parking_lot = "0.12.1" serde = { version = "1.0.136", features = ["derive"] } thiserror = "1.0.30" diff --git a/client/transaction-pool/api/src/lib.rs b/client/transaction-pool/api/src/lib.rs index c1e49ad07d7b1..870760962a520 100644 --- a/client/transaction-pool/api/src/lib.rs +++ b/client/transaction-pool/api/src/lib.rs @@ -30,6 +30,8 @@ use sp_runtime::{ }; use std::{collections::HashMap, hash::Hash, pin::Pin, sync::Arc}; +const LOG_TARGET: &str = "txpool::api"; + pub use sp_runtime::transaction_validity::{ TransactionLongevity, TransactionPriority, TransactionSource, TransactionTag, }; @@ -353,7 +355,7 @@ impl OffchainSubmitTransaction for TP extrinsic: ::Extrinsic, ) -> Result<(), ()> { log::debug!( - target: "txpool", + target: LOG_TARGET, "(offchain call) Submitting a transaction to the pool: {:?}", extrinsic ); @@ -362,7 +364,7 @@ impl OffchainSubmitTransaction for TP result.map(|_| ()).map_err(|e| { log::warn!( - target: "txpool", + target: LOG_TARGET, "(offchain call) Error submitting a transaction to the pool: {}", e ) diff --git a/client/transaction-pool/src/api.rs b/client/transaction-pool/src/api.rs index a2b317cfb3998..0166dfaced208 100644 --- a/client/transaction-pool/src/api.rs +++ b/client/transaction-pool/src/api.rs @@ -18,6 +18,7 @@ //! Chain api required for the transaction pool. +use crate::LOG_TARGET; use codec::Encode; use futures::{ channel::{mpsc, oneshot}, @@ -85,7 +86,7 @@ impl FullChainApi { let metrics = prometheus.map(ApiMetrics::register).and_then(|r| match r { Err(err) => { log::warn!( - target: "txpool", + target: LOG_TARGET, "Failed to register transaction pool api prometheus metrics: {:?}", err, ); diff --git a/client/transaction-pool/src/enactment_state.rs b/client/transaction-pool/src/enactment_state.rs index 6aac98641cf85..7252c0dec7f2a 100644 --- a/client/transaction-pool/src/enactment_state.rs +++ b/client/transaction-pool/src/enactment_state.rs @@ -18,13 +18,22 @@ //! Substrate transaction pool implementation. +use crate::LOG_TARGET; +use num_traits::CheckedSub; use sc_transaction_pool_api::ChainEvent; use sp_blockchain::TreeRoute; -use sp_runtime::traits::Block as BlockT; +use sp_runtime::traits::{Block as BlockT, NumberFor}; + +/// The threshold since the last update where we will skip any maintenance for blocks. +/// +/// This includes tracking re-orgs and sending out certain notifications. In general this shouldn't +/// happen and may only happen when the node is doing a full sync. +const SKIP_MAINTENANCE_THRESHOLD: u16 = 20; /// Helper struct for keeping track of the current state of processed new best /// block and finalized events. The main purpose of keeping track of this state -/// is to figure out if a transaction pool enactment is needed or not. +/// is to figure out which phases (enactment / finalization) of transaction pool +/// maintenance are needed. /// /// Given the following chain: /// @@ -54,6 +63,17 @@ where recent_finalized_block: Block::Hash, } +/// Enactment action that should be performed after processing the `ChainEvent` +#[derive(Debug)] +pub enum EnactmentAction { + /// Both phases of maintenance shall be skipped + Skip, + /// Both phases of maintenance shall be performed + HandleEnactment(TreeRoute), + /// Enactment phase of maintenance shall be skipped + HandleFinalization, +} + impl EnactmentState where Block: BlockT, @@ -71,23 +91,38 @@ where /// Updates the state according to the given `ChainEvent`, returning /// `Some(tree_route)` with a tree route including the blocks that need to /// be enacted/retracted. If no enactment is needed then `None` is returned. - pub fn update( + pub fn update( &mut self, event: &ChainEvent, - tree_route: &F, - ) -> Result>, String> + tree_route: &TreeRouteF, + hash_to_number: &BlockNumberF, + ) -> Result, String> where - F: Fn(Block::Hash, Block::Hash) -> Result, String>, + TreeRouteF: Fn(Block::Hash, Block::Hash) -> Result, String>, + BlockNumberF: Fn(Block::Hash) -> Result>, String>, { - let (new_hash, finalized) = match event { - ChainEvent::NewBestBlock { hash, .. } => (*hash, false), - ChainEvent::Finalized { hash, .. } => (*hash, true), + let (new_hash, current_hash, finalized) = match event { + ChainEvent::NewBestBlock { hash, .. } => (*hash, self.recent_best_block, false), + ChainEvent::Finalized { hash, .. } => (*hash, self.recent_finalized_block, true), }; + // do not proceed with txpool maintain if block distance is to high + let skip_maintenance = match (hash_to_number(new_hash), hash_to_number(current_hash)) { + (Ok(Some(new)), Ok(Some(current))) => + new.checked_sub(¤t) > Some(SKIP_MAINTENANCE_THRESHOLD.into()), + _ => true, + }; + + if skip_maintenance { + log::debug!(target: LOG_TARGET, "skip maintain: tree_route would be too long"); + self.force_update(event); + return Ok(EnactmentAction::Skip) + } + // block was already finalized if self.recent_finalized_block == new_hash { - log::debug!(target: "txpool", "handle_enactment: block already finalized"); - return Ok(None) + log::debug!(target: LOG_TARGET, "handle_enactment: block already finalized"); + return Ok(EnactmentAction::Skip) } // compute actual tree route from best_block to notified block, and use @@ -95,9 +130,13 @@ where let tree_route = tree_route(self.recent_best_block, new_hash)?; log::debug!( - target: "txpool", + target: LOG_TARGET, "resolve hash:{:?} finalized:{:?} tree_route:{:?} best_block:{:?} finalized_block:{:?}", - new_hash, finalized, tree_route, self.recent_best_block, self.recent_finalized_block + new_hash, + finalized, + tree_route, + self.recent_best_block, + self.recent_finalized_block ); // check if recently finalized block is on retracted path. this could be @@ -105,11 +144,12 @@ where // best event for some old stale best head. if tree_route.retracted().iter().any(|x| x.hash == self.recent_finalized_block) { log::debug!( - target: "txpool", + target: LOG_TARGET, "Recently finalized block {} would be retracted by ChainEvent {}, skipping", - self.recent_finalized_block, new_hash + self.recent_finalized_block, + new_hash ); - return Ok(None) + return Ok(EnactmentAction::Skip) } if finalized { @@ -121,10 +161,10 @@ where // remains valid. if tree_route.enacted().is_empty() { log::trace!( - target: "txpool", + target: LOG_TARGET, "handle_enactment: no newly enacted blocks since recent best block" ); - return Ok(None) + return Ok(EnactmentAction::HandleFinalization) } // otherwise enacted finalized block becomes best block... @@ -132,7 +172,7 @@ where self.recent_best_block = new_hash; - Ok(Some(tree_route)) + Ok(EnactmentAction::HandleEnactment(tree_route)) } /// Forces update of the state according to the given `ChainEvent`. Intended to be used as a @@ -142,15 +182,21 @@ where ChainEvent::NewBestBlock { hash, .. } => self.recent_best_block = *hash, ChainEvent::Finalized { hash, .. } => self.recent_finalized_block = *hash, }; - log::debug!(target: "txpool", "forced update: {:?}, {:?}", self.recent_best_block, self.recent_finalized_block); + log::debug!( + target: LOG_TARGET, + "forced update: {:?}, {:?}", + self.recent_best_block, + self.recent_finalized_block, + ); } } #[cfg(test)] mod enactment_state_tests { - use super::EnactmentState; + use super::{EnactmentAction, EnactmentState}; use sc_transaction_pool_api::ChainEvent; use sp_blockchain::{HashAndNumber, TreeRoute}; + use sp_runtime::traits::NumberFor; use std::sync::Arc; use substrate_test_runtime_client::runtime::{Block, Hash}; @@ -170,6 +216,9 @@ mod enactment_state_tests { fn e1() -> HashAndNumber { HashAndNumber { number: 5, hash: Hash::from([0xE1; 32]) } } + fn x1() -> HashAndNumber { + HashAndNumber { number: 22, hash: Hash::from([0x1E; 32]) } + } fn b2() -> HashAndNumber { HashAndNumber { number: 2, hash: Hash::from([0xB2; 32]) } } @@ -182,11 +231,22 @@ mod enactment_state_tests { fn e2() -> HashAndNumber { HashAndNumber { number: 5, hash: Hash::from([0xE2; 32]) } } + fn x2() -> HashAndNumber { + HashAndNumber { number: 22, hash: Hash::from([0x2E; 32]) } + } + + fn test_chain() -> Vec> { + vec![x1(), e1(), d1(), c1(), b1(), a(), b2(), c2(), d2(), e2(), x2()] + } + + fn block_hash_to_block_number(hash: Hash) -> Result>, String> { + Ok(test_chain().iter().find(|x| x.hash == hash).map(|x| x.number)) + } /// mock tree_route computing function for simple two-forks chain fn tree_route(from: Hash, to: Hash) -> Result, String> { - let chain = vec![e1(), d1(), c1(), b1(), a(), b2(), c2(), d2(), e2()]; - let pivot = 4_usize; + let chain = test_chain(); + let pivot = chain.iter().position(|x| x.number == a().number).unwrap(); let from = chain .iter() @@ -197,13 +257,13 @@ mod enactment_state_tests { .position(|bn| bn.hash == to) .ok_or("existing block should be given")?; - // B1-C1-D1-E1 + // B1-C1-D1-E1-..-X1 // / // A // \ - // B2-C2-D2-E2 + // B2-C2-D2-E2-..-X2 // - // [E1 D1 C1 B1 A B2 C2 D2 E2] + // [X1 E1 D1 C1 B1 A B2 C2 D2 E2 X2] let vec: Vec> = if from < to { chain.into_iter().skip(from).take(to - from + 1).collect() @@ -373,13 +433,20 @@ mod enactment_state_tests { let expected = TreeRoute::new(vec![a()], 0); assert_treeroute_eq(result, expected); } + + #[test] + fn tree_route_mock_test_17() { + let result = tree_route(x2().hash, b1().hash); + let expected = TreeRoute::new(vec![x2(), e2(), d2(), c2(), b2(), a(), b1()], 5); + assert_treeroute_eq(result, expected); + } } fn trigger_new_best_block( state: &mut EnactmentState, from: HashAndNumber, acted_on: HashAndNumber, - ) -> bool { + ) -> EnactmentAction { let (from, acted_on) = (from.hash, acted_on.hash); let event_tree_route = tree_route(from, acted_on).expect("Tree route exists"); @@ -391,16 +458,16 @@ mod enactment_state_tests { tree_route: Some(Arc::new(event_tree_route)), }, &tree_route, + &block_hash_to_block_number, ) .unwrap() - .is_some() } fn trigger_finalized( state: &mut EnactmentState, from: HashAndNumber, acted_on: HashAndNumber, - ) -> bool { + ) -> EnactmentAction { let (from, acted_on) = (from.hash, acted_on.hash); let v = tree_route(from, acted_on) @@ -411,9 +478,12 @@ mod enactment_state_tests { .collect::>(); state - .update(&ChainEvent::Finalized { hash: acted_on, tree_route: v.into() }, &tree_route) + .update( + &ChainEvent::Finalized { hash: acted_on, tree_route: v.into() }, + &tree_route, + &block_hash_to_block_number, + ) .unwrap() - .is_some() } fn assert_es_eq( @@ -437,51 +507,51 @@ mod enactment_state_tests { // B2-C2-D2-E2 let result = trigger_new_best_block(&mut es, a(), d1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, d1(), a()); let result = trigger_new_best_block(&mut es, d1(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), a()); let result = trigger_finalized(&mut es, a(), d2()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, d2(), d2()); let result = trigger_new_best_block(&mut es, d2(), e1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_finalized(&mut es, a(), b2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_finalized(&mut es, a(), b1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_new_best_block(&mut es, a(), d2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_finalized(&mut es, a(), d2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_new_best_block(&mut es, a(), c2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_new_best_block(&mut es, a(), c1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, d2(), d2()); let result = trigger_new_best_block(&mut es, d2(), e2()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e2(), d2()); let result = trigger_finalized(&mut es, d2(), e2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::HandleFinalization)); assert_es_eq(&es, e2(), e2()); } @@ -493,27 +563,27 @@ mod enactment_state_tests { // A-B1-C1-D1-E1 let result = trigger_new_best_block(&mut es, a(), b1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, b1(), a()); let result = trigger_new_best_block(&mut es, b1(), c1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, c1(), a()); let result = trigger_new_best_block(&mut es, c1(), d1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, d1(), a()); let result = trigger_new_best_block(&mut es, d1(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), a()); let result = trigger_finalized(&mut es, a(), c1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::HandleFinalization)); assert_es_eq(&es, e1(), c1()); let result = trigger_finalized(&mut es, c1(), e1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::HandleFinalization)); assert_es_eq(&es, e1(), e1()); } @@ -525,11 +595,11 @@ mod enactment_state_tests { // A-B1-C1-D1-E1 let result = trigger_new_best_block(&mut es, a(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), a()); let result = trigger_finalized(&mut es, a(), b1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::HandleFinalization)); assert_es_eq(&es, e1(), b1()); } @@ -541,11 +611,11 @@ mod enactment_state_tests { // A-B1-C1-D1-E1 let result = trigger_finalized(&mut es, a(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), e1()); let result = trigger_finalized(&mut es, e1(), b1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, e1(), e1()); } @@ -561,11 +631,11 @@ mod enactment_state_tests { // B2-C2-D2-E2 let result = trigger_finalized(&mut es, a(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), e1()); let result = trigger_finalized(&mut es, e1(), e2()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, e1(), e1()); } @@ -577,19 +647,19 @@ mod enactment_state_tests { // A-B1-C1-D1-E1 let result = trigger_new_best_block(&mut es, a(), b1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, b1(), a()); let result = trigger_finalized(&mut es, a(), d1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, d1(), d1()); let result = trigger_new_best_block(&mut es, a(), e1()); - assert!(result); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); assert_es_eq(&es, e1(), d1()); let result = trigger_new_best_block(&mut es, a(), c1()); - assert_eq!(result, false); + assert!(matches!(result, EnactmentAction::Skip)); assert_es_eq(&es, e1(), d1()); } @@ -610,4 +680,26 @@ mod enactment_state_tests { es.force_update(&ChainEvent::Finalized { hash: b1().hash, tree_route: Arc::from([]) }); assert_es_eq(&es, a(), b1()); } + + #[test] + fn test_enactment_skip_long_enacted_path() { + sp_tracing::try_init_simple(); + let mut es = EnactmentState::new(a().hash, a().hash); + + // A-B1-C1-..-X1 + let result = trigger_new_best_block(&mut es, a(), x1()); + assert!(matches!(result, EnactmentAction::Skip)); + assert_es_eq(&es, x1(), a()); + } + + #[test] + fn test_enactment_proceed_with_enacted_path_at_threshold() { + sp_tracing::try_init_simple(); + let mut es = EnactmentState::new(b1().hash, b1().hash); + + // A-B1-C1-..-X1 + let result = trigger_new_best_block(&mut es, b1(), x1()); + assert!(matches!(result, EnactmentAction::HandleEnactment { .. })); + assert_es_eq(&es, x1(), b1()); + } } diff --git a/client/transaction-pool/src/graph/base_pool.rs b/client/transaction-pool/src/graph/base_pool.rs index 67580d698b8c1..44371be62c621 100644 --- a/client/transaction-pool/src/graph/base_pool.rs +++ b/client/transaction-pool/src/graph/base_pool.rs @@ -22,6 +22,7 @@ use std::{cmp::Ordering, collections::HashSet, fmt, hash, sync::Arc}; +use crate::LOG_TARGET; use log::{debug, trace, warn}; use sc_transaction_pool_api::{error, InPoolTransaction, PoolStatus}; use serde::Serialize; @@ -272,9 +273,9 @@ impl BasePool BasePool if first { - debug!(target: "txpool", "[{:?}] Error importing: {:?}", current_hash, e); + debug!(target: LOG_TARGET, "[{:?}] Error importing: {:?}", current_hash, e); return Err(e) } else { failed.push(current_hash); @@ -347,7 +348,7 @@ impl BasePool BasePool promoted.push(res), Err(e) => { - warn!(target: "txpool", "[{:?}] Failed to promote during pruning: {:?}", hash, e); + warn!( + target: LOG_TARGET, + "[{:?}] Failed to promote during pruning: {:?}", hash, e, + ); failed.push(hash) }, } diff --git a/client/transaction-pool/src/graph/listener.rs b/client/transaction-pool/src/graph/listener.rs index 776749abf2d5d..c603a542c2767 100644 --- a/client/transaction-pool/src/graph/listener.rs +++ b/client/transaction-pool/src/graph/listener.rs @@ -18,6 +18,7 @@ use std::{collections::HashMap, fmt::Debug, hash}; +use crate::LOG_TARGET; use linked_hash_map::LinkedHashMap; use log::{debug, trace}; use serde::Serialize; @@ -67,13 +68,13 @@ impl Listener { /// Notify the listeners about extrinsic broadcast. pub fn broadcasted(&mut self, hash: &H, peers: Vec) { - trace!(target: "txpool", "[{:?}] Broadcasted", hash); + trace!(target: LOG_TARGET, "[{:?}] Broadcasted", hash); self.fire(hash, |watcher| watcher.broadcast(peers)); } /// New transaction was added to the ready pool or promoted from the future pool. pub fn ready(&mut self, tx: &H, old: Option<&H>) { - trace!(target: "txpool", "[{:?}] Ready (replaced with {:?})", tx, old); + trace!(target: LOG_TARGET, "[{:?}] Ready (replaced with {:?})", tx, old); self.fire(tx, |watcher| watcher.ready()); if let Some(old) = old { self.fire(old, |watcher| watcher.usurped(tx.clone())); @@ -82,13 +83,13 @@ impl Listener { /// New transaction was added to the future pool. pub fn future(&mut self, tx: &H) { - trace!(target: "txpool", "[{:?}] Future", tx); + trace!(target: LOG_TARGET, "[{:?}] Future", tx); self.fire(tx, |watcher| watcher.future()); } /// Transaction was dropped from the pool because of the limit. pub fn dropped(&mut self, tx: &H, by: Option<&H>) { - trace!(target: "txpool", "[{:?}] Dropped (replaced with {:?})", tx, by); + trace!(target: LOG_TARGET, "[{:?}] Dropped (replaced with {:?})", tx, by); self.fire(tx, |watcher| match by { Some(t) => watcher.usurped(t.clone()), None => watcher.dropped(), @@ -97,13 +98,13 @@ impl Listener { /// Transaction was removed as invalid. pub fn invalid(&mut self, tx: &H) { - debug!(target: "txpool", "[{:?}] Extrinsic invalid", tx); + debug!(target: LOG_TARGET, "[{:?}] Extrinsic invalid", tx); self.fire(tx, |watcher| watcher.invalid()); } /// Transaction was pruned from the pool. pub fn pruned(&mut self, block_hash: BlockHash, tx: &H) { - debug!(target: "txpool", "[{:?}] Pruned at {:?}", tx, block_hash); + debug!(target: LOG_TARGET, "[{:?}] Pruned at {:?}", tx, block_hash); // Get the transactions included in the given block hash. let txs = self.finality_watchers.entry(block_hash).or_insert(vec![]); txs.push(tx.clone()); @@ -134,7 +135,12 @@ impl Listener { pub fn finalized(&mut self, block_hash: BlockHash) { if let Some(hashes) = self.finality_watchers.remove(&block_hash) { for (tx_index, hash) in hashes.into_iter().enumerate() { - log::debug!(target: "txpool", "[{:?}] Sent finalization event (block {:?})", hash, block_hash); + log::debug!( + target: LOG_TARGET, + "[{:?}] Sent finalization event (block {:?})", + hash, + block_hash, + ); self.fire(&hash, |watcher| watcher.finalized(block_hash, tx_index)) } } diff --git a/client/transaction-pool/src/graph/pool.rs b/client/transaction-pool/src/graph/pool.rs index 8e3570d1dbd1f..14217d56aaa57 100644 --- a/client/transaction-pool/src/graph/pool.rs +++ b/client/transaction-pool/src/graph/pool.rs @@ -18,6 +18,7 @@ use std::{collections::HashMap, sync::Arc, time::Duration}; +use crate::LOG_TARGET; use futures::{channel::mpsc::Receiver, Future}; use sc_transaction_pool_api::error; use sp_blockchain::TreeRoute; @@ -208,7 +209,8 @@ impl Pool { ) { let now = Instant::now(); self.validated_pool.resubmit(revalidated_transactions); - log::debug!(target: "txpool", + log::debug!( + target: LOG_TARGET, "Resubmitted. Took {} ms. Status: {:?}", now.elapsed().as_millis(), self.validated_pool.status() @@ -249,7 +251,7 @@ impl Pool { extrinsics: &[ExtrinsicFor], ) -> Result<(), B::Error> { log::debug!( - target: "txpool", + target: LOG_TARGET, "Starting pruning of block {:?} (extrinsics: {})", at, extrinsics.len() @@ -287,7 +289,10 @@ impl Pool { future_tags.extend(validity.provides); } } else { - log::trace!(target: "txpool", "txpool is empty, skipping validation for block {at:?}"); + log::trace!( + target: LOG_TARGET, + "txpool is empty, skipping validation for block {at:?}", + ); } }, } @@ -323,7 +328,7 @@ impl Pool { tags: impl IntoIterator, known_imported_hashes: impl IntoIterator> + Clone, ) -> Result<(), B::Error> { - log::debug!(target: "txpool", "Pruning at {:?}", at); + log::debug!(target: LOG_TARGET, "Pruning at {:?}", at); // Prune all transactions that provide given tags let prune_status = self.validated_pool.prune_tags(tags)?; @@ -342,7 +347,7 @@ impl Pool { let reverified_transactions = self.verify(at, pruned_transactions, CheckBannedBeforeVerify::Yes).await?; - log::trace!(target: "txpool", "Pruning at {:?}. Resubmitting transactions.", at); + log::trace!(target: LOG_TARGET, "Pruning at {:?}. Resubmitting transactions.", at); // And finally - submit reverified transactions back to the pool self.validated_pool.resubmit_pruned( diff --git a/client/transaction-pool/src/graph/ready.rs b/client/transaction-pool/src/graph/ready.rs index b52372a3c4d10..9040c81b3c434 100644 --- a/client/transaction-pool/src/graph/ready.rs +++ b/client/transaction-pool/src/graph/ready.rs @@ -23,6 +23,7 @@ use std::{ sync::Arc, }; +use crate::LOG_TARGET; use log::{debug, trace}; use sc_transaction_pool_api::error; use serde::Serialize; @@ -314,7 +315,7 @@ impl ReadyTransactions { } // add to removed - trace!(target: "txpool", "[{:?}] Removed as part of the subtree.", hash); + trace!(target: LOG_TARGET, "[{:?}] Removed as part of the subtree.", hash); removed.push(tx.transaction.transaction); } } @@ -521,7 +522,7 @@ impl BestIterator { pub fn report_invalid(&mut self, tx: &Arc>) { if let Some(to_report) = self.all.get(&tx.hash) { debug!( - target: "txpool", + target: LOG_TARGET, "[{:?}] Reported as invalid. Will skip sub-chains while iterating.", to_report.transaction.transaction.hash ); @@ -544,9 +545,8 @@ impl Iterator for BestIterator { // Check if the transaction was marked invalid. if self.invalid.contains(hash) { debug!( - target: "txpool", - "[{:?}] Skipping invalid child transaction while iterating.", - hash + target: LOG_TARGET, + "[{:?}] Skipping invalid child transaction while iterating.", hash, ); continue } diff --git a/client/transaction-pool/src/graph/validated_pool.rs b/client/transaction-pool/src/graph/validated_pool.rs index ab99a090e5654..bf8a059af2ff1 100644 --- a/client/transaction-pool/src/graph/validated_pool.rs +++ b/client/transaction-pool/src/graph/validated_pool.rs @@ -22,6 +22,7 @@ use std::{ sync::Arc, }; +use crate::LOG_TARGET; use futures::channel::mpsc::{channel, Sender}; use parking_lot::{Mutex, RwLock}; use sc_transaction_pool_api::{error, PoolStatus, ReadyTransactions}; @@ -199,7 +200,7 @@ impl ValidatedPool { Err(e) => if e.is_full() { log::warn!( - target: "txpool", + target: LOG_TARGET, "[{:?}] Trying to notify an import but the channel is full", hash, ); @@ -230,15 +231,17 @@ impl ValidatedPool { let ready_limit = &self.options.ready; let future_limit = &self.options.future; - log::debug!(target: "txpool", "Pool Status: {:?}", status); + log::debug!(target: LOG_TARGET, "Pool Status: {:?}", status); if ready_limit.is_exceeded(status.ready, status.ready_bytes) || future_limit.is_exceeded(status.future, status.future_bytes) { log::debug!( - target: "txpool", + target: LOG_TARGET, "Enforcing limits ({}/{}kB ready, {}/{}kB future", - ready_limit.count, ready_limit.total_bytes / 1024, - future_limit.count, future_limit.total_bytes / 1024, + ready_limit.count, + ready_limit.total_bytes / 1024, + future_limit.count, + future_limit.total_bytes / 1024, ); // clean up the pool @@ -254,7 +257,7 @@ impl ValidatedPool { removed }; if !removed.is_empty() { - log::debug!(target: "txpool", "Enforcing limits: {} dropped", removed.len()); + log::debug!(target: LOG_TARGET, "Enforcing limits: {} dropped", removed.len()); } // run notifications @@ -385,7 +388,7 @@ impl ValidatedPool { // unknown to caller => let's just notify listeners (and issue debug // message) log::warn!( - target: "txpool", + target: LOG_TARGET, "[{:?}] Removing invalid transaction from update: {}", hash, err, @@ -595,14 +598,14 @@ impl ValidatedPool { return vec![] } - log::debug!(target: "txpool", "Removing invalid transactions: {:?}", hashes); + log::debug!(target: LOG_TARGET, "Removing invalid transactions: {:?}", hashes); // temporarily ban invalid transactions self.rotator.ban(&Instant::now(), hashes.iter().cloned()); let invalid = self.pool.write().remove_subtree(hashes); - log::debug!(target: "txpool", "Removed invalid transactions: {:?}", invalid); + log::debug!(target: LOG_TARGET, "Removed invalid transactions: {:?}", invalid); let mut listener = self.listener.write(); for tx in &invalid { @@ -629,7 +632,11 @@ impl ValidatedPool { /// Notify all watchers that transactions in the block with hash have been finalized pub async fn on_block_finalized(&self, block_hash: BlockHash) -> Result<(), B::Error> { - log::trace!(target: "txpool", "Attempting to notify watchers of finalization for {}", block_hash); + log::trace!( + target: LOG_TARGET, + "Attempting to notify watchers of finalization for {}", + block_hash, + ); self.listener.write().finalized(block_hash); Ok(()) } diff --git a/client/transaction-pool/src/lib.rs b/client/transaction-pool/src/lib.rs index f3797b180f14d..44bda82da38a1 100644 --- a/client/transaction-pool/src/lib.rs +++ b/client/transaction-pool/src/lib.rs @@ -33,7 +33,7 @@ mod tests; pub use crate::api::FullChainApi; use async_trait::async_trait; -use enactment_state::EnactmentState; +use enactment_state::{EnactmentAction, EnactmentState}; use futures::{ channel::oneshot, future::{self, ready}, @@ -67,6 +67,8 @@ use prometheus_endpoint::Registry as PrometheusRegistry; use sp_blockchain::{HashAndNumber, TreeRoute}; +pub(crate) const LOG_TARGET: &str = "txpool"; + type BoxedReadyIterator = Box>> + Send>; @@ -116,7 +118,7 @@ impl ReadyPoll { while idx < self.pollers.len() { if self.pollers[idx].0 <= number { let poller_sender = self.pollers.swap_remove(idx); - log::debug!(target: "txpool", "Sending ready signal at block {}", number); + log::debug!(target: LOG_TARGET, "Sending ready signal at block {}", number); let _ = poller_sender.1.send(iterator_factory()); } else { idx += 1; @@ -336,7 +338,7 @@ where } if self.ready_poll.lock().updated_at() >= at { - log::trace!(target: "txpool", "Transaction pool already processed block #{}", at); + log::trace!(target: LOG_TARGET, "Transaction pool already processed block #{}", at); let iterator: ReadyIteratorFor = Box::new(self.pool.validated_pool().ready()); return async move { iterator }.boxed() } @@ -554,16 +556,16 @@ async fn prune_known_txs_for_block>(); - log::trace!(target: "txpool", "Pruning transactions: {:?}", hashes); + log::trace!(target: LOG_TARGET, "Pruning transactions: {:?}", hashes); let header = match api.block_header(block_hash) { Ok(Some(h)) => h, Ok(None) => { - log::debug!(target: "txpool", "Could not find header for {:?}.", block_hash); + log::debug!(target: LOG_TARGET, "Could not find header for {:?}.", block_hash); return hashes }, Err(e) => { - log::debug!(target: "txpool", "Error retrieving header for {:?}: {}", block_hash, e); + log::debug!(target: LOG_TARGET, "Error retrieving header for {:?}: {}", block_hash, e); return hashes }, }; @@ -587,7 +589,7 @@ where /// (that have already been enacted) and resubmits transactions that were /// retracted. async fn handle_enactment(&self, tree_route: TreeRoute) { - log::trace!(target: "txpool", "handle_enactment tree_route: {tree_route:?}"); + log::trace!(target: LOG_TARGET, "handle_enactment tree_route: {tree_route:?}"); let pool = self.pool.clone(); let api = self.api.clone(); @@ -595,7 +597,7 @@ where Some(HashAndNumber { hash, number }) => (hash, number), None => { log::warn!( - target: "txpool", + target: LOG_TARGET, "Skipping ChainEvent - no last block in tree route {:?}", tree_route, ); @@ -666,7 +668,7 @@ where if !contains { log::debug!( - target: "txpool", + target: LOG_TARGET, "[{:?}]: Resubmitting from retracted block {:?}", tx_hash, hash, @@ -691,7 +693,7 @@ where .await { log::debug!( - target: "txpool", + target: LOG_TARGET, "[{:?}] Error re-submitting transactions: {}", hash, e, @@ -732,23 +734,29 @@ where )), } }; + let block_id_to_number = + |hash| self.api.block_id_to_number(&BlockId::Hash(hash)).map_err(|e| format!("{}", e)); - let result = self.enactment_state.lock().update(&event, &compute_tree_route); + let result = + self.enactment_state + .lock() + .update(&event, &compute_tree_route, &block_id_to_number); match result { Err(msg) => { - log::debug!(target: "txpool", "{msg}"); + log::debug!(target: LOG_TARGET, "{msg}"); self.enactment_state.lock().force_update(&event); }, - Ok(None) => {}, - Ok(Some(tree_route)) => { + Ok(EnactmentAction::Skip) => return, + Ok(EnactmentAction::HandleFinalization) => {}, + Ok(EnactmentAction::HandleEnactment(tree_route)) => { self.handle_enactment(tree_route).await; }, }; if let ChainEvent::Finalized { hash, tree_route } = event { log::trace!( - target: "txpool", + target: LOG_TARGET, "on-finalized enacted: {tree_route:?}, previously finalized: \ {prev_finalized_block:?}", ); @@ -756,7 +764,7 @@ where for hash in tree_route.iter().chain(std::iter::once(&hash)) { if let Err(e) = self.pool.validated_pool().on_block_finalized(*hash).await { log::warn!( - target: "txpool", + target: LOG_TARGET, "Error occurred while attempting to notify watchers about finalization {}: {}", hash, e ) diff --git a/client/transaction-pool/src/revalidation.rs b/client/transaction-pool/src/revalidation.rs index d8c8bea625fb3..99602b50a2269 100644 --- a/client/transaction-pool/src/revalidation.rs +++ b/client/transaction-pool/src/revalidation.rs @@ -24,7 +24,10 @@ use std::{ sync::Arc, }; -use crate::graph::{ChainApi, ExtrinsicHash, NumberFor, Pool, ValidatedTransaction}; +use crate::{ + graph::{ChainApi, ExtrinsicHash, NumberFor, Pool, ValidatedTransaction}, + LOG_TARGET, +}; use sc_utils::mpsc::{tracing_unbounded, TracingUnboundedReceiver, TracingUnboundedSender}; use sp_runtime::{ generic::BlockId, @@ -82,13 +85,23 @@ async fn batch_revalidate( for (validation_result, ext_hash, ext) in validation_results { match validation_result { Ok(Err(TransactionValidityError::Invalid(err))) => { - log::debug!(target: "txpool", "[{:?}]: Revalidation: invalid {:?}", ext_hash, err); + log::debug!( + target: LOG_TARGET, + "[{:?}]: Revalidation: invalid {:?}", + ext_hash, + err, + ); invalid_hashes.push(ext_hash); }, Ok(Err(TransactionValidityError::Unknown(err))) => { // skipping unknown, they might be pushed by valid or invalid transaction // when latter resubmitted. - log::trace!(target: "txpool", "[{:?}]: Unknown during revalidation: {:?}", ext_hash, err); + log::trace!( + target: LOG_TARGET, + "[{:?}]: Unknown during revalidation: {:?}", + ext_hash, + err, + ); }, Ok(Ok(validity)) => { revalidated.insert( @@ -105,7 +118,7 @@ async fn batch_revalidate( }, Err(validation_err) => { log::debug!( - target: "txpool", + target: LOG_TARGET, "[{:?}]: Removing due to error during revalidation: {}", ext_hash, validation_err @@ -183,7 +196,7 @@ impl RevalidationWorker { // we don't add something that already scheduled for revalidation if self.members.contains_key(&ext_hash) { log::trace!( - target: "txpool", + target: LOG_TARGET, "[{:?}] Skipped adding for revalidation: Already there.", ext_hash, ); @@ -231,7 +244,7 @@ impl RevalidationWorker { if batch_len > 0 || this.len() > 0 { log::debug!( - target: "txpool", + target: LOG_TARGET, "Revalidated {} transactions. Left in the queue for revalidation: {}.", batch_len, this.len(), @@ -248,7 +261,7 @@ impl RevalidationWorker { if this.members.len() > 0 { log::debug!( - target: "txpool", + target: LOG_TARGET, "Updated revalidation queue at {:?}. Transactions: {:?}", this.best_block, this.members, @@ -320,14 +333,15 @@ where ) { if transactions.len() > 0 { log::debug!( - target: "txpool", "Sent {} transactions to revalidation queue", + target: LOG_TARGET, + "Sent {} transactions to revalidation queue", transactions.len(), ); } if let Some(ref to_worker) = self.background { if let Err(e) = to_worker.unbounded_send(WorkerPayload { at, transactions }) { - log::warn!(target: "txpool", "Failed to update background worker: {:?}", e); + log::warn!(target: LOG_TARGET, "Failed to update background worker: {:?}", e); } } else { let pool = self.pool.clone(); diff --git a/client/transaction-pool/tests/pool.rs b/client/transaction-pool/tests/pool.rs index 27d62c3250abc..511f6d9e24b5f 100644 --- a/client/transaction-pool/tests/pool.rs +++ b/client/transaction-pool/tests/pool.rs @@ -45,6 +45,8 @@ use substrate_test_runtime_client::{ }; use substrate_test_runtime_transaction_pool::{uxt, TestApi}; +const LOG_TARGET: &str = "txpool"; + fn pool() -> Pool { Pool::new(Default::default(), true.into(), TestApi::with_alice_nonce(209).into()) } @@ -505,7 +507,7 @@ fn fork_aware_finalization() { canon_watchers.push((watcher, header.hash())); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None }; b1 = header.hash(); block_on(pool.maintain(event)); @@ -521,7 +523,7 @@ fn fork_aware_finalization() { block_on(pool.submit_and_watch(&BlockId::number(1), SOURCE, from_dave.clone())) .expect("1. Imported"); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> C2: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> C2: {:?} {:?}", header.hash(), header); let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None }; c2 = header.hash(); block_on(pool.maintain(event)); @@ -536,7 +538,7 @@ fn fork_aware_finalization() { assert_eq!(pool.status().ready, 1); let header = pool.api().push_block_with_parent(c2, vec![from_bob.clone()], true); - log::trace!(target:"txpool", ">> D2: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> D2: {:?} {:?}", header.hash(), header); let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None }; d2 = header.hash(); block_on(pool.maintain(event)); @@ -550,7 +552,7 @@ fn fork_aware_finalization() { .expect("1.Imported"); assert_eq!(pool.status().ready, 1); let header = pool.api().push_block_with_parent(b1, vec![from_charlie.clone()], true); - log::trace!(target:"txpool", ">> C1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header); c1 = header.hash(); canon_watchers.push((watcher, header.hash())); let event = block_event_with_retracted(header.clone(), d2, pool.api()); @@ -568,7 +570,7 @@ fn fork_aware_finalization() { .expect("1. Imported"); assert_eq!(pool.status().ready, 3); let header = pool.api().push_block_with_parent(c1, vec![xt.clone()], true); - log::trace!(target:"txpool", ">> D1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> D1: {:?} {:?}", header.hash(), header); d1 = header.hash(); canon_watchers.push((w, header.hash())); @@ -584,7 +586,7 @@ fn fork_aware_finalization() { // block E1 { let header = pool.api().push_block_with_parent(d1, vec![from_dave, from_bob], true); - log::trace!(target:"txpool", ">> E1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> E1: {:?} {:?}", header.hash(), header); e1 = header.hash(); let event = ChainEvent::NewBestBlock { hash: header.hash(), tree_route: None }; block_on(pool.maintain(event)); @@ -1115,7 +1117,7 @@ fn switching_fork_with_finalized_works() { pool.api() .push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); b1_header = header; } @@ -1131,7 +1133,7 @@ fn switching_fork_with_finalized_works() { ); assert_eq!(pool.status().ready, 2); - log::trace!(target:"txpool", ">> B2: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B2: {:?} {:?}", header.hash(), header); b2_header = header; } @@ -1193,7 +1195,7 @@ fn switching_fork_multiple_times_works() { pool.api() .push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); b1_header = header; } @@ -1209,7 +1211,7 @@ fn switching_fork_multiple_times_works() { ); assert_eq!(pool.status().ready, 2); - log::trace!(target:"txpool", ">> B2: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B2: {:?} {:?}", header.hash(), header); b2_header = header; } @@ -1306,7 +1308,7 @@ fn two_blocks_delayed_finalization_works() { .push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); b1_header = header; } @@ -1320,7 +1322,7 @@ fn two_blocks_delayed_finalization_works() { .push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true); assert_eq!(pool.status().ready, 2); - log::trace!(target:"txpool", ">> C1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header); c1_header = header; } @@ -1334,7 +1336,7 @@ fn two_blocks_delayed_finalization_works() { .push_block_with_parent(c1_header.hash(), vec![from_charlie.clone()], true); assert_eq!(pool.status().ready, 3); - log::trace!(target:"txpool", ">> D1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> D1: {:?} {:?}", header.hash(), header); d1_header = header; } @@ -1418,7 +1420,7 @@ fn delayed_finalization_does_not_retract() { .push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); b1_header = header; } @@ -1432,7 +1434,7 @@ fn delayed_finalization_does_not_retract() { .push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true); assert_eq!(pool.status().ready, 2); - log::trace!(target:"txpool", ">> C1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header); c1_header = header; } @@ -1513,7 +1515,7 @@ fn best_block_after_finalization_does_not_retract() { .push_block_with_parent(a_header.hash(), vec![from_alice.clone()], true); assert_eq!(pool.status().ready, 1); - log::trace!(target:"txpool", ">> B1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> B1: {:?} {:?}", header.hash(), header); b1_header = header; } @@ -1527,7 +1529,7 @@ fn best_block_after_finalization_does_not_retract() { .push_block_with_parent(b1_header.hash(), vec![from_bob.clone()], true); assert_eq!(pool.status().ready, 2); - log::trace!(target:"txpool", ">> C1: {:?} {:?}", header.hash(), header); + log::trace!(target: LOG_TARGET, ">> C1: {:?} {:?}", header.hash(), header); c1_header = header; } diff --git a/frame/alliance/Cargo.toml b/frame/alliance/Cargo.toml index da0a7d665747d..d6a16e2c32e36 100644 --- a/frame/alliance/Cargo.toml +++ b/frame/alliance/Cargo.toml @@ -17,7 +17,7 @@ array-bytes = { version = "4.1", optional = true } sha2 = { version = "0.10.1", default-features = false, optional = true } log = { version = "0.4.14", default-features = false } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } diff --git a/frame/assets/Cargo.toml b/frame/assets/Cargo.toml index 84bfd9535a461..6fe0e3e6c3042 100644 --- a/frame/assets/Cargo.toml +++ b/frame/assets/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-std = { version = "5.0.0", default-features = false, path = "../../primitives/std" } # Needed for various traits. In our case, `OnFinalize`. diff --git a/frame/atomic-swap/Cargo.toml b/frame/atomic-swap/Cargo.toml index 5220edb9d17c7..9c3bcf73af985 100644 --- a/frame/atomic-swap/Cargo.toml +++ b/frame/atomic-swap/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/aura/Cargo.toml b/frame/aura/Cargo.toml index 552f13301d311..d2fd5a5b9c372 100644 --- a/frame/aura/Cargo.toml +++ b/frame/aura/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/authority-discovery/Cargo.toml b/frame/authority-discovery/Cargo.toml index 47bd1a126f4da..50c6c411c5ae0 100644 --- a/frame/authority-discovery/Cargo.toml +++ b/frame/authority-discovery/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/authorship/Cargo.toml b/frame/authorship/Cargo.toml index 7c0289909f806..84d00a6fbc54e 100644 --- a/frame/authorship/Cargo.toml +++ b/frame/authorship/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } impl-trait-for-tuples = "0.2.2" diff --git a/frame/babe/Cargo.toml b/frame/babe/Cargo.toml index a3232f6f981d0..aa581392721ae 100644 --- a/frame/babe/Cargo.toml +++ b/frame/babe/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/bags-list/Cargo.toml b/frame/bags-list/Cargo.toml index 52dd14b7d01c8..e3a4965f6c086 100644 --- a/frame/bags-list/Cargo.toml +++ b/frame/bags-list/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # parity -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # primitives diff --git a/frame/balances/Cargo.toml b/frame/balances/Cargo.toml index 934138a900214..15ef136aa3800 100644 --- a/frame/balances/Cargo.toml +++ b/frame/balances/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/balances/src/benchmarking.rs b/frame/balances/src/benchmarking.rs index 206adba0f044b..f85ff43b715fe 100644 --- a/frame/balances/src/benchmarking.rs +++ b/frame/balances/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2020-2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,22 +20,25 @@ #![cfg(feature = "runtime-benchmarks")] use super::*; +use crate::Pallet as Balances; -use frame_benchmarking::{account, benchmarks_instance_pallet, whitelisted_caller}; +use frame_benchmarking::{account, impl_benchmark_test_suite, whitelisted_caller}; +use frame_support::benchmarking::*; use frame_system::RawOrigin; -use sp_runtime::traits::Bounded; - -use crate::Pallet as Balances; const SEED: u32 = 0; // existential deposit multiplier const ED_MULTIPLIER: u32 = 10; -benchmarks_instance_pallet! { +#[instance_benchmarks] +mod benchmarks { + use super::*; + // Benchmark `transfer` extrinsic with the worst possible conditions: // * Transfer will kill the sender account. // * Transfer will create the recipient account. - transfer { + #[benchmark] + fn transfer() { let existential_deposit = T::ExistentialDeposit::get(); let caller = whitelisted_caller(); @@ -47,53 +50,66 @@ benchmarks_instance_pallet! { // and reap this user. let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } // Benchmark `transfer` with the best possible condition: // * Both accounts exist and will continue to exist. - #[extra] - transfer_best_case { + #[benchmark(extra)] + fn transfer_best_case() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - // Give the sender account max funds for transfer (their account will never reasonably be killed). - let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + // Give the sender account max funds for transfer (their account will never reasonably be + // killed). + let _ = + as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); // Give the recipient account existential deposit (thus their account already exists). let existential_deposit = T::ExistentialDeposit::get(); - let _ = as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); + let _ = + as Currency<_>>::make_free_balance_be(&recipient, existential_deposit); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + + #[extrinsic_call] + transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert!(!Balances::::free_balance(&caller).is_zero()); assert!(!Balances::::free_balance(&recipient).is_zero()); } // Benchmark `transfer_keep_alive` with the worst possible condition: // * The recipient account is created. - transfer_keep_alive { + #[benchmark] + fn transfer_keep_alive() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); // Give the sender account max funds, thus a transfer will not kill account. - let _ = as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); + let _ = + as Currency<_>>::make_free_balance_be(&caller, T::Balance::max_value()); let existential_deposit = T::ExistentialDeposit::get(); let transfer_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); - }: _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert!(!Balances::::free_balance(&caller).is_zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } // Benchmark `set_balance` coming from ROOT account. This always creates an account. - set_balance_creating { + #[benchmark] + fn set_balance_creating() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -101,14 +117,17 @@ benchmarks_instance_pallet! { let existential_deposit = T::ExistentialDeposit::get(); let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); - }: set_balance(RawOrigin::Root, user_lookup, balance_amount, balance_amount) - verify { + + #[extrinsic_call] + set_balance(RawOrigin::Root, user_lookup, balance_amount, balance_amount); + assert_eq!(Balances::::free_balance(&user), balance_amount); assert_eq!(Balances::::reserved_balance(&user), balance_amount); } // Benchmark `set_balance` coming from ROOT account. This always kills an account. - set_balance_killing { + #[benchmark] + fn set_balance_killing() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -116,15 +135,18 @@ benchmarks_instance_pallet! { let existential_deposit = T::ExistentialDeposit::get(); let balance_amount = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&user, balance_amount); - }: set_balance(RawOrigin::Root, user_lookup, Zero::zero(), Zero::zero()) - verify { + + #[extrinsic_call] + set_balance(RawOrigin::Root, user_lookup, Zero::zero(), Zero::zero()); + assert!(Balances::::free_balance(&user).is_zero()); } // Benchmark `force_transfer` extrinsic with the worst possible conditions: // * Transfer will kill the sender account. // * Transfer will create the recipient account. - force_transfer { + #[benchmark] + fn force_transfer() { let existential_deposit = T::ExistentialDeposit::get(); let source: T::AccountId = account("source", 0, SEED); let source_lookup = T::Lookup::unlookup(source.clone()); @@ -133,12 +155,16 @@ benchmarks_instance_pallet! { let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&source, balance); - // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, and reap this user. + // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, + // and reap this user. let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); - }: force_transfer(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount) - verify { + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + + #[extrinsic_call] + _(RawOrigin::Root, source_lookup, recipient_lookup, transfer_amount); + assert_eq!(Balances::::free_balance(&source), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } @@ -146,10 +172,9 @@ benchmarks_instance_pallet! { // This benchmark performs the same operation as `transfer` in the worst case scenario, // but additionally introduces many new users into the storage, increasing the the merkle // trie and PoV size. - #[extra] - transfer_increasing_users { + #[benchmark(extra)] + fn transfer_increasing_users(u: Linear<0, 1_000>) { // 1_000 is not very much, but this upper bound can be controlled by the CLI. - let u in 0 .. 1_000; let existential_deposit = T::ExistentialDeposit::get(); let caller = whitelisted_caller(); @@ -161,17 +186,20 @@ benchmarks_instance_pallet! { // and reap this user. let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); - let transfer_amount = existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); + let transfer_amount = + existential_deposit.saturating_mul((ED_MULTIPLIER - 1).into()) + 1u32.into(); // Create a bunch of users in storage. - for i in 0 .. u { + for i in 0..u { // The `account` function uses `blake2_256` to generate unique accounts, so these // should be quite random and evenly distributed in the trie. let new_user: T::AccountId = account("new_user", i, SEED); let _ = as Currency<_>>::make_free_balance_be(&new_user, balance); } - }: transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount) - verify { + + #[extrinsic_call] + transfer(RawOrigin::Signed(caller.clone()), recipient_lookup, transfer_amount); + assert_eq!(Balances::::free_balance(&caller), Zero::zero()); assert_eq!(Balances::::free_balance(&recipient), transfer_amount); } @@ -179,7 +207,8 @@ benchmarks_instance_pallet! { // Benchmark `transfer_all` with the worst possible condition: // * The recipient account is created // * The sender is killed - transfer_all { + #[benchmark] + fn transfer_all() { let caller = whitelisted_caller(); let recipient: T::AccountId = account("recipient", 0, SEED); let recipient_lookup = T::Lookup::unlookup(recipient.clone()); @@ -188,13 +217,16 @@ benchmarks_instance_pallet! { let existential_deposit = T::ExistentialDeposit::get(); let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into()); let _ = as Currency<_>>::make_free_balance_be(&caller, balance); - }: _(RawOrigin::Signed(caller.clone()), recipient_lookup, false) - verify { + + #[extrinsic_call] + _(RawOrigin::Signed(caller.clone()), recipient_lookup, false); + assert!(Balances::::free_balance(&caller).is_zero()); assert_eq!(Balances::::free_balance(&recipient), balance); } - force_unreserve { + #[benchmark] + fn force_unreserve() { let user: T::AccountId = account("user", 0, SEED); let user_lookup = T::Lookup::unlookup(user.clone()); @@ -208,15 +240,16 @@ benchmarks_instance_pallet! { assert_eq!(Balances::::reserved_balance(&user), balance); assert!(Balances::::free_balance(&user).is_zero()); - }: _(RawOrigin::Root, user_lookup, balance) - verify { + #[extrinsic_call] + _(RawOrigin::Root, user_lookup, balance); + assert!(Balances::::reserved_balance(&user).is_zero()); assert_eq!(Balances::::free_balance(&user), balance); } - impl_benchmark_test_suite!( + impl_benchmark_test_suite! { Balances, crate::tests_composite::ExtBuilder::default().build(), crate::tests_composite::Test, - ) + } } diff --git a/frame/beefy-mmr/Cargo.toml b/frame/beefy-mmr/Cargo.toml index f65270acdc3f8..54da26c394f0c 100644 --- a/frame/beefy-mmr/Cargo.toml +++ b/frame/beefy-mmr/Cargo.toml @@ -10,7 +10,7 @@ homepage = "https://substrate.io" [dependencies] array-bytes = { version = "4.1", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } diff --git a/frame/beefy/Cargo.toml b/frame/beefy/Cargo.toml index 707e8e25712ab..4db8c05682840 100644 --- a/frame/beefy/Cargo.toml +++ b/frame/beefy/Cargo.toml @@ -9,7 +9,7 @@ description = "BEEFY FRAME pallet" homepage = "https://substrate.io" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } beefy-primitives = { version = "4.0.0-dev", default-features = false, path = "../../primitives/beefy", package = "sp-beefy" } diff --git a/frame/benchmarking/Cargo.toml b/frame/benchmarking/Cargo.toml index 7c18b69401884..168b2605738ab 100644 --- a/frame/benchmarking/Cargo.toml +++ b/frame/benchmarking/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } linregress = { version = "0.4.4", optional = true } log = { version = "0.4.17", default-features = false } paste = "1.0" diff --git a/frame/benchmarking/src/lib.rs b/frame/benchmarking/src/lib.rs index 29fa0b6a6af70..e4ebd2f228afe 100644 --- a/frame/benchmarking/src/lib.rs +++ b/frame/benchmarking/src/lib.rs @@ -1836,15 +1836,15 @@ macro_rules! add_benchmark { /// Callback for `define_benchmarks` to call `add_benchmark`. #[macro_export] macro_rules! cb_add_benchmarks { - // anchor + // anchor ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] ) => { - add_benchmark!( $params, $batches, $name, $( $location )* ); + $crate::add_benchmark!( $params, $batches, $name, $( $location )* ); }; - // recursion tail + // recursion tail ( $params:ident, $batches:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { - cb_add_benchmarks!( $params, $batches, [ $name, $( $location )* ] ); - cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); - } + $crate::cb_add_benchmarks!( $params, $batches, [ $name, $( $location )* ] ); + $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); + } } /// This macro allows users to easily generate a list of benchmarks for the pallets configured @@ -1884,24 +1884,23 @@ macro_rules! list_benchmark { /// Callback for `define_benchmarks` to call `list_benchmark`. #[macro_export] macro_rules! cb_list_benchmarks { - // anchor + // anchor ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] ) => { - list_benchmark!( $list, $extra, $name, $( $location )* ); + $crate::list_benchmark!( $list, $extra, $name, $( $location )* ); }; - // recursion tail + // recursion tail ( $list:ident, $extra:ident, [ $name:path, $( $location:tt )* ] $([ $names:path, $( $locations:tt )* ])+ ) => { - cb_list_benchmarks!( $list, $extra, [ $name, $( $location )* ] ); - cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); - } + $crate::cb_list_benchmarks!( $list, $extra, [ $name, $( $location )* ] ); + $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); + } } /// Defines pallet configs that `add_benchmarks` and `list_benchmarks` use. /// Should be preferred instead of having a repetitive list of configs /// in `add_benchmark` and `list_benchmark`. - #[macro_export] macro_rules! define_benchmarks { - ( $([ $names:path, $( $locations:tt )* ])* ) => { + ( $([ $names:path, $( $locations:tt )* ])* ) => { /// Calls `list_benchmark` with all configs from `define_benchmarks` /// and passes the first two parameters on. /// @@ -1910,11 +1909,11 @@ macro_rules! define_benchmarks { /// list_benchmarks!(list, extra); /// ``` #[macro_export] - macro_rules! list_benchmarks { - ( $list:ident, $extra:ident ) => { - cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); - } - } + macro_rules! list_benchmarks { + ( $list:ident, $extra:ident ) => { + $crate::cb_list_benchmarks!( $list, $extra, $([ $names, $( $locations )* ])+ ); + } + } /// Calls `add_benchmark` with all configs from `define_benchmarks` /// and passes the first two parameters on. @@ -1924,10 +1923,10 @@ macro_rules! define_benchmarks { /// add_benchmarks!(params, batches); /// ``` #[macro_export] - macro_rules! add_benchmarks { - ( $params:ident, $batches:ident ) => { - cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); - } - } - } + macro_rules! add_benchmarks { + ( $params:ident, $batches:ident ) => { + $crate::cb_add_benchmarks!( $params, $batches, $([ $names, $( $locations )* ])+ ); + } + } + } } diff --git a/frame/bounties/Cargo.toml b/frame/bounties/Cargo.toml index a5411952a385a..dcb1adae05777 100644 --- a/frame/bounties/Cargo.toml +++ b/frame/bounties/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } log = { version = "0.4.17", default-features = false } diff --git a/frame/child-bounties/Cargo.toml b/frame/child-bounties/Cargo.toml index 6b0a672d04225..8d29b7619759d 100644 --- a/frame/child-bounties/Cargo.toml +++ b/frame/child-bounties/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } log = { version = "0.4.17", default-features = false } diff --git a/frame/collective/Cargo.toml b/frame/collective/Cargo.toml index 0e8c5421f5ff1..26cd5a8ab44cc 100644 --- a/frame/collective/Cargo.toml +++ b/frame/collective/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index 8e6368490f6d7..0ee9e36f1dc78 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -8,13 +8,14 @@ homepage = "https://substrate.io" repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for WASM contracts" readme = "README.md" +include = ["src/**/*", "README.md", "CHANGELOG.md"] [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] [dependencies] bitflags = "1.3" -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/frame/contracts/primitives/Cargo.toml b/frame/contracts/primitives/Cargo.toml index d0c3a34ddfccb..aafcd912af657 100644 --- a/frame/contracts/primitives/Cargo.toml +++ b/frame/contracts/primitives/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] bitflags = "1.0" -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } # Substrate Dependencies (This crate should not rely on frame) sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } diff --git a/frame/contracts/proc-macro/src/lib.rs b/frame/contracts/proc-macro/src/lib.rs index a31d39f47e215..d0247d720a39a 100644 --- a/frame/contracts/proc-macro/src/lib.rs +++ b/frame/contracts/proc-macro/src/lib.rs @@ -122,23 +122,23 @@ fn iterate_fields(data: &syn::DataStruct, fmt: impl Fn(&Ident) -> TokenStream2) fn format_weight(field: &Ident) -> TokenStream2 { quote_spanned! { field.span() => - &if self.#field > 1_000_000_000 { + &if self.#field.ref_time() > 1_000_000_000 { format!( "{:.1?} ms", - Fixed::saturating_from_rational(self.#field, 1_000_000_000).to_float() + Fixed::saturating_from_rational(self.#field.ref_time(), 1_000_000_000).to_float() ) - } else if self.#field > 1_000_000 { + } else if self.#field.ref_time() > 1_000_000 { format!( "{:.1?} µs", - Fixed::saturating_from_rational(self.#field, 1_000_000).to_float() + Fixed::saturating_from_rational(self.#field.ref_time(), 1_000_000).to_float() ) - } else if self.#field > 1_000 { + } else if self.#field.ref_time() > 1_000 { format!( "{:.1?} ns", - Fixed::saturating_from_rational(self.#field, 1_000).to_float() + Fixed::saturating_from_rational(self.#field.ref_time(), 1_000).to_float() ) } else { - format!("{} ps", self.#field) + format!("{} ps", self.#field.ref_time()) } } } diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index d46da8469eb41..a750a6b1728d6 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -2928,7 +2928,7 @@ benchmarks! { // configured `Schedule` during benchmark development. // It can be outputed using the following command: // cargo run --manifest-path=bin/node/cli/Cargo.toml --release \ - // --features runtime-benchmarks -- benchmark --extra --dev --execution=native \ + // --features runtime-benchmarks -- benchmark pallet --extra --dev --execution=native \ // -p pallet_contracts -e print_schedule --no-median-slopes --no-min-squares #[extra] print_schedule { diff --git a/frame/contracts/src/chain_extension.rs b/frame/contracts/src/chain_extension.rs index dfa9c7c2de268..3974bdba30d14 100644 --- a/frame/contracts/src/chain_extension.rs +++ b/frame/contracts/src/chain_extension.rs @@ -227,7 +227,7 @@ impl<'a, 'b, E: Ext, S: State> Environment<'a, 'b, E, S> { /// /// Weight is synonymous with gas in substrate. pub fn charge_weight(&mut self, amount: Weight) -> Result { - self.inner.runtime.charge_gas(RuntimeCosts::ChainExtension(amount.ref_time())) + self.inner.runtime.charge_gas(RuntimeCosts::ChainExtension(amount)) } /// Adjust a previously charged amount down to its actual amount. @@ -237,7 +237,7 @@ impl<'a, 'b, E: Ext, S: State> Environment<'a, 'b, E, S> { pub fn adjust_weight(&mut self, charged: ChargedAmount, actual_weight: Weight) { self.inner .runtime - .adjust_gas(charged, RuntimeCosts::ChainExtension(actual_weight.ref_time())) + .adjust_gas(charged, RuntimeCosts::ChainExtension(actual_weight)) } /// Grants access to the execution environment of the current contract call. @@ -408,8 +408,7 @@ impl<'a, 'b, E: Ext, S: BufOut> Environment<'a, 'b, E, S> { buffer, allow_skip, |len| { - weight_per_byte - .map(|w| RuntimeCosts::ChainExtension(w.ref_time().saturating_mul(len.into()))) + weight_per_byte.map(|w| RuntimeCosts::ChainExtension(w.saturating_mul(len.into()))) }, ) } diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 8ae70b804d69b..b5b630653667b 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -1336,7 +1336,18 @@ where fn append_debug_buffer(&mut self, msg: &str) -> bool { if let Some(buffer) = &mut self.debug_message { - let mut msg = msg.bytes(); + let err_msg = scale_info::prelude::format!( + "Debug message too big (size={}) for debug buffer (bound={})", + msg.len(), + DebugBufferVec::::bound(), + ); + + let mut msg = if msg.len() > DebugBufferVec::::bound() { + err_msg.bytes() + } else { + msg.bytes() + }; + let num_drain = { let capacity = DebugBufferVec::::bound().checked_sub(buffer.len()).expect( " @@ -1349,16 +1360,7 @@ where msg.len().saturating_sub(capacity).min(buffer.len()) }; buffer.drain(0..num_drain); - buffer - .try_extend(&mut msg) - .map_err(|_| { - log::debug!( - target: "runtime::contracts", - "Debug message to big (size={}) for debug buffer (bound={})", - msg.len(), DebugBufferVec::::bound(), - ); - }) - .ok(); + buffer.try_extend(&mut msg).ok(); true } else { false diff --git a/frame/contracts/src/gas.rs b/frame/contracts/src/gas.rs index ccbe680b2f767..c8383f307c0ea 100644 --- a/frame/contracts/src/gas.rs +++ b/frame/contracts/src/gas.rs @@ -153,8 +153,8 @@ impl GasMeter { /// Returns `OutOfGas` if there is not enough gas or addition of the specified /// amount of gas has lead to overflow. On success returns `Proceed`. /// - /// NOTE that amount is always consumed, i.e. if there is not enough gas - /// then the counter will be set to zero. + /// NOTE that amount isn't consumed if there is not enough gas. This is considered + /// safe because we always charge gas before performing any resource-spending action. #[inline] pub fn charge>(&mut self, token: Tok) -> Result { #[cfg(test)] diff --git a/frame/contracts/src/lib.rs b/frame/contracts/src/lib.rs index 9efeec65f51f5..672c4517d06a1 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -123,6 +123,7 @@ use pallet_contracts_primitives::{ StorageDeposit, }; use scale_info::TypeInfo; +use smallvec::Array; use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup, TrailingZeroInput}; use sp_std::{fmt::Debug, marker::PhantomData, prelude::*}; @@ -272,7 +273,10 @@ pub mod pallet { /// The allowed depth is `CallStack::size() + 1`. /// Therefore a size of `0` means that a contract cannot use call or instantiate. /// In other words only the origin called "root contract" is allowed to execute then. - type CallStack: smallvec::Array>; + /// + /// This setting along with [`MaxCodeLen`](#associatedtype.MaxCodeLen) directly affects + /// memory usage of your runtime. + type CallStack: Array>; /// The maximum number of contracts that can be pending for deletion. /// @@ -323,6 +327,10 @@ pub mod pallet { /// The maximum length of a contract code in bytes. This limit applies to the instrumented /// version of the code. Therefore `instantiate_with_code` can fail even when supplying /// a wasm binary below this maximum size. + /// + /// The value should be chosen carefully taking into the account the overall memory limit + /// your runtime has, as well as the [maximum allowed callstack + /// depth](#associatedtype.CallStack). Look into the `integrity_test()` for some insights. #[pallet::constant] type MaxCodeLen: Get; @@ -372,6 +380,71 @@ pub mod pallet { T::WeightInfo::on_process_deletion_queue_batch() } } + + fn integrity_test() { + // Total runtime memory is expected to have 128Mb upper limit + const MAX_RUNTIME_MEM: u32 = 1024 * 1024 * 128; + // Memory limits for a single contract: + // Value stack size: 1Mb per contract, default defined in wasmi + const MAX_STACK_SIZE: u32 = 1024 * 1024; + // Heap limit is normally 16 mempages of 64kb each = 1Mb per contract + let max_heap_size = T::Schedule::get().limits.max_memory_size(); + // Max call depth is CallStack::size() + 1 + let max_call_depth = u32::try_from(T::CallStack::size().saturating_add(1)) + .expect("CallStack size is too big"); + + // Check that given configured `MaxCodeLen`, runtime heap memory limit can't be broken. + // + // In worst case, the decoded wasm contract code would be `x16` times larger than the + // encoded one. This is because even a single-byte wasm instruction has 16-byte size in + // wasmi. This gives us `MaxCodeLen*16` safety margin. + // + // Next, the pallet keeps both the original and instrumented wasm blobs for each + // contract, hence we add up `MaxCodeLen*2` more to the safety margin. + // + // Finally, the inefficiencies of the freeing-bump allocator + // being used in the client for the runtime memory allocations, could lead to possible + // memory allocations for contract code grow up to `x4` times in some extreme cases, + // which gives us total multiplier of `18*4` for `MaxCodeLen`. + // + // That being said, for every contract executed in runtime, at least `MaxCodeLen*18*4` + // memory should be available. Note that maximum allowed heap memory and stack size per + // each contract (stack frame) should also be counted. + // + // Finally, we allow 50% of the runtime memory to be utilized by the contracts call + // stack, keeping the rest for other facilities, such as PoV, etc. + // + // This gives us the following formula: + // + // `(MaxCodeLen * 18 * 4 + MAX_STACK_SIZE + max_heap_size) * max_call_depth < + // MAX_RUNTIME_MEM/2` + // + // Hence the upper limit for the `MaxCodeLen` can be defined as follows: + let code_len_limit = MAX_RUNTIME_MEM + .saturating_div(2) + .saturating_div(max_call_depth) + .saturating_sub(max_heap_size) + .saturating_sub(MAX_STACK_SIZE) + .saturating_div(18 * 4); + + assert!( + T::MaxCodeLen::get() < code_len_limit, + "Given `CallStack` height {:?}, `MaxCodeLen` should be set less than {:?} \ + (current value is {:?}), to avoid possible runtime oom issues.", + max_call_depth, + code_len_limit, + T::MaxCodeLen::get(), + ); + + // Debug buffer should at least be large enough to accomodate a simple error message + const MIN_DEBUG_BUF_SIZE: u32 = 256; + assert!( + T::MaxDebugBufferLen::get() > MIN_DEBUG_BUF_SIZE, + "Debug buffer should have minimum size of {} (current setting is {})", + MIN_DEBUG_BUF_SIZE, + T::MaxDebugBufferLen::get(), + ) + } } #[pallet::call] @@ -381,7 +454,7 @@ pub mod pallet { { /// Deprecated version if [`Self::call`] for use in an in-storage `Call`. #[pallet::call_index(0)] - #[pallet::weight(T::WeightInfo::call().saturating_add(>::compat_weight(*gas_limit)))] + #[pallet::weight(T::WeightInfo::call().saturating_add(>::compat_weight_limit(*gas_limit)))] #[allow(deprecated)] #[deprecated(note = "1D weight is used in this extrinsic, please migrate to `call`")] pub fn call_old_weight( @@ -396,7 +469,7 @@ pub mod pallet { origin, dest, value, - >::compat_weight(gas_limit), + >::compat_weight_limit(gas_limit), storage_deposit_limit, data, ) @@ -406,7 +479,7 @@ pub mod pallet { #[pallet::call_index(1)] #[pallet::weight( T::WeightInfo::instantiate_with_code(code.len() as u32, data.len() as u32, salt.len() as u32) - .saturating_add(>::compat_weight(*gas_limit)) + .saturating_add(>::compat_weight_limit(*gas_limit)) )] #[allow(deprecated)] #[deprecated( @@ -424,7 +497,7 @@ pub mod pallet { Self::instantiate_with_code( origin, value, - >::compat_weight(gas_limit), + >::compat_weight_limit(gas_limit), storage_deposit_limit, code, data, @@ -435,7 +508,7 @@ pub mod pallet { /// Deprecated version if [`Self::instantiate`] for use in an in-storage `Call`. #[pallet::call_index(2)] #[pallet::weight( - T::WeightInfo::instantiate(data.len() as u32, salt.len() as u32).saturating_add(>::compat_weight(*gas_limit)) + T::WeightInfo::instantiate(data.len() as u32, salt.len() as u32).saturating_add(>::compat_weight_limit(*gas_limit)) )] #[allow(deprecated)] #[deprecated(note = "1D weight is used in this extrinsic, please migrate to `instantiate`")] @@ -451,7 +524,7 @@ pub mod pallet { Self::instantiate( origin, value, - >::compat_weight(gas_limit), + >::compat_weight_limit(gas_limit), storage_deposit_limit, code_hash, data, @@ -1216,12 +1289,12 @@ impl Pallet { >>::minimum_balance() } - /// Convert a 1D Weight to a 2D weight. + /// Convert gas_limit from 1D Weight to a 2D Weight. /// - /// Used by backwards compatible extrinsics. We cannot just set the proof to zero - /// or an old `Call` will just fail. - fn compat_weight(gas_limit: OldWeight) -> Weight { - Weight::from(gas_limit).set_proof_size(u64::from(T::MaxCodeLen::get()) * 2) + /// Used by backwards compatible extrinsics. We cannot just set the proof_size weight limit to + /// zero or an old `Call` will just fail with OutOfGas. + fn compat_weight_limit(gas_limit: OldWeight) -> Weight { + Weight::from_parts(gas_limit.0, u64::from(T::MaxCodeLen::get()) * 2) } } diff --git a/frame/contracts/src/schedule.rs b/frame/contracts/src/schedule.rs index 912e58b048ff4..eabd18c818952 100644 --- a/frame/contracts/src/schedule.rs +++ b/frame/contracts/src/schedule.rs @@ -21,7 +21,7 @@ use crate::{wasm::Determinism, weights::WeightInfo, Config}; use codec::{Decode, Encode}; -use frame_support::DefaultNoBound; +use frame_support::{weights::Weight, DefaultNoBound}; use pallet_contracts_proc_macro::{ScheduleDebug, WeightDebug}; use scale_info::TypeInfo; #[cfg(feature = "std")] @@ -134,9 +134,6 @@ pub struct Limits { /// The maximum length of a subject in bytes used for PRNG generation. pub subject_len: u32, - /// The maximum nesting level of the call stack. - pub call_depth: u32, - /// The maximum size of a storage value and event payload in bytes. pub payload_len: u32, } @@ -169,7 +166,7 @@ impl Limits { /// that use them as supporting instructions. Supporting means mainly pushing arguments /// and dropping return values in order to maintain a valid module. #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] -#[derive(Clone, Encode, Decode, PartialEq, Eq, WeightDebug, TypeInfo)] +#[derive(Clone, Encode, Decode, PartialEq, Eq, ScheduleDebug, TypeInfo)] #[scale_info(skip_type_params(T))] pub struct InstructionWeights { /// Version of the instruction weights. @@ -255,178 +252,178 @@ pub struct InstructionWeights { #[scale_info(skip_type_params(T))] pub struct HostFnWeights { /// Weight of calling `seal_caller`. - pub caller: u64, + pub caller: Weight, /// Weight of calling `seal_is_contract`. - pub is_contract: u64, + pub is_contract: Weight, /// Weight of calling `seal_code_hash`. - pub code_hash: u64, + pub code_hash: Weight, /// Weight of calling `seal_own_code_hash`. - pub own_code_hash: u64, + pub own_code_hash: Weight, /// Weight of calling `seal_caller_is_origin`. - pub caller_is_origin: u64, + pub caller_is_origin: Weight, /// Weight of calling `seal_address`. - pub address: u64, + pub address: Weight, /// Weight of calling `seal_gas_left`. - pub gas_left: u64, + pub gas_left: Weight, /// Weight of calling `seal_balance`. - pub balance: u64, + pub balance: Weight, /// Weight of calling `seal_value_transferred`. - pub value_transferred: u64, + pub value_transferred: Weight, /// Weight of calling `seal_minimum_balance`. - pub minimum_balance: u64, + pub minimum_balance: Weight, /// Weight of calling `seal_block_number`. - pub block_number: u64, + pub block_number: Weight, /// Weight of calling `seal_now`. - pub now: u64, + pub now: Weight, /// Weight of calling `seal_weight_to_fee`. - pub weight_to_fee: u64, + pub weight_to_fee: Weight, /// Weight of calling `gas`. - pub gas: u64, + pub gas: Weight, /// Weight of calling `seal_input`. - pub input: u64, + pub input: Weight, /// Weight per input byte copied to contract memory by `seal_input`. - pub input_per_byte: u64, + pub input_per_byte: Weight, /// Weight of calling `seal_return`. - pub r#return: u64, + pub r#return: Weight, /// Weight per byte returned through `seal_return`. - pub return_per_byte: u64, + pub return_per_byte: Weight, /// Weight of calling `seal_terminate`. - pub terminate: u64, + pub terminate: Weight, /// Weight of calling `seal_random`. - pub random: u64, + pub random: Weight, /// Weight of calling `seal_reposit_event`. - pub deposit_event: u64, + pub deposit_event: Weight, /// Weight per topic supplied to `seal_deposit_event`. - pub deposit_event_per_topic: u64, + pub deposit_event_per_topic: Weight, /// Weight per byte of an event deposited through `seal_deposit_event`. - pub deposit_event_per_byte: u64, + pub deposit_event_per_byte: Weight, /// Weight of calling `seal_debug_message`. - pub debug_message: u64, + pub debug_message: Weight, /// Weight of calling `seal_set_storage`. - pub set_storage: u64, + pub set_storage: Weight, /// Weight per written byten of an item stored with `seal_set_storage`. - pub set_storage_per_new_byte: u64, + pub set_storage_per_new_byte: Weight, /// Weight per overwritten byte of an item stored with `seal_set_storage`. - pub set_storage_per_old_byte: u64, + pub set_storage_per_old_byte: Weight, /// Weight of calling `seal_set_code_hash`. - pub set_code_hash: u64, + pub set_code_hash: Weight, /// Weight of calling `seal_clear_storage`. - pub clear_storage: u64, + pub clear_storage: Weight, /// Weight of calling `seal_clear_storage` per byte of the stored item. - pub clear_storage_per_byte: u64, + pub clear_storage_per_byte: Weight, /// Weight of calling `seal_contains_storage`. - pub contains_storage: u64, + pub contains_storage: Weight, /// Weight of calling `seal_contains_storage` per byte of the stored item. - pub contains_storage_per_byte: u64, + pub contains_storage_per_byte: Weight, /// Weight of calling `seal_get_storage`. - pub get_storage: u64, + pub get_storage: Weight, /// Weight per byte of an item received via `seal_get_storage`. - pub get_storage_per_byte: u64, + pub get_storage_per_byte: Weight, /// Weight of calling `seal_take_storage`. - pub take_storage: u64, + pub take_storage: Weight, /// Weight per byte of an item received via `seal_take_storage`. - pub take_storage_per_byte: u64, + pub take_storage_per_byte: Weight, /// Weight of calling `seal_transfer`. - pub transfer: u64, + pub transfer: Weight, /// Weight of calling `seal_call`. - pub call: u64, + pub call: Weight, /// Weight of calling `seal_delegate_call`. - pub delegate_call: u64, + pub delegate_call: Weight, /// Weight surcharge that is claimed if `seal_call` does a balance transfer. - pub call_transfer_surcharge: u64, + pub call_transfer_surcharge: Weight, /// Weight per byte that is cloned by supplying the `CLONE_INPUT` flag. - pub call_per_cloned_byte: u64, + pub call_per_cloned_byte: Weight, /// Weight of calling `seal_instantiate`. - pub instantiate: u64, + pub instantiate: Weight, /// Weight surcharge that is claimed if `seal_instantiate` does a balance transfer. - pub instantiate_transfer_surcharge: u64, + pub instantiate_transfer_surcharge: Weight, /// Weight per input byte supplied to `seal_instantiate`. - pub instantiate_per_input_byte: u64, + pub instantiate_per_input_byte: Weight, /// Weight per salt byte supplied to `seal_instantiate`. - pub instantiate_per_salt_byte: u64, + pub instantiate_per_salt_byte: Weight, /// Weight of calling `seal_hash_sha_256`. - pub hash_sha2_256: u64, + pub hash_sha2_256: Weight, /// Weight per byte hashed by `seal_hash_sha_256`. - pub hash_sha2_256_per_byte: u64, + pub hash_sha2_256_per_byte: Weight, /// Weight of calling `seal_hash_keccak_256`. - pub hash_keccak_256: u64, + pub hash_keccak_256: Weight, /// Weight per byte hashed by `seal_hash_keccak_256`. - pub hash_keccak_256_per_byte: u64, + pub hash_keccak_256_per_byte: Weight, /// Weight of calling `seal_hash_blake2_256`. - pub hash_blake2_256: u64, + pub hash_blake2_256: Weight, /// Weight per byte hashed by `seal_hash_blake2_256`. - pub hash_blake2_256_per_byte: u64, + pub hash_blake2_256_per_byte: Weight, /// Weight of calling `seal_hash_blake2_128`. - pub hash_blake2_128: u64, + pub hash_blake2_128: Weight, /// Weight per byte hashed by `seal_hash_blake2_128`. - pub hash_blake2_128_per_byte: u64, + pub hash_blake2_128_per_byte: Weight, /// Weight of calling `seal_ecdsa_recover`. - pub ecdsa_recover: u64, + pub ecdsa_recover: Weight, /// Weight of calling `seal_ecdsa_to_eth_address`. - pub ecdsa_to_eth_address: u64, + pub ecdsa_to_eth_address: Weight, /// Weight of calling `reentrance_count`. - pub reentrance_count: u64, + pub reentrance_count: Weight, /// Weight of calling `account_reentrance_count`. - pub account_reentrance_count: u64, + pub account_reentrance_count: Weight, /// Weight of calling `instantiation_nonce`. - pub instantiation_nonce: u64, + pub instantiation_nonce: Weight, /// The type parameter is used in the default implementation. #[codec(skip)] @@ -514,6 +511,12 @@ macro_rules! cost_byte_batched { }; } +macro_rules! to_weight { + ($ref_time:expr $(, $proof_size:expr )?) => { + Weight::from_ref_time($ref_time)$(.set_proof_size($proof_size))? + }; +} + impl Default for Limits { fn default() -> Self { Self { @@ -526,7 +529,6 @@ impl Default for Limits { table_size: 4096, br_table_size: 256, subject_len: 32, - call_depth: 32, payload_len: 16 * 1024, } } @@ -596,85 +598,115 @@ impl Default for InstructionWeights { } impl Default for HostFnWeights { + /// PoV should contain all trie nodes that are read during state transition (i.e. block + /// production). Hence we need to charge the `proof_size` weight for every host function which + /// reads storage, namely: + /// - get_storage, + /// - take_storage, + /// - contains_storage, + /// - clear_storage, + /// - set_storage. + /// + /// The last two functions write to storage, but they also do read storage in order to return + /// the size of the pre-existed value. Till we have PoV benchmarks implemented, we approximate + /// `proof_size` as being equal to the size of storage read. fn default() -> Self { Self { - caller: cost_batched!(seal_caller), - is_contract: cost_batched!(seal_is_contract), - code_hash: cost_batched!(seal_code_hash), - own_code_hash: cost_batched!(seal_own_code_hash), - caller_is_origin: cost_batched!(seal_caller_is_origin), - address: cost_batched!(seal_address), - gas_left: cost_batched!(seal_gas_left), - balance: cost_batched!(seal_balance), - value_transferred: cost_batched!(seal_value_transferred), - minimum_balance: cost_batched!(seal_minimum_balance), - block_number: cost_batched!(seal_block_number), - now: cost_batched!(seal_now), - weight_to_fee: cost_batched!(seal_weight_to_fee), - gas: cost_batched!(seal_gas), - input: cost_batched!(seal_input), - input_per_byte: cost_byte_batched!(seal_input_per_kb), - r#return: cost!(seal_return), - return_per_byte: cost_byte!(seal_return_per_kb), - terminate: cost!(seal_terminate), - random: cost_batched!(seal_random), - deposit_event: cost_batched!(seal_deposit_event), - deposit_event_per_topic: cost_batched_args!(seal_deposit_event_per_topic_and_kb, 1, 0), - deposit_event_per_byte: cost_byte_batched_args!( + caller: to_weight!(cost_batched!(seal_caller)), + is_contract: to_weight!(cost_batched!(seal_is_contract)), + code_hash: to_weight!(cost_batched!(seal_code_hash)), + own_code_hash: to_weight!(cost_batched!(seal_own_code_hash)), + caller_is_origin: to_weight!(cost_batched!(seal_caller_is_origin)), + address: to_weight!(cost_batched!(seal_address)), + gas_left: to_weight!(cost_batched!(seal_gas_left)), + balance: to_weight!(cost_batched!(seal_balance)), + value_transferred: to_weight!(cost_batched!(seal_value_transferred)), + minimum_balance: to_weight!(cost_batched!(seal_minimum_balance)), + block_number: to_weight!(cost_batched!(seal_block_number)), + now: to_weight!(cost_batched!(seal_now)), + weight_to_fee: to_weight!(cost_batched!(seal_weight_to_fee)), + gas: to_weight!(cost_batched!(seal_gas)), + input: to_weight!(cost_batched!(seal_input)), + input_per_byte: to_weight!(cost_byte_batched!(seal_input_per_kb)), + r#return: to_weight!(cost!(seal_return)), + return_per_byte: to_weight!(cost_byte!(seal_return_per_kb)), + terminate: to_weight!(cost!(seal_terminate)), + random: to_weight!(cost_batched!(seal_random)), + deposit_event: to_weight!(cost_batched!(seal_deposit_event)), + deposit_event_per_topic: to_weight!(cost_batched_args!( + seal_deposit_event_per_topic_and_kb, + 1, + 0 + )), + deposit_event_per_byte: to_weight!(cost_byte_batched_args!( seal_deposit_event_per_topic_and_kb, 0, 1 + )), + debug_message: to_weight!(cost_batched!(seal_debug_message)), + set_storage: to_weight!(cost_batched!(seal_set_storage)), + set_code_hash: to_weight!(cost_batched!(seal_set_code_hash)), + set_storage_per_new_byte: to_weight!(cost_byte_batched!(seal_set_storage_per_new_kb)), + set_storage_per_old_byte: to_weight!( + cost_byte_batched!(seal_set_storage_per_old_kb), + 1u64 ), - debug_message: cost_batched!(seal_debug_message), - set_storage: cost_batched!(seal_set_storage), - set_code_hash: cost_batched!(seal_set_code_hash), - set_storage_per_new_byte: cost_byte_batched!(seal_set_storage_per_new_kb), - set_storage_per_old_byte: cost_byte_batched!(seal_set_storage_per_old_kb), - clear_storage: cost_batched!(seal_clear_storage), - clear_storage_per_byte: cost_byte_batched!(seal_clear_storage_per_kb), - contains_storage: cost_batched!(seal_contains_storage), - contains_storage_per_byte: cost_byte_batched!(seal_contains_storage_per_kb), - get_storage: cost_batched!(seal_get_storage), - get_storage_per_byte: cost_byte_batched!(seal_get_storage_per_kb), - take_storage: cost_batched!(seal_take_storage), - take_storage_per_byte: cost_byte_batched!(seal_take_storage_per_kb), - transfer: cost_batched!(seal_transfer), - call: cost_batched!(seal_call), - delegate_call: cost_batched!(seal_delegate_call), - call_transfer_surcharge: cost_batched_args!(seal_call_per_transfer_clone_kb, 1, 0), - call_per_cloned_byte: cost_batched_args!(seal_call_per_transfer_clone_kb, 0, 1), - instantiate: cost_batched!(seal_instantiate), - instantiate_transfer_surcharge: cost_byte_batched_args!( + clear_storage: to_weight!(cost_batched!(seal_clear_storage)), + clear_storage_per_byte: to_weight!(cost_byte_batched!(seal_clear_storage_per_kb), 1u64), + contains_storage: to_weight!(cost_batched!(seal_contains_storage)), + contains_storage_per_byte: to_weight!( + cost_byte_batched!(seal_contains_storage_per_kb), + 1u64 + ), + get_storage: to_weight!(cost_batched!(seal_get_storage)), + get_storage_per_byte: to_weight!(cost_byte_batched!(seal_get_storage_per_kb), 1u64), + take_storage: to_weight!(cost_batched!(seal_take_storage)), + take_storage_per_byte: to_weight!(cost_byte_batched!(seal_take_storage_per_kb), 1u64), + transfer: to_weight!(cost_batched!(seal_transfer)), + call: to_weight!(cost_batched!(seal_call)), + delegate_call: to_weight!(cost_batched!(seal_delegate_call)), + call_transfer_surcharge: to_weight!(cost_batched_args!( + seal_call_per_transfer_clone_kb, + 1, + 0 + )), + call_per_cloned_byte: to_weight!(cost_batched_args!( + seal_call_per_transfer_clone_kb, + 0, + 1 + )), + instantiate: to_weight!(cost_batched!(seal_instantiate)), + instantiate_transfer_surcharge: to_weight!(cost_byte_batched_args!( seal_instantiate_per_transfer_input_salt_kb, 1, 0, 0 - ), - instantiate_per_input_byte: cost_byte_batched_args!( + )), + instantiate_per_input_byte: to_weight!(cost_byte_batched_args!( seal_instantiate_per_transfer_input_salt_kb, 0, 1, 0 - ), - instantiate_per_salt_byte: cost_byte_batched_args!( + )), + instantiate_per_salt_byte: to_weight!(cost_byte_batched_args!( seal_instantiate_per_transfer_input_salt_kb, 0, 0, 1 - ), - hash_sha2_256: cost_batched!(seal_hash_sha2_256), - hash_sha2_256_per_byte: cost_byte_batched!(seal_hash_sha2_256_per_kb), - hash_keccak_256: cost_batched!(seal_hash_keccak_256), - hash_keccak_256_per_byte: cost_byte_batched!(seal_hash_keccak_256_per_kb), - hash_blake2_256: cost_batched!(seal_hash_blake2_256), - hash_blake2_256_per_byte: cost_byte_batched!(seal_hash_blake2_256_per_kb), - hash_blake2_128: cost_batched!(seal_hash_blake2_128), - hash_blake2_128_per_byte: cost_byte_batched!(seal_hash_blake2_128_per_kb), - ecdsa_recover: cost_batched!(seal_ecdsa_recover), - ecdsa_to_eth_address: cost_batched!(seal_ecdsa_to_eth_address), - reentrance_count: cost_batched!(seal_reentrance_count), - account_reentrance_count: cost_batched!(seal_account_reentrance_count), - instantiation_nonce: cost_batched!(seal_instantiation_nonce), + )), + hash_sha2_256: to_weight!(cost_batched!(seal_hash_sha2_256)), + hash_sha2_256_per_byte: to_weight!(cost_byte_batched!(seal_hash_sha2_256_per_kb)), + hash_keccak_256: to_weight!(cost_batched!(seal_hash_keccak_256)), + hash_keccak_256_per_byte: to_weight!(cost_byte_batched!(seal_hash_keccak_256_per_kb)), + hash_blake2_256: to_weight!(cost_batched!(seal_hash_blake2_256)), + hash_blake2_256_per_byte: to_weight!(cost_byte_batched!(seal_hash_blake2_256_per_kb)), + hash_blake2_128: to_weight!(cost_batched!(seal_hash_blake2_128)), + hash_blake2_128_per_byte: to_weight!(cost_byte_batched!(seal_hash_blake2_128_per_kb)), + ecdsa_recover: to_weight!(cost_batched!(seal_ecdsa_recover)), + ecdsa_to_eth_address: to_weight!(cost_batched!(seal_ecdsa_to_eth_address)), + reentrance_count: to_weight!(cost_batched!(seal_reentrance_count)), + account_reentrance_count: to_weight!(cost_batched!(seal_account_reentrance_count)), + instantiation_nonce: to_weight!(cost_batched!(seal_instantiation_nonce)), _phantom: PhantomData, } } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 7a0736d1ed61a..ce84da743d112 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -394,7 +394,7 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeCall = RuntimeCall; type CallFilter = TestFilter; - type CallStack = [Frame; 31]; + type CallStack = [Frame; 5]; type WeightPrice = Self; type WeightInfo = (); type ChainExtension = @@ -405,7 +405,7 @@ impl Config for Test { type DepositPerByte = DepositPerByte; type DepositPerItem = DepositPerItem; type AddressGenerator = DefaultAddressGenerator; - type MaxCodeLen = ConstU32<{ 128 * 1024 }>; + type MaxCodeLen = ConstU32<{ 123 * 1024 }>; type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = UnstableInterface; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; @@ -416,7 +416,7 @@ pub const BOB: AccountId32 = AccountId32::new([2u8; 32]); pub const CHARLIE: AccountId32 = AccountId32::new([3u8; 32]); pub const DJANGO: AccountId32 = AccountId32::new([4u8; 32]); -pub const GAS_LIMIT: Weight = Weight::from_ref_time(100_000_000_000).set_proof_size(256 * 1024); +pub const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 512 * 1024); pub struct ExtBuilder { existential_deposit: u64, @@ -2837,7 +2837,7 @@ fn gas_estimation_call_runtime() { let call = RuntimeCall::Contracts(crate::Call::call { dest: addr_callee, value: 0, - gas_limit: GAS_LIMIT.set_ref_time(GAS_LIMIT.ref_time() / 3), + gas_limit: GAS_LIMIT / 3, storage_deposit_limit: None, data: vec![], }); diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index b540b3deb448d..c2b0611769429 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -430,7 +430,7 @@ mod tests { events: Default::default(), runtime_calls: Default::default(), schedule: Default::default(), - gas_meter: GasMeter::new(Weight::from_ref_time(10_000_000_000)), + gas_meter: GasMeter::new(Weight::from_parts(10_000_000_000, 10 * 1024 * 1024)), debug_buffer: Default::default(), ecdsa_recover: Default::default(), } diff --git a/frame/contracts/src/wasm/runtime.rs b/frame/contracts/src/wasm/runtime.rs index b5398bb6206c5..745583b337653 100644 --- a/frame/contracts/src/wasm/runtime.rs +++ b/frame/contracts/src/wasm/runtime.rs @@ -253,7 +253,7 @@ pub enum RuntimeCosts { /// Weight of calling `seal_ecdsa_recover`. EcdsaRecovery, /// Weight charged by a chain extension through `seal_call_chain_extension`. - ChainExtension(u64), + ChainExtension(Weight), /// Weight charged for calling into the runtime. CallRuntime(Weight), /// Weight of calling `seal_set_code_hash` @@ -272,7 +272,7 @@ impl RuntimeCosts { fn token(&self, s: &HostFnWeights) -> RuntimeToken { use self::RuntimeCosts::*; let weight = match *self { - MeteringBlock(amount) => s.gas.saturating_add(amount), + MeteringBlock(amount) => s.gas.saturating_add(Weight::from_ref_time(amount)), CopyFromContract(len) => s.return_per_byte.saturating_mul(len.into()), CopyToContract(len) => s.input_per_byte.saturating_mul(len.into()), Caller => s.caller, @@ -335,8 +335,8 @@ impl RuntimeCosts { .hash_blake2_128 .saturating_add(s.hash_blake2_128_per_byte.saturating_mul(len.into())), EcdsaRecovery => s.ecdsa_recover, - ChainExtension(amount) => amount, - CallRuntime(weight) => weight.ref_time(), + ChainExtension(weight) => weight, + CallRuntime(weight) => weight, SetCodeHash => s.set_code_hash, EcdsaToEthAddress => s.ecdsa_to_eth_address, ReentrantCount => s.reentrance_count, @@ -346,7 +346,7 @@ impl RuntimeCosts { RuntimeToken { #[cfg(test)] _created_from: *self, - weight: Weight::from_ref_time(weight), + weight, } } } diff --git a/frame/conviction-voting/Cargo.toml b/frame/conviction-voting/Cargo.toml index 9bfc93f2d9ff5..d112c54fb64a7 100644 --- a/frame/conviction-voting/Cargo.toml +++ b/frame/conviction-voting/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] assert_matches = "1.3.0" -codec = { package = "parity-scale-codec", version = "3.0.3", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/frame/democracy/Cargo.toml b/frame/democracy/Cargo.toml index 49dbe133d6919..fa107218cf698 100644 --- a/frame/democracy/Cargo.toml +++ b/frame/democracy/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/election-provider-multi-phase/Cargo.toml b/frame/election-provider-multi-phase/Cargo.toml index a21740dd72a02..aa734850aae43 100644 --- a/frame/election-provider-multi-phase/Cargo.toml +++ b/frame/election-provider-multi-phase/Cargo.toml @@ -12,7 +12,7 @@ description = "PALLET two phase election providers" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = [ diff --git a/frame/election-provider-support/Cargo.toml b/frame/election-provider-support/Cargo.toml index 108a49eb08abc..c7f47e721d1aa 100644 --- a/frame/election-provider-support/Cargo.toml +++ b/frame/election-provider-support/Cargo.toml @@ -12,7 +12,7 @@ description = "election provider supporting traits" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-election-provider-solution-type = { version = "4.0.0-dev", path = "solution-type" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/election-provider-support/benchmarking/Cargo.toml b/frame/election-provider-support/benchmarking/Cargo.toml index 60538997773d4..bef371ec5efbf 100644 --- a/frame/election-provider-support/benchmarking/Cargo.toml +++ b/frame/election-provider-support/benchmarking/Cargo.toml @@ -12,7 +12,7 @@ description = "Benchmarking for election provider support onchain config trait" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../benchmarking" } diff --git a/frame/election-provider-support/solution-type/Cargo.toml b/frame/election-provider-support/solution-type/Cargo.toml index 50c39c547fb9b..eb9598dca20b8 100644 --- a/frame/election-provider-support/solution-type/Cargo.toml +++ b/frame/election-provider-support/solution-type/Cargo.toml @@ -21,7 +21,7 @@ proc-macro2 = "1.0.37" proc-macro-crate = "1.1.3" [dev-dependencies] -parity-scale-codec = "3.0.0" +parity-scale-codec = "3.2.2" scale-info = "2.1.1" sp-arithmetic = { version = "6.0.0", path = "../../../primitives/arithmetic" } # used by generate_solution_type: diff --git a/frame/election-provider-support/solution-type/fuzzer/Cargo.toml b/frame/election-provider-support/solution-type/fuzzer/Cargo.toml index 34aeaf9300352..9275692788d48 100644 --- a/frame/election-provider-support/solution-type/fuzzer/Cargo.toml +++ b/frame/election-provider-support/solution-type/fuzzer/Cargo.toml @@ -17,7 +17,7 @@ clap = { version = "4.0.9", features = ["derive"] } honggfuzz = "0.5" rand = { version = "0.8", features = ["std", "small_rng"] } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-election-provider-solution-type = { version = "4.0.0-dev", path = ".." } frame-election-provider-support = { version = "4.0.0-dev", path = "../.." } diff --git a/frame/elections-phragmen/Cargo.toml b/frame/elections-phragmen/Cargo.toml index fb1d924dbd1bd..ce39e42b8eeb5 100644 --- a/frame/elections-phragmen/Cargo.toml +++ b/frame/elections-phragmen/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } log = { version = "0.4.14", default-features = false } diff --git a/frame/examples/basic/Cargo.toml b/frame/examples/basic/Cargo.toml index 8c69dc6c3e9ce..47f623f45381c 100644 --- a/frame/examples/basic/Cargo.toml +++ b/frame/examples/basic/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../../benchmarking" } diff --git a/frame/examples/basic/src/benchmarking.rs b/frame/examples/basic/src/benchmarking.rs index 13f069c23e27b..699e56685fbe9 100644 --- a/frame/examples/basic/src/benchmarking.rs +++ b/frame/examples/basic/src/benchmarking.rs @@ -15,12 +15,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Benchmarking for pallet-example-basic. +//! Benchmarking for `pallet-example-basic`. +// Only enable this module for benchmarking. #![cfg(feature = "runtime-benchmarks")] use crate::*; -use frame_benchmarking::{benchmarks, whitelisted_caller}; +use frame_benchmarking::{impl_benchmark_test_suite, whitelisted_caller}; +use frame_support::benchmarking::{benchmarks, Linear}; use frame_system::RawOrigin; // To actually run this benchmark on pallet-example-basic, we need to put this pallet into the @@ -33,14 +35,19 @@ use frame_system::RawOrigin; // Details on using the benchmarks macro can be seen at: // https://paritytech.github.io/substrate/master/frame_benchmarking/trait.Benchmarking.html#tymethod.benchmarks -benchmarks! { +#[benchmarks] +mod benchmarks { + use super::*; + // This will measure the execution time of `set_dummy`. - set_dummy_benchmark { + #[benchmark] + fn set_dummy_benchmark() { // This is the benchmark setup phase. // `set_dummy` is a constant time function, hence we hard-code some random value here. let value = 1000u32.into(); - }: set_dummy(RawOrigin::Root, value) // The execution phase is just running `set_dummy` extrinsic call - verify { + #[extrinsic_call] + set_dummy(RawOrigin::Root, value); // The execution phase is just running `set_dummy` extrinsic call + // This is the optional benchmark verification phase, asserting certain states. assert_eq!(Pallet::::dummy(), Some(value)) } @@ -49,22 +56,43 @@ benchmarks! { // The benchmark execution phase is shorthanded. When the name of the benchmark case is the same // as the extrinsic call. `_(...)` is used to represent the extrinsic name. // The benchmark verification phase is omitted. - accumulate_dummy { + #[benchmark] + fn accumulate_dummy() { let value = 1000u32.into(); // The caller account is whitelisted for DB reads/write by the benchmarking macro. let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller), value) + + // You can use `_` if the name of the Call matches the benchmark name. + #[extrinsic_call] + _(RawOrigin::Signed(caller), value); + } + + /// You can write helper functions in here since its a normal Rust module. + fn setup_vector(len: u32) -> Vec { + let mut vector = Vec::::new(); + for i in (0..len).rev() { + vector.push(i); + } + vector + } // This will measure the execution time of sorting a vector. - sort_vector { - let x in 0 .. 10000; - let mut m = Vec::::new(); - for i in (0..x).rev() { - m.push(i); + // + // Define `x` as a linear component with range `[0, =10_000]`. This means that the benchmarking + // will assume that the weight grows at a linear rate depending on `x`. + #[benchmark] + fn sort_vector(x: Linear<0, 10_000>) { + let mut vector = setup_vector(x); + + // The benchmark execution phase could also be a closure with custom code: + #[block] + { + vector.sort(); } - }: { - // The benchmark execution phase could also be a closure with custom code - m.sort(); + + // Check that it was sorted correctly. This will not be benchmarked and is just for + // verification. + vector.windows(2).for_each(|w| assert!(w[0] <= w[1])); } // This line generates test cases for benchmarking, and could be run by: @@ -75,5 +103,5 @@ benchmarks! { // // The line generates three steps per benchmark, with repeat=1 and the three steps are // [low, mid, high] of the range. - impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test) + impl_benchmark_test_suite!(Pallet, crate::tests::new_test_ext(), crate::tests::Test); } diff --git a/frame/examples/offchain-worker/Cargo.toml b/frame/examples/offchain-worker/Cargo.toml index 446af8dda9198..f94cf36b2b992 100644 --- a/frame/examples/offchain-worker/Cargo.toml +++ b/frame/examples/offchain-worker/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } lite-json = { version = "0.2.0", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/executive/Cargo.toml b/frame/executive/Cargo.toml index a6d16e9b0793f..1636cb3929d92 100644 --- a/frame/executive/Cargo.toml +++ b/frame/executive/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/fast-unstake/Cargo.toml b/frame/fast-unstake/Cargo.toml index b61060e775a9f..36b4c3a5ec1cb 100644 --- a/frame/fast-unstake/Cargo.toml +++ b/frame/fast-unstake/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME fast unstake pallet" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/grandpa/Cargo.toml b/frame/grandpa/Cargo.toml index 93dd4143af2d7..fa63b8427523a 100644 --- a/frame/grandpa/Cargo.toml +++ b/frame/grandpa/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/identity/Cargo.toml b/frame/identity/Cargo.toml index 8c7655af6ab34..c1a250368fade 100644 --- a/frame/identity/Cargo.toml +++ b/frame/identity/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } enumflags2 = { version = "0.7.4" } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/im-online/Cargo.toml b/frame/im-online/Cargo.toml index c0058e9f2371d..d76fba4fb6495 100644 --- a/frame/im-online/Cargo.toml +++ b/frame/im-online/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/indices/Cargo.toml b/frame/indices/Cargo.toml index b3afa397c45f5..b667916ebcb49 100644 --- a/frame/indices/Cargo.toml +++ b/frame/indices/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/lottery/Cargo.toml b/frame/lottery/Cargo.toml index 9ac69d63eb983..8c1bf30106852 100644 --- a/frame/lottery/Cargo.toml +++ b/frame/lottery/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME Participation Lottery Pallet" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/membership/Cargo.toml b/frame/membership/Cargo.toml index b457c4c2911bd..bba5700cdd848 100644 --- a/frame/membership/Cargo.toml +++ b/frame/membership/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/merkle-mountain-range/Cargo.toml b/frame/merkle-mountain-range/Cargo.toml index cf26cfb231c85..3f7180f79adbe 100644 --- a/frame/merkle-mountain-range/Cargo.toml +++ b/frame/merkle-mountain-range/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME Merkle Mountain Range pallet." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/message-queue/Cargo.toml b/frame/message-queue/Cargo.toml index 47d114902f52c..115200b826763 100644 --- a/frame/message-queue/Cargo.toml +++ b/frame/message-queue/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet to queue and process messages" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.2", default-features = false, features = ["derive"] } serde = { version = "1.0.137", optional = true, features = ["derive"] } log = { version = "0.4.17", default-features = false } diff --git a/frame/message-queue/src/benchmarking.rs b/frame/message-queue/src/benchmarking.rs index 9cd6b75e4d0ae..9651c81e5e66d 100644 --- a/frame/message-queue/src/benchmarking.rs +++ b/frame/message-queue/src/benchmarking.rs @@ -1,6 +1,6 @@ // This file is part of Substrate. -// Copyright (C) 2022 Parity Technologies (UK) Ltd. +// Copyright (C) 2023 Parity Technologies (UK) Ltd. // SPDX-License-Identifier: Apache-2.0 // Licensed under the Apache License, Version 2.0 (the "License"); @@ -22,68 +22,85 @@ use super::{mock_helpers::*, Pallet as MessageQueue, *}; -use frame_benchmarking::{benchmarks, whitelisted_caller}; -use frame_support::traits::Get; +use frame_benchmarking::{impl_benchmark_test_suite, whitelisted_caller}; +use frame_support::{benchmarking::*, traits::Get}; use frame_system::RawOrigin; use sp_std::prelude::*; -benchmarks! { - where_clause { - where - // NOTE: We need to generate multiple origins, therefore Origin is `From`. The - // `PartialEq` is for asserting the outcome of the ring (un)knitting and *could* be - // removed if really necessary. - <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, - ::Size: From, - } +#[benchmarks( + where + <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, + ::Size: From, + // NOTE: We need to generate multiple origins, therefore Origin is `From`. The + // `PartialEq` is for asserting the outcome of the ring (un)knitting and *could* be + // removed if really necessary. +)] +mod benchmarks { + use super::*; // Worst case path of `ready_ring_knit`. - ready_ring_knit { - let mid: MessageOriginOf:: = 1.into(); + #[benchmark] + fn ready_ring_knit() { + let mid: MessageOriginOf = 1.into(); build_ring::(&[0.into(), mid.clone(), 2.into()]); unknit::(&mid); assert_ring::(&[0.into(), 2.into()]); let mut neighbours = None; - }: { - neighbours = MessageQueue::::ready_ring_knit(&mid).ok(); - } verify { + + #[block] + { + neighbours = MessageQueue::::ready_ring_knit(&mid).ok(); + } + // The neighbours needs to be modified manually. - BookStateFor::::mutate(&mid, |b| { b.ready_neighbours = neighbours }); + BookStateFor::::mutate(&mid, |b| b.ready_neighbours = neighbours); assert_ring::(&[0.into(), 2.into(), mid]); } // Worst case path of `ready_ring_unknit`. - ready_ring_unknit { + #[benchmark] + fn ready_ring_unknit() { build_ring::(&[0.into(), 1.into(), 2.into()]); assert_ring::(&[0.into(), 1.into(), 2.into()]); - let o: MessageOriginOf:: = 0.into(); + let o: MessageOriginOf = 0.into(); let neighbours = BookStateFor::::get(&o).ready_neighbours.unwrap(); - }: { - MessageQueue::::ready_ring_unknit(&o, neighbours); - } verify { + + #[block] + { + MessageQueue::::ready_ring_unknit(&o, neighbours); + } + assert_ring::(&[1.into(), 2.into()]); } // `service_queues` without any queue processing. - service_queue_base { - }: { - MessageQueue::::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX) + #[benchmark] + fn service_queue_base() { + #[block] + { + MessageQueue::::service_queue(0.into(), &mut WeightMeter::max_limit(), Weight::MAX); + } } // `service_page` without any message processing but with page completion. - service_page_base_completion { + #[benchmark] + fn service_page_base_completion() { let origin: MessageOriginOf = 0.into(); let page = PageOf::::default(); Pages::::insert(&origin, 0, &page); let mut book_state = single_page_book::(); let mut meter = WeightMeter::max_limit(); let limit = Weight::MAX; - }: { - MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit) + + #[block] + { + MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); + } } // `service_page` without any message processing and without page completion. - service_page_base_no_completion { + #[benchmark] + fn service_page_base_no_completion() { let origin: MessageOriginOf = 0.into(); let mut page = PageOf::::default(); // Mock the storage such that `is_complete` returns `false` but `peek_first` returns `None`. @@ -93,49 +110,73 @@ benchmarks! { let mut book_state = single_page_book::(); let mut meter = WeightMeter::max_limit(); let limit = Weight::MAX; - }: { - MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit) + + #[block] + { + MessageQueue::::service_page(&origin, &mut book_state, &mut meter, limit); + } } // Processing a single message from a page. - service_page_item { + #[benchmark] + fn service_page_item() { let msg = vec![1u8; MaxMessageLenOf::::get() as usize]; let mut page = page::(&msg.clone()); let mut book = book_for::(&page); assert!(page.peek_first().is_some(), "There is one message"); let mut weight = WeightMeter::max_limit(); - }: { - let status = MessageQueue::::service_page_item(&0u32.into(), 0, &mut book, &mut page, &mut weight, Weight::MAX); - assert_eq!(status, ItemExecutionStatus::Executed(true)); - } verify { + + #[block] + { + let status = MessageQueue::::service_page_item( + &0u32.into(), + 0, + &mut book, + &mut page, + &mut weight, + Weight::MAX, + ); + assert_eq!(status, ItemExecutionStatus::Executed(true)); + } + // Check that it was processed. - assert_last_event::(Event::Processed { - hash: T::Hashing::hash(&msg), origin: 0.into(), - weight_used: 1.into_weight(), success: true - }.into()); + assert_last_event::( + Event::Processed { + hash: T::Hashing::hash(&msg), + origin: 0.into(), + weight_used: 1.into_weight(), + success: true, + } + .into(), + ); let (_, processed, _) = page.peek_index(0).unwrap(); assert!(processed); assert_eq!(book.message_count, 0); } // Worst case for calling `bump_service_head`. - bump_service_head { + #[benchmark] + fn bump_service_head() { setup_bump_service_head::(0.into(), 10.into()); let mut weight = WeightMeter::max_limit(); - }: { - MessageQueue::::bump_service_head(&mut weight); - } verify { + + #[block] + { + MessageQueue::::bump_service_head(&mut weight); + } + assert_eq!(ServiceHead::::get().unwrap(), 10u32.into()); assert_eq!(weight.consumed, T::WeightInfo::bump_service_head()); } - reap_page { + #[benchmark] + fn reap_page() { // Mock the storage to get a *cullable* but not *reapable* page. let origin: MessageOriginOf = 0.into(); let mut book = single_page_book::(); let (page, msgs) = full_page::(); - for p in 0 .. T::MaxStale::get() * T::MaxStale::get() { + for p in 0..T::MaxStale::get() * T::MaxStale::get() { if p == 0 { Pages::::insert(&origin, p, &page); } @@ -148,16 +189,19 @@ benchmarks! { BookStateFor::::insert(&origin, &book); assert!(Pages::::contains_key(&origin, 0)); - }: _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0) - verify { - assert_last_event::(Event::PageReaped{ origin: 0.into(), index: 0 }.into()); + #[extrinsic_call] + _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); + + assert_last_event::(Event::PageReaped { origin: 0.into(), index: 0 }.into()); assert!(!Pages::::contains_key(&origin, 0)); } // Worst case for `execute_overweight` where the page is removed as completed. // - // The worst case occurs when executing the last message in a page of which all are skipped since it is using `peek_index` which has linear complexities. - execute_overweight_page_removed { + // The worst case occurs when executing the last message in a page of which all are skipped + // since it is using `peek_index` which has linear complexities. + #[benchmark] + fn execute_overweight_page_removed() { let origin: MessageOriginOf = 0.into(); let (mut page, msgs) = full_page::(); // Skip all messages. @@ -168,19 +212,34 @@ benchmarks! { let book = book_for::(&page); Pages::::insert(&origin, 0, &page); BookStateFor::::insert(&origin, &book); - }: { - MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap() - } - verify { - assert_last_event::(Event::Processed { - hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(), - weight_used: Weight::from_parts(1, 1), success: true - }.into()); + + #[block] + { + MessageQueue::::execute_overweight( + RawOrigin::Signed(whitelisted_caller()).into(), + 0u32.into(), + 0u32, + ((msgs - 1) as u32).into(), + Weight::MAX, + ) + .unwrap(); + } + + assert_last_event::( + Event::Processed { + hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), + origin: 0.into(), + weight_used: Weight::from_parts(1, 1), + success: true, + } + .into(), + ); assert!(!Pages::::contains_key(&origin, 0), "Page must be removed"); } // Worst case for `execute_overweight` where the page is updated. - execute_overweight_page_updated { + #[benchmark] + fn execute_overweight_page_updated() { let origin: MessageOriginOf = 0.into(); let (mut page, msgs) = full_page::(); // Skip all messages. @@ -190,16 +249,34 @@ benchmarks! { let book = book_for::(&page); Pages::::insert(&origin, 0, &page); BookStateFor::::insert(&origin, &book); - }: { - MessageQueue::::execute_overweight(RawOrigin::Signed(whitelisted_caller()).into(), 0u32.into(), 0u32, ((msgs - 1) as u32).into(), Weight::MAX).unwrap() - } - verify { - assert_last_event::(Event::Processed { - hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), origin: 0.into(), - weight_used: Weight::from_parts(1, 1), success: true - }.into()); + + #[block] + { + MessageQueue::::execute_overweight( + RawOrigin::Signed(whitelisted_caller()).into(), + 0u32.into(), + 0u32, + ((msgs - 1) as u32).into(), + Weight::MAX, + ) + .unwrap(); + } + + assert_last_event::( + Event::Processed { + hash: T::Hashing::hash(&((msgs - 1) as u32).encode()), + origin: 0.into(), + weight_used: Weight::from_parts(1, 1), + success: true, + } + .into(), + ); assert!(Pages::::contains_key(&origin, 0), "Page must be updated"); } - impl_benchmark_test_suite!(MessageQueue, crate::mock::new_test_ext::(), crate::integration_test::Test); + impl_benchmark_test_suite! { + MessageQueue, + crate::mock::new_test_ext::(), + crate::integration_test::Test + } } diff --git a/frame/multisig/Cargo.toml b/frame/multisig/Cargo.toml index 4e9f4f5d832e6..5cd744124ef24 100644 --- a/frame/multisig/Cargo.toml +++ b/frame/multisig/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/nfts/Cargo.toml b/frame/nfts/Cargo.toml index d2cdb48532fd0..9e010cb557958 100644 --- a/frame/nfts/Cargo.toml +++ b/frame/nfts/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } enumflags2 = { version = "0.7.5" } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/nicks/Cargo.toml b/frame/nicks/Cargo.toml index 2390060c71698..1c829efc2d601 100644 --- a/frame/nicks/Cargo.toml +++ b/frame/nicks/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/nis/Cargo.toml b/frame/nis/Cargo.toml index be12d97dd871d..a5fc29cd79150 100644 --- a/frame/nis/Cargo.toml +++ b/frame/nis/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/nis/src/benchmarking.rs b/frame/nis/src/benchmarking.rs index 606b1c484b1e8..b45c982bd150a 100644 --- a/frame/nis/src/benchmarking.rs +++ b/frame/nis/src/benchmarking.rs @@ -21,7 +21,7 @@ use super::*; use frame_benchmarking::{account, benchmarks, whitelisted_caller}; -use frame_support::traits::{Currency, EnsureOrigin, Get}; +use frame_support::traits::{nonfungible::Inspect, Currency, EnsureOrigin, Get}; use frame_system::RawOrigin; use sp_arithmetic::Perquintill; use sp_runtime::{ @@ -106,6 +106,7 @@ benchmarks! { T::Currency::make_free_balance_be(&caller, bid); Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), bid, 1)?; Nis::::process_queues(Perquintill::one(), 1, 1, &mut WeightCounter::unlimited()); + Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; let original = T::Currency::free_balance(&Nis::::account_id()); T::Currency::make_free_balance_be(&Nis::::account_id(), BalanceOf::::min_value()); }: _(origin) @@ -116,7 +117,7 @@ benchmarks! { assert!(missing <= Perquintill::one() / 100_000); } - thaw { + thaw_private { let caller: T::AccountId = whitelisted_caller(); T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; @@ -128,6 +129,42 @@ benchmarks! { assert!(Receipts::::get(0).is_none()); } + thaw_communal { + let caller: T::AccountId = whitelisted_caller(); + T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); + Receipts::::mutate(0, |m_g| if let Some(ref mut g) = m_g { g.expiry = Zero::zero() }); + Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; + }: _(RawOrigin::Signed(caller.clone()), 0) + verify { + assert!(Receipts::::get(0).is_none()); + } + + privatize { + let caller: T::AccountId = whitelisted_caller(); + T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); + Nis::::communify(RawOrigin::Signed(caller.clone()).into(), 0)?; + }: _(RawOrigin::Signed(caller.clone()), 0) + verify { + assert_eq!(Nis::::owner(&0), Some(caller)); + } + + communify { + let caller: T::AccountId = whitelisted_caller(); + T::Currency::make_free_balance_be(&caller, T::MinBid::get() * BalanceOf::::from(3u32)); + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::place_bid(RawOrigin::Signed(caller.clone()).into(), T::MinBid::get(), 1)?; + Nis::::process_queues(Perquintill::one(), 1, 2, &mut WeightCounter::unlimited()); + }: _(RawOrigin::Signed(caller.clone()), 0) + verify { + assert_eq!(Nis::::owner(&0), None); + } + process_queues { fill_queues::()?; }: { diff --git a/frame/nis/src/lib.rs b/frame/nis/src/lib.rs index dff64625a3654..fa0163fb60bfe 100644 --- a/frame/nis/src/lib.rs +++ b/frame/nis/src/lib.rs @@ -169,7 +169,7 @@ pub mod pallet { nonfungible::{Inspect as NonfungibleInspect, Transfer as NonfungibleTransfer}, Currency, Defensive, DefensiveSaturating, ExistenceRequirement::AllowDeath, - OnUnbalanced, ReservableCurrency, + NamedReservableCurrency, OnUnbalanced, }, PalletId, }; @@ -189,9 +189,10 @@ pub mod pallet { type ReceiptRecordOf = ReceiptRecord< ::AccountId, ::BlockNumber, + BalanceOf, >; type IssuanceInfoOf = IssuanceInfo>; - type SummaryRecordOf = SummaryRecord<::BlockNumber>; + type SummaryRecordOf = SummaryRecord<::BlockNumber, BalanceOf>; type BidOf = Bid, ::AccountId>; type QueueTotalsTypeOf = BoundedVec<(u32, BalanceOf), ::QueueCount>; @@ -208,7 +209,7 @@ pub mod pallet { type PalletId: Get; /// Currency type that this works on. - type Currency: ReservableCurrency; + type Currency: NamedReservableCurrency; /// Just the `Currency::Balance` type; we have this item to allow us to constrain it to /// `From`. @@ -300,6 +301,12 @@ pub mod pallet { /// The maximum proportion which may be thawed and the period over which it is reset. #[pallet::constant] type ThawThrottle: Get<(Perquintill, Self::BlockNumber)>; + + /// The name for the reserve ID. + #[pallet::constant] + type ReserveId: Get< + >::ReserveIdentifier, + >; } #[pallet::pallet] @@ -321,11 +328,13 @@ pub mod pallet { #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct ReceiptRecord { + pub struct ReceiptRecord { /// The proportion of the effective total issuance. pub proportion: Perquintill, - /// The account to whom this receipt belongs. - pub who: AccountId, + /// The account to whom this receipt belongs and the amount of funds on hold in their + /// account for servicing this receipt. If `None`, then it is a communal receipt and + /// fungible counterparts have been issued. + pub owner: Option<(AccountId, Balance)>, /// The time after which this receipt can be thawed. pub expiry: BlockNumber, } @@ -344,7 +353,7 @@ pub mod pallet { #[derive( Clone, Eq, PartialEq, Default, Encode, Decode, RuntimeDebug, TypeInfo, MaxEncodedLen, )] - pub struct SummaryRecord { + pub struct SummaryRecord { /// The total proportion over all outstanding receipts. pub proportion_owed: Perquintill, /// The total number of receipts created so far. @@ -353,6 +362,9 @@ pub mod pallet { pub thawed: Perquintill, /// The current thaw period's beginning. pub last_period: BlockNumber, + /// The total amount of funds on hold for receipts. This doesn't include the pot or funds + /// on hold for bids. + pub receipts_on_hold: Balance, } pub struct OnEmptyQueueTotals(sp_std::marker::PhantomData); @@ -440,24 +452,28 @@ pub mod pallet { /// The queue for the bid's duration is full and the amount bid is too low to get in /// through replacing an existing bid. BidTooLow, - /// Bond index is unknown. - Unknown, + /// Receipt index is unknown. + UnknownReceipt, /// Not the owner of the receipt. NotOwner, /// Bond not yet at expiry date. NotExpired, /// The given bid for retraction is not found. - NotFound, + UnknownBid, /// The portion supplied is beyond the value of the receipt. - TooMuch, + PortionTooBig, /// Not enough funds are held to pay out. Unfunded, /// There are enough funds for what is required. - Funded, + AlreadyFunded, /// The thaw throttle has been reached for this period. Throttled, /// The operation would result in a receipt worth an insignficant value. MakesDust, + /// The receipt is already communal. + AlreadyCommunal, + /// The receipt is already private. + AlreadyPrivate, } pub(crate) struct WeightCounter { @@ -539,13 +555,17 @@ pub mod pallet { |q| -> Result<(u32, BalanceOf), DispatchError> { let queue_full = q.len() == T::MaxQueueLen::get() as usize; ensure!(!queue_full || q[0].amount < amount, Error::::BidTooLow); - T::Currency::reserve(&who, amount)?; + T::Currency::reserve_named(&T::ReserveId::get(), &who, amount)?; // queue is let mut bid = Bid { amount, who: who.clone() }; let net = if queue_full { sp_std::mem::swap(&mut q[0], &mut bid); - let _ = T::Currency::unreserve(&bid.who, bid.amount); + let _ = T::Currency::unreserve_named( + &T::ReserveId::get(), + &bid.who, + bid.amount, + ); Self::deposit_event(Event::::BidDropped { who: bid.who, amount: bid.amount, @@ -597,7 +617,7 @@ pub mod pallet { let bid = Bid { amount, who }; let new_len = Queues::::try_mutate(duration, |q| -> Result { - let pos = q.iter().position(|i| i == &bid).ok_or(Error::::NotFound)?; + let pos = q.iter().position(|i| i == &bid).ok_or(Error::::UnknownBid)?; q.remove(pos); Ok(q.len() as u32) })?; @@ -608,7 +628,7 @@ pub mod pallet { qs[queue_index].1.saturating_reduce(bid.amount); }); - T::Currency::unreserve(&bid.who, bid.amount); + T::Currency::unreserve_named(&T::ReserveId::get(), &bid.who, bid.amount); Self::deposit_event(Event::BidRetracted { who: bid.who, amount: bid.amount, duration }); Ok(()) @@ -625,7 +645,7 @@ pub mod pallet { let our_account = Self::account_id(); let issuance = Self::issuance_with(&our_account, &summary); let deficit = issuance.required.saturating_sub(issuance.holdings); - ensure!(!deficit.is_zero(), Error::::Funded); + ensure!(!deficit.is_zero(), Error::::AlreadyFunded); T::Deficit::on_unbalanced(T::Currency::deposit_creating(&our_account, deficit)); Self::deposit_event(Event::::Funded { deficit }); Ok(()) @@ -640,27 +660,28 @@ pub mod pallet { /// - `portion`: If `Some`, then only the given portion of the receipt should be thawed. If /// `None`, then all of it should be. #[pallet::call_index(3)] - #[pallet::weight(T::WeightInfo::thaw())] - pub fn thaw( + #[pallet::weight(T::WeightInfo::thaw_private())] + pub fn thaw_private( origin: OriginFor, #[pallet::compact] index: ReceiptIndex, - portion: Option<>::Balance>, + maybe_proportion: Option, ) -> DispatchResult { let who = ensure_signed(origin)?; // Look for `index` let mut receipt: ReceiptRecordOf = - Receipts::::get(index).ok_or(Error::::Unknown)?; + Receipts::::get(index).ok_or(Error::::UnknownReceipt)?; // If found, check the owner is `who`. - ensure!(receipt.who == who, Error::::NotOwner); + let (owner, mut on_hold) = receipt.owner.ok_or(Error::::AlreadyCommunal)?; + ensure!(owner == who, Error::::NotOwner); + let now = frame_system::Pallet::::block_number(); ensure!(now >= receipt.expiry, Error::::NotExpired); let mut summary: SummaryRecordOf = Summary::::get(); - let proportion = if let Some(counterpart) = portion { - let proportion = T::CounterpartAmount::convert_back(counterpart); - ensure!(proportion <= receipt.proportion, Error::::TooMuch); + let proportion = if let Some(proportion) = maybe_proportion { + ensure!(proportion <= receipt.proportion, Error::::PortionTooBig); let remaining = receipt.proportion.saturating_sub(proportion); ensure!( remaining.is_zero() || remaining >= T::MinReceipt::get(), @@ -679,8 +700,6 @@ pub mod pallet { summary.thawed.saturating_accrue(proportion); ensure!(summary.thawed <= throttle, Error::::Throttled); - T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(proportion))?; - // Multiply the proportion it is by the total issued. let our_account = Self::account_id(); let effective_issuance = Self::issuance_with(&our_account, &summary).effective; @@ -689,13 +708,55 @@ pub mod pallet { receipt.proportion.saturating_reduce(proportion); summary.proportion_owed.saturating_reduce(proportion); - T::Currency::transfer(&our_account, &who, amount, AllowDeath) - .map_err(|_| Error::::Unfunded)?; - let dropped = receipt.proportion.is_zero(); + + if amount > on_hold { + T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold); + let deficit = amount - on_hold; + // Try to transfer deficit from pot to receipt owner. + summary.receipts_on_hold.saturating_reduce(on_hold); + on_hold = Zero::zero(); + T::Currency::transfer(&our_account, &who, deficit, AllowDeath) + .map_err(|_| Error::::Unfunded)?; + } else { + T::Currency::unreserve_named(&T::ReserveId::get(), &who, amount); + on_hold.saturating_reduce(amount); + summary.receipts_on_hold.saturating_reduce(amount); + if dropped && !on_hold.is_zero() { + // Reclaim any remainder: + // Transfer `excess` to the pot if we have now fully compensated for the + // receipt. + // + // This will legitimately fail if there is no pot account in existance. + // There's nothing we can do about this so we just swallow the error. + // This code is not ideal and could fail in the second phase leaving + // the system in an invalid state. It can be fixed properly with the + // new API in https://github.com/paritytech/substrate/pull/12951 + // + // Below is what it should look like then: + // let _ = T::Currency::repatriate_reserved_named( + // &T::ReserveId::get(), + // &who, + // &our_account, + // excess, + // BalanceStatus::Free, + // ).defensive(); + T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold); + // It could theoretically be locked, so really we should be using a more + // forceful variant. But the alternative `repatriate_reserved_named` will + // fail if the destination account doesn't exist. This should be fixed when + // we move to the `fungible::*` traits, which should include a force + // transfer function to transfer the reserved balance into free balance in + // the destination regardless of locks and create it if it doesn't exist. + let _ = T::Currency::transfer(&who, &Self::account_id(), on_hold, AllowDeath); + summary.receipts_on_hold.saturating_reduce(on_hold); + } + } + if dropped { Receipts::::remove(index); } else { + receipt.owner = Some((owner, on_hold)); Receipts::::insert(index, &receipt); } Summary::::put(&summary); @@ -704,20 +765,159 @@ pub mod pallet { Ok(()) } + + /// Reduce or remove an outstanding receipt, placing the according proportion of funds into + /// the account of the owner. + /// + /// - `origin`: Must be Signed and the account must be the owner of the fungible counterpart + /// for receipt `index`. + /// - `index`: The index of the receipt. + #[pallet::call_index(4)] + #[pallet::weight(T::WeightInfo::thaw_communal())] + pub fn thaw_communal( + origin: OriginFor, + #[pallet::compact] index: ReceiptIndex, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Look for `index` + let receipt: ReceiptRecordOf = + Receipts::::get(index).ok_or(Error::::UnknownReceipt)?; + // If found, check it is actually communal. + ensure!(receipt.owner.is_none(), Error::::NotOwner); + let now = frame_system::Pallet::::block_number(); + ensure!(now >= receipt.expiry, Error::::NotExpired); + + let mut summary: SummaryRecordOf = Summary::::get(); + + let (throttle, throttle_period) = T::ThawThrottle::get(); + if now.saturating_sub(summary.last_period) >= throttle_period { + summary.thawed = Zero::zero(); + summary.last_period = now; + } + summary.thawed.saturating_accrue(receipt.proportion); + ensure!(summary.thawed <= throttle, Error::::Throttled); + + T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(receipt.proportion))?; + + // Multiply the proportion it is by the total issued. + let our_account = Self::account_id(); + let effective_issuance = Self::issuance_with(&our_account, &summary).effective; + let amount = receipt.proportion * effective_issuance; + + summary.proportion_owed.saturating_reduce(receipt.proportion); + + // Try to transfer amount owed from pot to receipt owner. + T::Currency::transfer(&our_account, &who, amount, AllowDeath) + .map_err(|_| Error::::Unfunded)?; + + Receipts::::remove(index); + Summary::::put(&summary); + + let e = + Event::Thawed { index, who, amount, proportion: receipt.proportion, dropped: true }; + Self::deposit_event(e); + + Ok(()) + } + + /// Make a private receipt communal and create fungible counterparts for its owner. + #[pallet::call_index(5)] + #[pallet::weight(T::WeightInfo::communify())] + pub fn communify( + origin: OriginFor, + #[pallet::compact] index: ReceiptIndex, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Look for `index` + let mut receipt: ReceiptRecordOf = + Receipts::::get(index).ok_or(Error::::UnknownReceipt)?; + + // Check it's not already communal and make it so. + let (owner, on_hold) = receipt.owner.take().ok_or(Error::::AlreadyCommunal)?; + + // If found, check the owner is `who`. + ensure!(owner == who, Error::::NotOwner); + + // Unreserve and transfer the funds to the pot. + T::Currency::unreserve_named(&T::ReserveId::get(), &who, on_hold); + // Transfer `excess` to the pot if we have now fully compensated for the receipt. + T::Currency::transfer(&who, &Self::account_id(), on_hold, AllowDeath) + .map_err(|_| Error::::Unfunded)?; + // TODO #12951: ^^^ The above should be done in a single operation `transfer_on_hold`. + + // Record that we've moved the amount reserved. + let mut summary: SummaryRecordOf = Summary::::get(); + summary.receipts_on_hold.saturating_reduce(on_hold); + Summary::::put(&summary); + Receipts::::insert(index, &receipt); + + // Mint fungibles. + let fung_eq = T::CounterpartAmount::convert(receipt.proportion); + let _ = T::Counterpart::mint_into(&who, fung_eq).defensive(); + + Ok(()) + } + + /// Make a communal receipt private and burn fungible counterparts from its owner. + #[pallet::call_index(6)] + #[pallet::weight(T::WeightInfo::privatize())] + pub fn privatize( + origin: OriginFor, + #[pallet::compact] index: ReceiptIndex, + ) -> DispatchResult { + let who = ensure_signed(origin)?; + + // Look for `index` + let mut receipt: ReceiptRecordOf = + Receipts::::get(index).ok_or(Error::::UnknownReceipt)?; + + // If found, check there is no owner. + ensure!(receipt.owner.is_none(), Error::::AlreadyPrivate); + + // Multiply the proportion it is by the total issued. + let mut summary: SummaryRecordOf = Summary::::get(); + let our_account = Self::account_id(); + let effective_issuance = Self::issuance_with(&our_account, &summary).effective; + let max_amount = receipt.proportion * effective_issuance; + // Avoid trying to place more in the account's reserve than we have available in the pot + let amount = max_amount.min(T::Currency::free_balance(&our_account)); + + // Burn fungible counterparts. + T::Counterpart::burn_from(&who, T::CounterpartAmount::convert(receipt.proportion))?; + + // Transfer the funds from the pot to the owner and reserve + T::Currency::transfer(&Self::account_id(), &who, amount, AllowDeath) + .map_err(|_| Error::::Unfunded)?; + T::Currency::reserve_named(&T::ReserveId::get(), &who, amount)?; + // TODO: ^^^ The above should be done in a single operation `transfer_and_hold`. + + // Record that we've moved the amount reserved. + summary.receipts_on_hold.saturating_accrue(amount); + + receipt.owner = Some((who, amount)); + + Summary::::put(&summary); + Receipts::::insert(index, &receipt); + + Ok(()) + } } /// Issuance information returned by `issuance()`. - #[derive(RuntimeDebug)] + #[derive(Debug)] pub struct IssuanceInfo { - /// The balance held in reserve by this pallet instance. + /// The balance held by this pallet instance together with the balances on hold across + /// all receipt-owning accounts. pub holdings: Balance, /// The (non-ignored) issuance in the system, not including this pallet's account. pub other: Balance, /// The effective total issuance, hypothetically if all outstanding receipts were thawed at /// present. pub effective: Balance, - /// The amount needed to be the pallet instance's account in case all outstanding receipts - /// were thawed at present. + /// The amount needed to be accessible to this pallet in case all outstanding receipts were + /// thawed at present. If it is more than `holdings`, then the pallet will need funding. pub required: Balance, } @@ -725,7 +925,7 @@ pub mod pallet { type ItemId = ReceiptIndex; fn owner(item: &ReceiptIndex) -> Option { - Receipts::::get(item).map(|r| r.who) + Receipts::::get(item).and_then(|r| r.owner).map(|(who, _)| who) } fn attribute(item: &Self::ItemId, key: &[u8]) -> Option> { @@ -733,6 +933,8 @@ pub mod pallet { match key { b"proportion" => Some(item.proportion.encode()), b"expiry" => Some(item.expiry.encode()), + b"owner" => item.owner.as_ref().map(|x| x.0.encode()), + b"on_hold" => item.owner.as_ref().map(|x| x.1.encode()), _ => None, } } @@ -741,12 +943,28 @@ pub mod pallet { impl NonfungibleTransfer for Pallet { fn transfer(index: &ReceiptIndex, destination: &T::AccountId) -> DispatchResult { let mut item = Receipts::::get(index).ok_or(TokenError::UnknownAsset)?; - let from = item.who; - item.who = destination.clone(); + let (owner, on_hold) = item.owner.take().ok_or(Error::::AlreadyCommunal)?; + + // TODO: This should all be replaced by a single call `transfer_held`. + let shortfall = T::Currency::unreserve_named(&T::ReserveId::get(), &owner, on_hold); + if !shortfall.is_zero() { + let _ = + T::Currency::reserve_named(&T::ReserveId::get(), &owner, on_hold - shortfall); + return Err(TokenError::NoFunds.into()) + } + if let Err(e) = T::Currency::transfer(&owner, destination, on_hold, AllowDeath) { + let _ = T::Currency::reserve_named(&T::ReserveId::get(), &owner, on_hold); + return Err(e) + } + // This can never fail, and if it somehow does, then we can't handle this gracefully. + let _ = + T::Currency::reserve_named(&T::ReserveId::get(), destination, on_hold).defensive(); + + item.owner = Some((destination.clone(), on_hold)); Receipts::::insert(&index, &item); Pallet::::deposit_event(Event::::Transferred { - from, - to: item.who, + from: owner, + to: destination.clone(), index: *index, }); Ok(()) @@ -781,7 +999,8 @@ pub mod pallet { ) -> IssuanceInfo> { let total_issuance = T::Currency::total_issuance().saturating_sub(T::IgnoredIssuance::get()); - let holdings = T::Currency::free_balance(our_account); + let holdings = + T::Currency::free_balance(our_account).saturating_add(summary.receipts_on_hold); let other = total_issuance.saturating_sub(holdings); let effective = summary.proportion_owed.left_from_one().saturating_reciprocal_mul(other); @@ -893,7 +1112,7 @@ pub mod pallet { pub(crate) fn process_bid( mut bid: BidOf, expiry: T::BlockNumber, - our_account: &T::AccountId, + _our_account: &T::AccountId, issuance: &IssuanceInfo>, remaining: &mut BalanceOf, queue_amount: &mut BalanceOf, @@ -906,10 +1125,8 @@ pub mod pallet { } else { None }; - let amount = bid.amount.saturating_sub(T::Currency::unreserve(&bid.who, bid.amount)); - if T::Currency::transfer(&bid.who, &our_account, amount, AllowDeath).is_err() { - return result - } + let amount = bid.amount; + summary.receipts_on_hold.saturating_accrue(amount); // Can never overflow due to block above. remaining.saturating_reduce(amount); @@ -928,12 +1145,9 @@ pub mod pallet { let e = Event::Issued { index, expiry, who: who.clone(), amount, proportion }; Self::deposit_event(e); - let receipt = ReceiptRecord { proportion, who: who.clone(), expiry }; + let receipt = ReceiptRecord { proportion, owner: Some((who, amount)), expiry }; Receipts::::insert(index, receipt); - // issue the fungible counterpart - let fung_eq = T::CounterpartAmount::convert(proportion); - let _ = T::Counterpart::mint_into(&who, fung_eq).defensive(); result } } diff --git a/frame/nis/src/mock.rs b/frame/nis/src/mock.rs index ebe073d683309..585ed2d8b278f 100644 --- a/frame/nis/src/mock.rs +++ b/frame/nis/src/mock.rs @@ -84,7 +84,7 @@ impl pallet_balances::Config for Test { type AccountStore = System; type WeightInfo = (); type MaxLocks = (); - type MaxReserves = (); + type MaxReserves = ConstU32<1>; type ReserveIdentifier = [u8; 8]; } @@ -112,6 +112,7 @@ parameter_types! { pub const MinReceipt: Perquintill = Perquintill::from_percent(1); pub const ThawThrottle: (Perquintill, u64) = (Perquintill::from_percent(25), 5); pub static MaxIntakeWeight: Weight = Weight::from_ref_time(2_000_000_000_000); + pub const ReserveId: [u8; 8] = *b"py/nis "; } ord_parameter_types! { @@ -139,6 +140,7 @@ impl pallet_nis::Config for Test { type MaxIntakeWeight = MaxIntakeWeight; type MinReceipt = MinReceipt; type ThawThrottle = ThawThrottle; + type ReserveId = ReserveId; } // This function basically just builds a genesis storage key/value store according to diff --git a/frame/nis/src/tests.rs b/frame/nis/src/tests.rs index f0c45cc80b0e5..b5feb4df16aab 100644 --- a/frame/nis/src/tests.rs +++ b/frame/nis/src/tests.rs @@ -34,8 +34,16 @@ fn pot() -> u64 { Balances::free_balance(&Nis::account_id()) } +fn holdings() -> u64 { + Nis::issuance().holdings +} + +fn signed(who: u64) -> RuntimeOrigin { + RuntimeOrigin::signed(who) +} + fn enlarge(amount: u64, max_bids: u32) { - let summary: SummaryRecord = Summary::::get(); + let summary: SummaryRecord = Summary::::get(); let increase_in_proportion_owed = Perquintill::from_rational(amount, Nis::issuance().effective); let target = summary.proportion_owed.saturating_add(increase_in_proportion_owed); Nis::process_queues(target, u32::max_value(), max_bids, &mut WeightCounter::unlimited()); @@ -55,7 +63,8 @@ fn basic_setup_works() { proportion_owed: Perquintill::zero(), index: 0, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 0, } ); }); @@ -65,16 +74,13 @@ fn basic_setup_works() { fn place_bid_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 1, 2), Error::::AmountTooSmall); + assert_noop!(Nis::place_bid(signed(1), 1, 2), Error::::AmountTooSmall); assert_noop!( - Nis::place_bid(RuntimeOrigin::signed(1), 101, 2), + Nis::place_bid(signed(1), 101, 2), BalancesError::::InsufficientBalance ); - assert_noop!( - Nis::place_bid(RuntimeOrigin::signed(1), 10, 4), - Error::::DurationTooBig - ); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_noop!(Nis::place_bid(signed(1), 10, 4), Error::::DurationTooBig); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); assert_eq!(Balances::reserved_balance(1), 10); assert_eq!(Queues::::get(2), vec![Bid { amount: 10, who: 1 }]); assert_eq!(QueueTotals::::get(), vec![(0, 0), (1, 10), (0, 0)]); @@ -85,16 +91,16 @@ fn place_bid_works() { fn place_bid_queuing_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 20, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2)); - assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 5, 2), Error::::BidTooLow); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 15, 2)); + assert_ok!(Nis::place_bid(signed(1), 20, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 5, 2)); + assert_noop!(Nis::place_bid(signed(1), 5, 2), Error::::BidTooLow); + assert_ok!(Nis::place_bid(signed(1), 15, 2)); assert_eq!(Balances::reserved_balance(1), 45); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 25, 2)); + assert_ok!(Nis::place_bid(signed(1), 25, 2)); assert_eq!(Balances::reserved_balance(1), 60); - assert_noop!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2), Error::::BidTooLow); + assert_noop!(Nis::place_bid(signed(1), 10, 2), Error::::BidTooLow); assert_eq!( Queues::::get(2), vec![ @@ -111,11 +117,11 @@ fn place_bid_queuing_works() { fn place_bid_fails_when_queue_full() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 10, 2)); - assert_noop!(Nis::place_bid(RuntimeOrigin::signed(4), 10, 2), Error::::BidTooLow); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(4), 10, 3)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(2), 10, 2)); + assert_ok!(Nis::place_bid(signed(3), 10, 2)); + assert_noop!(Nis::place_bid(signed(4), 10, 2), Error::::BidTooLow); + assert_ok!(Nis::place_bid(signed(4), 10, 3)); }); } @@ -123,11 +129,11 @@ fn place_bid_fails_when_queue_full() { fn multiple_place_bids_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 3)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 1)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 3)); + assert_ok!(Nis::place_bid(signed(2), 10, 2)); assert_eq!(Balances::reserved_balance(1), 40); assert_eq!(Balances::reserved_balance(2), 10); @@ -149,9 +155,9 @@ fn multiple_place_bids_works() { fn retract_single_item_queue_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 1)); + assert_ok!(Nis::place_bid(signed(1), 10, 1)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::retract_bid(signed(1), 10, 1)); assert_eq!(Balances::reserved_balance(1), 10); assert_eq!(Queues::::get(1), vec![]); @@ -164,12 +170,12 @@ fn retract_single_item_queue_works() { fn retract_with_other_and_duplicate_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 1)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(1), 10, 2)); + assert_ok!(Nis::place_bid(signed(2), 10, 2)); - assert_ok!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 2)); + assert_ok!(Nis::retract_bid(signed(1), 10, 2)); assert_eq!(Balances::reserved_balance(1), 20); assert_eq!(Balances::reserved_balance(2), 10); assert_eq!(Queues::::get(1), vec![Bid { amount: 10, who: 1 },]); @@ -185,11 +191,11 @@ fn retract_with_other_and_duplicate_works() { fn retract_non_existent_item_fails() { new_test_ext().execute_with(|| { run_to_block(1); - assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 1), Error::::NotFound); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 10, 1)); - assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 20, 1), Error::::NotFound); - assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(1), 10, 2), Error::::NotFound); - assert_noop!(Nis::retract_bid(RuntimeOrigin::signed(2), 10, 1), Error::::NotFound); + assert_noop!(Nis::retract_bid(signed(1), 10, 1), Error::::UnknownBid); + assert_ok!(Nis::place_bid(signed(1), 10, 1)); + assert_noop!(Nis::retract_bid(signed(1), 20, 1), Error::::UnknownBid); + assert_noop!(Nis::retract_bid(signed(1), 10, 2), Error::::UnknownBid); + assert_noop!(Nis::retract_bid(signed(2), 10, 1), Error::::UnknownBid); }); } @@ -197,14 +203,14 @@ fn retract_non_existent_item_fails() { fn basic_enlarge_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(2), 40, 2)); enlarge(40, 2); // Takes 2/2, then stopped because it reaches its max amount assert_eq!(Balances::reserved_balance(1), 40); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(pot(), 40); + assert_eq!(Balances::reserved_balance(2), 40); + assert_eq!(holdings(), 40); assert_eq!(Queues::::get(1), vec![Bid { amount: 40, who: 1 }]); assert_eq!(Queues::::get(2), vec![]); @@ -216,12 +222,17 @@ fn basic_enlarge_works() { proportion_owed: Perquintill::from_percent(10), index: 1, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 40, } ); assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 7 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((2, 40)), + expiry: 7 + } ); }); } @@ -230,10 +241,10 @@ fn basic_enlarge_works() { fn enlarge_respects_bids_limit() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 40, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(4), 40, 3)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(2), 40, 2)); + assert_ok!(Nis::place_bid(signed(3), 40, 2)); + assert_ok!(Nis::place_bid(signed(4), 40, 3)); enlarge(100, 2); // Should have taken 4/3 and 2/2, then stopped because it's only allowed 2. @@ -244,11 +255,19 @@ fn enlarge_respects_bids_limit() { assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 4, expiry: 10 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((4, 40)), + expiry: 10 + } ); assert_eq!( Receipts::::get(1).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 7 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((2, 40)), + expiry: 7 + } ); assert_eq!( Summary::::get(), @@ -256,7 +275,8 @@ fn enlarge_respects_bids_limit() { proportion_owed: Perquintill::from_percent(20), index: 2, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 80, } ); }); @@ -266,7 +286,7 @@ fn enlarge_respects_bids_limit() { fn enlarge_respects_amount_limit_and_will_split() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 80, 1)); + assert_ok!(Nis::place_bid(signed(1), 80, 1)); enlarge(40, 2); // Takes 2/2, then stopped because it reaches its max amount @@ -275,7 +295,11 @@ fn enlarge_respects_amount_limit_and_will_split() { assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 4 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((1, 40)), + expiry: 4 + } ); assert_eq!( Summary::::get(), @@ -283,7 +307,8 @@ fn enlarge_respects_amount_limit_and_will_split() { proportion_owed: Perquintill::from_percent(10), index: 1, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 40, } ); }); @@ -293,25 +318,25 @@ fn enlarge_respects_amount_limit_and_will_split() { fn basic_thaw_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 60); assert_eq!(Balances::reserved_balance(1), 40); - assert_eq!(pot(), 0); + assert_eq!(holdings(), 0); enlarge(40, 1); assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 60); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(pot(), 40); + assert_eq!(Balances::reserved_balance(1), 40); + assert_eq!(holdings(), 40); run_to_block(3); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::NotExpired); + assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::::NotExpired); run_to_block(4); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 1, None), Error::::Unknown); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0, None), Error::::NotOwner); + assert_noop!(Nis::thaw_private(signed(1), 1, None), Error::::UnknownReceipt); + assert_noop!(Nis::thaw_private(signed(2), 0, None), Error::::NotOwner); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); assert_eq!(NisBalances::free_balance(1), 0); assert_eq!(Nis::typed_attribute::<_, Perquintill>(&0, b"proportion"), None); assert_eq!(Nis::issuance().effective, 400); @@ -323,7 +348,8 @@ fn basic_thaw_works() { proportion_owed: Perquintill::zero(), index: 1, last_period: 0, - thawed: Perquintill::from_percent(10) + thawed: Perquintill::from_percent(10), + receipts_on_hold: 0, } ); assert_eq!(Receipts::::get(0), None); @@ -334,18 +360,16 @@ fn basic_thaw_works() { fn partial_thaw_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 80, 1)); + assert_ok!(Nis::place_bid(signed(1), 80, 1)); enlarge(80, 1); - assert_eq!(pot(), 80); + assert_eq!(holdings(), 80); run_to_block(4); - assert_noop!( - Nis::thaw(RuntimeOrigin::signed(1), 0, Some(4_100_000)), - Error::::MakesDust - ); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, Some(1_050_000))); + let prop = Perquintill::from_rational(4_100_000, 21_000_000u64); + assert_noop!(Nis::thaw_private(signed(1), 0, Some(prop)), Error::::MakesDust); + let prop = Perquintill::from_rational(1_050_000, 21_000_000u64); + assert_ok!(Nis::thaw_private(signed(1), 0, Some(prop))); - assert_eq!(NisBalances::free_balance(1), 3_150_000); assert_eq!( Nis::typed_attribute::<_, Perquintill>(&0, b"proportion"), Some(Perquintill::from_rational(3_150_000u64, 21_000_000u64)), @@ -353,9 +377,9 @@ fn partial_thaw_works() { assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 40); - assert_eq!(pot(), 60); + assert_eq!(holdings(), 60); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); assert_eq!(Nis::issuance().effective, 400); assert_eq!(Balances::free_balance(1), 100); @@ -367,7 +391,8 @@ fn partial_thaw_works() { proportion_owed: Perquintill::zero(), index: 1, last_period: 0, - thawed: Perquintill::from_percent(20) + thawed: Perquintill::from_percent(20), + receipts_on_hold: 0, } ); assert_eq!(Receipts::::get(0), None); @@ -378,35 +403,144 @@ fn partial_thaw_works() { fn thaw_respects_transfers() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); enlarge(40, 1); run_to_block(4); assert_eq!(Nis::owner(&0), Some(1)); + assert_eq!(Balances::reserved_balance(&1), 40); + assert_eq!(Balances::reserved_balance(&2), 0); assert_ok!(Nis::transfer(&0, &2)); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(Balances::reserved_balance(&2), 40); // Transfering the receipt... - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::NotOwner); - // ...can't be thawed due to missing counterpart - assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 0, None), TokenError::NoFunds); + assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::::NotOwner); - // Transfer the counterpart also... - assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(1), 2, 2100000)); // ...and thawing is possible. - assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 0, None)); + assert_ok!(Nis::thaw_private(signed(2), 0, None)); - assert_eq!(Balances::free_balance(2), 140); - assert_eq!(Balances::free_balance(1), 60); + assert_eq!(Balances::total_balance(&2), 140); + assert_eq!(Balances::total_balance(&1), 60); + }); +} + +#[test] +fn communify_works() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + enlarge(40, 1); + run_to_block(4); + + assert_eq!(Nis::owner(&0), Some(1)); + assert_eq!(Balances::reserved_balance(&1), 40); + assert_eq!(pot(), 0); + assert_eq!(NisBalances::free_balance(&1), 0); + assert_ok!(Nis::communify(signed(1), 0)); + assert_eq!(Nis::owner(&0), None); + assert_eq!(Balances::reserved_balance(&1), 0); + assert_eq!(pot(), 40); + // We now have fungibles. + assert_eq!(NisBalances::free_balance(&1), 2_100_000); + + // Can't transfer the NFT or thaw it as a private. + assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::::AlreadyCommunal); + assert_noop!(Nis::transfer(&0, &2), Error::::AlreadyCommunal); + // Communal thawing would be possible, except it's the wrong receipt. + assert_noop!(Nis::thaw_communal(signed(1), 1), Error::::UnknownReceipt); + + // Transfer some of the fungibles away. + assert_ok!(NisBalances::transfer(signed(1), 2, 100_000)); + assert_eq!(NisBalances::free_balance(&1), 2_000_000); + assert_eq!(NisBalances::free_balance(&2), 100_000); + + // Communal thawing with the correct index is not possible now. + assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::NoFunds); + assert_noop!(Nis::thaw_communal(signed(2), 0), TokenError::NoFunds); + + // Transfer the rest to 2... + assert_ok!(NisBalances::transfer(signed(1), 2, 2_000_000)); + assert_eq!(NisBalances::free_balance(&1), 0); + assert_eq!(NisBalances::free_balance(&2), 2_100_000); + + // ...and thawing becomes possible. + assert_ok!(Nis::thaw_communal(signed(2), 0)); + assert_eq!(NisBalances::free_balance(&1), 0); + assert_eq!(NisBalances::free_balance(&2), 0); + assert_eq!(pot(), 0); + assert_eq!(Balances::total_balance(&1), 60); + assert_eq!(Balances::total_balance(&2), 140); + + assert_noop!(Nis::thaw_communal(signed(2), 0), Error::::UnknownReceipt); + }); +} + +#[test] +fn privatize_works() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + enlarge(40, 1); + run_to_block(4); + assert_noop!(Nis::privatize(signed(2), 0), Error::::AlreadyPrivate); + assert_ok!(Nis::communify(signed(1), 0)); + + // Transfer the fungibles to #2 + assert_ok!(NisBalances::transfer(signed(1), 2, 2_100_000)); + assert_noop!(Nis::privatize(signed(1), 0), TokenError::NoFunds); + + // Privatize + assert_ok!(Nis::privatize(signed(2), 0)); + assert_noop!(Nis::privatize(signed(2), 0), Error::::AlreadyPrivate); + assert_eq!(NisBalances::free_balance(&2), 0); + assert_eq!(Nis::owner(&0), Some(2)); + assert_eq!(Balances::reserved_balance(&2), 40); + assert_eq!(pot(), 0); }); } #[test] -fn thaw_when_issuance_higher_works() { +fn privatize_and_thaw_with_another_receipt_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(2), 40, 1)); + enlarge(80, 2); + run_to_block(4); + + assert_ok!(Nis::communify(signed(1), 0)); + assert_ok!(Nis::communify(signed(2), 1)); + + // Transfer half of fungibles to #3 from each of #1 and #2, and the other half from #2 to #4 + assert_ok!(NisBalances::transfer(signed(1), 3, 1_050_000)); + assert_ok!(NisBalances::transfer(signed(2), 3, 1_050_000)); + assert_ok!(NisBalances::transfer(signed(2), 4, 1_050_000)); + + // #3 privatizes, partially thaws, then re-communifies with #0, then transfers the fungibles + // to #2 + assert_ok!(Nis::privatize(signed(3), 0)); + assert_ok!(Nis::thaw_private(signed(3), 0, Some(Perquintill::from_percent(5)))); + assert_ok!(Nis::communify(signed(3), 0)); + assert_ok!(NisBalances::transfer(signed(3), 1, 1_050_000)); + + // #1 now has enough to thaw using receipt 1 + assert_ok!(Nis::thaw_communal(signed(1), 1)); + + // #4 now has enough to thaw using receipt 0 + assert_ok!(Nis::thaw_communal(signed(4), 0)); + }); +} + +#[test] +fn communal_thaw_when_issuance_higher_works() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(Nis::place_bid(signed(1), 100, 1)); enlarge(100, 1); + assert_ok!(Nis::communify(signed(1), 0)); + assert_eq!(NisBalances::free_balance(1), 5_250_000); // (25% of 21m) // Everybody else's balances goes up by 50% @@ -417,19 +551,45 @@ fn thaw_when_issuance_higher_works() { run_to_block(4); // Unfunded initially... - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::Unfunded); + assert_noop!(Nis::thaw_communal(signed(1), 0), Error::::Unfunded); // ...so we fund. - assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); + assert_ok!(Nis::fund_deficit(signed(1))); - // Transfer counterpart away... - assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(1), 2, 250_000)); + // Transfer counterparts away... + assert_ok!(NisBalances::transfer(signed(1), 2, 250_000)); // ...and it's not thawable. - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), TokenError::NoFunds); + assert_noop!(Nis::thaw_communal(signed(1), 0), TokenError::NoFunds); - // Transfer counterpart back... - assert_ok!(NisBalances::transfer(RuntimeOrigin::signed(2), 1, 250_000)); + // Transfer counterparts back... + assert_ok!(NisBalances::transfer(signed(2), 1, 250_000)); // ...and it is. - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_communal(signed(1), 0)); + + assert_eq!(Balances::free_balance(1), 150); + assert_eq!(Balances::reserved_balance(1), 0); + }); +} + +#[test] +fn private_thaw_when_issuance_higher_works() { + new_test_ext().execute_with(|| { + run_to_block(1); + assert_ok!(Nis::place_bid(signed(1), 100, 1)); + enlarge(100, 1); + + // Everybody else's balances goes up by 50% + Balances::make_free_balance_be(&2, 150); + Balances::make_free_balance_be(&3, 150); + Balances::make_free_balance_be(&4, 150); + + run_to_block(4); + + // Unfunded initially... + assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::::Unfunded); + // ...so we fund. + assert_ok!(Nis::fund_deficit(signed(1))); + + assert_ok!(Nis::thaw_private(signed(1), 0, None)); assert_eq!(Balances::free_balance(1), 150); assert_eq!(Balances::reserved_balance(1), 0); @@ -443,21 +603,21 @@ fn thaw_with_ignored_issuance_works() { // Give account zero some balance. Balances::make_free_balance_be(&0, 200); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); + assert_ok!(Nis::place_bid(signed(1), 100, 1)); enlarge(100, 1); // Account zero transfers 50 into everyone else's accounts. - assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 2, 50)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 3, 50)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(0), 4, 50)); + assert_ok!(Balances::transfer(signed(0), 2, 50)); + assert_ok!(Balances::transfer(signed(0), 3, 50)); + assert_ok!(Balances::transfer(signed(0), 4, 50)); run_to_block(4); // Unfunded initially... - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 0, None), Error::::Unfunded); + assert_noop!(Nis::thaw_private(signed(1), 0, None), Error::::Unfunded); // ...so we fund... - assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); + assert_ok!(Nis::fund_deficit(signed(1))); // ...and then it's ok. - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); // Account zero changes have been ignored. assert_eq!(Balances::free_balance(1), 150); @@ -469,7 +629,7 @@ fn thaw_with_ignored_issuance_works() { fn thaw_when_issuance_lower_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 100, 1)); + assert_ok!(Nis::place_bid(signed(1), 100, 1)); enlarge(100, 1); // Everybody else's balances goes down by 25% @@ -478,7 +638,7 @@ fn thaw_when_issuance_lower_works() { Balances::make_free_balance_be(&4, 75); run_to_block(4); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); assert_eq!(Balances::free_balance(1), 75); assert_eq!(Balances::reserved_balance(1), 0); @@ -489,23 +649,23 @@ fn thaw_when_issuance_lower_works() { fn multiple_thaws_works() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 60, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 50, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(1), 60, 1)); + assert_ok!(Nis::place_bid(signed(2), 50, 1)); enlarge(200, 3); // Double everyone's free balances. Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 200); Balances::make_free_balance_be(&4, 200); - assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); + assert_ok!(Nis::fund_deficit(signed(1))); run_to_block(4); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1, None)); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(2), 2, None), Error::::Throttled); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(1), 1, None)); + assert_noop!(Nis::thaw_private(signed(2), 2, None), Error::::Throttled); run_to_block(5); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2, None)); + assert_ok!(Nis::thaw_private(signed(2), 2, None)); assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(2), 200); @@ -516,24 +676,24 @@ fn multiple_thaws_works() { fn multiple_thaws_works_in_alternative_thaw_order() { new_test_ext().execute_with(|| { run_to_block(1); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 60, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 50, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(1), 60, 1)); + assert_ok!(Nis::place_bid(signed(2), 50, 1)); enlarge(200, 3); // Double everyone's free balances. Balances::make_free_balance_be(&2, 100); Balances::make_free_balance_be(&3, 200); Balances::make_free_balance_be(&4, 200); - assert_ok!(Nis::fund_deficit(RuntimeOrigin::signed(1))); + assert_ok!(Nis::fund_deficit(signed(1))); run_to_block(4); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(2), 2, None)); - assert_noop!(Nis::thaw(RuntimeOrigin::signed(1), 1, None), Error::::Throttled); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 0, None)); + assert_ok!(Nis::thaw_private(signed(2), 2, None)); + assert_noop!(Nis::thaw_private(signed(1), 1, None), Error::::Throttled); + assert_ok!(Nis::thaw_private(signed(1), 0, None)); run_to_block(5); - assert_ok!(Nis::thaw(RuntimeOrigin::signed(1), 1, None)); + assert_ok!(Nis::thaw_private(signed(1), 1, None)); assert_eq!(Balances::free_balance(1), 200); assert_eq!(Balances::free_balance(2), 200); @@ -548,11 +708,11 @@ fn enlargement_to_target_works() { <() as WeightInfo>::process_queue() + (<() as WeightInfo>::process_bid() * 2); super::mock::MaxIntakeWeight::set(w); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 1)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(1), 40, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 2)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(2), 40, 3)); - assert_ok!(Nis::place_bid(RuntimeOrigin::signed(3), 40, 3)); + assert_ok!(Nis::place_bid(signed(1), 40, 1)); + assert_ok!(Nis::place_bid(signed(1), 40, 2)); + assert_ok!(Nis::place_bid(signed(2), 40, 2)); + assert_ok!(Nis::place_bid(signed(2), 40, 3)); + assert_ok!(Nis::place_bid(signed(3), 40, 3)); Target::set(Perquintill::from_percent(40)); run_to_block(3); @@ -571,11 +731,19 @@ fn enlargement_to_target_works() { // Two new items should have been issued to 2 & 3 for 40 each & duration of 3. assert_eq!( Receipts::::get(0).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 13 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((2, 40)), + expiry: 13 + } ); assert_eq!( Receipts::::get(1).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 3, expiry: 13 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((3, 40)), + expiry: 13 + } ); assert_eq!( Summary::::get(), @@ -584,6 +752,7 @@ fn enlargement_to_target_works() { index: 2, last_period: 0, thawed: Perquintill::zero(), + receipts_on_hold: 80, } ); @@ -595,7 +764,8 @@ fn enlargement_to_target_works() { proportion_owed: Perquintill::from_percent(20), index: 2, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 80, } ); @@ -603,11 +773,19 @@ fn enlargement_to_target_works() { // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. assert_eq!( Receipts::::get(2).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 12 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((1, 40)), + expiry: 12 + } ); assert_eq!( Receipts::::get(3).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 2, expiry: 12 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((2, 40)), + expiry: 12 + } ); assert_eq!( Summary::::get(), @@ -615,7 +793,8 @@ fn enlargement_to_target_works() { proportion_owed: Perquintill::from_percent(40), index: 4, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 160, } ); @@ -627,7 +806,8 @@ fn enlargement_to_target_works() { proportion_owed: Perquintill::from_percent(40), index: 4, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 160, } ); @@ -635,10 +815,14 @@ fn enlargement_to_target_works() { Target::set(Perquintill::from_percent(60)); run_to_block(10); - // Two new items should have been issued to 1 & 2 for 40 each & duration of 2. + // One new item should have been issued to 1 for 40 each & duration of 2. assert_eq!( Receipts::::get(4).unwrap(), - ReceiptRecord { proportion: Perquintill::from_percent(10), who: 1, expiry: 13 } + ReceiptRecord { + proportion: Perquintill::from_percent(10), + owner: Some((1, 40)), + expiry: 13 + } ); assert_eq!( @@ -647,7 +831,8 @@ fn enlargement_to_target_works() { proportion_owed: Perquintill::from_percent(50), index: 5, last_period: 0, - thawed: Perquintill::zero() + thawed: Perquintill::zero(), + receipts_on_hold: 200, } ); }); diff --git a/frame/nis/src/weights.rs b/frame/nis/src/weights.rs index 71577075ada91..769ff795514c6 100644 --- a/frame/nis/src/weights.rs +++ b/frame/nis/src/weights.rs @@ -1,40 +1,31 @@ -// This file is part of Substrate. - -// Copyright (C) 2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. //! Autogenerated weights for pallet_nis //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-11-07, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` -//! HOSTNAME: `bm2`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` +//! DATE: 2022-12-15, STEPS: `5`, REPEAT: 2, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `Workhorse.local`, CPU: `` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// ../../../target/release/substrate // benchmark // pallet -// --chain=dev -// --steps=50 -// --repeat=20 -// --pallet=pallet_nis -// --extrinsic=* +// --chain +// dev +// --steps +// 5 +// --repeat +// 2 +// --pallet +// pallet_nis +// --extrinsic +// * // --execution=wasm // --wasm-execution=compiled -// --template=./.maintain/frame-weight-template.hbs -// --output=./frame/nis/src/weights.rs +// --output +// ../../../frame/nis/src/weights.rs +// --template +// ../../../.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -48,8 +39,11 @@ pub trait WeightInfo { fn place_bid(l: u32, ) -> Weight; fn place_bid_max() -> Weight; fn retract_bid(l: u32, ) -> Weight; - fn thaw() -> Weight; fn fund_deficit() -> Weight; + fn thaw_private() -> Weight; + fn thaw_communal() -> Weight; + fn privatize() -> Weight; + fn communify() -> Weight; fn process_queues() -> Weight; fn process_queue() -> Weight; fn process_bid() -> Weight; @@ -59,143 +53,223 @@ pub trait WeightInfo { pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { // Storage: Nis Queues (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) + /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { - // Minimum execution time: 42_332 nanoseconds. - Weight::from_ref_time(45_584_514 as u64) - // Standard Error: 129 - .saturating_add(Weight::from_ref_time(45_727 as u64).saturating_mul(l as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 47_000 nanoseconds. + Weight::from_ref_time(53_822_030) + // Standard Error: 4_869 + .saturating_add(Weight::from_ref_time(47_431).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) } // Storage: Nis Queues (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) fn place_bid_max() -> Weight { - // Minimum execution time: 85_866 nanoseconds. - Weight::from_ref_time(87_171_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 109_000 nanoseconds. + Weight::from_ref_time(109_000_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) } // Storage: Nis Queues (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { - // Minimum execution time: 44_605 nanoseconds. - Weight::from_ref_time(46_850_108 as u64) - // Standard Error: 135 - .saturating_add(Weight::from_ref_time(34_178 as u64).saturating_mul(l as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Nis Active (r:1 w:1) - // Storage: Nis ActiveTotal (r:1 w:1) - fn thaw() -> Weight { - // Minimum execution time: 55_143 nanoseconds. - Weight::from_ref_time(55_845_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } - // Storage: Nis Active (r:1 w:1) - // Storage: Nis ActiveTotal (r:1 w:1) + // Minimum execution time: 50_000 nanoseconds. + Weight::from_ref_time(54_479_879) + // Standard Error: 4_891 + .saturating_add(Weight::from_ref_time(38_224).saturating_mul(l.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: Nis Summary (r:1 w:0) + // Storage: System Account (r:1 w:1) fn fund_deficit() -> Weight { - Weight::from_ref_time(47_753_000 as u64) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 57_000 nanoseconds. + Weight::from_ref_time(62_000_000) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: Nis ActiveTotal (r:1 w:0) - fn process_queues() -> Weight { - Weight::from_ref_time(1_663_000 as u64) - .saturating_add(T::DbWeight::get().reads(1 as u64)) + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:0) + // Storage: Balances Reserves (r:1 w:1) + fn thaw_private() -> Weight { + // Minimum execution time: 84_000 nanoseconds. + Weight::from_ref_time(85_000_000) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn thaw_communal() -> Weight { + // Minimum execution time: 108_000 nanoseconds. + Weight::from_ref_time(115_000_000) + .saturating_add(T::DbWeight::get().reads(5)) + .saturating_add(T::DbWeight::get().writes(5)) } - // Storage: Nis ActiveTotal (r:1 w:1) + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + fn privatize() -> Weight { + // Minimum execution time: 107_000 nanoseconds. + Weight::from_ref_time(110_000_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) + } + // Storage: Nis Receipts (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + fn communify() -> Weight { + // Minimum execution time: 89_000 nanoseconds. + Weight::from_ref_time(89_000_000) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(6)) + } + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:0) // Storage: Nis QueueTotals (r:1 w:1) + fn process_queues() -> Weight { + // Minimum execution time: 34_000 nanoseconds. + Weight::from_ref_time(38_000_000) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(2)) + } // Storage: Nis Queues (r:1 w:1) - // Storage: Nis Active (r:0 w:1) fn process_queue() -> Weight { - Weight::from_ref_time(40_797_000 as u64) - // Standard Error: 1_000 - .saturating_add(T::DbWeight::get().reads(3 as u64)) - .saturating_add(T::DbWeight::get().writes(3 as u64)) + // Minimum execution time: 6_000 nanoseconds. + Weight::from_ref_time(7_000_000) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) } - // Storage: Nis ActiveTotal (r:1 w:1) - // Storage: Nis QueueTotals (r:1 w:1) - // Storage: Nis Queues (r:1 w:1) - // Storage: Nis Active (r:0 w:1) + // Storage: Nis Receipts (r:0 w:1) fn process_bid() -> Weight { - Weight::from_ref_time(14_944_000 as u64) - // Standard Error: 6_000 - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + // Minimum execution time: 14_000 nanoseconds. + Weight::from_ref_time(15_000_000) + .saturating_add(T::DbWeight::get().writes(1)) } } // For backwards compatibility and tests impl WeightInfo for () { // Storage: Nis Queues (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) + /// The range of component `l` is `[0, 999]`. fn place_bid(l: u32, ) -> Weight { - // Minimum execution time: 42_332 nanoseconds. - Weight::from_ref_time(45_584_514 as u64) - // Standard Error: 129 - .saturating_add(Weight::from_ref_time(45_727 as u64).saturating_mul(l as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 47_000 nanoseconds. + Weight::from_ref_time(53_822_030) + // Standard Error: 4_869 + .saturating_add(Weight::from_ref_time(47_431).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(3)) } // Storage: Nis Queues (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) fn place_bid_max() -> Weight { - // Minimum execution time: 85_866 nanoseconds. - Weight::from_ref_time(87_171_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 109_000 nanoseconds. + Weight::from_ref_time(109_000_000) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(3)) } // Storage: Nis Queues (r:1 w:1) // Storage: Nis QueueTotals (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + /// The range of component `l` is `[1, 1000]`. fn retract_bid(l: u32, ) -> Weight { - // Minimum execution time: 44_605 nanoseconds. - Weight::from_ref_time(46_850_108 as u64) - // Standard Error: 135 - .saturating_add(Weight::from_ref_time(34_178 as u64).saturating_mul(l as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Nis Active (r:1 w:1) - // Storage: Nis ActiveTotal (r:1 w:1) - fn thaw() -> Weight { - // Minimum execution time: 55_143 nanoseconds. - Weight::from_ref_time(55_845_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } - // Storage: Nis Active (r:1 w:1) - // Storage: Nis ActiveTotal (r:1 w:1) + // Minimum execution time: 50_000 nanoseconds. + Weight::from_ref_time(54_479_879) + // Standard Error: 4_891 + .saturating_add(Weight::from_ref_time(38_224).saturating_mul(l.into())) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(3)) + } + // Storage: Nis Summary (r:1 w:0) + // Storage: System Account (r:1 w:1) fn fund_deficit() -> Weight { - Weight::from_ref_time(47_753_000 as u64) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 57_000 nanoseconds. + Weight::from_ref_time(62_000_000) + .saturating_add(RocksDbWeight::get().reads(2)) + .saturating_add(RocksDbWeight::get().writes(1)) } - // Storage: Nis ActiveTotal (r:1 w:0) - fn process_queues() -> Weight { - Weight::from_ref_time(1_663_000 as u64) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:0) + // Storage: Balances Reserves (r:1 w:1) + fn thaw_private() -> Weight { + // Minimum execution time: 84_000 nanoseconds. + Weight::from_ref_time(85_000_000) + .saturating_add(RocksDbWeight::get().reads(4)) + .saturating_add(RocksDbWeight::get().writes(3)) + } + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + // Storage: System Account (r:1 w:1) + fn thaw_communal() -> Weight { + // Minimum execution time: 108_000 nanoseconds. + Weight::from_ref_time(115_000_000) + .saturating_add(RocksDbWeight::get().reads(5)) + .saturating_add(RocksDbWeight::get().writes(5)) + } + // Storage: Nis Receipts (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + fn privatize() -> Weight { + // Minimum execution time: 107_000 nanoseconds. + Weight::from_ref_time(110_000_000) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(6)) } - // Storage: Nis ActiveTotal (r:1 w:1) + // Storage: Nis Receipts (r:1 w:1) + // Storage: Balances Reserves (r:1 w:1) + // Storage: System Account (r:1 w:1) + // Storage: Nis Summary (r:1 w:1) + // Storage: Assets Asset (r:1 w:1) + // Storage: Assets Account (r:1 w:1) + fn communify() -> Weight { + // Minimum execution time: 89_000 nanoseconds. + Weight::from_ref_time(89_000_000) + .saturating_add(RocksDbWeight::get().reads(6)) + .saturating_add(RocksDbWeight::get().writes(6)) + } + // Storage: Nis Summary (r:1 w:1) + // Storage: System Account (r:1 w:0) // Storage: Nis QueueTotals (r:1 w:1) + fn process_queues() -> Weight { + // Minimum execution time: 34_000 nanoseconds. + Weight::from_ref_time(38_000_000) + .saturating_add(RocksDbWeight::get().reads(3)) + .saturating_add(RocksDbWeight::get().writes(2)) + } // Storage: Nis Queues (r:1 w:1) - // Storage: Nis Active (r:0 w:1) fn process_queue() -> Weight { - Weight::from_ref_time(40_797_000 as u64) - // Standard Error: 1_000 - .saturating_add(RocksDbWeight::get().reads(3 as u64)) - .saturating_add(RocksDbWeight::get().writes(3 as u64)) + // Minimum execution time: 6_000 nanoseconds. + Weight::from_ref_time(7_000_000) + .saturating_add(RocksDbWeight::get().reads(1)) + .saturating_add(RocksDbWeight::get().writes(1)) } - // Storage: Nis ActiveTotal (r:1 w:1) - // Storage: Nis QueueTotals (r:1 w:1) - // Storage: Nis Queues (r:1 w:1) - // Storage: Nis Active (r:0 w:1) + // Storage: Nis Receipts (r:0 w:1) fn process_bid() -> Weight { - Weight::from_ref_time(14_944_000 as u64) - // Standard Error: 6_000 - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + // Minimum execution time: 14_000 nanoseconds. + Weight::from_ref_time(15_000_000) + .saturating_add(RocksDbWeight::get().writes(1)) } } diff --git a/frame/node-authorization/Cargo.toml b/frame/node-authorization/Cargo.toml index fbf486644e15c..c60ce1d12ce09 100644 --- a/frame/node-authorization/Cargo.toml +++ b/frame/node-authorization/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME pallet for node authorization" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/nomination-pools/Cargo.toml b/frame/nomination-pools/Cargo.toml index 3eb2d4bc5fd9b..d51bcec51f478 100644 --- a/frame/nomination-pools/Cargo.toml +++ b/frame/nomination-pools/Cargo.toml @@ -13,7 +13,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # parity -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # FRAME diff --git a/frame/nomination-pools/benchmarking/Cargo.toml b/frame/nomination-pools/benchmarking/Cargo.toml index 74b71a353fe7f..b96024af7d3c5 100644 --- a/frame/nomination-pools/benchmarking/Cargo.toml +++ b/frame/nomination-pools/benchmarking/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] # parity -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } # FRAME diff --git a/frame/nomination-pools/runtime-api/Cargo.toml b/frame/nomination-pools/runtime-api/Cargo.toml index cf72d795c9faa..d1e4fbb30df52 100644 --- a/frame/nomination-pools/runtime-api/Cargo.toml +++ b/frame/nomination-pools/runtime-api/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/api" } sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } diff --git a/frame/nomination-pools/test-staking/Cargo.toml b/frame/nomination-pools/test-staking/Cargo.toml index a45c7852d4151..fbe5feca0febe 100644 --- a/frame/nomination-pools/test-staking/Cargo.toml +++ b/frame/nomination-pools/test-staking/Cargo.toml @@ -13,7 +13,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dev-dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } scale-info = { version = "2.0.1", features = ["derive"] } sp-runtime = { version = "7.0.0", path = "../../../primitives/runtime" } diff --git a/frame/offences/Cargo.toml b/frame/offences/Cargo.toml index 107a0489cd594..47ae264a69cf8 100644 --- a/frame/offences/Cargo.toml +++ b/frame/offences/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } diff --git a/frame/offences/benchmarking/Cargo.toml b/frame/offences/benchmarking/Cargo.toml index e20aefd69ad4d..23377f7883dae 100644 --- a/frame/offences/benchmarking/Cargo.toml +++ b/frame/offences/benchmarking/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking" } frame-election-provider-support = { version = "4.0.0-dev", default-features = false, path = "../../election-provider-support" } @@ -29,6 +29,7 @@ pallet-staking = { version = "4.0.0-dev", default-features = false, path = "../. sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/staking" } sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } +log = { version = "0.4.17", default-features = false } [dev-dependencies] pallet-staking-reward-curve = { version = "4.0.0-dev", path = "../../staking/reward-curve" } @@ -55,6 +56,7 @@ std = [ "sp-runtime/std", "sp-staking/std", "sp-std/std", + "log/std", ] runtime-benchmarks = [ diff --git a/frame/offences/benchmarking/src/lib.rs b/frame/offences/benchmarking/src/lib.rs index e5ec2952f8114..7e586a96bbe3e 100644 --- a/frame/offences/benchmarking/src/lib.rs +++ b/frame/offences/benchmarking/src/lib.rs @@ -28,8 +28,10 @@ use frame_benchmarking::{account, benchmarks}; use frame_support::traits::{Currency, Get, ValidatorSet, ValidatorSetWithIdentification}; use frame_system::{Config as SystemConfig, Pallet as System, RawOrigin}; +#[cfg(test)] +use sp_runtime::traits::UniqueSaturatedInto; use sp_runtime::{ - traits::{Convert, Saturating, StaticLookup, UniqueSaturatedInto}, + traits::{Convert, Saturating, StaticLookup}, Perbill, }; use sp_staking::offence::{Offence, ReportOffence}; @@ -43,9 +45,11 @@ use pallet_session::{ historical::{Config as HistoricalConfig, IdentificationTuple}, Config as SessionConfig, SessionManager, }; +#[cfg(test)] +use pallet_staking::Event as StakingEvent; use pallet_staking::{ - Config as StakingConfig, Event as StakingEvent, Exposure, IndividualExposure, - Pallet as Staking, RewardDestination, ValidatorPrefs, + Config as StakingConfig, Exposure, IndividualExposure, Pallet as Staking, RewardDestination, + ValidatorPrefs, }; const SEED: u32 = 0; @@ -89,7 +93,9 @@ type BalanceOf = struct Offender { pub controller: T::AccountId, + #[allow(dead_code)] pub stash: T::AccountId, + #[allow(dead_code)] pub nominator_stashes: Vec, } @@ -217,55 +223,63 @@ fn make_offenders_im_online( } #[cfg(test)] -fn check_events::RuntimeEvent>>(expected: I) { +fn check_events< + T: Config, + I: Iterator, + Item: sp_std::borrow::Borrow<::RuntimeEvent> + sp_std::fmt::Debug, +>( + expected: I, +) { let events = System::::events() .into_iter() .map(|frame_system::EventRecord { event, .. }| event) .collect::>(); let expected = expected.collect::>(); - fn pretty(header: &str, ev: &[D], offset: usize) { - println!("{}", header); + fn pretty(header: &str, ev: &[D], offset: usize) { + log::info!("{}", header); for (idx, ev) in ev.iter().enumerate() { - println!("\t[{:04}] {:?}", idx + offset, ev); + log::info!("\t[{:04}] {:?}", idx + offset, ev); } } - fn print_events(idx: usize, events: &[D], expected: &[D]) { + fn print_events( + idx: usize, + events: &[D], + expected: &[E], + ) { let window = 10; let start = idx.saturating_sub(window / 2); let end_got = (idx + window / 2).min(events.len()); pretty("Got(window):", &events[start..end_got], start); let end_expected = (idx + window / 2).min(expected.len()); pretty("Expected(window):", &expected[start..end_expected], start); - println!("---------------"); + log::info!("---------------"); let start_got = events.len().saturating_sub(window); pretty("Got(end):", &events[start_got..], start_got); let start_expected = expected.len().saturating_sub(window); pretty("Expected(end):", &expected[start_expected..], start_expected); } - let events_copy = events.clone(); - let expected_copy = expected.clone(); - - for (idx, (a, b)) in events.into_iter().zip(expected).enumerate() { - if a != b { - print_events(idx, &events_copy, &expected_copy); - println!("Mismatch at: {}", idx); - println!(" Got: {:?}", b); - println!("Expected: {:?}", a); - if events_copy.len() != expected_copy.len() { - println!( + + for (idx, (a, b)) in events.iter().zip(expected.iter()).enumerate() { + if a != sp_std::borrow::Borrow::borrow(b) { + print_events(idx, &events, &expected); + log::info!("Mismatch at: {}", idx); + log::info!(" Got: {:?}", b); + log::info!("Expected: {:?}", a); + if events.len() != expected.len() { + log::info!( "Mismatching lengths. Got: {}, Expected: {}", - events_copy.len(), - expected_copy.len() + events.len(), + expected.len() ) } panic!("Mismatching events."); } } - if events_copy.len() != expected_copy.len() { - print_events(0, &events_copy, &expected_copy); - panic!("Mismatching lengths. Got: {}, Expected: {}", events_copy.len(), expected_copy.len()) + if events.len() != expected.len() { + print_events(0, &events, &expected); + panic!("Mismatching lengths. Got: {}, Expected: {}", events.len(), expected.len(),) } } @@ -304,79 +318,78 @@ benchmarks! { ); } verify { - let bond_amount: u32 = UniqueSaturatedInto::::unique_saturated_into(bond_amount::()); - let slash_amount = slash_fraction * bond_amount; - let reward_amount = slash_amount.saturating_mul(1 + n) / 2; - let reward = reward_amount / r; - let slash_report = |id| core::iter::once( - ::RuntimeEvent::from(StakingEvent::::SlashReported{ validator: id, fraction: slash_fraction, slash_era: 0}) - ); - let slash = |id| core::iter::once( - ::RuntimeEvent::from(StakingEvent::::Slashed{ staker: id, amount: BalanceOf::::from(slash_amount) }) - ); - let balance_slash = |id| core::iter::once( - ::RuntimeEvent::from(pallet_balances::Event::::Slashed{ who: id, amount: slash_amount.into() }) - ); - let chill = |id| core::iter::once( - ::RuntimeEvent::from(StakingEvent::::Chilled{ stash: id }) - ); - let balance_deposit = |id, amount: u32| + #[cfg(test)] + { + let bond_amount: u32 = UniqueSaturatedInto::::unique_saturated_into(bond_amount::()); + let slash_amount = slash_fraction * bond_amount; + let reward_amount = slash_amount.saturating_mul(1 + n) / 2; + let reward = reward_amount / r; + let slash_report = |id| core::iter::once( + ::RuntimeEvent::from(StakingEvent::::SlashReported{ validator: id, fraction: slash_fraction, slash_era: 0}) + ); + let slash = |id| core::iter::once( + ::RuntimeEvent::from(StakingEvent::::Slashed{ staker: id, amount: BalanceOf::::from(slash_amount) }) + ); + let balance_slash = |id| core::iter::once( + ::RuntimeEvent::from(pallet_balances::Event::::Slashed{ who: id, amount: slash_amount.into() }) + ); + let chill = |id| core::iter::once( + ::RuntimeEvent::from(StakingEvent::::Chilled{ stash: id }) + ); + let balance_deposit = |id, amount: u32| ::RuntimeEvent::from(pallet_balances::Event::::Deposit{ who: id, amount: amount.into() }); - let mut first = true; - let slash_events = raw_offenders.into_iter() - .flat_map(|offender| { - let nom_slashes = offender.nominator_stashes.into_iter().flat_map(|nom| { - balance_slash(nom.clone()).map(Into::into) - .chain(slash(nom).map(Into::into)) - }); - - let mut events = chill(offender.stash.clone()).map(Into::into) - .chain(slash_report(offender.stash.clone()).map(Into::into)) - .chain(balance_slash(offender.stash.clone()).map(Into::into)) - .chain(slash(offender.stash).map(Into::into)) - .chain(nom_slashes) - .collect::>(); - - // the first deposit creates endowed events, see `endowed_reward_events` - if first { - first = false; - let mut reward_events = reporters.clone().into_iter() - .flat_map(|reporter| vec![ - balance_deposit(reporter.clone(), reward).into(), - frame_system::Event::::NewAccount { account: reporter.clone() }.into(), - ::RuntimeEvent::from( - pallet_balances::Event::::Endowed{account: reporter, free_balance: reward.into()} - ).into(), - ]) + let mut first = true; + + // We need to box all events to prevent running into too big allocations in wasm. + // The event in FRAME is represented as an enum and the size of the enum depends on the biggest variant. + // So, instead of requiring `size_of() * expected_events` we only need to + // allocate `size_of>() * expected_events`. + let slash_events = raw_offenders.into_iter() + .flat_map(|offender| { + let nom_slashes = offender.nominator_stashes.into_iter().flat_map(|nom| { + balance_slash(nom.clone()).map(Into::into).chain(slash(nom).map(Into::into)).map(Box::new) + }); + + let events = chill(offender.stash.clone()).map(Into::into).map(Box::new) + .chain(slash_report(offender.stash.clone()).map(Into::into).map(Box::new)) + .chain(balance_slash(offender.stash.clone()).map(Into::into).map(Box::new)) + .chain(slash(offender.stash).map(Into::into).map(Box::new)) + .chain(nom_slashes) .collect::>(); - events.append(&mut reward_events); - events.into_iter() - } else { - let mut reward_events = reporters.clone().into_iter() - .map(|reporter| balance_deposit(reporter, reward).into()) - .collect::>(); - events.append(&mut reward_events); - events.into_iter() - } - }) - .collect::>(); - + // the first deposit creates endowed events, see `endowed_reward_events` + if first { + first = false; + let reward_events = reporters.iter() + .flat_map(|reporter| vec![ + Box::new(balance_deposit(reporter.clone(), reward).into()), + Box::new(frame_system::Event::::NewAccount { account: reporter.clone() }.into()), + Box::new(::RuntimeEvent::from( + pallet_balances::Event::::Endowed{ account: reporter.clone(), free_balance: reward.into() } + ).into()), + ]) + .collect::>(); + events.into_iter().chain(reward_events) + } else { + let reward_events = reporters.iter() + .map(|reporter| Box::new(balance_deposit(reporter.clone(), reward).into())) + .collect::>(); + events.into_iter().chain(reward_events) + } + }); - #[cfg(test)] - { // In case of error it's useful to see the inputs - println!("Inputs: r: {}, o: {}, n: {}", r, o, n); + log::info!("Inputs: r: {}, o: {}, n: {}", r, o, n); // make sure that all slashes have been applied - check_events::( - std::iter::empty() - .chain(slash_events.into_iter().map(Into::into)) - .chain(std::iter::once(::RuntimeEvent::from( + check_events::( + sp_std::iter::empty() + .chain(slash_events) + .chain(sp_std::iter::once(Box::new(::RuntimeEvent::from( pallet_offences::Event::Offence{ kind: UnresponsivenessOffence::::ID, timeslot: 0_u32.to_le_bytes().to_vec(), } - ).into())) + ).into()))) ); } } diff --git a/frame/preimage/Cargo.toml b/frame/preimage/Cargo.toml index def39d61d5175..6bc6c8c53c012 100644 --- a/frame/preimage/Cargo.toml +++ b/frame/preimage/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/paritytech/substrate/" description = "FRAME pallet for storing preimages of hashes" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/proxy/Cargo.toml b/frame/proxy/Cargo.toml index 1674e408668d2..5c69fcfb9f55e 100644 --- a/frame/proxy/Cargo.toml +++ b/frame/proxy/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["max-encoded-len"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/randomness-collective-flip/Cargo.toml b/frame/randomness-collective-flip/Cargo.toml index 5a20949e9f243..435c38d632b69 100644 --- a/frame/randomness-collective-flip/Cargo.toml +++ b/frame/randomness-collective-flip/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } safe-mix = { version = "1.0", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/ranked-collective/Cargo.toml b/frame/ranked-collective/Cargo.toml index c5e79eb68f24d..e19aaa4439716 100644 --- a/frame/ranked-collective/Cargo.toml +++ b/frame/ranked-collective/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.16", default-features = false } scale-info = { version = "2.0.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/recovery/Cargo.toml b/frame/recovery/Cargo.toml index cdcebbec161bc..3c25c0002b709 100644 --- a/frame/recovery/Cargo.toml +++ b/frame/recovery/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/referenda/Cargo.toml b/frame/referenda/Cargo.toml index 02894e1499d93..ac4c8044da359 100644 --- a/frame/referenda/Cargo.toml +++ b/frame/referenda/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] assert_matches = { version = "1.5", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.3", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/remark/Cargo.toml b/frame/remark/Cargo.toml index a827d165f8389..151cc38884490 100644 --- a/frame/remark/Cargo.toml +++ b/frame/remark/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/root-offences/Cargo.toml b/frame/root-offences/Cargo.toml index 76eb832c88e1b..fd1f7c6325ea2 100644 --- a/frame/root-offences/Cargo.toml +++ b/frame/root-offences/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } pallet-session = { version = "4.0.0-dev", features = [ "historical" ], path = "../../frame/session", default-features = false } diff --git a/frame/root-testing/Cargo.toml b/frame/root-testing/Cargo.toml index 4d3f70c5d0d9f..09262b3d31db2 100644 --- a/frame/root-testing/Cargo.toml +++ b/frame/root-testing/Cargo.toml @@ -14,7 +14,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/scheduler/Cargo.toml b/frame/scheduler/Cargo.toml index 25ac602681cc0..9a67e68a01640 100644 --- a/frame/scheduler/Cargo.toml +++ b/frame/scheduler/Cargo.toml @@ -10,7 +10,7 @@ description = "FRAME Scheduler pallet" readme = "README.md" [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/scored-pool/Cargo.toml b/frame/scored-pool/Cargo.toml index a1e8dc453df6c..d44fc1b2f1548 100644 --- a/frame/scored-pool/Cargo.toml +++ b/frame/scored-pool/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/session/Cargo.toml b/frame/session/Cargo.toml index 57b519e81e59b..bd28bffffd8d9 100644 --- a/frame/session/Cargo.toml +++ b/frame/session/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/session/benchmarking/Cargo.toml b/frame/session/benchmarking/Cargo.toml index 53d6ff0cd18a0..4d1d9b4eda491 100644 --- a/frame/session/benchmarking/Cargo.toml +++ b/frame/session/benchmarking/Cargo.toml @@ -24,7 +24,7 @@ sp-session = { version = "4.0.0-dev", default-features = false, path = "../../.. sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } [dev-dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } scale-info = "2.1.1" frame-election-provider-support = { version = "4.0.0-dev", path = "../../election-provider-support" } pallet-balances = { version = "4.0.0-dev", path = "../../balances" } diff --git a/frame/society/Cargo.toml b/frame/society/Cargo.toml index 40b78c8922299..ddc6ea6aac7d7 100644 --- a/frame/society/Cargo.toml +++ b/frame/society/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } rand_chacha = { version = "0.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/staking/Cargo.toml b/frame/staking/Cargo.toml index f6b3b95d0beb9..3d5cf1161e8e5 100644 --- a/frame/staking/Cargo.toml +++ b/frame/staking/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.136", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/state-trie-migration/Cargo.toml b/frame/state-trie-migration/Cargo.toml index 90c8f426d0e10..36b5912a60302 100644 --- a/frame/state-trie-migration/Cargo.toml +++ b/frame/state-trie-migration/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME pallet migration of trie" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.133", optional = true } diff --git a/frame/sudo/Cargo.toml b/frame/sudo/Cargo.toml index b0e38b0139c11..56d04b172c268 100644 --- a/frame/sudo/Cargo.toml +++ b/frame/sudo/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } diff --git a/frame/support/Cargo.toml b/frame/support/Cargo.toml index 4945b5ab915f9..ae2fc358974b6 100644 --- a/frame/support/Cargo.toml +++ b/frame/support/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.136", optional = true, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-metadata = { version = "15.0.0", default-features = false, features = ["v14"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } @@ -27,6 +27,7 @@ sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../pri sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../primitives/inherents" } sp-staking = { version = "4.0.0-dev", default-features = false, path = "../../primitives/staking" } sp-weights = { version = "4.0.0", default-features = false, path = "../../primitives/weights" } +static_assertions = "1.1.0" tt-call = "1.0.8" frame-support-procedural = { version = "4.0.0-dev", default-features = false, path = "./procedural" } paste = "1.0" diff --git a/frame/support/procedural/Cargo.toml b/frame/support/procedural/Cargo.toml index 06b8056aff982..ee1ca4dff8873 100644 --- a/frame/support/procedural/Cargo.toml +++ b/frame/support/procedural/Cargo.toml @@ -15,6 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] +derive-syn-parse = "0.1.5" Inflector = "0.11.4" cfg-expr = "0.10.3" itertools = "0.10.3" diff --git a/frame/support/procedural/src/benchmark.rs b/frame/support/procedural/src/benchmark.rs new file mode 100644 index 0000000000000..43e3e47de5259 --- /dev/null +++ b/frame/support/procedural/src/benchmark.rs @@ -0,0 +1,860 @@ +// This file is part of Substrate. + +// Copyright (C) 2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Home of the parsing and expansion code for the new pallet benchmarking syntax + +use derive_syn_parse::Parse; +use frame_support_procedural_tools::generate_crate_access_2018; +use proc_macro::TokenStream; +use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; +use quote::{quote, quote_spanned, ToTokens}; +use syn::{ + parenthesized, + parse::{Nothing, ParseStream}, + punctuated::Punctuated, + spanned::Spanned, + token::{Colon2, Comma, Gt, Lt, Paren}, + Attribute, Error, Expr, ExprBlock, ExprCall, ExprPath, FnArg, Item, ItemFn, ItemMod, LitInt, + Pat, Path, PathArguments, PathSegment, Result, Stmt, Token, Type, WhereClause, +}; + +mod keywords { + use syn::custom_keyword; + + custom_keyword!(benchmark); + custom_keyword!(benchmarks); + custom_keyword!(block); + custom_keyword!(extra); + custom_keyword!(extrinsic_call); + custom_keyword!(skip_meta); +} + +/// This represents the raw parsed data for a param definition such as `x: Linear<10, 20>`. +#[derive(Clone)] +struct ParamDef { + name: String, + typ: Type, + start: u32, + end: u32, +} + +/// Allows easy parsing of the `<10, 20>` component of `x: Linear<10, 20>`. +#[derive(Parse)] +struct RangeArgs { + _lt_token: Lt, + start: LitInt, + _comma: Comma, + end: LitInt, + _gt_token: Gt, +} + +#[derive(Clone, Debug)] +struct BenchmarkAttrs { + skip_meta: bool, + extra: bool, +} + +/// Represents a single benchmark option +enum BenchmarkAttrKeyword { + Extra, + SkipMeta, +} + +impl syn::parse::Parse for BenchmarkAttrKeyword { + fn parse(input: ParseStream) -> Result { + let lookahead = input.lookahead1(); + if lookahead.peek(keywords::extra) { + let _extra: keywords::extra = input.parse()?; + return Ok(BenchmarkAttrKeyword::Extra) + } else if lookahead.peek(keywords::skip_meta) { + let _skip_meta: keywords::skip_meta = input.parse()?; + return Ok(BenchmarkAttrKeyword::SkipMeta) + } else { + return Err(lookahead.error()) + } + } +} + +impl syn::parse::Parse for BenchmarkAttrs { + fn parse(input: ParseStream) -> syn::Result { + let lookahead = input.lookahead1(); + if !lookahead.peek(Paren) { + let _nothing: Nothing = input.parse()?; + return Ok(BenchmarkAttrs { skip_meta: false, extra: false }) + } + let content; + let _paren: Paren = parenthesized!(content in input); + let mut extra = false; + let mut skip_meta = false; + let args = Punctuated::::parse_terminated(&content)?; + for arg in args.into_iter() { + match arg { + BenchmarkAttrKeyword::Extra => { + if extra { + return Err(content.error("`extra` can only be specified once")) + } + extra = true; + }, + BenchmarkAttrKeyword::SkipMeta => { + if skip_meta { + return Err(content.error("`skip_meta` can only be specified once")) + } + skip_meta = true; + }, + } + } + Ok(BenchmarkAttrs { extra, skip_meta }) + } +} + +/// Represents the parsed extrinsic call for a benchmark +#[derive(Clone)] +enum BenchmarkCallDef { + ExtrinsicCall { origin: Expr, expr_call: ExprCall, attr_span: Span }, // #[extrinsic_call] + Block { block: ExprBlock, attr_span: Span }, // #[block] +} + +impl BenchmarkCallDef { + /// Returns the `span()` for attribute + fn attr_span(&self) -> Span { + match self { + BenchmarkCallDef::ExtrinsicCall { origin: _, expr_call: _, attr_span } => *attr_span, + BenchmarkCallDef::Block { block: _, attr_span } => *attr_span, + } + } +} + +/// Represents a parsed `#[benchmark]` or `#[instance_banchmark]` item. +#[derive(Clone)] +struct BenchmarkDef { + params: Vec, + setup_stmts: Vec, + call_def: BenchmarkCallDef, + verify_stmts: Vec, + extra: bool, + skip_meta: bool, +} + +impl BenchmarkDef { + /// Constructs a [`BenchmarkDef`] by traversing an existing [`ItemFn`] node. + pub fn from(item_fn: &ItemFn, extra: bool, skip_meta: bool) -> Result { + let mut params: Vec = Vec::new(); + + // parse params such as "x: Linear<0, 1>" + for arg in &item_fn.sig.inputs { + let invalid_param = |span| { + return Err(Error::new(span, "Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`.", )) + }; + + let FnArg::Typed(arg) = arg else { return invalid_param(arg.span()) }; + let Pat::Ident(ident) = &*arg.pat else { return invalid_param(arg.span()) }; + + // check param name + let var_span = ident.span(); + let invalid_param_name = || { + return Err(Error::new( + var_span, + "Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters.", + )) + }; + let name = ident.ident.to_token_stream().to_string(); + if name.len() > 1 { + return invalid_param_name() + }; + let Some(name_char) = name.chars().next() else { return invalid_param_name() }; + if !name_char.is_alphabetic() || !name_char.is_lowercase() { + return invalid_param_name() + } + + // parse type + let typ = &*arg.ty; + let Type::Path(tpath) = typ else { return invalid_param(typ.span()) }; + let Some(segment) = tpath.path.segments.last() else { return invalid_param(typ.span()) }; + let args = segment.arguments.to_token_stream().into(); + let Ok(args) = syn::parse::(args) else { return invalid_param(typ.span()) }; + let Ok(start) = args.start.base10_parse::() else { return invalid_param(args.start.span()) }; + let Ok(end) = args.end.base10_parse::() else { return invalid_param(args.end.span()) }; + + if end < start { + return Err(Error::new( + args.start.span(), + "The start of a `ParamRange` must be less than or equal to the end", + )) + } + + params.push(ParamDef { name, typ: typ.clone(), start, end }); + } + + // #[extrinsic_call] / #[block] handling + let call_defs = item_fn.block.stmts.iter().enumerate().filter_map(|(i, child)| { + if let Stmt::Semi(Expr::Call(expr_call), _semi) = child { + // #[extrinsic_call] case + expr_call.attrs.iter().enumerate().find_map(|(k, attr)| { + let segment = attr.path.segments.last()?; + let _: keywords::extrinsic_call = syn::parse(segment.ident.to_token_stream().into()).ok()?; + let mut expr_call = expr_call.clone(); + + // consume #[extrinsic_call] tokens + expr_call.attrs.remove(k); + + // extract origin from expr_call + let Some(origin) = expr_call.args.first().cloned() else { + return Some(Err(Error::new(expr_call.span(), "Single-item extrinsic calls must specify their origin as the first argument."))) + }; + + Some(Ok((i, BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: attr.span() }))) + }) + } else if let Stmt::Expr(Expr::Block(block)) = child { + // #[block] case + block.attrs.iter().enumerate().find_map(|(k, attr)| { + let segment = attr.path.segments.last()?; + let _: keywords::block = syn::parse(segment.ident.to_token_stream().into()).ok()?; + let mut block = block.clone(); + + // consume #[block] tokens + block.attrs.remove(k); + + Some(Ok((i, BenchmarkCallDef::Block { block, attr_span: attr.span() }))) + }) + } else { + None + } + }).collect::>>()?; + let (i, call_def) = match &call_defs[..] { + [(i, call_def)] => (*i, call_def.clone()), // = 1 + [] => return Err(Error::new( // = 0 + item_fn.block.brace_token.span, + "No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body." + )), + _ => return Err(Error::new( // > 1 + call_defs[1].1.attr_span(), + "Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark." + )), + }; + + Ok(BenchmarkDef { + params, + setup_stmts: Vec::from(&item_fn.block.stmts[0..i]), + call_def, + verify_stmts: Vec::from(&item_fn.block.stmts[(i + 1)..item_fn.block.stmts.len()]), + extra, + skip_meta, + }) + } +} + +/// Parses and expands a `#[benchmarks]` or `#[instance_benchmarks]` invocation +pub fn benchmarks( + attrs: TokenStream, + tokens: TokenStream, + instance: bool, +) -> syn::Result { + // gather module info + let module: ItemMod = syn::parse(tokens)?; + let mod_span = module.span(); + let where_clause = match syn::parse::(attrs.clone()) { + Ok(_) => quote!(), + Err(_) => syn::parse::(attrs)?.predicates.to_token_stream(), + }; + let mod_vis = module.vis; + let mod_name = module.ident; + + // consume #[benchmarks] attribute by exclusing it from mod_attrs + let mod_attrs: Vec<&Attribute> = module + .attrs + .iter() + .filter(|attr| syn::parse2::(attr.to_token_stream()).is_err()) + .collect(); + + let mut benchmark_names: Vec = Vec::new(); + let mut extra_benchmark_names: Vec = Vec::new(); + let mut skip_meta_benchmark_names: Vec = Vec::new(); + + let (_brace, mut content) = + module.content.ok_or(syn::Error::new(mod_span, "Module cannot be empty!"))?; + + // find all function defs marked with #[benchmark] + let benchmark_fn_metas = content.iter_mut().filter_map(|stmt| { + // parse as a function def first + let Item::Fn(func) = stmt else { return None }; + + // find #[benchmark] attribute on function def + let benchmark_attr = func.attrs.iter().find_map(|attr| { + let seg = attr.path.segments.last()?; + syn::parse::(seg.ident.to_token_stream().into()).ok()?; + Some(attr) + })?; + + Some((benchmark_attr.clone(), func.clone(), stmt)) + }); + + // parse individual benchmark defs and args + for (benchmark_attr, func, stmt) in benchmark_fn_metas { + // parse any args provided to #[benchmark] + let attr_tokens = benchmark_attr.tokens.to_token_stream().into(); + let benchmark_args: BenchmarkAttrs = syn::parse(attr_tokens)?; + + // parse benchmark def + let benchmark_def = + BenchmarkDef::from(&func, benchmark_args.extra, benchmark_args.skip_meta)?; + + // record benchmark name + let name = &func.sig.ident; + benchmark_names.push(name.clone()); + + // record name sets + if benchmark_def.extra { + extra_benchmark_names.push(name.clone()); + } + if benchmark_def.skip_meta { + skip_meta_benchmark_names.push(name.clone()) + } + + // expand benchmark + let expanded = expand_benchmark(benchmark_def, name, instance, where_clause.clone()); + + // replace original function def with expanded code + *stmt = Item::Verbatim(expanded); + } + + // generics + let type_use_generics = match instance { + false => quote!(T), + true => quote!(T, I), + }; + let type_impl_generics = match instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + let krate = generate_crate_access_2018("frame-benchmarking")?; + let support = quote!(#krate::frame_support); + + // benchmark name variables + let benchmark_names_str: Vec = benchmark_names.iter().map(|n| n.to_string()).collect(); + let extra_benchmark_names_str: Vec = + extra_benchmark_names.iter().map(|n| n.to_string()).collect(); + let skip_meta_benchmark_names_str: Vec = + skip_meta_benchmark_names.iter().map(|n| n.to_string()).collect(); + let mut selected_benchmark_mappings: Vec = Vec::new(); + let mut benchmarks_by_name_mappings: Vec = Vec::new(); + let test_idents: Vec = benchmark_names_str + .iter() + .map(|n| Ident::new(format!("test_{}", n).as_str(), Span::call_site())) + .collect(); + for i in 0..benchmark_names.len() { + let name_ident = &benchmark_names[i]; + let name_str = &benchmark_names_str[i]; + let test_ident = &test_idents[i]; + selected_benchmark_mappings.push(quote!(#name_str => SelectedBenchmark::#name_ident)); + benchmarks_by_name_mappings.push(quote!(#name_str => Self::#test_ident())) + } + + // emit final quoted tokens + let res = quote! { + #(#mod_attrs) + * + #mod_vis mod #mod_name { + #(#content) + * + + #[allow(non_camel_case_types)] + enum SelectedBenchmark { + #(#benchmark_names), + * + } + + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> for SelectedBenchmark where #where_clause { + fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup<#type_use_generics>>::components(&#benchmark_names) + } + ) + * + } + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool, + ) -> Result< + #krate::Box Result<(), #krate::BenchmarkError>>, + #krate::BenchmarkError, + > { + match self { + #( + Self::#benchmark_names => { + <#benchmark_names as #krate::BenchmarkingSetup< + #type_use_generics + >>::instance(&#benchmark_names, components, verify) + } + ) + * + } + } + } + #[cfg(any(feature = "runtime-benchmarks", test))] + impl<#type_impl_generics> #krate::Benchmarking for Pallet<#type_use_generics> + where T: frame_system::Config, #where_clause + { + fn benchmarks( + extra: bool, + ) -> #krate::Vec<#krate::BenchmarkMetadata> { + let mut all_names = #krate::vec![ + #(#benchmark_names_str), + * + ]; + if !extra { + let extra = [ + #(#extra_benchmark_names_str), + * + ]; + all_names.retain(|x| !extra.contains(x)); + } + all_names.into_iter().map(|benchmark| { + let selected_benchmark = match benchmark { + #(#selected_benchmark_mappings), + *, + _ => panic!("all benchmarks should be selectable") + }; + let components = >::components(&selected_benchmark); + #krate::BenchmarkMetadata { + name: benchmark.as_bytes().to_vec(), + components, + } + }).collect::<#krate::Vec<_>>() + } + + fn run_benchmark( + extrinsic: &[u8], + c: &[(#krate::BenchmarkParameter, u32)], + whitelist: &[#krate::TrackedStorageKey], + verify: bool, + internal_repeats: u32, + ) -> Result<#krate::Vec<#krate::BenchmarkResult>, #krate::BenchmarkError> { + let extrinsic = #krate::str::from_utf8(extrinsic).map_err(|_| "`extrinsic` is not a valid utf-8 string!")?; + let selected_benchmark = match extrinsic { + #(#selected_benchmark_mappings), + *, + _ => return Err("Could not find extrinsic.".into()), + }; + let mut whitelist = whitelist.to_vec(); + let whitelisted_caller_key = as #support::storage::StorageMap<_, _,>>::hashed_key_for( + #krate::whitelisted_caller::() + ); + whitelist.push(whitelisted_caller_key.into()); + let transactional_layer_key = #krate::TrackedStorageKey::new( + #support::storage::transactional::TRANSACTION_LEVEL_KEY.into(), + ); + whitelist.push(transactional_layer_key); + #krate::benchmarking::set_whitelist(whitelist); + let mut results: #krate::Vec<#krate::BenchmarkResult> = #krate::Vec::new(); + + // Always do at least one internal repeat... + for _ in 0 .. internal_repeats.max(1) { + // Always reset the state after the benchmark. + #krate::defer!(#krate::benchmarking::wipe_db()); + + // Set up the externalities environment for the setup we want to + // benchmark. + let closure_to_benchmark = < + SelectedBenchmark as #krate::BenchmarkingSetup<#type_use_generics> + >::instance(&selected_benchmark, c, verify)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::Zero::is_zero(&frame_system::Pallet::::block_number()) { + frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Commit the externalities to the database, flushing the DB cache. + // This will enable worst case scenario for reading from the database. + #krate::benchmarking::commit_db(); + + // Reset the read/write counter so we don't count operations in the setup process. + #krate::benchmarking::reset_read_write_count(); + + // Time the extrinsic logic. + #krate::log::trace!( + target: "benchmark", + "Start Benchmark: {} ({:?})", + extrinsic, + c + ); + + let start_pov = #krate::benchmarking::proof_size(); + let start_extrinsic = #krate::benchmarking::current_time(); + + closure_to_benchmark()?; + + let finish_extrinsic = #krate::benchmarking::current_time(); + let end_pov = #krate::benchmarking::proof_size(); + + // Calculate the diff caused by the benchmark. + let elapsed_extrinsic = finish_extrinsic.saturating_sub(start_extrinsic); + let diff_pov = match (start_pov, end_pov) { + (Some(start), Some(end)) => end.saturating_sub(start), + _ => Default::default(), + }; + + // Commit the changes to get proper write count + #krate::benchmarking::commit_db(); + #krate::log::trace!( + target: "benchmark", + "End Benchmark: {} ns", elapsed_extrinsic + ); + let read_write_count = #krate::benchmarking::read_write_count(); + #krate::log::trace!( + target: "benchmark", + "Read/Write Count {:?}", read_write_count + ); + + // Time the storage root recalculation. + let start_storage_root = #krate::benchmarking::current_time(); + #krate::storage_root(#krate::StateVersion::V1); + let finish_storage_root = #krate::benchmarking::current_time(); + let elapsed_storage_root = finish_storage_root - start_storage_root; + + let skip_meta = [ #(#skip_meta_benchmark_names_str),* ]; + let read_and_written_keys = if skip_meta.contains(&extrinsic) { + #krate::vec![(b"Skipped Metadata".to_vec(), 0, 0, false)] + } else { + #krate::benchmarking::get_read_and_written_keys() + }; + + results.push(#krate::BenchmarkResult { + components: c.to_vec(), + extrinsic_time: elapsed_extrinsic, + storage_root_time: elapsed_storage_root, + reads: read_write_count.0, + repeat_reads: read_write_count.1, + writes: read_write_count.2, + repeat_writes: read_write_count.3, + proof_size: diff_pov, + keys: read_and_written_keys, + }); + } + + return Ok(results); + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: ::frame_system::Config, #where_clause { + /// Test a particular benchmark by name. + /// + /// This isn't called `test_benchmark_by_name` just in case some end-user eventually + /// writes a benchmark, itself called `by_name`; the function would be shadowed in + /// that case. + /// + /// This is generally intended to be used by child test modules such as those created + /// by the `impl_benchmark_test_suite` macro. However, it is not an error if a pallet + /// author chooses not to implement benchmarks. + #[allow(unused)] + fn test_bench_by_name(name: &[u8]) -> Result<(), #krate::BenchmarkError> { + let name = #krate::str::from_utf8(name) + .map_err(|_| -> #krate::BenchmarkError { "`name` is not a valid utf8 string!".into() })?; + match name { + #(#benchmarks_by_name_mappings), + *, + _ => Err("Could not find test for requested benchmark.".into()), + } + } + } + } + #mod_vis use #mod_name::*; + }; + Ok(res.into()) +} + +/// Prepares a [`Vec`] to be interpolated by [`quote!`] by creating easily-iterable +/// arrays formatted in such a way that they can be interpolated directly. +struct UnrolledParams { + param_ranges: Vec, + param_names: Vec, + param_types: Vec, +} + +impl UnrolledParams { + /// Constructs an [`UnrolledParams`] from a [`Vec`] + fn from(params: &Vec) -> UnrolledParams { + let param_ranges: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + let start = p.start; + let end = p.end; + quote!(#name, #start, #end) + }) + .collect(); + let param_names: Vec = params + .iter() + .map(|p| { + let name = Ident::new(&p.name, Span::call_site()); + quote!(#name) + }) + .collect(); + let param_types: Vec = params + .iter() + .map(|p| { + let typ = &p.typ; + quote!(#typ) + }) + .collect(); + UnrolledParams { param_ranges, param_names, param_types } + } +} + +/// Performs expansion of an already-parsed [`BenchmarkDef`]. +fn expand_benchmark( + benchmark_def: BenchmarkDef, + name: &Ident, + is_instance: bool, + where_clause: TokenStream2, +) -> TokenStream2 { + // set up variables needed during quoting + let krate = match generate_crate_access_2018("frame-benchmarking") { + Ok(ident) => ident, + Err(err) => return err.to_compile_error().into(), + }; + let home = quote!(#krate::frame_support::benchmarking); + let codec = quote!(#krate::frame_support::codec); + let traits = quote!(#krate::frame_support::traits); + let setup_stmts = benchmark_def.setup_stmts; + let verify_stmts = benchmark_def.verify_stmts; + let test_ident = Ident::new(format!("test_{}", name.to_string()).as_str(), Span::call_site()); + + // unroll params (prepare for quoting) + let unrolled = UnrolledParams::from(&benchmark_def.params); + let param_names = unrolled.param_names; + let param_ranges = unrolled.param_ranges; + let param_types = unrolled.param_types; + + let type_use_generics = match is_instance { + false => quote!(T), + true => quote!(T, I), + }; + + let type_impl_generics = match is_instance { + false => quote!(T: Config), + true => quote!(T: Config, I: 'static), + }; + + let (pre_call, post_call) = match benchmark_def.call_def { + BenchmarkCallDef::ExtrinsicCall { origin, expr_call, attr_span: _ } => { + let mut expr_call = expr_call.clone(); + + // remove first arg from expr_call + let mut final_args = Punctuated::::new(); + let args: Vec<&Expr> = expr_call.args.iter().collect(); + for arg in &args[1..] { + final_args.push((*(*arg)).clone()); + } + expr_call.args = final_args; + + // determine call name (handles `_` and normal call syntax) + let expr_span = expr_call.span(); + let call_err = || { + quote_spanned!(expr_span=> "Extrinsic call must be a function call or `_`".to_compile_error()).into() + }; + let call_name = match *expr_call.func { + Expr::Path(expr_path) => { + // normal function call + let Some(segment) = expr_path.path.segments.last() else { return call_err(); }; + segment.ident.to_string() + }, + Expr::Verbatim(tokens) => { + // `_` style + // replace `_` with fn name + let Ok(_) = syn::parse::(tokens.to_token_stream().into()) else { return call_err(); }; + name.to_string() + }, + _ => return call_err(), + }; + + // modify extrinsic call to be prefixed with "new_call_variant" + let call_name = format!("new_call_variant_{}", call_name); + let mut punct: Punctuated = Punctuated::new(); + punct.push(PathSegment { + arguments: PathArguments::None, + ident: Ident::new(call_name.as_str(), Span::call_site()), + }); + *expr_call.func = Expr::Path(ExprPath { + attrs: vec![], + qself: None, + path: Path { leading_colon: None, segments: punct }, + }); + + ( + // (pre_call, post_call): + quote! { + let __call = Call::<#type_use_generics>::#expr_call; + let __benchmarked_call_encoded = #codec::Encode::encode(&__call); + }, + quote! { + let __call_decoded = as #codec::Decode> + ::decode(&mut &__benchmarked_call_encoded[..]) + .expect("call is encoded above, encoding must be correct"); + let __origin = #origin.into(); + as #traits::UnfilteredDispatchable>::dispatch_bypass_filter( + __call_decoded, + __origin, + )?; + }, + ) + }, + BenchmarkCallDef::Block { block, attr_span: _ } => (quote!(), quote!(#block)), + }; + + // generate final quoted tokens + let res = quote! { + // compile-time assertions that each referenced param type implements ParamRange + #( + #home::assert_impl_all!(#param_types: #home::ParamRange); + )* + + #[allow(non_camel_case_types)] + struct #name; + + #[allow(unused_variables)] + impl<#type_impl_generics> #krate::BenchmarkingSetup<#type_use_generics> + for #name where #where_clause { + fn components(&self) -> #krate::Vec<(#krate::BenchmarkParameter, u32, u32)> { + #krate::vec! [ + #( + (#krate::BenchmarkParameter::#param_ranges) + ),* + ] + } + + fn instance( + &self, + components: &[(#krate::BenchmarkParameter, u32)], + verify: bool + ) -> Result<#krate::Box Result<(), #krate::BenchmarkError>>, #krate::BenchmarkError> { + #( + // prepare instance #param_names + let #param_names = components.iter() + .find(|&c| c.0 == #krate::BenchmarkParameter::#param_names) + .ok_or("Could not find component during benchmark preparation.")? + .1; + )* + + // benchmark setup code + #( + #setup_stmts + )* + #pre_call + Ok(#krate::Box::new(move || -> Result<(), #krate::BenchmarkError> { + #post_call + if verify { + #( + #verify_stmts + )* + } + Ok(()) + })) + } + } + + #[cfg(test)] + impl<#type_impl_generics> Pallet<#type_use_generics> where T: ::frame_system::Config, #where_clause { + #[allow(unused)] + fn #test_ident() -> Result<(), #krate::BenchmarkError> { + let selected_benchmark = SelectedBenchmark::#name; + let components = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::components(&selected_benchmark); + let execute_benchmark = | + c: #krate::Vec<(#krate::BenchmarkParameter, u32)> + | -> Result<(), #krate::BenchmarkError> { + // Always reset the state after the benchmark. + #krate::defer!(#krate::benchmarking::wipe_db()); + + // Set up the benchmark, return execution + verification function. + let closure_to_verify = < + SelectedBenchmark as #krate::BenchmarkingSetup + >::instance(&selected_benchmark, &c, true)?; + + // Set the block number to at least 1 so events are deposited. + if #krate::Zero::is_zero(&frame_system::Pallet::::block_number()) { + frame_system::Pallet::::set_block_number(1u32.into()); + } + + // Run execution + verification + closure_to_verify() + }; + + if components.is_empty() { + execute_benchmark(Default::default())?; + } else { + let num_values: u32 = if let Ok(ev) = std::env::var("VALUES_PER_COMPONENT") { + ev.parse().map_err(|_| { + #krate::BenchmarkError::Stop( + "Could not parse env var `VALUES_PER_COMPONENT` as u32." + ) + })? + } else { + 6 + }; + + if num_values < 2 { + return Err("`VALUES_PER_COMPONENT` must be at least 2".into()); + } + + for (name, low, high) in components.clone().into_iter() { + // Test the lowest, highest (if its different from the lowest) + // and up to num_values-2 more equidistant values in between. + // For 0..10 and num_values=6 this would mean: [0, 2, 4, 6, 8, 10] + + let mut values = #krate::vec![low]; + let diff = (high - low).min(num_values - 1); + let slope = (high - low) as f32 / diff as f32; + + for i in 1..=diff { + let value = ((low as f32 + slope * i as f32) as u32) + .clamp(low, high); + values.push(value); + } + + for component_value in values { + // Select the max value for all the other components. + let c: #krate::Vec<(#krate::BenchmarkParameter, u32)> = components + .iter() + .map(|(n, _, h)| + if *n == name { + (*n, component_value) + } else { + (*n, *h) + } + ) + .collect(); + + execute_benchmark(c)?; + } + } + } + return Ok(()); + } + } + }; + res +} diff --git a/frame/support/procedural/src/lib.rs b/frame/support/procedural/src/lib.rs index 41dbc4ee9592c..9bdbbd1f27bb2 100644 --- a/frame/support/procedural/src/lib.rs +++ b/frame/support/procedural/src/lib.rs @@ -19,6 +19,7 @@ #![recursion_limit = "512"] +mod benchmark; mod clone_no_bound; mod construct_runtime; mod crate_version; @@ -479,6 +480,69 @@ pub fn pallet(attr: TokenStream, item: TokenStream) -> TokenStream { pallet::pallet(attr, item) } +/// An attribute macro that can be attached to a (non-empty) module declaration. Doing so will +/// designate that module as a benchmarking module. +/// +/// See `frame_support::benchmarking` for more info. +#[proc_macro_attribute] +pub fn benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { + match benchmark::benchmarks(attr, tokens, false) { + Ok(tokens) => tokens, + Err(err) => err.to_compile_error().into(), + } +} + +/// An attribute macro that can be attached to a (non-empty) module declaration. Doing so will +/// designate that module as an instance benchmarking module. +/// +/// See `frame_support::benchmarking` for more info. +#[proc_macro_attribute] +pub fn instance_benchmarks(attr: TokenStream, tokens: TokenStream) -> TokenStream { + match benchmark::benchmarks(attr, tokens, true) { + Ok(tokens) => tokens, + Err(err) => err.to_compile_error().into(), + } +} + +/// An attribute macro used to declare a benchmark within a benchmarking module. Must be +/// attached to a function definition containing an `#[extrinsic_call]` or `#[block]` +/// attribute. +/// +/// See `frame_support::benchmarking` for more info. +#[proc_macro_attribute] +pub fn benchmark(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { + quote!(compile_error!( + "`#[benchmark]` must be in a module labeled with #[benchmarks] or #[instance_benchmarks]." + )) + .into() +} + +/// An attribute macro used to specify the extrinsic call inside a benchmark function, and also +/// used as a boundary designating where the benchmark setup code ends, and the benchmark +/// verification code begins. +/// +/// See `frame_support::benchmarking` for more info. +#[proc_macro_attribute] +pub fn extrinsic_call(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { + quote!(compile_error!( + "`#[extrinsic_call]` must be in a benchmark function definition labeled with `#[benchmark]`." + );) + .into() +} + +/// An attribute macro used to specify that a block should be the measured portion of the +/// enclosing benchmark function, This attribute is also used as a boundary designating where +/// the benchmark setup code ends, and the benchmark verification code begins. +/// +/// See `frame_support::benchmarking` for more info. +#[proc_macro_attribute] +pub fn block(_attrs: TokenStream, _tokens: TokenStream) -> TokenStream { + quote!(compile_error!( + "`#[block]` must be in a benchmark function definition labeled with `#[benchmark]`." + )) + .into() +} + /// Execute the annotated function in a new storage transaction. /// /// The return type of the annotated function must be `Result`. All changes to storage performed diff --git a/frame/support/src/lib.rs b/frame/support/src/lib.rs index 902893972f0b1..40bc878cff365 100644 --- a/frame/support/src/lib.rs +++ b/frame/support/src/lib.rs @@ -2745,5 +2745,228 @@ pub mod pallet_macros { }; } +/// Contains macros, structs, and traits associated with v2 of the pallet benchmarking syntax. +/// This module contains macros, structs, and traits associated with v2 of the pallet +/// benchmarking syntax. +/// +/// The [`benchmarking::benchmarks`] and [`benchmarking::instance_benchmarks`] macros can be +/// used to designate a module as a benchmarking module that can contain benchmarks and +/// benchmark tests. The `#[benchmarks]` variant will set up a regular, non-instance +/// benchmarking module, and the `#[instance_benchmarks]` variant will set up the module in +/// instance benchmarking mode. +/// +/// Benchmarking modules should be gated behind a `#[cfg(feature = "runtime-benchmarks")]` +/// feature gate to ensure benchmarking code that is only compiled when the +/// `runtime-benchmarks` feature is enabled is not referenced. +/// +/// The following is the general syntax for a benchmarks (or instance benchmarks) module: +/// +/// ## General Syntax +/// +/// ```ignore +/// #![cfg(feature = "runtime-benchmarks")] +/// +/// use super::{mock_helpers::*, Pallet as MyPallet}; +/// use frame_support::benchmarking::*; +/// use frame_benchmarking::whitelisted_caller; +/// +/// #[benchmarks] +/// mod benchmarks { +/// use super::*; +/// +/// #[benchmark] +/// fn bench_name_1(x: Linear<7, 1_000>, y: Linear<1_000, 100_0000>) { +/// // setup code +/// let z = x + y; +/// let caller = whitelisted_caller(); +/// +/// #[extrinsic_call] +/// extrinsic_name(SystemOrigin::Signed(caller), other, arguments); +/// +/// // verification code +/// assert_eq!(MyPallet::::my_var(), z); +/// } +/// +/// #[benchmark] +/// fn bench_name_2() { +/// // setup code +/// let caller = whitelisted_caller(); +/// +/// #[block] +/// { +/// something(some, thing); +/// my_extrinsic(RawOrigin::Signed(caller), some, argument); +/// something_else(foo, bar); +/// } +/// +/// // verification code +/// assert_eq!(MyPallet::::something(), 37); +/// } +/// } +/// ``` +/// +/// ## Benchmark Definitions +/// +/// Within a `#[benchmarks]` or `#[instance_benchmarks]` module, you can define individual +/// benchmarks using the `#[benchmark]` attribute, as shown in the example above. +/// +/// The `#[benchmark]` attribute expects a function definition with a blank return type and +/// zero or more arguments whose names are valid `frame_benchmarking::BenchmarkParamater` +/// parameters, such as `x`, `y`, `a`, `b`, etc., and whose param types must implement +/// [`benchmarking::ParamRange`]. At the moment the only valid type that implements +/// [`benchmarking::ParamRange`] is [`benchmarking::Linear`]. +/// +/// The valid syntax for defining a `Linear` is `Linear` where `A`, and `B` are +/// valid integer literals (that fit in a `u32`), such that `B` >= `A`. +/// +/// Note that the benchmark function definition does not actually expand as a function +/// definition, but rather is used to automatically create a number of impls and structs +/// required by the benchmarking engine. For this reason, the visibility of the function +/// definition as well as the return type are not used for any purpose and are discarded by the +/// expansion code. +/// +/// Also note that the `// setup code` and `// verification code` comments shown above are not +/// required and are included simply for demonstration purposes. +/// +/// ### `#[extrinsic_call]` and `#[block]` +/// +/// Within the benchmark function body, either an `#[extrinsic_call]` or a `#[block]` +/// annotation is required. These attributes should be attached to a block (shown in +/// `bench_name_2` above) or a one-line function call (shown in `bench_name_1` above, in `syn` +/// parlance this should be an `ExprCall`), respectively. +/// +/// The `#[block]` syntax is broad and will benchmark any code contained within the block the +/// attribute is attached to. If `#[block]` is attached to something other than a block, a +/// compiler error will be emitted. +/// +/// The one-line `#[extrinsic_call]` syntax must consist of a function call to an extrinsic, +/// where the first argument is the origin. If `#[extrinsic_call]` is attached to an item that +/// doesn't meet these requirements, a compiler error will be emitted. +/// +/// As a short-hand, you may substitute the name of the extrinsic call with `_`, such as the +/// following: +/// +/// ```ignore +/// #[extrinsic_call] +/// _(RawOrigin::Signed(whitelisted_caller()), 0u32.into(), 0); +/// ``` +/// +/// The underscore will be substituted with the name of the benchmark (i.e. the name of the +/// function in the benchmark function definition). +/// +/// Regardless of whether `#[extrinsic_call]` or `#[block]` is used, this attribute also serves +/// the purpose of designating the boundary between the setup code portion of the benchmark +/// (everything before the `#[extrinsic_call]` or `#[block]` attribute) and the verification +/// stage (everything after the item that the `#[extrinsic_call]` or `#[block]` attribute is +/// attached to). The setup code section should contain any code that needs to execute before +/// the measured portion of the benchmark executes. The verification section is where you can +/// perform assertions to verify that the extrinsic call (or whatever is happening in your +/// block, if you used the `#[block]` syntax) executed successfully. +/// +/// Note that neither `#[extrinsic_call]` nor `#[block]` are real attribute macros and are +/// instead consumed by the outer macro pattern as part of the enclosing benchmark function +/// definition. This is why we are able to use `#[extrinsic_call]` and `#[block]` within a +/// function definition even though this behavior has not been stabilized +/// yet—`#[extrinsic_call]` and `#[block]` are parsed and consumed as part of the benchmark +/// definition parsing code, so they never expand as their own attribute macros. +/// +/// ### Optional Attributes +/// +/// The keywords `extra` and `skip_meta` can be provided as optional arguments to the +/// `#[benchmark]` attribute, i.e. `#[benchmark(extra, skip_meta)]`. Including either of these +/// will enable the `extra` or `skip_meta` option, respectively. These options enable the same +/// behavior they did in the old benchmarking syntax in `frame_benchmarking`, namely: +/// +/// #### `extra` +/// +/// Specifies that this benchmark should not normally run. To run benchmarks marked with +/// `extra`, you will need to invoke the `frame-benchmarking-cli` with `--extra`. +/// +/// #### `skip_meta` +/// +/// Specifies that the benchmarking framework should not analyze the storage keys that +/// benchmarked code read or wrote. This useful to suppress the prints in the form of unknown +/// 0x… in case a storage key that does not have metadata. Note that this skips the analysis +/// of all accesses, not just ones without metadata. +/// +/// ## Where Clause +/// +/// Some pallets require a where clause specifying constraints on their generics to make +/// writing benchmarks feasible. To accomodate this situation, you can provide such a where +/// clause as the (only) argument to the `#[benchmarks]` or `#[instance_benchmarks]` attribute +/// macros. Below is an example of this taken from the `message-queue` pallet. +/// +/// ```ignore +/// #[benchmarks( +/// where +/// <::MessageProcessor as ProcessMessage>::Origin: From + PartialEq, +/// ::Size: From, +/// )] +/// mod benchmarks { +/// use super::*; +/// // ... +/// } +/// ``` +/// +/// ## Benchmark Tests +/// +/// Benchmark tests can be generated using the old syntax in `frame_benchmarking`, +/// including the `frame_benchmarking::impl_benchmark_test_suite` macro. +/// +/// An example is shown below (taken from the `message-queue` pallet's `benchmarking` module): +/// ```ignore +/// #[benchmarks] +/// mod benchmarks { +/// use super::*; +/// // ... +/// impl_benchmark_test_suite!( +/// MessageQueue, +/// crate::mock::new_test_ext::(), +/// crate::integration_test::Test +/// ); +/// } +/// ``` +pub mod benchmarking { + pub use frame_support_procedural::{ + benchmark, benchmarks, block, extrinsic_call, instance_benchmarks, + }; + + // Used in #[benchmark] implementation to ensure that benchmark function arguments + // implement [`ParamRange`]. + #[doc(hidden)] + pub use static_assertions::assert_impl_all; + + /// Used by the new benchmarking code to specify that a benchmarking variable is linear + /// over some specified range, i.e. `Linear<0, 1_000>` means that the corresponding variable + /// is allowed to range from `0` to `1000`, inclusive. + /// + /// See [`frame_support::benchmarking`] for more info. + pub struct Linear; + + /// Trait that must be implemented by all structs that can be used as parameter range types + /// in the new benchmarking code (i.e. `Linear<0, 1_000>`). Right now there is just + /// [`Linear`] but this could later be extended to support additional non-linear parameter + /// ranges. + /// + /// See [`frame_support::benchmarking`] for more info. + pub trait ParamRange { + /// Represents the (inclusive) starting number of this [`ParamRange`]. + fn start(&self) -> u32; + + /// Represents the (inclusive) ending number of this [`ParamRange`] + fn end(&self) -> u32; + } + + impl ParamRange for Linear { + fn start(&self) -> u32 { + return A + } + + fn end(&self) -> u32 { + return B + } + } +} + // Generate a macro that will enable/disable code based on `std` feature being active. sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $); diff --git a/frame/support/src/storage/types/counted_map.rs b/frame/support/src/storage/types/counted_map.rs index 8c19434767f49..3361c4093dce0 100644 --- a/frame/support/src/storage/types/counted_map.rs +++ b/frame/support/src/storage/types/counted_map.rs @@ -134,7 +134,10 @@ where /// Store or remove the value to be associated with `key` so that `get` returns the `query`. pub fn set>(key: KeyArg, q: QueryKind::Query) { - ::Map::set(key, q) + match QueryKind::from_query_to_optional_value(q) { + Some(v) => Self::insert(key, v), + None => Self::remove(key), + } } /// Swap the values of two keys. @@ -745,6 +748,22 @@ mod test { // Test initialize_counter. assert_eq!(A::initialize_counter(), 2); + + // Set non-existing. + A::set(30, 100); + + assert_eq!(A::contains_key(30), true); + assert_eq!(A::get(30), 100); + assert_eq!(A::try_get(30), Ok(100)); + assert_eq!(A::count(), 3); + + // Set existing. + A::set(30, 101); + + assert_eq!(A::contains_key(30), true); + assert_eq!(A::get(30), 101); + assert_eq!(A::try_get(30), Ok(101)); + assert_eq!(A::count(), 3); }) } @@ -976,6 +995,40 @@ mod test { // Test initialize_counter. assert_eq!(B::initialize_counter(), 2); + + // Set non-existing. + B::set(30, Some(100)); + + assert_eq!(B::contains_key(30), true); + assert_eq!(B::get(30), Some(100)); + assert_eq!(B::try_get(30), Ok(100)); + assert_eq!(B::count(), 3); + + // Set existing. + B::set(30, Some(101)); + + assert_eq!(B::contains_key(30), true); + assert_eq!(B::get(30), Some(101)); + assert_eq!(B::try_get(30), Ok(101)); + assert_eq!(B::count(), 3); + + // Unset existing. + B::set(30, None); + + assert_eq!(B::contains_key(30), false); + assert_eq!(B::get(30), None); + assert_eq!(B::try_get(30), Err(())); + + assert_eq!(B::count(), 2); + + // Unset non-existing. + B::set(31, None); + + assert_eq!(B::contains_key(31), false); + assert_eq!(B::get(31), None); + assert_eq!(B::try_get(31), Err(())); + + assert_eq!(B::count(), 2); }) } diff --git a/frame/support/src/storage/types/map.rs b/frame/support/src/storage/types/map.rs index 0f89e2378a55d..3e2b744d72f89 100644 --- a/frame/support/src/storage/types/map.rs +++ b/frame/support/src/storage/types/map.rs @@ -627,6 +627,48 @@ mod test { assert_eq!(AValueQueryWithAnOnEmpty::take(2), 97); assert_eq!(A::contains_key(2), false); + // Set non-existing. + B::set(30, 100); + + assert_eq!(B::contains_key(30), true); + assert_eq!(B::get(30), 100); + assert_eq!(B::try_get(30), Ok(100)); + + // Set existing. + B::set(30, 101); + + assert_eq!(B::contains_key(30), true); + assert_eq!(B::get(30), 101); + assert_eq!(B::try_get(30), Ok(101)); + + // Set non-existing. + A::set(30, Some(100)); + + assert_eq!(A::contains_key(30), true); + assert_eq!(A::get(30), Some(100)); + assert_eq!(A::try_get(30), Ok(100)); + + // Set existing. + A::set(30, Some(101)); + + assert_eq!(A::contains_key(30), true); + assert_eq!(A::get(30), Some(101)); + assert_eq!(A::try_get(30), Ok(101)); + + // Unset existing. + A::set(30, None); + + assert_eq!(A::contains_key(30), false); + assert_eq!(A::get(30), None); + assert_eq!(A::try_get(30), Err(())); + + // Unset non-existing. + A::set(31, None); + + assert_eq!(A::contains_key(31), false); + assert_eq!(A::get(31), None); + assert_eq!(A::try_get(31), Err(())); + B::insert(2, 10); assert_eq!(A::migrate_key::(2), Some(10)); assert_eq!(A::contains_key(2), true); diff --git a/frame/support/src/traits/tokens/fungible.rs b/frame/support/src/traits/tokens/fungible.rs index 05e109b870ec0..a36e63e717f94 100644 --- a/frame/support/src/traits/tokens/fungible.rs +++ b/frame/support/src/traits/tokens/fungible.rs @@ -78,6 +78,7 @@ pub trait Mutate: Inspect { /// returned and nothing is changed. If successful, the amount of tokens reduced is returned. fn burn_from(who: &AccountId, amount: Self::Balance) -> Result; + // TODO: Remove. /// Attempt to reduce the balance of `who` by as much as possible up to `amount`, and possibly /// slightly more due to minimum_balance requirements. If no decrease is possible then an `Err` /// is returned and nothing is changed. If successful, the amount of tokens reduced is returned. @@ -143,6 +144,7 @@ pub trait InspectHold: Inspect { fn can_hold(who: &AccountId, amount: Self::Balance) -> bool; } +// TODO: Introduce `HoldReason`. /// Trait for mutating a fungible asset which can be reserved. pub trait MutateHold: InspectHold + Transfer { /// Hold some funds in an account. @@ -160,6 +162,8 @@ pub trait MutateHold: InspectHold + Transfer { best_effort: bool, ) -> Result; + // TODO: Introduce repatriate_held + /// Transfer held funds into a destination account. /// /// If `on_hold` is `true`, then the destination account must already exist and the assets @@ -195,6 +199,7 @@ pub trait BalancedHold: Balanced + MutateHold { } impl + MutateHold> BalancedHold for T { + // TODO: This should be implemented properly, and `slash` should be removed. fn slash_held( who: &AccountId, amount: Self::Balance, diff --git a/frame/support/test/Cargo.toml b/frame/support/test/Cargo.toml index 7fe1bcb5674bb..90ef243eed6c6 100644 --- a/frame/support/test/Cargo.toml +++ b/frame/support/test/Cargo.toml @@ -13,12 +13,13 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] serde = { version = "1.0.136", default-features = false, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../../../primitives/arithmetic" } sp-io = { version = "7.0.0", path = "../../../primitives/io", default-features = false } sp-state-machine = { version = "0.13.0", optional = true, path = "../../../primitives/state-machine" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../" } +frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" } sp-core = { version = "7.0.0", default-features = false, path = "../../../primitives/core" } sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } @@ -36,6 +37,7 @@ std = [ "serde/std", "codec/std", "scale-info/std", + "frame-benchmarking/std", "frame-support/std", "frame-system/std", "sp-core/std", diff --git a/frame/support/test/compile_pass/Cargo.toml b/frame/support/test/compile_pass/Cargo.toml index ea22a735b3698..dd5e1b996db9c 100644 --- a/frame/support/test/compile_pass/Cargo.toml +++ b/frame/support/test/compile_pass/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../system" } diff --git a/frame/support/test/pallet/Cargo.toml b/frame/support/test/pallet/Cargo.toml index bf5febeb45441..135d0e64b8ff4 100644 --- a/frame/support/test/pallet/Cargo.toml +++ b/frame/support/test/pallet/Cargo.toml @@ -12,7 +12,7 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../../../system" } diff --git a/frame/support/test/tests/benchmark_ui.rs b/frame/support/test/tests/benchmark_ui.rs new file mode 100644 index 0000000000000..243030bd07fcd --- /dev/null +++ b/frame/support/test/tests/benchmark_ui.rs @@ -0,0 +1,36 @@ +// This file is part of Substrate. + +// Copyright (C) 2020-2023 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[rustversion::attr(not(stable), ignore)] +#[cfg(not(feature = "disable-ui-tests"))] +#[test] +fn benchmark_ui() { + // Only run the ui tests when `RUN_UI_TESTS` is set. + if std::env::var("RUN_UI_TESTS").is_err() { + return + } + + // As trybuild is using `cargo check`, we don't need the real WASM binaries. + std::env::set_var("SKIP_WASM_BUILD", "1"); + + // Deny all warnings since we emit warnings as part of a Pallet's UI. + std::env::set_var("RUSTFLAGS", "--deny warnings"); + + let t = trybuild::TestCases::new(); + t.compile_fail("tests/benchmark_ui/*.rs"); + t.pass("tests/benchmark_ui/pass/*.rs"); +} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name.rs b/frame/support/test/tests/benchmark_ui/bad_param_name.rs new file mode 100644 index 0000000000000..ad4db4f0a4ba4 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name.rs @@ -0,0 +1,18 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench(winton: Linear<1, 2>) { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name.stderr b/frame/support/test/tests/benchmark_ui/bad_param_name.stderr new file mode 100644 index 0000000000000..4e2d63a6b5030 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name.stderr @@ -0,0 +1,5 @@ +error: Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters. + --> tests/benchmark_ui/bad_param_name.rs:10:11 + | +10 | fn bench(winton: Linear<1, 2>) { + | ^^^^^^ diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs new file mode 100644 index 0000000000000..50e4dc6fd4609 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.rs @@ -0,0 +1,14 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + #[benchmark] + fn bench(xx: Linear<1, 2>) { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.stderr b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.stderr new file mode 100644 index 0000000000000..32f6bf8e47d09 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_too_long.stderr @@ -0,0 +1,5 @@ +error: Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters. + --> tests/benchmark_ui/bad_param_name_too_long.rs:8:11 + | +8 | fn bench(xx: Linear<1, 2>) { + | ^^ diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs new file mode 100644 index 0000000000000..0270582b3b85b --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.rs @@ -0,0 +1,14 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + #[benchmark] + fn bench(D: Linear<1, 2>) { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.stderr b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.stderr new file mode 100644 index 0000000000000..48dd41d3262d7 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_name_upper_case.stderr @@ -0,0 +1,5 @@ +error: Benchmark parameter names must consist of a single lowercase letter (a-z) and no other characters. + --> tests/benchmark_ui/bad_param_name_upper_case.rs:8:11 + | +8 | fn bench(D: Linear<1, 2>) { + | ^ diff --git a/frame/support/test/tests/benchmark_ui/bad_param_range.rs b/frame/support/test/tests/benchmark_ui/bad_param_range.rs new file mode 100644 index 0000000000000..aabb9fa7403a1 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_range.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench(x: Linear<3, 1>) { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_param_range.stderr b/frame/support/test/tests/benchmark_ui/bad_param_range.stderr new file mode 100644 index 0000000000000..1347af0a07b8e --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_param_range.stderr @@ -0,0 +1,5 @@ +error: The start of a `ParamRange` must be less than or equal to the end + --> tests/benchmark_ui/bad_param_range.rs:10:21 + | +10 | fn bench(x: Linear<3, 1>) { + | ^ diff --git a/frame/support/test/tests/benchmark_ui/bad_params.rs b/frame/support/test/tests/benchmark_ui/bad_params.rs new file mode 100644 index 0000000000000..a0c9236982c62 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_params.rs @@ -0,0 +1,18 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench(y: Linear<1, 2>, x: u32) { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/bad_params.stderr b/frame/support/test/tests/benchmark_ui/bad_params.stderr new file mode 100644 index 0000000000000..068eaedd531b9 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/bad_params.stderr @@ -0,0 +1,5 @@ +error: Invalid benchmark function param. A valid example would be `x: Linear<5, 10>`. + --> tests/benchmark_ui/bad_params.rs:10:31 + | +10 | fn bench(y: Linear<1, 2>, x: u32) { + | ^^^ diff --git a/frame/support/test/tests/benchmark_ui/dup_block.rs b/frame/support/test/tests/benchmark_ui/dup_block.rs new file mode 100644 index 0000000000000..4c4a8fc11d30a --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_block.rs @@ -0,0 +1,20 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() { + let a = 2 + 2; + #[block] + {} + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/dup_block.stderr b/frame/support/test/tests/benchmark_ui/dup_block.stderr new file mode 100644 index 0000000000000..3d73c3d6609b1 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_block.stderr @@ -0,0 +1,5 @@ +error: Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark. + --> tests/benchmark_ui/dup_block.rs:14:3 + | +14 | #[block] + | ^ diff --git a/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs new file mode 100644 index 0000000000000..1a91b7c16d6a5 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.rs @@ -0,0 +1,20 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() { + let a = 2 + 2; + #[extrinsic_call] + _(stuff); + #[extrinsic_call] + _(other_stuff); + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.stderr b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.stderr new file mode 100644 index 0000000000000..593f7072bfa51 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/dup_extrinsic_call.stderr @@ -0,0 +1,5 @@ +error: Only one #[extrinsic_call] or #[block] attribute is allowed per benchmark. + --> tests/benchmark_ui/dup_extrinsic_call.rs:14:3 + | +14 | #[extrinsic_call] + | ^ diff --git a/frame/support/test/tests/benchmark_ui/extra_extra.rs b/frame/support/test/tests/benchmark_ui/extra_extra.rs new file mode 100644 index 0000000000000..021106c7afc85 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_extra.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(extra, extra)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/extra_extra.stderr b/frame/support/test/tests/benchmark_ui/extra_extra.stderr new file mode 100644 index 0000000000000..bf36b4f08054a --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_extra.stderr @@ -0,0 +1,5 @@ +error: unexpected end of input, `extra` can only be specified once + --> tests/benchmark_ui/extra_extra.rs:9:26 + | +9 | #[benchmark(extra, extra)] + | ^ diff --git a/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs b/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs new file mode 100644 index 0000000000000..1940f4cf1f040 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_skip_meta.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(skip_meta, skip_meta)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/extra_skip_meta.stderr b/frame/support/test/tests/benchmark_ui/extra_skip_meta.stderr new file mode 100644 index 0000000000000..4d48a8ad77a45 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extra_skip_meta.stderr @@ -0,0 +1,5 @@ +error: unexpected end of input, `skip_meta` can only be specified once + --> tests/benchmark_ui/extra_skip_meta.rs:9:34 + | +9 | #[benchmark(skip_meta, skip_meta)] + | ^ diff --git a/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs new file mode 100644 index 0000000000000..4cb6bfc34c58e --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.rs @@ -0,0 +1,6 @@ +use frame_support::benchmarking::*; + +#[extrinsic_call] +mod stuff {} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr new file mode 100644 index 0000000000000..c5194d7a66502 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/extrinsic_call_out_of_fn.stderr @@ -0,0 +1,7 @@ +error: `#[extrinsic_call]` must be in a benchmark function definition labeled with `#[benchmark]`. + --> tests/benchmark_ui/extrinsic_call_out_of_fn.rs:3:1 + | +3 | #[extrinsic_call] + | ^^^^^^^^^^^^^^^^^ + | + = note: this error originates in the attribute macro `extrinsic_call` (in Nightly builds, run with -Z macro-backtrace for more info) diff --git a/frame/support/test/tests/benchmark_ui/missing_call.rs b/frame/support/test/tests/benchmark_ui/missing_call.rs new file mode 100644 index 0000000000000..74370493542c4 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_call.rs @@ -0,0 +1,13 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() {} +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/missing_call.stderr b/frame/support/test/tests/benchmark_ui/missing_call.stderr new file mode 100644 index 0000000000000..3b55f77d42562 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_call.stderr @@ -0,0 +1,5 @@ +error: No valid #[extrinsic_call] or #[block] annotation could be found in benchmark function body. + --> tests/benchmark_ui/missing_call.rs:10:13 + | +10 | fn bench() {} + | ^^ diff --git a/frame/support/test/tests/benchmark_ui/missing_origin.rs b/frame/support/test/tests/benchmark_ui/missing_origin.rs new file mode 100644 index 0000000000000..aad91bc79f825 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_origin.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark] + fn bench() { + #[extrinsic_call] + thing(); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/missing_origin.stderr b/frame/support/test/tests/benchmark_ui/missing_origin.stderr new file mode 100644 index 0000000000000..0e72bff4747a3 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/missing_origin.stderr @@ -0,0 +1,5 @@ +error: Single-item extrinsic calls must specify their origin as the first argument. + --> tests/benchmark_ui/missing_origin.rs:12:3 + | +12 | thing(); + | ^^^^^ diff --git a/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs b/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs new file mode 100644 index 0000000000000..5c84d7f76d874 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/pass/valid_basic.rs @@ -0,0 +1,17 @@ +use frame_support::benchmarking::*; +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(skip_meta, extra)] + fn bench() { + let a = 2 + 2; + #[block] + {} + assert_eq!(a, 4); + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/unrecognized_option.rs b/frame/support/test/tests/benchmark_ui/unrecognized_option.rs new file mode 100644 index 0000000000000..4c2cea139f80c --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/unrecognized_option.rs @@ -0,0 +1,16 @@ +use frame_support::benchmarking::*; +#[allow(unused_imports)] +use frame_support_test::Config; + +#[benchmarks] +mod benches { + use super::*; + + #[benchmark(skip_meta, extra, bad)] + fn bench() { + #[block] + {} + } +} + +fn main() {} diff --git a/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr b/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr new file mode 100644 index 0000000000000..5cebe9eab05e9 --- /dev/null +++ b/frame/support/test/tests/benchmark_ui/unrecognized_option.stderr @@ -0,0 +1,5 @@ +error: expected `extra` or `skip_meta` + --> tests/benchmark_ui/unrecognized_option.rs:9:32 + | +9 | #[benchmark(skip_meta, extra, bad)] + | ^^^ diff --git a/frame/system/Cargo.toml b/frame/system/Cargo.toml index 55c9b5bda54fa..7f573a9cbd575 100644 --- a/frame/system/Cargo.toml +++ b/frame/system/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } diff --git a/frame/system/benchmarking/Cargo.toml b/frame/system/benchmarking/Cargo.toml index 30b299ea6a56e..8f00097254609 100644 --- a/frame/system/benchmarking/Cargo.toml +++ b/frame/system/benchmarking/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../../support" } diff --git a/frame/system/rpc/runtime-api/Cargo.toml b/frame/system/rpc/runtime-api/Cargo.toml index 63d76d731e26e..cedb4e35be0b8 100644 --- a/frame/system/rpc/runtime-api/Cargo.toml +++ b/frame/system/rpc/runtime-api/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../../primitives/api" } [features] diff --git a/frame/system/src/lib.rs b/frame/system/src/lib.rs index 3909b1e9c5257..4bd5cf629873b 100644 --- a/frame/system/src/lib.rs +++ b/frame/system/src/lib.rs @@ -1247,6 +1247,8 @@ impl Pallet { } /// Deposits an event into this block's event record. + /// + /// NOTE: Events not registered at the genesis block and quietly omitted. pub fn deposit_event(event: impl Into) { Self::deposit_event_indexed(&[], event.into()); } @@ -1256,6 +1258,8 @@ impl Pallet { /// /// This will update storage entries that correspond to the specified topics. /// It is expected that light-clients could subscribe to this topics. + /// + /// NOTE: Events not registered at the genesis block and quietly omitted. pub fn deposit_event_indexed(topics: &[T::Hash], event: T::RuntimeEvent) { let block_number = Self::block_number(); // Don't populate events on genesis. @@ -1445,8 +1449,14 @@ impl Pallet { /// NOTE: This should only be used in tests. Reading events from the runtime can have a large /// impact on the PoV size of a block. Users should use alternative and well bounded storage /// items for any behavior like this. + /// + /// NOTE: Events not registered at the genesis block and quietly omitted. #[cfg(any(feature = "std", feature = "runtime-benchmarks", test))] pub fn events() -> Vec> { + debug_assert!( + !Self::block_number().is_zero(), + "events not registered at the genesis block" + ); // Dereferencing the events here is fine since we are not in the // memory-restricted runtime. Self::read_events_no_consensus().map(|e| *e).collect() @@ -1501,15 +1511,27 @@ impl Pallet { } /// Assert the given `event` exists. + /// + /// NOTE: Events not registered at the genesis block and quietly omitted. #[cfg(any(feature = "std", feature = "runtime-benchmarks", test))] pub fn assert_has_event(event: T::RuntimeEvent) { - assert!(Self::events().iter().any(|record| record.event == event)) + let events = Self::events(); + assert!( + events.iter().any(|record| record.event == event), + "expected event {event:?} not found in events {events:?}", + ); } /// Assert the last event equal to the given `event`. + /// + /// NOTE: Events not registered at the genesis block and quietly omitted. #[cfg(any(feature = "std", feature = "runtime-benchmarks", test))] pub fn assert_last_event(event: T::RuntimeEvent) { - assert_eq!(Self::events().last().expect("events expected").event, event); + let last_event = Self::events().last().expect("events expected").event.clone(); + assert_eq!( + last_event, event, + "expected event {event:?} is not equal to the last event {last_event:?}", + ); } /// Return the chain's current runtime version. diff --git a/frame/system/src/tests.rs b/frame/system/src/tests.rs index c42131c450228..8223f66744151 100644 --- a/frame/system/src/tests.rs +++ b/frame/system/src/tests.rs @@ -627,7 +627,8 @@ fn events_not_emitted_during_genesis() { assert!(System::block_number().is_zero()); let mut account_data = AccountInfo::default(); System::on_created_account(Default::default(), &mut account_data); - assert!(System::events().is_empty()); + // No events registered at the genesis block + assert!(!System::read_events_no_consensus().any(|_| true)); // Events will be emitted starting on block 1 System::set_block_number(1); System::on_created_account(Default::default(), &mut account_data); diff --git a/frame/timestamp/Cargo.toml b/frame/timestamp/Cargo.toml index df63ed0d72b8e..84c56da24e9d4 100644 --- a/frame/timestamp/Cargo.toml +++ b/frame/timestamp/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/tips/Cargo.toml b/frame/tips/Cargo.toml index 7d0576ec28cce..c7db61613c03a 100644 --- a/frame/tips/Cargo.toml +++ b/frame/tips/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } diff --git a/frame/transaction-payment/Cargo.toml b/frame/transaction-payment/Cargo.toml index a2f77b6cf2279..0c98796e4d1a7 100644 --- a/frame/transaction-payment/Cargo.toml +++ b/frame/transaction-payment/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/frame/transaction-payment/asset-tx-payment/Cargo.toml b/frame/transaction-payment/asset-tx-payment/Cargo.toml index 8e4645a2677f9..324e17a0808a4 100644 --- a/frame/transaction-payment/asset-tx-payment/Cargo.toml +++ b/frame/transaction-payment/asset-tx-payment/Cargo.toml @@ -25,7 +25,7 @@ pallet-transaction-payment = { version = "4.0.0-dev", default-features = false, frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../../benchmarking", optional = true } # Other dependencies -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } diff --git a/frame/transaction-payment/asset-tx-payment/src/lib.rs b/frame/transaction-payment/asset-tx-payment/src/lib.rs index 43cc1efa0858e..230b307317f8b 100644 --- a/frame/transaction-payment/asset-tx-payment/src/lib.rs +++ b/frame/transaction-payment/asset-tx-payment/src/lib.rs @@ -59,6 +59,8 @@ use sp_runtime::{ FixedPointOperand, }; +#[cfg(test)] +mod mock; #[cfg(test)] mod tests; diff --git a/frame/transaction-payment/asset-tx-payment/src/mock.rs b/frame/transaction-payment/asset-tx-payment/src/mock.rs new file mode 100644 index 0000000000000..ddb02f5a611ff --- /dev/null +++ b/frame/transaction-payment/asset-tx-payment/src/mock.rs @@ -0,0 +1,213 @@ +// Copyright (C) 2021-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use crate as pallet_asset_tx_payment; + +use codec; +use frame_support::{ + dispatch::DispatchClass, + pallet_prelude::*, + parameter_types, + traits::{AsEnsureOriginWithArg, ConstU32, ConstU64, ConstU8, FindAuthor}, + weights::{Weight, WeightToFee as WeightToFeeT}, + ConsensusEngineId, +}; +use frame_system as system; +use frame_system::EnsureRoot; +use pallet_transaction_payment::CurrencyAdapter; +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, ConvertInto, IdentityLookup, SaturatedConversion}, +}; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; +type Balance = u64; +type AccountId = u64; + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + Assets: pallet_assets::{Pallet, Call, Storage, Event}, + Authorship: pallet_authorship::{Pallet, Call, Storage}, + AssetTxPayment: pallet_asset_tx_payment::{Pallet, Event}, + } +); + +parameter_types! { + pub(crate) static ExtrinsicBaseWeight: Weight = Weight::zero(); +} + +pub struct BlockWeights; +impl Get for BlockWeights { + fn get() -> frame_system::limits::BlockWeights { + frame_system::limits::BlockWeights::builder() + .base_block(Weight::zero()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); + }) + .for_class(DispatchClass::non_mandatory(), |weights| { + weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); + }) + .build_or_panic() + } +} + +parameter_types! { + pub static WeightToFee: u64 = 1; + pub static TransactionByteFee: u64 = 1; +} + +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = AccountId; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +parameter_types! { + pub const ExistentialDeposit: u64 = 10; +} + +impl pallet_balances::Config for Runtime { + type Balance = Balance; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<10>; + type AccountStore = System; + type MaxLocks = (); + type WeightInfo = (); + type MaxReserves = ConstU32<50>; + type ReserveIdentifier = [u8; 8]; +} + +impl WeightToFeeT for WeightToFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) + } +} + +impl WeightToFeeT for TransactionByteFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) + } +} + +impl pallet_transaction_payment::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter; + type WeightToFee = WeightToFee; + type LengthToFee = TransactionByteFee; + type FeeMultiplierUpdate = (); + type OperationalFeeMultiplier = ConstU8<5>; +} + +type AssetId = u32; + +impl pallet_assets::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type AssetId = AssetId; + type AssetIdParameter = codec::Compact; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; + type AssetDeposit = ConstU64<2>; + type AssetAccountDeposit = ConstU64<2>; + type MetadataDepositBase = ConstU64<0>; + type MetadataDepositPerByte = ConstU64<0>; + type ApprovalDeposit = ConstU64<0>; + type StringLimit = ConstU32<20>; + type Freezer = (); + type Extra = (); + type CallbackHandle = (); + type WeightInfo = (); + type RemoveItemsLimit = ConstU32<1000>; + pallet_assets::runtime_benchmarks_enabled! { + type BenchmarkHelper = (); + } +} + +pub struct HardcodedAuthor; +pub(crate) const BLOCK_AUTHOR: AccountId = 1234; +impl FindAuthor for HardcodedAuthor { + fn find_author<'a, I>(_: I) -> Option + where + I: 'a + IntoIterator, + { + Some(BLOCK_AUTHOR) + } +} + +impl pallet_authorship::Config for Runtime { + type FindAuthor = HardcodedAuthor; + type UncleGenerations = (); + type FilterUncle = (); + type EventHandler = (); +} + +pub struct CreditToBlockAuthor; +impl HandleCredit for CreditToBlockAuthor { + fn handle_credit(credit: CreditOf) { + if let Some(author) = pallet_authorship::Pallet::::author() { + // What to do in case paying the author fails (e.g. because `fee < min_balance`) + // default: drop the result which will trigger the `OnDrop` of the imbalance. + let _ = >::resolve(&author, credit); + } + } +} + +impl Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Fungibles = Assets; + type OnChargeAssetTransaction = FungiblesAdapter< + pallet_assets::BalanceToAssetBalance, + CreditToBlockAuthor, + >; +} diff --git a/frame/transaction-payment/asset-tx-payment/src/payment.rs b/frame/transaction-payment/asset-tx-payment/src/payment.rs index 80ff4e40dcffa..85d1bec4b275c 100644 --- a/frame/transaction-payment/asset-tx-payment/src/payment.rs +++ b/frame/transaction-payment/asset-tx-payment/src/payment.rs @@ -21,15 +21,13 @@ use codec::FullCodec; use frame_support::{ traits::{ fungibles::{Balanced, CreditOf, Inspect}, - tokens::BalanceConversion, + tokens::{Balance, BalanceConversion}, }, unsigned::TransactionValidityError, }; use scale_info::TypeInfo; use sp_runtime::{ - traits::{ - AtLeast32BitUnsigned, DispatchInfoOf, MaybeSerializeDeserialize, One, PostDispatchInfoOf, - }, + traits::{DispatchInfoOf, MaybeSerializeDeserialize, One, PostDispatchInfoOf}, transaction_validity::InvalidTransaction, }; use sp_std::{fmt::Debug, marker::PhantomData}; @@ -37,13 +35,7 @@ use sp_std::{fmt::Debug, marker::PhantomData}; /// Handle withdrawing, refunding and depositing of transaction fees. pub trait OnChargeAssetTransaction { /// The underlying integer type in which fees are calculated. - type Balance: AtLeast32BitUnsigned - + FullCodec - + Copy - + MaybeSerializeDeserialize - + Debug - + Default - + TypeInfo; + type Balance: Balance; /// The type used to identify the assets used for transaction payment. type AssetId: FullCodec + Copy + MaybeSerializeDeserialize + Debug + Default + Eq + TypeInfo; /// The type used to store the intermediate values between pre- and post-dispatch. diff --git a/frame/transaction-payment/asset-tx-payment/src/tests.rs b/frame/transaction-payment/asset-tx-payment/src/tests.rs index b79b87a93c7a4..f76293f456497 100644 --- a/frame/transaction-payment/asset-tx-payment/src/tests.rs +++ b/frame/transaction-payment/asset-tx-payment/src/tests.rs @@ -14,209 +14,22 @@ // limitations under the License. use super::*; -use crate as pallet_asset_tx_payment; -use codec; use frame_support::{ assert_ok, - dispatch::{DispatchClass, DispatchInfo, PostDispatchInfo}, + dispatch::{DispatchInfo, PostDispatchInfo}, pallet_prelude::*, - parameter_types, - traits::{fungibles::Mutate, AsEnsureOriginWithArg, ConstU32, ConstU64, ConstU8, FindAuthor}, - weights::{Weight, WeightToFee as WeightToFeeT}, - ConsensusEngineId, + traits::fungibles::Mutate, + weights::Weight, }; use frame_system as system; -use frame_system::EnsureRoot; +use mock::{ExtrinsicBaseWeight, *}; use pallet_balances::Call as BalancesCall; -use pallet_transaction_payment::CurrencyAdapter; -use sp_core::H256; -use sp_runtime::{ - testing::Header, - traits::{BlakeTwo256, ConvertInto, IdentityLookup, SaturatedConversion, StaticLookup}, -}; - -type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; -type Block = frame_system::mocking::MockBlock; -type Balance = u64; -type AccountId = u64; - -frame_support::construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, - Assets: pallet_assets::{Pallet, Call, Storage, Event}, - Authorship: pallet_authorship::{Pallet, Call, Storage}, - AssetTxPayment: pallet_asset_tx_payment::{Pallet, Event}, - } -); +use sp_runtime::traits::StaticLookup; const CALL: &::RuntimeCall = &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); -parameter_types! { - static ExtrinsicBaseWeight: Weight = Weight::zero(); -} - -pub struct BlockWeights; -impl Get for BlockWeights { - fn get() -> frame_system::limits::BlockWeights { - frame_system::limits::BlockWeights::builder() - .base_block(Weight::zero()) - .for_class(DispatchClass::all(), |weights| { - weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); - }) - .for_class(DispatchClass::non_mandatory(), |weights| { - weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); - }) - .build_or_panic() - } -} - -parameter_types! { - pub static WeightToFee: u64 = 1; - pub static TransactionByteFee: u64 = 1; -} - -impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = AccountId; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; -} - -parameter_types! { - pub const ExistentialDeposit: u64 = 10; -} - -impl pallet_balances::Config for Runtime { - type Balance = Balance; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU64<10>; - type AccountStore = System; - type MaxLocks = (); - type WeightInfo = (); - type MaxReserves = ConstU32<50>; - type ReserveIdentifier = [u8; 8]; -} - -impl WeightToFeeT for WeightToFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) - } -} - -impl WeightToFeeT for TransactionByteFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) - } -} - -impl pallet_transaction_payment::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter; - type WeightToFee = WeightToFee; - type LengthToFee = TransactionByteFee; - type FeeMultiplierUpdate = (); - type OperationalFeeMultiplier = ConstU8<5>; -} - -type AssetId = u32; - -impl pallet_assets::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Balance = Balance; - type AssetId = AssetId; - type AssetIdParameter = codec::Compact; - type Currency = Balances; - type CreateOrigin = AsEnsureOriginWithArg>; - type ForceOrigin = EnsureRoot; - type AssetDeposit = ConstU64<2>; - type AssetAccountDeposit = ConstU64<2>; - type MetadataDepositBase = ConstU64<0>; - type MetadataDepositPerByte = ConstU64<0>; - type ApprovalDeposit = ConstU64<0>; - type StringLimit = ConstU32<20>; - type Freezer = (); - type Extra = (); - type CallbackHandle = (); - type WeightInfo = (); - type RemoveItemsLimit = ConstU32<1000>; - pallet_assets::runtime_benchmarks_enabled! { - type BenchmarkHelper = (); - } -} - -pub struct HardcodedAuthor; -const BLOCK_AUTHOR: AccountId = 1234; -impl FindAuthor for HardcodedAuthor { - fn find_author<'a, I>(_: I) -> Option - where - I: 'a + IntoIterator, - { - Some(BLOCK_AUTHOR) - } -} - -impl pallet_authorship::Config for Runtime { - type FindAuthor = HardcodedAuthor; - type UncleGenerations = (); - type FilterUncle = (); - type EventHandler = (); -} - -pub struct CreditToBlockAuthor; -impl HandleCredit for CreditToBlockAuthor { - fn handle_credit(credit: CreditOf) { - if let Some(author) = pallet_authorship::Pallet::::author() { - // What to do in case paying the author fails (e.g. because `fee < min_balance`) - // default: drop the result which will trigger the `OnDrop` of the imbalance. - let _ = >::resolve(&author, credit); - } - } -} - -impl Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Fungibles = Assets; - type OnChargeAssetTransaction = FungiblesAdapter< - pallet_assets::BalanceToAssetBalance, - CreditToBlockAuthor, - >; -} - pub struct ExtBuilder { balance_factor: u64, base_weight: Weight, diff --git a/frame/transaction-payment/rpc/Cargo.toml b/frame/transaction-payment/rpc/Cargo.toml index b77143201ffd4..b9bf226e2da9e 100644 --- a/frame/transaction-payment/rpc/Cargo.toml +++ b/frame/transaction-payment/rpc/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } pallet-transaction-payment-rpc-runtime-api = { version = "4.0.0-dev", path = "./runtime-api" } sp-api = { version = "4.0.0-dev", path = "../../../primitives/api" } diff --git a/frame/transaction-payment/rpc/runtime-api/Cargo.toml b/frame/transaction-payment/rpc/runtime-api/Cargo.toml index 86753526fef47..854e4310b46dd 100644 --- a/frame/transaction-payment/rpc/runtime-api/Cargo.toml +++ b/frame/transaction-payment/rpc/runtime-api/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } pallet-transaction-payment = { version = "4.0.0-dev", default-features = false, path = "../../../transaction-payment" } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../../../primitives/api" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../../../primitives/runtime" } diff --git a/frame/transaction-payment/src/lib.rs b/frame/transaction-payment/src/lib.rs index ce747fa6bd85c..13adbf89c4270 100644 --- a/frame/transaction-payment/src/lib.rs +++ b/frame/transaction-payment/src/lib.rs @@ -70,6 +70,11 @@ use frame_support::{ weights::{Weight, WeightToFee}, }; +#[cfg(test)] +mod mock; +#[cfg(test)] +mod tests; + mod payment; mod types; @@ -847,959 +852,3 @@ where Self::compute_actual_fee(len, &info, &post_info, Zero::zero()) } } - -#[cfg(test)] -mod tests { - use super::*; - use crate as pallet_transaction_payment; - - use codec::Encode; - - use sp_core::H256; - use sp_runtime::{ - testing::{Header, TestXt}, - traits::{BlakeTwo256, IdentityLookup, One}, - transaction_validity::InvalidTransaction, - }; - - use frame_support::{ - assert_noop, assert_ok, - dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, - parameter_types, - traits::{ConstU32, ConstU64, Currency, GenesisBuild, Imbalance, OnUnbalanced}, - weights::{Weight, WeightToFee as WeightToFeeT}, - }; - use frame_system as system; - use pallet_balances::Call as BalancesCall; - - type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; - type Block = frame_system::mocking::MockBlock; - - frame_support::construct_runtime!( - pub enum Runtime where - Block = Block, - NodeBlock = Block, - UncheckedExtrinsic = UncheckedExtrinsic, - { - System: system::{Pallet, Call, Config, Storage, Event}, - Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, - TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, - } - ); - - const CALL: &::RuntimeCall = - &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); - - parameter_types! { - static ExtrinsicBaseWeight: Weight = Weight::zero(); - } - - pub struct BlockWeights; - impl Get for BlockWeights { - fn get() -> frame_system::limits::BlockWeights { - frame_system::limits::BlockWeights::builder() - .base_block(Weight::zero()) - .for_class(DispatchClass::all(), |weights| { - weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); - }) - .for_class(DispatchClass::non_mandatory(), |weights| { - weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); - }) - .build_or_panic() - } - } - - parameter_types! { - pub static WeightToFee: u64 = 1; - pub static TransactionByteFee: u64 = 1; - pub static OperationalFeeMultiplier: u8 = 5; - } - - impl frame_system::Config for Runtime { - type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = BlockWeights; - type BlockLength = (); - type DbWeight = (); - type RuntimeOrigin = RuntimeOrigin; - type Index = u64; - type BlockNumber = u64; - type RuntimeCall = RuntimeCall; - type Hash = H256; - type Hashing = BlakeTwo256; - type AccountId = u64; - type Lookup = IdentityLookup; - type Header = Header; - type RuntimeEvent = RuntimeEvent; - type BlockHashCount = ConstU64<250>; - type Version = (); - type PalletInfo = PalletInfo; - type AccountData = pallet_balances::AccountData; - type OnNewAccount = (); - type OnKilledAccount = (); - type SystemWeightInfo = (); - type SS58Prefix = (); - type OnSetCode = (); - type MaxConsumers = ConstU32<16>; - } - - impl pallet_balances::Config for Runtime { - type Balance = u64; - type RuntimeEvent = RuntimeEvent; - type DustRemoval = (); - type ExistentialDeposit = ConstU64<1>; - type AccountStore = System; - type MaxLocks = (); - type MaxReserves = (); - type ReserveIdentifier = [u8; 8]; - type WeightInfo = (); - } - - impl WeightToFeeT for WeightToFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) - } - } - - impl WeightToFeeT for TransactionByteFee { - type Balance = u64; - - fn weight_to_fee(weight: &Weight) -> Self::Balance { - Self::Balance::saturated_from(weight.ref_time()) - .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) - } - } - - parameter_types! { - static TipUnbalancedAmount: u64 = 0; - static FeeUnbalancedAmount: u64 = 0; - } - - pub struct DealWithFees; - impl OnUnbalanced> for DealWithFees { - fn on_unbalanceds( - mut fees_then_tips: impl Iterator>, - ) { - if let Some(fees) = fees_then_tips.next() { - FeeUnbalancedAmount::mutate(|a| *a += fees.peek()); - if let Some(tips) = fees_then_tips.next() { - TipUnbalancedAmount::mutate(|a| *a += tips.peek()); - } - } - } - } - - impl Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type OnChargeTransaction = CurrencyAdapter; - type OperationalFeeMultiplier = OperationalFeeMultiplier; - type WeightToFee = WeightToFee; - type LengthToFee = TransactionByteFee; - type FeeMultiplierUpdate = (); - } - - pub struct ExtBuilder { - balance_factor: u64, - base_weight: Weight, - byte_fee: u64, - weight_to_fee: u64, - initial_multiplier: Option, - } - - impl Default for ExtBuilder { - fn default() -> Self { - Self { - balance_factor: 1, - base_weight: Weight::zero(), - byte_fee: 1, - weight_to_fee: 1, - initial_multiplier: None, - } - } - } - - impl ExtBuilder { - pub fn base_weight(mut self, base_weight: Weight) -> Self { - self.base_weight = base_weight; - self - } - pub fn byte_fee(mut self, byte_fee: u64) -> Self { - self.byte_fee = byte_fee; - self - } - pub fn weight_fee(mut self, weight_to_fee: u64) -> Self { - self.weight_to_fee = weight_to_fee; - self - } - pub fn balance_factor(mut self, factor: u64) -> Self { - self.balance_factor = factor; - self - } - pub fn with_initial_multiplier(mut self, multiplier: Multiplier) -> Self { - self.initial_multiplier = Some(multiplier); - self - } - fn set_constants(&self) { - ExtrinsicBaseWeight::mutate(|v| *v = self.base_weight); - TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.byte_fee); - WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); - } - pub fn build(self) -> sp_io::TestExternalities { - self.set_constants(); - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - pallet_balances::GenesisConfig:: { - balances: if self.balance_factor > 0 { - vec![ - (1, 10 * self.balance_factor), - (2, 20 * self.balance_factor), - (3, 30 * self.balance_factor), - (4, 40 * self.balance_factor), - (5, 50 * self.balance_factor), - (6, 60 * self.balance_factor), - ] - } else { - vec![] - }, - } - .assimilate_storage(&mut t) - .unwrap(); - - if let Some(multiplier) = self.initial_multiplier { - let genesis = pallet::GenesisConfig { multiplier }; - GenesisBuild::::assimilate_storage(&genesis, &mut t).unwrap(); - } - - t.into() - } - } - - /// create a transaction info struct from weight. Handy to avoid building the whole struct. - pub fn info_from_weight(w: Weight) -> DispatchInfo { - // pays_fee: Pays::Yes -- class: DispatchClass::Normal - DispatchInfo { weight: w, ..Default::default() } - } - - fn post_info_from_weight(w: Weight) -> PostDispatchInfo { - PostDispatchInfo { actual_weight: Some(w), pays_fee: Default::default() } - } - - fn post_info_from_pays(p: Pays) -> PostDispatchInfo { - PostDispatchInfo { actual_weight: None, pays_fee: p } - } - - fn default_post_info() -> PostDispatchInfo { - PostDispatchInfo { actual_weight: None, pays_fee: Default::default() } - } - - #[test] - fn signed_extension_transaction_payment_work() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - let len = 10; - let pre = ChargeTransactionPayment::::from(0) - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(5)), len) - .unwrap(); - assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(5)), - &default_post_info(), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); - assert_eq!(FeeUnbalancedAmount::get(), 5 + 5 + 10); - assert_eq!(TipUnbalancedAmount::get(), 0); - - FeeUnbalancedAmount::mutate(|a| *a = 0); - - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 50 - 5); - assert_eq!(FeeUnbalancedAmount::get(), 5 + 10 + 50); - assert_eq!(TipUnbalancedAmount::get(), 5); - }); - } - - #[test] - fn signed_extension_transaction_payment_multiplied_refund_works() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - let len = 10; - >::put(Multiplier::saturating_from_rational(3, 2)); - - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), - len, - &Ok(()) - )); - // 75 (3/2 of the returned 50 units of weight) is refunded - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 75 - 5); - }); - } - - #[test] - fn signed_extension_transaction_payment_is_bounded() { - ExtBuilder::default().balance_factor(1000).byte_fee(0).build().execute_with(|| { - // maximum weight possible - assert_ok!(ChargeTransactionPayment::::from(0).pre_dispatch( - &1, - CALL, - &info_from_weight(Weight::MAX), - 10 - )); - // fee will be proportional to what is the actual maximum weight in the runtime. - assert_eq!( - Balances::free_balance(&1), - (10000 - - ::BlockWeights::get().max_block.ref_time()) as u64 - ); - }); - } - - #[test] - fn signed_extension_allows_free_transactions() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .balance_factor(0) - .build() - .execute_with(|| { - // 1 ain't have a penny. - assert_eq!(Balances::free_balance(1), 0); - - let len = 100; - - // This is a completely free (and thus wholly insecure/DoS-ridden) transaction. - let operational_transaction = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::No, - }; - assert_ok!(ChargeTransactionPayment::::from(0).validate( - &1, - CALL, - &operational_transaction, - len - )); - - // like a InsecureFreeNormal - let free_transaction = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - assert_noop!( - ChargeTransactionPayment::::from(0).validate( - &1, - CALL, - &free_transaction, - len - ), - TransactionValidityError::Invalid(InvalidTransaction::Payment), - ); - }); - } - - #[test] - fn signed_ext_length_fee_is_also_updated_per_congestion() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) - .balance_factor(10) - .build() - .execute_with(|| { - // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); - let len = 10; - - assert_ok!( - ChargeTransactionPayment::::from(10) // tipped - .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(3)), len) - ); - assert_eq!( - Balances::free_balance(1), - 100 // original - - 10 // tip - - 5 // base - - 10 // len - - (3 * 3 / 2) // adjusted weight - ); - }) - } - - #[test] - fn query_info_and_fee_details_works() { - let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); - let origin = 111111; - let extra = (); - let xt = TestXt::new(call.clone(), Some((origin, extra))); - let info = xt.get_dispatch_info(); - let ext = xt.encode(); - let len = ext.len() as u32; - - let unsigned_xt = TestXt::<_, ()>::new(call, None); - let unsigned_xt_info = unsigned_xt.get_dispatch_info(); - - ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) - .weight_fee(2) - .build() - .execute_with(|| { - // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); - - assert_eq!( - TransactionPayment::query_info(xt.clone(), len), - RuntimeDispatchInfo { - weight: info.weight, - class: info.class, - partial_fee: 5 * 2 /* base * weight_fee */ - + len as u64 /* len * 1 */ - + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ - }, - ); - - assert_eq!( - TransactionPayment::query_info(unsigned_xt.clone(), len), - RuntimeDispatchInfo { - weight: unsigned_xt_info.weight, - class: unsigned_xt_info.class, - partial_fee: 0, - }, - ); - - assert_eq!( - TransactionPayment::query_fee_details(xt, len), - FeeDetails { - inclusion_fee: Some(InclusionFee { - base_fee: 5 * 2, - len_fee: len as u64, - adjusted_weight_fee: info - .weight - .min(BlockWeights::get().max_block) - .ref_time() as u64 * 2 * 3 / 2 - }), - tip: 0, - }, - ); - - assert_eq!( - TransactionPayment::query_fee_details(unsigned_xt, len), - FeeDetails { inclusion_fee: None, tip: 0 }, - ); - }); - } - - #[test] - fn query_call_info_and_fee_details_works() { - let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); - let info = call.get_dispatch_info(); - let encoded_call = call.encode(); - let len = encoded_call.len() as u32; - - ExtBuilder::default() - .base_weight(Weight::from_ref_time(5)) - .weight_fee(2) - .build() - .execute_with(|| { - // all fees should be x1.5 - >::put(Multiplier::saturating_from_rational(3, 2)); - - assert_eq!( - TransactionPayment::query_call_info(call.clone(), len), - RuntimeDispatchInfo { - weight: info.weight, - class: info.class, - partial_fee: 5 * 2 /* base * weight_fee */ - + len as u64 /* len * 1 */ - + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ - }, - ); - - assert_eq!( - TransactionPayment::query_call_fee_details(call, len), - FeeDetails { - inclusion_fee: Some(InclusionFee { - base_fee: 5 * 2, /* base * weight_fee */ - len_fee: len as u64, /* len * 1 */ - adjusted_weight_fee: info - .weight - .min(BlockWeights::get().max_block) - .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multipler */ - }), - tip: 0, - }, - ); - }); - } - - #[test] - fn compute_fee_works_without_multiplier() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Next fee multiplier is zero - assert_eq!(>::get(), Multiplier::one()); - - // Tip only, no fees works - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::No, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 10), 10); - // No tip, only base fee works - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); - // Tip + base fee works - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 69), 169); - // Len (byte fee) + base fee works - assert_eq!(Pallet::::compute_fee(42, &dispatch_info, 0), 520); - // Weight fee + base fee works - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(1000), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 1100); - }); - } - - #[test] - fn compute_fee_works_with_multiplier() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Add a next fee multiplier. Fees will be x3/2. - >::put(Multiplier::saturating_from_rational(3, 2)); - // Base fee is unaffected by multiplier - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); - - // Everything works together :) - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(123), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - // 123 weight, 456 length, 100 base - assert_eq!( - Pallet::::compute_fee(456, &dispatch_info, 789), - 100 + (3 * 123 / 2) + 4560 + 789, - ); - }); - } - - #[test] - fn compute_fee_works_with_negative_multiplier() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Add a next fee multiplier. All fees will be x1/2. - >::put(Multiplier::saturating_from_rational(1, 2)); - - // Base fee is unaffected by multiplier. - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(0), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); - - // Everything works together. - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(123), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - // 123 weight, 456 length, 100 base - assert_eq!( - Pallet::::compute_fee(456, &dispatch_info, 789), - 100 + (123 / 2) + 4560 + 789, - ); - }); - } - - #[test] - fn compute_fee_does_not_overflow() { - ExtBuilder::default() - .base_weight(Weight::from_ref_time(100)) - .byte_fee(10) - .balance_factor(0) - .build() - .execute_with(|| { - // Overflow is handled - let dispatch_info = DispatchInfo { - weight: Weight::MAX, - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - assert_eq!( - Pallet::::compute_fee(u32::MAX, &dispatch_info, u64::MAX), - u64::MAX - ); - }); - } - - #[test] - fn refund_does_not_recreate_account() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - // So events are emitted - System::set_block_number(10); - let len = 10; - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - // kill the account between pre and post dispatch - assert_ok!(Balances::transfer(Some(2).into(), 3, Balances::free_balance(2))); - assert_eq!(Balances::free_balance(2), 0); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(50)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 0); - // Transfer Event - System::assert_has_event(RuntimeEvent::Balances( - pallet_balances::Event::Transfer { from: 2, to: 3, amount: 80 }, - )); - // Killed Event - System::assert_has_event(RuntimeEvent::System(system::Event::KilledAccount { - account: 2, - })); - }); - } - - #[test] - fn actual_weight_higher_than_max_refunds_nothing() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - let len = 10; - let pre = ChargeTransactionPayment::::from(5 /* tipped */) - .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) - .unwrap(); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info_from_weight(Weight::from_ref_time(100)), - &post_info_from_weight(Weight::from_ref_time(101)), - len, - &Ok(()) - )); - assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); - }); - } - - #[test] - fn zero_transfer_on_free_transaction() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(5)) - .build() - .execute_with(|| { - // So events are emitted - System::set_block_number(10); - let len = 10; - let dispatch_info = DispatchInfo { - weight: Weight::from_ref_time(100), - pays_fee: Pays::No, - class: DispatchClass::Normal, - }; - let user = 69; - let pre = ChargeTransactionPayment::::from(0) - .pre_dispatch(&user, CALL, &dispatch_info, len) - .unwrap(); - assert_eq!(Balances::total_balance(&user), 0); - assert_ok!(ChargeTransactionPayment::::post_dispatch( - Some(pre), - &dispatch_info, - &default_post_info(), - len, - &Ok(()) - )); - assert_eq!(Balances::total_balance(&user), 0); - // TransactionFeePaid Event - System::assert_has_event(RuntimeEvent::TransactionPayment( - pallet_transaction_payment::Event::TransactionFeePaid { - who: user, - actual_fee: 0, - tip: 0, - }, - )); - }); - } - - #[test] - fn refund_consistent_with_actual_weight() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(7)) - .build() - .execute_with(|| { - let info = info_from_weight(Weight::from_ref_time(100)); - let post_info = post_info_from_weight(Weight::from_ref_time(33)); - let prev_balance = Balances::free_balance(2); - let len = 10; - let tip = 5; - - >::put(Multiplier::saturating_from_rational(5, 4)); - - let pre = ChargeTransactionPayment::::from(tip) - .pre_dispatch(&2, CALL, &info, len) - .unwrap(); - - ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info, - &post_info, - len, - &Ok(()), - ) - .unwrap(); - - let refund_based_fee = prev_balance - Balances::free_balance(2); - let actual_fee = - Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); - - // 33 weight, 10 length, 7 base, 5 tip - assert_eq!(actual_fee, 7 + 10 + (33 * 5 / 4) + 5); - assert_eq!(refund_based_fee, actual_fee); - }); - } - - #[test] - fn should_alter_operational_priority() { - let tip = 5; - let len = 10; - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let normal = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - - assert_eq!(priority, 60); - - let priority = ChargeTransactionPayment::(2 * tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - - assert_eq!(priority, 110); - }); - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let op = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - assert_eq!(priority, 5810); - - let priority = ChargeTransactionPayment::(2 * tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - assert_eq!(priority, 6110); - }); - } - - #[test] - fn no_tip_has_some_priority() { - let tip = 0; - let len = 10; - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let normal = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - - assert_eq!(priority, 10); - }); - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let op = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - let priority = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - assert_eq!(priority, 5510); - }); - } - - #[test] - fn higher_tip_have_higher_priority() { - let get_priorities = |tip: u64| { - let mut priority1 = 0; - let mut priority2 = 0; - let len = 10; - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let normal = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Normal, - pays_fee: Pays::Yes, - }; - priority1 = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &normal, len) - .unwrap() - .priority; - }); - - ExtBuilder::default().balance_factor(100).build().execute_with(|| { - let op = DispatchInfo { - weight: Weight::from_ref_time(100), - class: DispatchClass::Operational, - pays_fee: Pays::Yes, - }; - priority2 = ChargeTransactionPayment::(tip) - .validate(&2, CALL, &op, len) - .unwrap() - .priority; - }); - - (priority1, priority2) - }; - - let mut prev_priorities = get_priorities(0); - - for tip in 1..3 { - let priorities = get_priorities(tip); - assert!(prev_priorities.0 < priorities.0); - assert!(prev_priorities.1 < priorities.1); - prev_priorities = priorities; - } - } - - #[test] - fn post_info_can_change_pays_fee() { - ExtBuilder::default() - .balance_factor(10) - .base_weight(Weight::from_ref_time(7)) - .build() - .execute_with(|| { - let info = info_from_weight(Weight::from_ref_time(100)); - let post_info = post_info_from_pays(Pays::No); - let prev_balance = Balances::free_balance(2); - let len = 10; - let tip = 5; - - >::put(Multiplier::saturating_from_rational(5, 4)); - - let pre = ChargeTransactionPayment::::from(tip) - .pre_dispatch(&2, CALL, &info, len) - .unwrap(); - - ChargeTransactionPayment::::post_dispatch( - Some(pre), - &info, - &post_info, - len, - &Ok(()), - ) - .unwrap(); - - let refund_based_fee = prev_balance - Balances::free_balance(2); - let actual_fee = - Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); - - // Only 5 tip is paid - assert_eq!(actual_fee, 5); - assert_eq!(refund_based_fee, actual_fee); - }); - } - - #[test] - fn genesis_config_works() { - ExtBuilder::default() - .with_initial_multiplier(Multiplier::from_u32(100)) - .build() - .execute_with(|| { - assert_eq!( - >::get(), - Multiplier::saturating_from_integer(100) - ); - }); - } - - #[test] - fn genesis_default_works() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(>::get(), Multiplier::saturating_from_integer(1)); - }); - } -} diff --git a/frame/transaction-payment/src/mock.rs b/frame/transaction-payment/src/mock.rs new file mode 100644 index 0000000000000..e214458b3766e --- /dev/null +++ b/frame/transaction-payment/src/mock.rs @@ -0,0 +1,162 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use crate as pallet_transaction_payment; + +use sp_core::H256; +use sp_runtime::{ + testing::Header, + traits::{BlakeTwo256, IdentityLookup}, +}; + +use frame_support::{ + dispatch::DispatchClass, + parameter_types, + traits::{ConstU32, ConstU64, Imbalance, OnUnbalanced}, + weights::{Weight, WeightToFee as WeightToFeeT}, +}; +use frame_system as system; +use pallet_balances::Call as BalancesCall; + +type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; +type Block = frame_system::mocking::MockBlock; + +frame_support::construct_runtime!( + pub enum Runtime where + Block = Block, + NodeBlock = Block, + UncheckedExtrinsic = UncheckedExtrinsic, + { + System: system::{Pallet, Call, Config, Storage, Event}, + Balances: pallet_balances::{Pallet, Call, Storage, Config, Event}, + TransactionPayment: pallet_transaction_payment::{Pallet, Storage, Event}, + } +); + +pub(crate) const CALL: &::RuntimeCall = + &RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + +parameter_types! { + pub(crate) static ExtrinsicBaseWeight: Weight = Weight::zero(); +} + +pub struct BlockWeights; +impl Get for BlockWeights { + fn get() -> frame_system::limits::BlockWeights { + frame_system::limits::BlockWeights::builder() + .base_block(Weight::zero()) + .for_class(DispatchClass::all(), |weights| { + weights.base_extrinsic = ExtrinsicBaseWeight::get().into(); + }) + .for_class(DispatchClass::non_mandatory(), |weights| { + weights.max_total = Weight::from_ref_time(1024).set_proof_size(u64::MAX).into(); + }) + .build_or_panic() + } +} + +parameter_types! { + pub static WeightToFee: u64 = 1; + pub static TransactionByteFee: u64 = 1; + pub static OperationalFeeMultiplier: u8 = 5; +} + +impl frame_system::Config for Runtime { + type BaseCallFilter = frame_support::traits::Everything; + type BlockWeights = BlockWeights; + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type Index = u64; + type BlockNumber = u64; + type RuntimeCall = RuntimeCall; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = ConstU64<250>; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = (); + type OnSetCode = (); + type MaxConsumers = ConstU32<16>; +} + +impl pallet_balances::Config for Runtime { + type Balance = u64; + type RuntimeEvent = RuntimeEvent; + type DustRemoval = (); + type ExistentialDeposit = ConstU64<1>; + type AccountStore = System; + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type WeightInfo = (); +} + +impl WeightToFeeT for WeightToFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(WEIGHT_TO_FEE.with(|v| *v.borrow())) + } +} + +impl WeightToFeeT for TransactionByteFee { + type Balance = u64; + + fn weight_to_fee(weight: &Weight) -> Self::Balance { + Self::Balance::saturated_from(weight.ref_time()) + .saturating_mul(TRANSACTION_BYTE_FEE.with(|v| *v.borrow())) + } +} + +parameter_types! { + pub(crate) static TipUnbalancedAmount: u64 = 0; + pub(crate) static FeeUnbalancedAmount: u64 = 0; +} + +pub struct DealWithFees; +impl OnUnbalanced> for DealWithFees { + fn on_unbalanceds( + mut fees_then_tips: impl Iterator>, + ) { + if let Some(fees) = fees_then_tips.next() { + FeeUnbalancedAmount::mutate(|a| *a += fees.peek()); + if let Some(tips) = fees_then_tips.next() { + TipUnbalancedAmount::mutate(|a| *a += tips.peek()); + } + } + } +} + +impl Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type OnChargeTransaction = CurrencyAdapter; + type OperationalFeeMultiplier = OperationalFeeMultiplier; + type WeightToFee = WeightToFee; + type LengthToFee = TransactionByteFee; + type FeeMultiplierUpdate = (); +} diff --git a/frame/transaction-payment/src/payment.rs b/frame/transaction-payment/src/payment.rs index ebc9c5c5afd62..bc871deafdc8b 100644 --- a/frame/transaction-payment/src/payment.rs +++ b/frame/transaction-payment/src/payment.rs @@ -1,15 +1,11 @@ /// ! Traits and default implementation for paying transaction fees. use crate::Config; -use codec::FullCodec; use sp_runtime::{ - traits::{ - AtLeast32BitUnsigned, DispatchInfoOf, MaybeSerializeDeserialize, PostDispatchInfoOf, - Saturating, Zero, - }, + traits::{DispatchInfoOf, PostDispatchInfoOf, Saturating, Zero}, transaction_validity::InvalidTransaction, }; -use sp_std::{fmt::Debug, marker::PhantomData}; +use sp_std::marker::PhantomData; use frame_support::{ traits::{Currency, ExistenceRequirement, Imbalance, OnUnbalanced, WithdrawReasons}, @@ -22,13 +18,8 @@ type NegativeImbalanceOf = /// Handle withdrawing, refunding and depositing of transaction fees. pub trait OnChargeTransaction { /// The underlying integer type in which fees are calculated. - type Balance: AtLeast32BitUnsigned - + FullCodec - + Copy - + MaybeSerializeDeserialize - + Debug - + Default - + scale_info::TypeInfo; + type Balance: frame_support::traits::tokens::Balance; + type LiquidityInfo: Default; /// Before the transaction is executed the payment of the transaction fees diff --git a/frame/transaction-payment/src/tests.rs b/frame/transaction-payment/src/tests.rs new file mode 100644 index 0000000000000..ee54c1130e699 --- /dev/null +++ b/frame/transaction-payment/src/tests.rs @@ -0,0 +1,836 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: Apache-2.0 + +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::*; +use crate as pallet_transaction_payment; + +use codec::Encode; + +use sp_runtime::{testing::TestXt, traits::One, transaction_validity::InvalidTransaction}; + +use frame_support::{ + assert_noop, assert_ok, + dispatch::{DispatchClass, DispatchInfo, GetDispatchInfo, PostDispatchInfo}, + traits::{Currency, GenesisBuild}, + weights::Weight, +}; +use frame_system as system; +use mock::*; +use pallet_balances::Call as BalancesCall; + +pub struct ExtBuilder { + balance_factor: u64, + base_weight: Weight, + byte_fee: u64, + weight_to_fee: u64, + initial_multiplier: Option, +} + +impl Default for ExtBuilder { + fn default() -> Self { + Self { + balance_factor: 1, + base_weight: Weight::zero(), + byte_fee: 1, + weight_to_fee: 1, + initial_multiplier: None, + } + } +} + +impl ExtBuilder { + pub fn base_weight(mut self, base_weight: Weight) -> Self { + self.base_weight = base_weight; + self + } + pub fn byte_fee(mut self, byte_fee: u64) -> Self { + self.byte_fee = byte_fee; + self + } + pub fn weight_fee(mut self, weight_to_fee: u64) -> Self { + self.weight_to_fee = weight_to_fee; + self + } + pub fn balance_factor(mut self, factor: u64) -> Self { + self.balance_factor = factor; + self + } + pub fn with_initial_multiplier(mut self, multiplier: Multiplier) -> Self { + self.initial_multiplier = Some(multiplier); + self + } + fn set_constants(&self) { + ExtrinsicBaseWeight::mutate(|v| *v = self.base_weight); + TRANSACTION_BYTE_FEE.with(|v| *v.borrow_mut() = self.byte_fee); + WEIGHT_TO_FEE.with(|v| *v.borrow_mut() = self.weight_to_fee); + } + pub fn build(self) -> sp_io::TestExternalities { + self.set_constants(); + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig:: { + balances: if self.balance_factor > 0 { + vec![ + (1, 10 * self.balance_factor), + (2, 20 * self.balance_factor), + (3, 30 * self.balance_factor), + (4, 40 * self.balance_factor), + (5, 50 * self.balance_factor), + (6, 60 * self.balance_factor), + ] + } else { + vec![] + }, + } + .assimilate_storage(&mut t) + .unwrap(); + + if let Some(multiplier) = self.initial_multiplier { + let genesis = pallet::GenesisConfig { multiplier }; + GenesisBuild::::assimilate_storage(&genesis, &mut t).unwrap(); + } + + t.into() + } +} + +/// create a transaction info struct from weight. Handy to avoid building the whole struct. +pub fn info_from_weight(w: Weight) -> DispatchInfo { + // pays_fee: Pays::Yes -- class: DispatchClass::Normal + DispatchInfo { weight: w, ..Default::default() } +} + +fn post_info_from_weight(w: Weight) -> PostDispatchInfo { + PostDispatchInfo { actual_weight: Some(w), pays_fee: Default::default() } +} + +fn post_info_from_pays(p: Pays) -> PostDispatchInfo { + PostDispatchInfo { actual_weight: None, pays_fee: p } +} + +fn default_post_info() -> PostDispatchInfo { + PostDispatchInfo { actual_weight: None, pays_fee: Default::default() } +} + +#[test] +fn signed_extension_transaction_payment_work() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + let len = 10; + let pre = ChargeTransactionPayment::::from(0) + .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(5)), len) + .unwrap(); + assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(5)), + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(1), 100 - 5 - 5 - 10); + assert_eq!(FeeUnbalancedAmount::get(), 5 + 5 + 10); + assert_eq!(TipUnbalancedAmount::get(), 0); + + FeeUnbalancedAmount::mutate(|a| *a = 0); + + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(50)), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 50 - 5); + assert_eq!(FeeUnbalancedAmount::get(), 5 + 10 + 50); + assert_eq!(TipUnbalancedAmount::get(), 5); + }); +} + +#[test] +fn signed_extension_transaction_payment_multiplied_refund_works() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + let len = 10; + >::put(Multiplier::saturating_from_rational(3, 2)); + + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + // 5 base fee, 10 byte fee, 3/2 * 100 weight fee, 5 tip + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 150 - 5); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(50)), + len, + &Ok(()) + )); + // 75 (3/2 of the returned 50 units of weight) is refunded + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 75 - 5); + }); +} + +#[test] +fn signed_extension_transaction_payment_is_bounded() { + ExtBuilder::default().balance_factor(1000).byte_fee(0).build().execute_with(|| { + // maximum weight possible + assert_ok!(ChargeTransactionPayment::::from(0).pre_dispatch( + &1, + CALL, + &info_from_weight(Weight::MAX), + 10 + )); + // fee will be proportional to what is the actual maximum weight in the runtime. + assert_eq!( + Balances::free_balance(&1), + (10000 - ::BlockWeights::get().max_block.ref_time()) + as u64 + ); + }); +} + +#[test] +fn signed_extension_allows_free_transactions() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .balance_factor(0) + .build() + .execute_with(|| { + // 1 ain't have a penny. + assert_eq!(Balances::free_balance(1), 0); + + let len = 100; + + // This is a completely free (and thus wholly insecure/DoS-ridden) transaction. + let operational_transaction = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::No, + }; + assert_ok!(ChargeTransactionPayment::::from(0).validate( + &1, + CALL, + &operational_transaction, + len + )); + + // like a InsecureFreeNormal + let free_transaction = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + assert_noop!( + ChargeTransactionPayment::::from(0).validate( + &1, + CALL, + &free_transaction, + len + ), + TransactionValidityError::Invalid(InvalidTransaction::Payment), + ); + }); +} + +#[test] +fn signed_ext_length_fee_is_also_updated_per_congestion() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(5)) + .balance_factor(10) + .build() + .execute_with(|| { + // all fees should be x1.5 + >::put(Multiplier::saturating_from_rational(3, 2)); + let len = 10; + + assert_ok!(ChargeTransactionPayment::::from(10) // tipped + .pre_dispatch(&1, CALL, &info_from_weight(Weight::from_ref_time(3)), len)); + assert_eq!( + Balances::free_balance(1), + 100 // original + - 10 // tip + - 5 // base + - 10 // len + - (3 * 3 / 2) // adjusted weight + ); + }) +} + +#[test] +fn query_info_and_fee_details_works() { + let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + let origin = 111111; + let extra = (); + let xt = TestXt::new(call.clone(), Some((origin, extra))); + let info = xt.get_dispatch_info(); + let ext = xt.encode(); + let len = ext.len() as u32; + + let unsigned_xt = TestXt::<_, ()>::new(call, None); + let unsigned_xt_info = unsigned_xt.get_dispatch_info(); + + ExtBuilder::default() + .base_weight(Weight::from_ref_time(5)) + .weight_fee(2) + .build() + .execute_with(|| { + // all fees should be x1.5 + >::put(Multiplier::saturating_from_rational(3, 2)); + + assert_eq!( + TransactionPayment::query_info(xt.clone(), len), + RuntimeDispatchInfo { + weight: info.weight, + class: info.class, + partial_fee: 5 * 2 /* base * weight_fee */ + + len as u64 /* len * 1 */ + + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ + }, + ); + + assert_eq!( + TransactionPayment::query_info(unsigned_xt.clone(), len), + RuntimeDispatchInfo { + weight: unsigned_xt_info.weight, + class: unsigned_xt_info.class, + partial_fee: 0, + }, + ); + + assert_eq!( + TransactionPayment::query_fee_details(xt, len), + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: 5 * 2, + len_fee: len as u64, + adjusted_weight_fee: info + .weight + .min(BlockWeights::get().max_block) + .ref_time() as u64 * 2 * 3 / 2 + }), + tip: 0, + }, + ); + + assert_eq!( + TransactionPayment::query_fee_details(unsigned_xt, len), + FeeDetails { inclusion_fee: None, tip: 0 }, + ); + }); +} + +#[test] +fn query_call_info_and_fee_details_works() { + let call = RuntimeCall::Balances(BalancesCall::transfer { dest: 2, value: 69 }); + let info = call.get_dispatch_info(); + let encoded_call = call.encode(); + let len = encoded_call.len() as u32; + + ExtBuilder::default() + .base_weight(Weight::from_ref_time(5)) + .weight_fee(2) + .build() + .execute_with(|| { + // all fees should be x1.5 + >::put(Multiplier::saturating_from_rational(3, 2)); + + assert_eq!( + TransactionPayment::query_call_info(call.clone(), len), + RuntimeDispatchInfo { + weight: info.weight, + class: info.class, + partial_fee: 5 * 2 /* base * weight_fee */ + + len as u64 /* len * 1 */ + + info.weight.min(BlockWeights::get().max_block).ref_time() as u64 * 2 * 3 / 2 /* weight */ + }, + ); + + assert_eq!( + TransactionPayment::query_call_fee_details(call, len), + FeeDetails { + inclusion_fee: Some(InclusionFee { + base_fee: 5 * 2, /* base * weight_fee */ + len_fee: len as u64, /* len * 1 */ + adjusted_weight_fee: info + .weight + .min(BlockWeights::get().max_block) + .ref_time() as u64 * 2 * 3 / 2 /* weight * weight_fee * multipler */ + }), + tip: 0, + }, + ); + }); +} + +#[test] +fn compute_fee_works_without_multiplier() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Next fee multiplier is zero + assert_eq!(>::get(), Multiplier::one()); + + // Tip only, no fees works + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::No, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 10), 10); + // No tip, only base fee works + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); + // Tip + base fee works + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 69), 169); + // Len (byte fee) + base fee works + assert_eq!(Pallet::::compute_fee(42, &dispatch_info, 0), 520); + // Weight fee + base fee works + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(1000), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 1100); + }); +} + +#[test] +fn compute_fee_works_with_multiplier() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Add a next fee multiplier. Fees will be x3/2. + >::put(Multiplier::saturating_from_rational(3, 2)); + // Base fee is unaffected by multiplier + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); + + // Everything works together :) + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(123), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + // 123 weight, 456 length, 100 base + assert_eq!( + Pallet::::compute_fee(456, &dispatch_info, 789), + 100 + (3 * 123 / 2) + 4560 + 789, + ); + }); +} + +#[test] +fn compute_fee_works_with_negative_multiplier() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Add a next fee multiplier. All fees will be x1/2. + >::put(Multiplier::saturating_from_rational(1, 2)); + + // Base fee is unaffected by multiplier. + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(0), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!(Pallet::::compute_fee(0, &dispatch_info, 0), 100); + + // Everything works together. + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(123), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + // 123 weight, 456 length, 100 base + assert_eq!( + Pallet::::compute_fee(456, &dispatch_info, 789), + 100 + (123 / 2) + 4560 + 789, + ); + }); +} + +#[test] +fn compute_fee_does_not_overflow() { + ExtBuilder::default() + .base_weight(Weight::from_ref_time(100)) + .byte_fee(10) + .balance_factor(0) + .build() + .execute_with(|| { + // Overflow is handled + let dispatch_info = DispatchInfo { + weight: Weight::MAX, + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + assert_eq!( + Pallet::::compute_fee(u32::MAX, &dispatch_info, u64::MAX), + u64::MAX + ); + }); +} + +#[test] +fn refund_does_not_recreate_account() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + // So events are emitted + System::set_block_number(10); + let len = 10; + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + + // kill the account between pre and post dispatch + assert_ok!(Balances::transfer(Some(2).into(), 3, Balances::free_balance(2))); + assert_eq!(Balances::free_balance(2), 0); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(50)), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(2), 0); + // Transfer Event + System::assert_has_event(RuntimeEvent::Balances(pallet_balances::Event::Transfer { + from: 2, + to: 3, + amount: 80, + })); + // Killed Event + System::assert_has_event(RuntimeEvent::System(system::Event::KilledAccount { + account: 2, + })); + }); +} + +#[test] +fn actual_weight_higher_than_max_refunds_nothing() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + let len = 10; + let pre = ChargeTransactionPayment::::from(5 /* tipped */) + .pre_dispatch(&2, CALL, &info_from_weight(Weight::from_ref_time(100)), len) + .unwrap(); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info_from_weight(Weight::from_ref_time(100)), + &post_info_from_weight(Weight::from_ref_time(101)), + len, + &Ok(()) + )); + assert_eq!(Balances::free_balance(2), 200 - 5 - 10 - 100 - 5); + }); +} + +#[test] +fn zero_transfer_on_free_transaction() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(5)) + .build() + .execute_with(|| { + // So events are emitted + System::set_block_number(10); + let len = 10; + let dispatch_info = DispatchInfo { + weight: Weight::from_ref_time(100), + pays_fee: Pays::No, + class: DispatchClass::Normal, + }; + let user = 69; + let pre = ChargeTransactionPayment::::from(0) + .pre_dispatch(&user, CALL, &dispatch_info, len) + .unwrap(); + assert_eq!(Balances::total_balance(&user), 0); + assert_ok!(ChargeTransactionPayment::::post_dispatch( + Some(pre), + &dispatch_info, + &default_post_info(), + len, + &Ok(()) + )); + assert_eq!(Balances::total_balance(&user), 0); + // TransactionFeePaid Event + System::assert_has_event(RuntimeEvent::TransactionPayment( + pallet_transaction_payment::Event::TransactionFeePaid { + who: user, + actual_fee: 0, + tip: 0, + }, + )); + }); +} + +#[test] +fn refund_consistent_with_actual_weight() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(7)) + .build() + .execute_with(|| { + let info = info_from_weight(Weight::from_ref_time(100)); + let post_info = post_info_from_weight(Weight::from_ref_time(33)); + let prev_balance = Balances::free_balance(2); + let len = 10; + let tip = 5; + + >::put(Multiplier::saturating_from_rational(5, 4)); + + let pre = ChargeTransactionPayment::::from(tip) + .pre_dispatch(&2, CALL, &info, len) + .unwrap(); + + ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info, + &post_info, + len, + &Ok(()), + ) + .unwrap(); + + let refund_based_fee = prev_balance - Balances::free_balance(2); + let actual_fee = + Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); + + // 33 weight, 10 length, 7 base, 5 tip + assert_eq!(actual_fee, 7 + 10 + (33 * 5 / 4) + 5); + assert_eq!(refund_based_fee, actual_fee); + }); +} + +#[test] +fn should_alter_operational_priority() { + let tip = 5; + let len = 10; + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let normal = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + + assert_eq!(priority, 60); + + let priority = ChargeTransactionPayment::(2 * tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + + assert_eq!(priority, 110); + }); + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let op = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + assert_eq!(priority, 5810); + + let priority = ChargeTransactionPayment::(2 * tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + assert_eq!(priority, 6110); + }); +} + +#[test] +fn no_tip_has_some_priority() { + let tip = 0; + let len = 10; + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let normal = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + + assert_eq!(priority, 10); + }); + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let op = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + let priority = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + assert_eq!(priority, 5510); + }); +} + +#[test] +fn higher_tip_have_higher_priority() { + let get_priorities = |tip: u64| { + let mut priority1 = 0; + let mut priority2 = 0; + let len = 10; + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let normal = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Normal, + pays_fee: Pays::Yes, + }; + priority1 = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &normal, len) + .unwrap() + .priority; + }); + + ExtBuilder::default().balance_factor(100).build().execute_with(|| { + let op = DispatchInfo { + weight: Weight::from_ref_time(100), + class: DispatchClass::Operational, + pays_fee: Pays::Yes, + }; + priority2 = ChargeTransactionPayment::(tip) + .validate(&2, CALL, &op, len) + .unwrap() + .priority; + }); + + (priority1, priority2) + }; + + let mut prev_priorities = get_priorities(0); + + for tip in 1..3 { + let priorities = get_priorities(tip); + assert!(prev_priorities.0 < priorities.0); + assert!(prev_priorities.1 < priorities.1); + prev_priorities = priorities; + } +} + +#[test] +fn post_info_can_change_pays_fee() { + ExtBuilder::default() + .balance_factor(10) + .base_weight(Weight::from_ref_time(7)) + .build() + .execute_with(|| { + let info = info_from_weight(Weight::from_ref_time(100)); + let post_info = post_info_from_pays(Pays::No); + let prev_balance = Balances::free_balance(2); + let len = 10; + let tip = 5; + + >::put(Multiplier::saturating_from_rational(5, 4)); + + let pre = ChargeTransactionPayment::::from(tip) + .pre_dispatch(&2, CALL, &info, len) + .unwrap(); + + ChargeTransactionPayment::::post_dispatch( + Some(pre), + &info, + &post_info, + len, + &Ok(()), + ) + .unwrap(); + + let refund_based_fee = prev_balance - Balances::free_balance(2); + let actual_fee = + Pallet::::compute_actual_fee(len as u32, &info, &post_info, tip); + + // Only 5 tip is paid + assert_eq!(actual_fee, 5); + assert_eq!(refund_based_fee, actual_fee); + }); +} + +#[test] +fn genesis_config_works() { + ExtBuilder::default() + .with_initial_multiplier(Multiplier::from_u32(100)) + .build() + .execute_with(|| { + assert_eq!( + >::get(), + Multiplier::saturating_from_integer(100) + ); + }); +} + +#[test] +fn genesis_default_works() { + ExtBuilder::default().build().execute_with(|| { + assert_eq!(>::get(), Multiplier::saturating_from_integer(1)); + }); +} diff --git a/frame/transaction-storage/Cargo.toml b/frame/transaction-storage/Cargo.toml index 73867c3643a69..527ff4f240169 100644 --- a/frame/transaction-storage/Cargo.toml +++ b/frame/transaction-storage/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = { version = "4.1", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/treasury/Cargo.toml b/frame/treasury/Cargo.toml index 993f89ff0faa5..1fd1d7b3d4620 100644 --- a/frame/treasury/Cargo.toml +++ b/frame/treasury/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/frame/try-runtime/Cargo.toml b/frame/try-runtime/Cargo.toml index 87aca0d1ed9f0..042dba5ed88ad 100644 --- a/frame/try-runtime/Cargo.toml +++ b/frame/try-runtime/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME pallet for democracy" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"]} +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"]} frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../primitives/runtime" } diff --git a/frame/uniques/Cargo.toml b/frame/uniques/Cargo.toml index 6e36240748c4b..a01ea60d8c3e5 100644 --- a/frame/uniques/Cargo.toml +++ b/frame/uniques/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } diff --git a/frame/utility/Cargo.toml b/frame/utility/Cargo.toml index de293ed5df8af..099526d3dcb9e 100644 --- a/frame/utility/Cargo.toml +++ b/frame/utility/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/frame/vesting/Cargo.toml b/frame/vesting/Cargo.toml index 23fa06454b23c..c18d1f45e038b 100644 --- a/frame/vesting/Cargo.toml +++ b/frame/vesting/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", ] } log = { version = "0.4.17", default-features = false } diff --git a/frame/whitelist/Cargo.toml b/frame/whitelist/Cargo.toml index f3f44d3187a8e..56ceaca9f81a4 100644 --- a/frame/whitelist/Cargo.toml +++ b/frame/whitelist/Cargo.toml @@ -12,7 +12,7 @@ description = "FRAME pallet for whitelisting call, and dispatch from specific or targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.1", default-features = false, features = ["derive"] } frame-benchmarking = { version = "4.0.0-dev", default-features = false, optional = true, path = "../benchmarking" } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } diff --git a/primitives/api/Cargo.toml b/primitives/api/Cargo.toml index 3139c66cef921..75197bcaea900 100644 --- a/primitives/api/Cargo.toml +++ b/primitives/api/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } sp-api-proc-macro = { version = "4.0.0-dev", path = "proc-macro" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } diff --git a/primitives/api/src/lib.rs b/primitives/api/src/lib.rs index 91d4b07a1cefc..4ff4becb80f48 100644 --- a/primitives/api/src/lib.rs +++ b/primitives/api/src/lib.rs @@ -519,6 +519,8 @@ pub enum ApiError { StateBackendIsNotTrie, #[error(transparent)] Application(#[from] Box), + #[error("Api called for an unknown Block: {0}")] + UnknownBlock(String), } /// Extends the runtime api implementation with some common functionality. diff --git a/primitives/api/test/Cargo.toml b/primitives/api/test/Cargo.toml index 7f15287cf36d6..b3ceb14fe0d66 100644 --- a/primitives/api/test/Cargo.toml +++ b/primitives/api/test/Cargo.toml @@ -19,7 +19,7 @@ sp-tracing = { version = "6.0.0", path = "../../tracing" } sp-runtime = { version = "7.0.0", path = "../../runtime" } sp-consensus = { version = "0.10.0-dev", path = "../../consensus/common" } sc-block-builder = { version = "0.10.0-dev", path = "../../../client/block-builder" } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } sp-state-machine = { version = "0.13.0", path = "../../state-machine" } trybuild = "1.0.74" rustversion = "1.0.6" diff --git a/primitives/application-crypto/Cargo.toml b/primitives/application-crypto/Cargo.toml index 39a3413bcf981..e7aa118dfd6ac 100644 --- a/primitives/application-crypto/Cargo.toml +++ b/primitives/application-crypto/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] sp-core = { version = "7.0.0", default-features = false, path = "../core" } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true, features = ["derive"] } sp-std = { version = "5.0.0", default-features = false, path = "../std" } diff --git a/primitives/arithmetic/Cargo.toml b/primitives/arithmetic/Cargo.toml index 2da8767fd2a7d..bbb4bbd1776b1 100644 --- a/primitives/arithmetic/Cargo.toml +++ b/primitives/arithmetic/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/primitives/arithmetic/src/lib.rs b/primitives/arithmetic/src/lib.rs index f1f8fc8b59830..590aaa3166448 100644 --- a/primitives/arithmetic/src/lib.rs +++ b/primitives/arithmetic/src/lib.rs @@ -50,14 +50,14 @@ pub use rational::{Rational128, RationalInfinite}; use sp_std::{cmp::Ordering, fmt::Debug, prelude::*}; use traits::{BaseArithmetic, One, SaturatedConversion, Unsigned, Zero}; -use codec::{Decode, Encode}; +use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; /// Arithmetic errors. -#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum ArithmeticError { /// Underflow. diff --git a/primitives/authority-discovery/Cargo.toml b/primitives/authority-discovery/Cargo.toml index 4b450a4da4a88..ff36cf58d366e 100644 --- a/primitives/authority-discovery/Cargo.toml +++ b/primitives/authority-discovery/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } diff --git a/primitives/authorship/Cargo.toml b/primitives/authorship/Cargo.toml index 49107ebed1db0..31ea3b2d4c8f3 100644 --- a/primitives/authorship/Cargo.toml +++ b/primitives/authorship/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../inherents" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } diff --git a/primitives/beefy/Cargo.toml b/primitives/beefy/Cargo.toml index b286d9878b44e..6a22f0383a3d0 100644 --- a/primitives/beefy/Cargo.toml +++ b/primitives/beefy/Cargo.toml @@ -12,7 +12,7 @@ description = "Primitives for BEEFY protocol." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } diff --git a/primitives/beefy/src/lib.rs b/primitives/beefy/src/lib.rs index d7ac091491bff..eb7b8db89b214 100644 --- a/primitives/beefy/src/lib.rs +++ b/primitives/beefy/src/lib.rs @@ -49,20 +49,14 @@ use sp_std::prelude::*; /// Key type for BEEFY module. pub const KEY_TYPE: sp_application_crypto::KeyTypeId = sp_application_crypto::KeyTypeId(*b"beef"); -/// Trait representing BEEFY authority id. -pub trait BeefyAuthorityId: RuntimeAppPublic {} - -/// Means of verification for a BEEFY authority signature. +/// Trait representing BEEFY authority id, including custom signature verification. /// /// Accepts custom hashing fn for the message and custom convertor fn for the signer. -pub trait BeefyVerify { - /// Type of the signer. - type Signer: BeefyAuthorityId; - +pub trait BeefyAuthorityId: RuntimeAppPublic { /// Verify a signature. /// - /// Return `true` if signature is valid for the value. - fn verify(&self, msg: &[u8], signer: &Self::Signer) -> bool; + /// Return `true` if signature over `msg` is valid for this id. + fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool; } /// BEEFY cryptographic types @@ -78,7 +72,7 @@ pub trait BeefyVerify { /// The current underlying crypto scheme used is ECDSA. This can be changed, /// without affecting code restricted against the above listed crypto types. pub mod crypto { - use super::{BeefyAuthorityId, BeefyVerify, Hash}; + use super::{BeefyAuthorityId, Hash, RuntimeAppPublic}; use sp_application_crypto::{app_crypto, ecdsa}; use sp_core::crypto::Wraps; app_crypto!(ecdsa, crate::KEY_TYPE); @@ -89,21 +83,17 @@ pub mod crypto { /// Signature for a BEEFY authority using ECDSA as its crypto. pub type AuthoritySignature = Signature; - impl BeefyAuthorityId for AuthorityId {} - - impl BeefyVerify for AuthoritySignature + impl BeefyAuthorityId for AuthorityId where ::Output: Into<[u8; 32]>, { - type Signer = AuthorityId; - - fn verify(&self, msg: &[u8], signer: &Self::Signer) -> bool { + fn verify(&self, signature: &::Signature, msg: &[u8]) -> bool { let msg_hash = ::hash(msg).into(); match sp_io::crypto::secp256k1_ecdsa_recover_compressed( - self.as_inner_ref().as_ref(), + signature.as_inner_ref().as_ref(), &msg_hash, ) { - Ok(raw_pubkey) => raw_pubkey.as_ref() == AsRef::<[u8]>::as_ref(signer), + Ok(raw_pubkey) => raw_pubkey.as_ref() == AsRef::<[u8]>::as_ref(self), _ => false, } } @@ -248,23 +238,31 @@ mod tests { pair.as_inner_ref().sign_prehashed(&blake2_256(msg)).into(); // Verification works if same hashing function is used when signing and verifying. - assert!(BeefyVerify::::verify(&keccak_256_signature, msg, &pair.public())); - assert!(BeefyVerify::::verify(&blake2_256_signature, msg, &pair.public())); + assert!(BeefyAuthorityId::::verify(&pair.public(), &keccak_256_signature, msg)); + assert!(BeefyAuthorityId::::verify( + &pair.public(), + &blake2_256_signature, + msg + )); // Verification fails if distinct hashing functions are used when signing and verifying. - assert!(!BeefyVerify::::verify(&blake2_256_signature, msg, &pair.public())); - assert!(!BeefyVerify::::verify(&keccak_256_signature, msg, &pair.public())); + assert!(!BeefyAuthorityId::::verify(&pair.public(), &blake2_256_signature, msg)); + assert!(!BeefyAuthorityId::::verify( + &pair.public(), + &keccak_256_signature, + msg + )); // Other public key doesn't work let (other_pair, _) = crypto::Pair::generate(); - assert!(!BeefyVerify::::verify( + assert!(!BeefyAuthorityId::::verify( + &other_pair.public(), &keccak_256_signature, msg, - &other_pair.public() )); - assert!(!BeefyVerify::::verify( + assert!(!BeefyAuthorityId::::verify( + &other_pair.public(), &blake2_256_signature, msg, - &other_pair.public() )); } } diff --git a/primitives/block-builder/Cargo.toml b/primitives/block-builder/Cargo.toml index 7770a0e210c63..6ccb7980df36e 100644 --- a/primitives/block-builder/Cargo.toml +++ b/primitives/block-builder/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../inherents" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } diff --git a/primitives/blockchain/Cargo.toml b/primitives/blockchain/Cargo.toml index 7f5f22fe09b73..68791852d8458 100644 --- a/primitives/blockchain/Cargo.toml +++ b/primitives/blockchain/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } futures = "0.3.21" log = "0.4.17" lru = "0.8.1" diff --git a/primitives/blockchain/src/error.rs b/primitives/blockchain/src/error.rs index 783c40c4061ad..6585cc54f1582 100644 --- a/primitives/blockchain/src/error.rs +++ b/primitives/blockchain/src/error.rs @@ -191,6 +191,7 @@ impl From> for Error { impl From for ApiError { fn from(err: Error) -> ApiError { match err { + Error::UnknownBlock(msg) => ApiError::UnknownBlock(msg), Error::RuntimeApiError(err) => err, e => ApiError::Application(Box::new(e)), } diff --git a/primitives/consensus/aura/Cargo.toml b/primitives/consensus/aura/Cargo.toml index 51b20e4fb7e01..001dc4a69aca4 100644 --- a/primitives/consensus/aura/Cargo.toml +++ b/primitives/consensus/aura/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../../application-crypto" } diff --git a/primitives/consensus/babe/Cargo.toml b/primitives/consensus/babe/Cargo.toml index 25cb8a2bf64da..334fff2811941 100644 --- a/primitives/consensus/babe/Cargo.toml +++ b/primitives/consensus/babe/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } merlin = { version = "2.0", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } diff --git a/primitives/consensus/common/Cargo.toml b/primitives/consensus/common/Cargo.toml index 9b4cac722adcd..83b09f9eb47e2 100644 --- a/primitives/consensus/common/Cargo.toml +++ b/primitives/consensus/common/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive", ] } futures = { version = "0.3.21", features = ["thread-pool"] } diff --git a/primitives/consensus/pow/Cargo.toml b/primitives/consensus/pow/Cargo.toml index 495372089e195..6cc832b5afa5f 100644 --- a/primitives/consensus/pow/Cargo.toml +++ b/primitives/consensus/pow/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../api" } sp-core = { version = "7.0.0", default-features = false, path = "../../core" } sp-runtime = { version = "7.0.0", default-features = false, path = "../../runtime" } diff --git a/primitives/consensus/slots/Cargo.toml b/primitives/consensus/slots/Cargo.toml index 3360e80772084..a4d85dff6ea85 100644 --- a/primitives/consensus/slots/Cargo.toml +++ b/primitives/consensus/slots/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } scale-info = { version = "2.0.0", default-features = false, features = ["derive"] } serde = { version = "1.0", features = ["derive"], optional = true } sp-std = { version = "5.0.0", default-features = false, path = "../../std" } diff --git a/primitives/consensus/vrf/Cargo.toml b/primitives/consensus/vrf/Cargo.toml index 7159da2aa1883..a4f92c30cca03 100644 --- a/primitives/consensus/vrf/Cargo.toml +++ b/primitives/consensus/vrf/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false } schnorrkel = { version = "0.9.1", default-features = false, features = ["preaudit_deprecated", "u64_backend"] } sp-core = { version = "7.0.0", default-features = false, path = "../../core" } diff --git a/primitives/core/Cargo.toml b/primitives/core/Cargo.toml index 68c780c473f0a..f9a522178f4c8 100644 --- a/primitives/core/Cargo.toml +++ b/primitives/core/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/sp-core" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = [ +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = [ "derive", "max-encoded-len", ] } diff --git a/primitives/core/src/crypto.rs b/primitives/core/src/crypto.rs index 06703acea7202..52c805bb6119f 100644 --- a/primitives/core/src/crypto.rs +++ b/primitives/core/src/crypto.rs @@ -84,20 +84,27 @@ impl> UncheckedInto for S { } /// An error with the interpretation of a secret. +#[cfg_attr(feature = "std", derive(thiserror::Error))] #[derive(Debug, Clone, PartialEq, Eq)] #[cfg(feature = "full_crypto")] pub enum SecretStringError { /// The overall format was invalid (e.g. the seed phrase contained symbols). + #[cfg_attr(feature = "std", error("Invalid format"))] InvalidFormat, /// The seed phrase provided is not a valid BIP39 phrase. + #[cfg_attr(feature = "std", error("Invalid phrase"))] InvalidPhrase, /// The supplied password was invalid. + #[cfg_attr(feature = "std", error("Invalid password"))] InvalidPassword, /// The seed is invalid (bad content). + #[cfg_attr(feature = "std", error("Invalid seed"))] InvalidSeed, /// The seed has an invalid length. + #[cfg_attr(feature = "std", error("Invalid seed length"))] InvalidSeedLength, /// The derivation path was invalid (e.g. contains soft junctions when they are not supported). + #[cfg_attr(feature = "std", error("Invalid path"))] InvalidPath, } diff --git a/primitives/externalities/Cargo.toml b/primitives/externalities/Cargo.toml index c3d32370dc32f..0777111d88c22 100644 --- a/primitives/externalities/Cargo.toml +++ b/primitives/externalities/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } environmental = { version = "1.1.3", default-features = false } sp-std = { version = "5.0.0", default-features = false, path = "../std" } sp-storage = { version = "7.0.0", default-features = false, path = "../storage" } diff --git a/primitives/finality-grandpa/Cargo.toml b/primitives/finality-grandpa/Cargo.toml index c33646e3cd2b8..39bb6713a2394 100644 --- a/primitives/finality-grandpa/Cargo.toml +++ b/primitives/finality-grandpa/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } grandpa = { package = "finality-grandpa", version = "0.16.1", default-features = false, features = ["derive-codec"] } log = { version = "0.4.17", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/primitives/inherents/Cargo.toml b/primitives/inherents/Cargo.toml index 8f6d8aef155ac..c901a6e68a181 100644 --- a/primitives/inherents/Cargo.toml +++ b/primitives/inherents/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" thiserror = { version = "1.0.30", optional = true } sp-core = { version = "7.0.0", default-features = false, path = "../core" } diff --git a/primitives/io/Cargo.toml b/primitives/io/Cargo.toml index d83c82c02227b..e56bfcf56041a 100644 --- a/primitives/io/Cargo.toml +++ b/primitives/io/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] bytes = { version = "1.1.0", default-features = false } -codec = { package = "parity-scale-codec", version = "3.1.3", default-features = false, features = ["bytes"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["bytes"] } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-keystore = { version = "0.13.0", default-features = false, optional = true, path = "../keystore" } sp-std = { version = "5.0.0", default-features = false, path = "../std" } diff --git a/primitives/keystore/Cargo.toml b/primitives/keystore/Cargo.toml index 0d5d7ca5637eb..9386cb5d104d2 100644 --- a/primitives/keystore/Cargo.toml +++ b/primitives/keystore/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } futures = "0.3.21" merlin = { version = "2.0", default-features = false } parking_lot = { version = "0.12.1", default-features = false } diff --git a/primitives/merkle-mountain-range/Cargo.toml b/primitives/merkle-mountain-range/Cargo.toml index 2e532990a5caf..97add1ed1d37c 100644 --- a/primitives/merkle-mountain-range/Cargo.toml +++ b/primitives/merkle-mountain-range/Cargo.toml @@ -12,7 +12,7 @@ description = "Merkle Mountain Range primitives." targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } mmr-lib = { package = "ckb-merkle-mountain-range", version = "0.5.2", default-features = false } diff --git a/primitives/npos-elections/Cargo.toml b/primitives/npos-elections/Cargo.toml index 702986da309ef..7a4bdbd679f0e 100644 --- a/primitives/npos-elections/Cargo.toml +++ b/primitives/npos-elections/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-arithmetic = { version = "6.0.0", default-features = false, path = "../arithmetic" } diff --git a/primitives/npos-elections/fuzzer/Cargo.toml b/primitives/npos-elections/fuzzer/Cargo.toml index 860ed6b18810d..8a058cc92edcf 100644 --- a/primitives/npos-elections/fuzzer/Cargo.toml +++ b/primitives/npos-elections/fuzzer/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] clap = { version = "4.0.9", features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } honggfuzz = "0.5" rand = { version = "0.8", features = ["std", "small_rng"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/primitives/runtime-interface/Cargo.toml b/primitives/runtime-interface/Cargo.toml index da746179e281d..f2ddc84b1e2c2 100644 --- a/primitives/runtime-interface/Cargo.toml +++ b/primitives/runtime-interface/Cargo.toml @@ -20,7 +20,7 @@ sp-std = { version = "5.0.0", default-features = false, path = "../std" } sp-tracing = { version = "6.0.0", default-features = false, path = "../tracing" } sp-runtime-interface-proc-macro = { version = "6.0.0", path = "proc-macro" } sp-externalities = { version = "0.13.0", default-features = false, path = "../externalities" } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["bytes"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["bytes"] } static_assertions = "1.0.0" primitive-types = { version = "0.12.0", default-features = false } sp-storage = { version = "7.0.0", default-features = false, path = "../storage" } diff --git a/primitives/runtime/Cargo.toml b/primitives/runtime/Cargo.toml index 79bbda700de29..3976de60cdcf9 100644 --- a/primitives/runtime/Cargo.toml +++ b/primitives/runtime/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive", "max-encoded-len"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive", "max-encoded-len"] } either = { version = "1.5", default-features = false } hash256-std-hasher = { version = "0.15.2", default-features = false } impl-trait-for-tuples = "0.2.2" diff --git a/primitives/runtime/src/lib.rs b/primitives/runtime/src/lib.rs index 96fe7d2487f48..8a32d0b9b4221 100644 --- a/primitives/runtime/src/lib.rs +++ b/primitives/runtime/src/lib.rs @@ -474,7 +474,7 @@ pub type DispatchResult = sp_std::result::Result<(), DispatchError>; pub type DispatchResultWithInfo = sp_std::result::Result>; /// Reason why a pallet call failed. -#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] +#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub struct ModuleError { /// Module index, matching the metadata module index. @@ -494,7 +494,7 @@ impl PartialEq for ModuleError { } /// Errors related to transactional storage layers. -#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum TransactionalError { /// Too many transactional layers have been spawned. @@ -519,7 +519,7 @@ impl From for DispatchError { } /// Reason why a dispatch call failed. -#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, PartialEq)] +#[derive(Eq, Clone, Copy, Encode, Decode, Debug, TypeInfo, PartialEq, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum DispatchError { /// Some error occurred. @@ -602,7 +602,7 @@ impl From for DispatchError { } /// Description of what went wrong when trying to complete an operation on a token. -#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo)] +#[derive(Eq, PartialEq, Clone, Copy, Encode, Decode, Debug, TypeInfo, MaxEncodedLen)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum TokenError { /// Funds are unavailable. diff --git a/primitives/session/Cargo.toml b/primitives/session/Cargo.toml index 94f9e8a23505a..31ec009ab2c64 100644 --- a/primitives/session/Cargo.toml +++ b/primitives/session/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-api = { version = "4.0.0-dev", default-features = false, path = "../api" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } diff --git a/primitives/staking/Cargo.toml b/primitives/staking/Cargo.toml index 35feae43ebb8c..a8e5a543dbf75 100644 --- a/primitives/staking/Cargo.toml +++ b/primitives/staking/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-core = { version = "7.0.0", default-features = false, path = "../core" } sp-runtime = { version = "7.0.0", default-features = false, path = "../runtime" } diff --git a/primitives/state-machine/Cargo.toml b/primitives/state-machine/Cargo.toml index 06419c5925cf8..595dfe286d225 100644 --- a/primitives/state-machine/Cargo.toml +++ b/primitives/state-machine/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } hash-db = { version = "0.15.2", default-features = false } log = { version = "0.4.17", optional = true } parking_lot = { version = "0.12.1", optional = true } diff --git a/primitives/storage/Cargo.toml b/primitives/storage/Cargo.toml index eb166ee3730ff..72ebc40fc1135 100644 --- a/primitives/storage/Cargo.toml +++ b/primitives/storage/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-serde = { version = "0.4.0", optional = true } ref-cast = "1.0.0" serde = { version = "1.0.136", features = ["derive"], optional = true } diff --git a/primitives/test-primitives/Cargo.toml b/primitives/test-primitives/Cargo.toml index 1d12ed74ee6ce..deb120104b717 100644 --- a/primitives/test-primitives/Cargo.toml +++ b/primitives/test-primitives/Cargo.toml @@ -12,7 +12,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } serde = { version = "1.0.136", features = ["derive"], optional = true } sp-application-crypto = { version = "7.0.0", default-features = false, path = "../application-crypto" } sp-core = { version = "7.0.0", default-features = false, path = "../core" } diff --git a/primitives/timestamp/Cargo.toml b/primitives/timestamp/Cargo.toml index 5d015086c4d79..27e306040f654 100644 --- a/primitives/timestamp/Cargo.toml +++ b/primitives/timestamp/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } futures-timer = { version = "3.0.2", optional = true } log = { version = "0.4.17", optional = true } thiserror = { version = "1.0.30", optional = true } diff --git a/primitives/tracing/Cargo.toml b/primitives/tracing/Cargo.toml index 794785085c8b4..602f83c7b4939 100644 --- a/primitives/tracing/Cargo.toml +++ b/primitives/tracing/Cargo.toml @@ -19,7 +19,7 @@ targets = ["x86_64-unknown-linux-gnu", "wasm32-unknown-unknown"] [dependencies] sp-std = { version = "5.0.0", path = "../std", default-features = false } -codec = { version = "3.0.0", package = "parity-scale-codec", default-features = false, features = [ +codec = { version = "3.2.2", package = "parity-scale-codec", default-features = false, features = [ "derive", ] } tracing = { version = "0.1.29", default-features = false } diff --git a/primitives/transaction-storage-proof/Cargo.toml b/primitives/transaction-storage-proof/Cargo.toml index ea6419dfaa1ba..565d3d8d29f1c 100644 --- a/primitives/transaction-storage-proof/Cargo.toml +++ b/primitives/transaction-storage-proof/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] async-trait = { version = "0.1.57", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } log = { version = "0.4.17", optional = true } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-core = { version = "7.0.0", optional = true, path = "../core" } diff --git a/primitives/trie/Cargo.toml b/primitives/trie/Cargo.toml index 68a5fb17c3c34..3f045a1cb216d 100644 --- a/primitives/trie/Cargo.toml +++ b/primitives/trie/Cargo.toml @@ -19,7 +19,7 @@ harness = false [dependencies] ahash = { version = "0.7.6", optional = true } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } hashbrown = { version = "0.12.3", optional = true } hash-db = { version = "0.15.2", default-features = false } lazy_static = { version = "1.4.0", optional = true } diff --git a/primitives/version/Cargo.toml b/primitives/version/Cargo.toml index 56fabcd566475..7d32c540f9a10 100644 --- a/primitives/version/Cargo.toml +++ b/primitives/version/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-serde = { version = "0.4.0", optional = true } parity-wasm = { version = "0.45", optional = true } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } diff --git a/primitives/version/proc-macro/Cargo.toml b/primitives/version/proc-macro/Cargo.toml index 3d129d4753a15..abe9e579a20da 100644 --- a/primitives/version/proc-macro/Cargo.toml +++ b/primitives/version/proc-macro/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] proc-macro = true [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = [ "derive" ] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = [ "derive" ] } proc-macro2 = "1.0.37" quote = "1.0.10" syn = { version = "1.0.98", features = ["full", "fold", "extra-traits", "visit"] } diff --git a/primitives/wasm-interface/Cargo.toml b/primitives/wasm-interface/Cargo.toml index 1822ae43edb68..69937e6b05205 100644 --- a/primitives/wasm-interface/Cargo.toml +++ b/primitives/wasm-interface/Cargo.toml @@ -14,7 +14,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } impl-trait-for-tuples = "0.2.2" log = { version = "0.4.17", optional = true } wasmi = { version = "0.13", optional = true } diff --git a/primitives/weights/Cargo.toml b/primitives/weights/Cargo.toml index 00da83069e388..2368b913b3b2f 100644 --- a/primitives/weights/Cargo.toml +++ b/primitives/weights/Cargo.toml @@ -13,7 +13,7 @@ documentation = "https://docs.rs/sp-wasm-interface" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } serde = { version = "1.0.136", optional = true, features = ["derive"] } smallvec = "1.8.0" diff --git a/scripts/ci/gitlab/pipeline/publish.yml b/scripts/ci/gitlab/pipeline/publish.yml index d1a7514d1707b..4eafb0a055399 100644 --- a/scripts/ci/gitlab/pipeline/publish.yml +++ b/scripts/ci/gitlab/pipeline/publish.yml @@ -184,19 +184,19 @@ publish-draft-release: .publish-crates-template: stage: publish - extends: .crates-publishing-template + extends: + - .crates-publishing-template + - .crates-publishing-pipeline # We don't want multiple jobs racing to publish crates as it's redundant and they might overwrite # the releases of one another. Use resource_group to ensure that at most one instance of this job # is running at any given time. resource_group: crates-publishing - variables: - # crates.io rate limits crates publishing by 1 per minute, so a delay needs to be inserted - # slightly higher than that after publishing each crate. The value is specified in seconds. - SPUB_AFTER_PUBLISH_DELAY: 64 - # We might have to publish lots of crates at a time. Given the 1 minute delay introduced above and - # taking into account the 202 (as of Dec 07, 2022) publishable Substrate crates, that would equate - # to roughly 202 minutes of delay, or 3h and 22 minutes. As such, the job needs to have a much - # higher timeout than average. + # crates.io currently rate limits crate publishing at 1 per minute: + # https://github.com/paritytech/release-engineering/issues/123#issuecomment-1335509748 + # Taking into account the 202 (as of Dec 07, 2022) publishable Substrate crates, in the worst + # case, due to the rate limits alone, we'd have to wait through at least 202 minutes of delay. + # Taking into account also the verification steps and extra synchronization delays after + # publishing the crate, the job needs to have a much higher timeout than average. timeout: 9h # A custom publishing environment is used for us to be able to set up protected secrets # specifically for it @@ -211,15 +211,9 @@ publish-draft-release: - rusty-cachier cache upload publish-crates: - extends: - - .publish-crates-template - - .scheduled-crate-publishing-pipeline - needs: - - job: publish-crates-locally - artifacts: false + extends: .publish-crates-template publish-crates-manual: extends: .publish-crates-template - rules: !reference [.crate-publishing-pipeline, rules] when: manual - allow_failure: true + interruptible: false diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index a468a7b04caeb..f00857ffa9935 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -1,6 +1,27 @@ # This file is part of .gitlab-ci.yml # Here are all jobs that are executed during "test" stage + +# It's more like a check and it belongs to the previous stage, but we want to run this job with real tests in parallel +find-fail-ci-phrase: + stage: test + variables: + CI_IMAGE: "paritytech/tools:latest" + ASSERT_REGEX: "FAIL-CI" + GIT_DEPTH: 1 + extends: + - .kubernetes-env + script: + - set +e + - rg --line-number --hidden --type rust --glob '!{.git,target}' "$ASSERT_REGEX" .; exit_status=$? + - if [ $exit_status -eq 0 ]; then + echo "$ASSERT_REGEX was found, exiting with 1"; + exit 1; + else + echo "No $ASSERT_REGEX was found, exiting with 0"; + exit 0; + fi + cargo-deny: stage: test extends: @@ -396,9 +417,14 @@ cargo-check-each-crate: parallel: 2 publish-crates-locally: + stage: test extends: - .test-refs - .crates-publishing-template + # When lots of crates are taken into account (for example on master where all crates are tested) + # the job might take a long time, as evidenced by: + # https://gitlab.parity.io/parity/mirrors/substrate/-/jobs/2269364 + timeout: 4h script: - rusty-cachier snapshot create - git clone diff --git a/scripts/ci/gitlab/pipeline/zombienet.yml b/scripts/ci/gitlab/pipeline/zombienet.yml index 8d772ff51f9a7..7dc98c006808f 100644 --- a/scripts/ci/gitlab/pipeline/zombienet.yml +++ b/scripts/ci/gitlab/pipeline/zombienet.yml @@ -30,7 +30,6 @@ after_script: - mkdir -p ./zombienet-logs - cp /tmp/zombie*/logs/* ./zombienet-logs/ - allow_failure: true retry: 2 tags: - zombienet-polkadot-integration-test diff --git a/test-utils/client/Cargo.toml b/test-utils/client/Cargo.toml index 106ec21d79464..682c868f290d2 100644 --- a/test-utils/client/Cargo.toml +++ b/test-utils/client/Cargo.toml @@ -15,7 +15,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] array-bytes = "4.1" async-trait = "0.1.57" -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" serde = "1.0.136" serde_json = "1.0.85" diff --git a/test-utils/client/src/lib.rs b/test-utils/client/src/lib.rs index 5dc93da13fe74..ff744b80cbff4 100644 --- a/test-utils/client/src/lib.rs +++ b/test-utils/client/src/lib.rs @@ -41,7 +41,7 @@ use futures::{future::Future, stream::StreamExt}; use sc_client_api::BlockchainEvents; use sc_service::client::{ClientConfig, LocalCallExecutor}; use serde::Deserialize; -use sp_core::storage::ChildInfo; +use sp_core::{storage::ChildInfo, testing::TaskExecutor}; use sp_runtime::{codec::Encode, traits::Block as BlockT, OpaqueExtrinsic}; use std::{ collections::{HashMap, HashSet}, @@ -62,7 +62,7 @@ impl GenesisInit for () { } /// A builder for creating a test client instance. -pub struct TestClientBuilder { +pub struct TestClientBuilder { execution_strategies: ExecutionStrategies, genesis_init: G, /// The key is an unprefixed storage key, this only contains @@ -237,9 +237,12 @@ impl ) .expect("Creates genesis block builder"); + let spawn_handle = Box::new(TaskExecutor::new()); + let client = client::Client::new( self.backend.clone(), executor, + spawn_handle, genesis_block_builder, self.fork_blocks, self.bad_blocks, diff --git a/test-utils/runtime/Cargo.toml b/test-utils/runtime/Cargo.toml index cff0227d03184..2c943c47f32b0 100644 --- a/test-utils/runtime/Cargo.toml +++ b/test-utils/runtime/Cargo.toml @@ -19,7 +19,7 @@ sp-application-crypto = { version = "7.0.0", default-features = false, path = ". sp-consensus-aura = { version = "0.10.0-dev", default-features = false, path = "../../primitives/consensus/aura" } sp-consensus-babe = { version = "0.10.0-dev", default-features = false, path = "../../primitives/consensus/babe" } sp-block-builder = { version = "4.0.0-dev", default-features = false, path = "../../primitives/block-builder" } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false, features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false, features = ["derive"] } scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } sp-inherents = { version = "4.0.0-dev", default-features = false, path = "../../primitives/inherents" } sp-keyring = { version = "7.0.0", optional = true, path = "../../primitives/keyring" } diff --git a/test-utils/runtime/client/Cargo.toml b/test-utils/runtime/client/Cargo.toml index 2ac944edc637f..7d5c9673f6e21 100644 --- a/test-utils/runtime/client/Cargo.toml +++ b/test-utils/runtime/client/Cargo.toml @@ -12,7 +12,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" sc-block-builder = { version = "0.10.0-dev", path = "../../../client/block-builder" } sc-client-api = { version = "4.0.0-dev", path = "../../../client/api" } diff --git a/test-utils/runtime/transaction-pool/Cargo.toml b/test-utils/runtime/transaction-pool/Cargo.toml index f5cba2b99be56..5ce397474fec6 100644 --- a/test-utils/runtime/transaction-pool/Cargo.toml +++ b/test-utils/runtime/transaction-pool/Cargo.toml @@ -12,7 +12,7 @@ publish = false targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } futures = "0.3.21" parking_lot = "0.12.1" thiserror = "1.0" diff --git a/utils/fork-tree/Cargo.toml b/utils/fork-tree/Cargo.toml index 4ac176f645bd1..c60ef8fd33e82 100644 --- a/utils/fork-tree/Cargo.toml +++ b/utils/fork-tree/Cargo.toml @@ -14,4 +14,4 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", features = ["derive"] } +codec = { package = "parity-scale-codec", version = "3.2.2", features = ["derive"] } diff --git a/utils/frame/benchmarking-cli/Cargo.toml b/utils/frame/benchmarking-cli/Cargo.toml index 9e231b8173fb1..fc23d07b6216f 100644 --- a/utils/frame/benchmarking-cli/Cargo.toml +++ b/utils/frame/benchmarking-cli/Cargo.toml @@ -16,7 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"] array-bytes = "4.1" chrono = "0.4" clap = { version = "4.0.9", features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } comfy-table = { version = "6.0.0", default-features = false } handlebars = "4.2.2" Inflector = "0.11.4" diff --git a/utils/frame/benchmarking-cli/src/lib.rs b/utils/frame/benchmarking-cli/src/lib.rs index a44a208b16ae9..5723a8038fdbb 100644 --- a/utils/frame/benchmarking-cli/src/lib.rs +++ b/utils/frame/benchmarking-cli/src/lib.rs @@ -27,7 +27,7 @@ mod storage; pub use block::BlockCmd; pub use extrinsic::{ExtrinsicBuilder, ExtrinsicCmd, ExtrinsicFactory}; -pub use machine::{MachineCmd, Requirements, SUBSTRATE_REFERENCE_HARDWARE}; +pub use machine::{MachineCmd, SUBSTRATE_REFERENCE_HARDWARE}; pub use overhead::OverheadCmd; pub use pallet::PalletCmd; pub use sc_service::BasePath; diff --git a/utils/frame/benchmarking-cli/src/machine/hardware.rs b/utils/frame/benchmarking-cli/src/machine/hardware.rs index 50c88ec74646c..318c193d7c08c 100644 --- a/utils/frame/benchmarking-cli/src/machine/hardware.rs +++ b/utils/frame/benchmarking-cli/src/machine/hardware.rs @@ -18,40 +18,7 @@ //! Contains types to define hardware requirements. use lazy_static::lazy_static; -use sc_sysinfo::Throughput; -use serde::{de::Visitor, Deserialize, Deserializer, Serialize, Serializer}; -use sp_std::{fmt, fmt::Formatter}; - -/// Serializes throughput into MiBs and represents it as `f64`. -fn serialize_throughput_as_f64(throughput: &Throughput, serializer: S) -> Result -where - S: Serializer, -{ - serializer.serialize_f64(throughput.as_mibs()) -} - -struct ThroughputVisitor; -impl<'de> Visitor<'de> for ThroughputVisitor { - type Value = Throughput; - - fn expecting(&self, formatter: &mut Formatter) -> fmt::Result { - formatter.write_str("A value that is a f64.") - } - - fn visit_f64(self, value: f64) -> Result - where - E: serde::de::Error, - { - Ok(Throughput::from_mibs(value)) - } -} - -fn deserialize_throughput<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - Ok(deserializer.deserialize_f64(ThroughputVisitor))? -} +use sc_sysinfo::Requirements; lazy_static! { /// The hardware requirements as measured on reference hardware. @@ -67,62 +34,6 @@ lazy_static! { }; } -/// Multiple requirements for the hardware. -#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)] -pub struct Requirements(pub Vec); - -/// A single requirement for the hardware. -#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] -pub struct Requirement { - /// The metric to measure. - pub metric: Metric, - /// The minimal throughput that needs to be archived for this requirement. - #[serde( - serialize_with = "serialize_throughput_as_f64", - deserialize_with = "deserialize_throughput" - )] - pub minimum: Throughput, -} - -/// A single hardware metric. -/// -/// The implementation of these is in `sc-sysinfo`. -#[derive(Deserialize, Serialize, Debug, Clone, Copy, PartialEq)] -pub enum Metric { - /// SR25519 signature verification. - Sr25519Verify, - /// Blake2-256 hashing algorithm. - Blake2256, - /// Copying data in RAM. - MemCopy, - /// Disk sequential write. - DiskSeqWrite, - /// Disk random write. - DiskRndWrite, -} - -impl Metric { - /// The category of the metric. - pub fn category(&self) -> &'static str { - match self { - Self::Sr25519Verify | Self::Blake2256 => "CPU", - Self::MemCopy => "Memory", - Self::DiskSeqWrite | Self::DiskRndWrite => "Disk", - } - } - - /// The name of the metric. It is always prefixed by the [`self::category()`]. - pub fn name(&self) -> &'static str { - match self { - Self::Sr25519Verify => "SR25519-Verify", - Self::Blake2256 => "BLAKE2-256", - Self::MemCopy => "Copy", - Self::DiskSeqWrite => "Seq Write", - Self::DiskRndWrite => "Rnd Write", - } - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/utils/frame/benchmarking-cli/src/machine/mod.rs b/utils/frame/benchmarking-cli/src/machine/mod.rs index 82b4e5be7358e..bcffef255e5b5 100644 --- a/utils/frame/benchmarking-cli/src/machine/mod.rs +++ b/utils/frame/benchmarking-cli/src/machine/mod.rs @@ -30,11 +30,12 @@ use sc_cli::{CliConfiguration, Result, SharedParams}; use sc_service::Configuration; use sc_sysinfo::{ benchmark_cpu, benchmark_disk_random_writes, benchmark_disk_sequential_writes, - benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, Throughput, + benchmark_memory, benchmark_sr25519_verify, ExecutionLimit, Metric, Requirement, Requirements, + Throughput, }; use crate::shared::check_build_profile; -pub use hardware::{Metric, Requirement, Requirements, SUBSTRATE_REFERENCE_HARDWARE}; +pub use hardware::SUBSTRATE_REFERENCE_HARDWARE; /// Command to benchmark the hardware. /// diff --git a/utils/frame/remote-externalities/Cargo.toml b/utils/frame/remote-externalities/Cargo.toml index 5a51dbc661c90..8611ae4980f12 100644 --- a/utils/frame/remote-externalities/Cargo.toml +++ b/utils/frame/remote-externalities/Cargo.toml @@ -12,7 +12,7 @@ description = "An externalities provided environment that can load itself from r targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } log = "0.4.17" serde = "1.0.136" frame-support = { version = "4.0.0-dev", optional = true, path = "../../../frame/support" } diff --git a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml index dd6456f714a3e..17a68e2f4cbe8 100644 --- a/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml +++ b/utils/frame/rpc/state-trie-migration-rpc/Cargo.toml @@ -14,7 +14,7 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] scale-info = { version = "2.1.1", default-features = false, features = ["derive"] } -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } +codec = { package = "parity-scale-codec", version = "3.2.2", default-features = false } serde = { version = "1", features = ["derive"] } log = { version = "0.4.17", default-features = false } diff --git a/utils/frame/rpc/support/Cargo.toml b/utils/frame/rpc/support/Cargo.toml index 066038469292d..d75d3a5af5da4 100644 --- a/utils/frame/rpc/support/Cargo.toml +++ b/utils/frame/rpc/support/Cargo.toml @@ -15,7 +15,7 @@ description = "Substrate RPC for FRAME's support" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } jsonrpsee = { version = "0.16.2", features = ["jsonrpsee-types"] } serde = "1" frame-support = { version = "4.0.0-dev", path = "../../../../frame/support" } diff --git a/utils/frame/rpc/system/Cargo.toml b/utils/frame/rpc/system/Cargo.toml index 55fc4b27dbf73..b6848ceb2911e 100644 --- a/utils/frame/rpc/system/Cargo.toml +++ b/utils/frame/rpc/system/Cargo.toml @@ -13,7 +13,7 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } +codec = { package = "parity-scale-codec", version = "3.2.2" } jsonrpsee = { version = "0.16.2", features = ["client-core", "server", "macros"] } futures = "0.3.21" log = "0.4.17" diff --git a/utils/frame/try-runtime/cli/Cargo.toml b/utils/frame/try-runtime/cli/Cargo.toml index ed0f25cae7a36..8c14ddc2916b0 100644 --- a/utils/frame/try-runtime/cli/Cargo.toml +++ b/utils/frame/try-runtime/cli/Cargo.toml @@ -33,7 +33,7 @@ substrate-rpc-client = { path = "../../rpc/client" } clap = { version = "4.0.9", features = ["derive"] } hex = { version = "0.4.3", default-features = false } log = "0.4.17" -parity-scale-codec = "3.0.0" +parity-scale-codec = "3.2.2" serde = "1.0.136" serde_json = "1.0.85" zstd = { version = "0.11.2", default-features = false }