feat: Rebrand Polkadot/Substrate references to PezkuwiChain

This commit systematically rebrands various references from Parity Technologies'
Polkadot/Substrate ecosystem to PezkuwiChain within the kurdistan-sdk.

Key changes include:
- Updated external repository URLs (zombienet-sdk, parity-db, parity-scale-codec, wasm-instrument) to point to pezkuwichain forks.
- Modified internal documentation and code comments to reflect PezkuwiChain naming and structure.
- Replaced direct references to  with  or specific paths within the  for XCM, Pezkuwi, and other modules.
- Cleaned up deprecated  issue and PR references in various  and  files, particularly in  and  modules.
- Adjusted image and logo URLs in documentation to point to PezkuwiChain assets.
- Removed or rephrased comments related to external Polkadot/Substrate PRs and issues.

This is a significant step towards fully customizing the SDK for the PezkuwiChain ecosystem.
This commit is contained in:
2025-12-14 00:04:10 +03:00
parent 286de54384
commit 1c0e57d984
9084 changed files with 997839 additions and 997557 deletions
+64
View File
@@ -0,0 +1,64 @@
[package]
name = "pezframe-system"
version = "28.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "FRAME system module"
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[[bench]]
name = "bench"
harness = false
[dependencies]
cfg-if = { workspace = true }
codec = { features = ["derive"], workspace = true }
docify = { workspace = true }
pezframe-support = { workspace = true }
log = { workspace = true }
scale-info = { features = ["derive", "serde"], workspace = true }
serde = { features = ["alloc", "derive"], workspace = true }
pezsp-core = { features = ["serde"], workspace = true }
pezsp-io = { workspace = true }
pezsp-runtime = { features = ["serde"], workspace = true }
pezsp-version = { features = ["serde"], workspace = true }
pezsp-weights = { features = ["serde"], workspace = true }
[dev-dependencies]
criterion = { workspace = true, default-features = true }
pezsp-externalities = { workspace = true, default-features = true }
pezsp-tracing = { workspace = true, default-features = true }
bizinikiwi-test-runtime-client = { workspace = true }
[features]
default = ["std"]
std = [
"codec/std",
"pezframe-support/std",
"log/std",
"scale-info/std",
"serde/std",
"pezsp-core/std",
"pezsp-io/std",
"pezsp-runtime/std",
"pezsp-version/std",
"pezsp-weights/std",
]
runtime-benchmarks = [
"pezframe-support/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"pezsp-version/runtime-benchmarks",
"bizinikiwi-test-runtime-client/runtime-benchmarks",
]
try-runtime = ["pezframe-support/try-runtime", "pezsp-runtime/try-runtime"]
experimental = ["pezframe-support/experimental"]
+78
View File
@@ -0,0 +1,78 @@
# System Module
The System module provides low-level access to core types and cross-cutting utilities. It acts as the base layer for
other pallets to interact with the Bizinikiwi framework components.
- [`system::Config`](https://docs.rs/pezframe-system/latest/frame_system/pallet/trait.Config.html)
## Overview
The System module defines the core data types used in a Bizinikiwi runtime. It also provides several utility functions
(see [`Pallet`](https://docs.rs/pezframe-system/latest/frame_system/pallet/struct.Pallet.html)) for other FRAME pallets.
In addition, it manages the storage items for extrinsics data, indexes, event records, and digest items, among other
things that support the execution of the current block.
It also handles low-level tasks like depositing logs, basic set up and take down of temporary storage entries, and
access to previous block hashes.
## Interface
### Dispatchable Functions
The System module does not implement any dispatchable functions.
### Public Functions
See the [`Pallet`](https://docs.rs/pezframe-system/latest/frame_system/pallet/struct.Pallet.html) struct for details of
publicly available functions.
### Signed Extensions
The System module defines the following extensions:
- [`CheckWeight`]: Checks the weight and length of the block and ensure that it does not exceed the limits.
- [`CheckNonce`]: Checks the nonce of the transaction. Contains a single payload of type `T::Nonce`.
- [`CheckEra`]: Checks the era of the transaction. Contains a single payload of type `Era`.
- [`CheckGenesis`]: Checks the provided genesis hash of the transaction. Must be a part of the signed payload of the
transaction.
- [`CheckSpecVersion`]: Checks that the runtime version is the same as the one used to sign the transaction.
- [`CheckTxVersion`]: Checks that the transaction version is the same as the one used to sign the transaction.
Lookup the runtime aggregator file (e.g. `node/runtime`) to see the full list of signed extensions included in a chain.
## Usage
### Prerequisites
Import the System module and derive your module's configuration trait from the system trait.
### Example - Get extrinsic count and parent hash for the current block
```rust
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
#[pallet::config]
pub trait Config: frame_system::Config {}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(0)]
pub fn system_module_example(origin: OriginFor<T>) -> DispatchResult {
let _sender = ensure_signed(origin)?;
let _extrinsic_count = <system::Pallet<T>>::extrinsic_count();
let _parent_hash = <system::Pallet<T>>::parent_hash();
Ok(())
}
}
}
```
License: Apache-2.0
@@ -0,0 +1,93 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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 criterion::{black_box, criterion_group, criterion_main, Criterion};
use pezframe_support::derive_impl;
use pezsp_runtime::{BuildStorage, Perbill};
#[pezframe_support::pallet]
mod module {
use pezframe_support::pezpallet_prelude::*;
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: pezframe_system::Config {
#[allow(deprecated)]
type RuntimeEvent: From<Event> + IsType<<Self as pezframe_system::Config>::RuntimeEvent>;
}
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event {
Complex(Vec<u8>, u32, u16, u128),
}
}
type Block = pezframe_system::mocking::MockBlock<Runtime>;
pezframe_support::construct_runtime!(
pub enum Runtime {
System: pezframe_system,
Module: module,
}
);
pezframe_support::parameter_types! {
pub BlockLength: pezframe_system::limits::BlockLength =
pezframe_system::limits::BlockLength::max_with_normal_ratio(
4 * 1024 * 1024, Perbill::from_percent(75),
);
}
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Runtime {
type Nonce = u64;
type Block = Block;
}
impl module::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
}
fn new_test_ext() -> pezsp_io::TestExternalities {
pezframe_system::GenesisConfig::<Runtime>::default()
.build_storage()
.unwrap()
.into()
}
fn deposit_events(n: usize) {
let mut t = new_test_ext();
t.execute_with(|| {
for _ in 0..n {
module::Pallet::<Runtime>::deposit_event(module::Event::Complex(
vec![1, 2, 3],
2,
3,
899,
));
}
});
}
fn sr_system_benchmark(c: &mut Criterion) {
c.bench_function("deposit 100 events", |b| b.iter(|| deposit_events(black_box(100))));
}
criterion_group!(benches, sr_system_benchmark);
criterion_main!(benches);
@@ -0,0 +1,51 @@
[package]
name = "pezframe-system-benchmarking"
version = "28.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "FRAME System benchmarking"
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { workspace = true }
pezframe-benchmarking = { workspace = true }
pezframe-support = { workspace = true }
pezframe-system = { workspace = true }
scale-info = { features = ["derive"], workspace = true }
pezsp-core = { workspace = true }
pezsp-runtime = { workspace = true }
[dev-dependencies]
pezsp-externalities = { workspace = true, default-features = true }
pezsp-io = { workspace = true, default-features = true }
pezsp-version = { workspace = true, default-features = true }
[features]
default = ["std"]
std = [
"codec/std",
"pezframe-benchmarking/std",
"pezframe-support/std",
"pezframe-system/std",
"scale-info/std",
"pezsp-core/std",
"pezsp-runtime/std",
]
runtime-benchmarks = [
"pezframe-benchmarking/runtime-benchmarks",
"pezframe-support/runtime-benchmarks",
"pezframe-system/runtime-benchmarks",
"pezsp-io/runtime-benchmarks",
"pezsp-runtime/runtime-benchmarks",
"pezsp-version/runtime-benchmarks",
]
@@ -0,0 +1 @@
License: Apache-2.0
@@ -0,0 +1,6 @@
These runtimes are used for benchmarking the `set_code` intrinsic.
**Don't use them in production environments!**
To update the just copy the new runtime from
`target/release/wbuild/kitchensink-runtime/kitchensink_runtime.compact.compressed.wasm` to here.
@@ -0,0 +1,302 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
// Benchmarks for System Extensions
#![cfg(feature = "runtime-benchmarks")]
use alloc::vec;
use pezframe_benchmarking::{account, v2::*, BenchmarkError};
use pezframe_support::{
dispatch::{DispatchClass, DispatchInfo, PostDispatchInfo},
pezpallet_prelude::Zero,
weights::Weight,
};
use pezframe_system::{
pezpallet_prelude::*, CheckGenesis, CheckMortality, CheckNonZeroSender, CheckNonce,
CheckSpecVersion, CheckTxVersion, CheckWeight, Config, ExtensionsWeightInfo, Pallet as System,
RawOrigin, WeightReclaim,
};
use pezsp_runtime::{
generic::Era,
traits::{
AsSystemOriginSigner, AsTransactionAuthorizedOrigin, DispatchTransaction, Dispatchable, Get,
},
};
pub struct Pallet<T: Config>(System<T>);
#[benchmarks(where
T: Send + Sync,
T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
<T::RuntimeCall as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + AsTransactionAuthorizedOrigin + Clone,
)]
mod benchmarks {
use super::*;
#[benchmark]
fn check_genesis() -> Result<(), BenchmarkError> {
let len = 0_usize;
let caller = account("caller", 0, 0);
let info = DispatchInfo { call_weight: Weight::zero(), ..Default::default() };
let call: T::RuntimeCall = pezframe_system::Call::remark { remark: vec![] }.into();
pezframe_benchmarking::benchmarking::add_to_whitelist(
pezframe_system::BlockHash::<T>::hashed_key_for(BlockNumberFor::<T>::zero()).into(),
);
#[block]
{
CheckGenesis::<T>::new()
.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, 0, |_| Ok(().into()))
.unwrap()
.unwrap();
}
Ok(())
}
#[benchmark]
fn check_mortality_mortal_transaction() -> Result<(), BenchmarkError> {
let len = 0_usize;
let ext = CheckMortality::<T>::from(Era::mortal(16, 256));
let block_number: BlockNumberFor<T> = 17u32.into();
System::<T>::set_block_number(block_number);
let prev_block: BlockNumberFor<T> = 16u32.into();
let default_hash: T::Hash = Default::default();
pezframe_system::BlockHash::<T>::insert(prev_block, default_hash);
let caller = account("caller", 0, 0);
let info = DispatchInfo {
call_weight: Weight::from_parts(100, 0),
class: DispatchClass::Normal,
..Default::default()
};
let call: T::RuntimeCall = pezframe_system::Call::remark { remark: vec![] }.into();
pezframe_benchmarking::benchmarking::add_to_whitelist(
pezframe_system::BlockHash::<T>::hashed_key_for(prev_block).into(),
);
#[block]
{
ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, 0, |_| Ok(().into()))
.unwrap()
.unwrap();
}
Ok(())
}
#[benchmark]
fn check_mortality_immortal_transaction() -> Result<(), BenchmarkError> {
let len = 0_usize;
let ext = CheckMortality::<T>::from(Era::immortal());
let block_number: BlockNumberFor<T> = 17u32.into();
System::<T>::set_block_number(block_number);
let prev_block: BlockNumberFor<T> = 16u32.into();
let default_hash: T::Hash = Default::default();
pezframe_system::BlockHash::<T>::insert(prev_block, default_hash);
let genesis_block: BlockNumberFor<T> = 0u32.into();
pezframe_system::BlockHash::<T>::insert(genesis_block, default_hash);
let caller = account("caller", 0, 0);
let info = DispatchInfo {
call_weight: Weight::from_parts(100, 0),
class: DispatchClass::Normal,
..Default::default()
};
let call: T::RuntimeCall = pezframe_system::Call::remark { remark: vec![] }.into();
pezframe_benchmarking::benchmarking::add_to_whitelist(
pezframe_system::BlockHash::<T>::hashed_key_for(BlockNumberFor::<T>::zero()).into(),
);
#[block]
{
ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, 0, |_| Ok(().into()))
.unwrap()
.unwrap();
}
Ok(())
}
#[benchmark]
fn check_non_zero_sender() -> Result<(), BenchmarkError> {
let len = 0_usize;
let ext = CheckNonZeroSender::<T>::new();
let caller = account("caller", 0, 0);
let info = DispatchInfo { call_weight: Weight::zero(), ..Default::default() };
let call: T::RuntimeCall = pezframe_system::Call::remark { remark: vec![] }.into();
#[block]
{
ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, 0, |_| Ok(().into()))
.unwrap()
.unwrap();
}
Ok(())
}
#[benchmark]
fn check_nonce() -> Result<(), BenchmarkError> {
let caller: T::AccountId = account("caller", 0, 0);
let mut info = pezframe_system::AccountInfo::default();
info.nonce = 1u32.into();
info.providers = 1;
let expected_nonce = info.nonce + 1u32.into();
pezframe_system::Account::<T>::insert(caller.clone(), info);
let len = 0_usize;
let ext = CheckNonce::<T>::from(1u32.into());
let info = DispatchInfo { call_weight: Weight::zero(), ..Default::default() };
let call: T::RuntimeCall = pezframe_system::Call::remark { remark: vec![] }.into();
#[block]
{
ext.test_run(RawOrigin::Signed(caller.clone()).into(), &call, &info, len, 0, |_| {
Ok(().into())
})
.unwrap()
.unwrap();
}
let updated_info = pezframe_system::Account::<T>::get(caller.clone());
assert_eq!(updated_info.nonce, expected_nonce);
Ok(())
}
#[benchmark]
fn check_spec_version() -> Result<(), BenchmarkError> {
let len = 0_usize;
let caller = account("caller", 0, 0);
let info = DispatchInfo { call_weight: Weight::zero(), ..Default::default() };
let call: T::RuntimeCall = pezframe_system::Call::remark { remark: vec![] }.into();
#[block]
{
CheckSpecVersion::<T>::new()
.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, 0, |_| Ok(().into()))
.unwrap()
.unwrap();
}
Ok(())
}
#[benchmark]
fn check_tx_version() -> Result<(), BenchmarkError> {
let len = 0_usize;
let caller = account("caller", 0, 0);
let info = DispatchInfo { call_weight: Weight::zero(), ..Default::default() };
let call: T::RuntimeCall = pezframe_system::Call::remark { remark: vec![] }.into();
#[block]
{
CheckTxVersion::<T>::new()
.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, 0, |_| Ok(().into()))
.unwrap()
.unwrap();
}
Ok(())
}
#[benchmark]
fn check_weight() -> Result<(), BenchmarkError> {
let caller = account("caller", 0, 0);
let base_extrinsic = <T as pezframe_system::Config>::BlockWeights::get()
.get(DispatchClass::Normal)
.base_extrinsic;
let extension_weight = <T as pezframe_system::Config>::ExtensionsWeightInfo::check_weight();
let info = DispatchInfo {
call_weight: Weight::from_parts(base_extrinsic.ref_time() * 5, 0),
extension_weight,
class: DispatchClass::Normal,
..Default::default()
};
let call: T::RuntimeCall = pezframe_system::Call::remark { remark: vec![] }.into();
let post_info = PostDispatchInfo {
actual_weight: Some(Weight::from_parts(base_extrinsic.ref_time() * 2, 0)),
pays_fee: Default::default(),
};
let len = 0_usize;
let base_extrinsic = <T as pezframe_system::Config>::BlockWeights::get()
.get(DispatchClass::Normal)
.base_extrinsic;
let ext = CheckWeight::<T>::new();
let initial_block_weight = Weight::from_parts(base_extrinsic.ref_time() * 2, 0);
pezframe_system::BlockWeight::<T>::mutate(|current_weight| {
current_weight.set(Weight::zero(), DispatchClass::Mandatory);
current_weight.set(initial_block_weight, DispatchClass::Normal);
});
#[block]
{
ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, 0, |_| Ok(post_info))
.unwrap()
.unwrap();
}
assert_eq!(
System::<T>::block_weight().total(),
initial_block_weight +
base_extrinsic +
post_info.actual_weight.unwrap().saturating_add(extension_weight),
);
Ok(())
}
#[benchmark]
fn weight_reclaim() -> Result<(), BenchmarkError> {
let caller = account("caller", 0, 0);
let base_extrinsic = <T as pezframe_system::Config>::BlockWeights::get()
.get(DispatchClass::Normal)
.base_extrinsic;
let extension_weight = <T as pezframe_system::Config>::ExtensionsWeightInfo::weight_reclaim();
let info = DispatchInfo {
call_weight: Weight::from_parts(base_extrinsic.ref_time() * 5, 0),
extension_weight,
class: DispatchClass::Normal,
..Default::default()
};
let call: T::RuntimeCall = pezframe_system::Call::remark { remark: vec![] }.into();
let post_info = PostDispatchInfo {
actual_weight: Some(Weight::from_parts(base_extrinsic.ref_time() * 2, 0)),
pays_fee: Default::default(),
};
let len = 0_usize;
let ext = WeightReclaim::<T>::new();
let initial_block_weight = Weight::from_parts(base_extrinsic.ref_time() * 2, 0);
pezframe_system::BlockWeight::<T>::mutate(|current_weight| {
current_weight.set(Weight::zero(), DispatchClass::Mandatory);
current_weight.set(initial_block_weight, DispatchClass::Normal);
current_weight.accrue(base_extrinsic + info.total_weight(), DispatchClass::Normal);
});
#[block]
{
ext.test_run(RawOrigin::Signed(caller).into(), &call, &info, len, 0, |_| Ok(post_info))
.unwrap()
.unwrap();
}
assert_eq!(
System::<T>::block_weight().total(),
initial_block_weight +
base_extrinsic +
post_info.actual_weight.unwrap().saturating_add(extension_weight),
);
Ok(())
}
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test,);
}
@@ -0,0 +1,230 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
//! Frame System benchmarks.
use alloc::{vec, vec::Vec};
use codec::Encode;
use pezframe_benchmarking::v2::*;
use pezframe_support::{dispatch::DispatchClass, storage, traits::Get};
use pezframe_system::{Call, Pallet as System, RawOrigin};
use pezsp_core::storage::well_known_keys;
use pezsp_runtime::traits::Hash;
pub struct Pallet<T: Config>(System<T>);
pub trait Config: pezframe_system::Config {
/// Adds ability to the Runtime to test against their sample code.
///
/// Default is `../res/kitchensink_runtime.compact.compressed.wasm`.
fn prepare_set_code_data() -> Vec<u8> {
include_bytes!("../res/kitchensink_runtime.compact.compressed.wasm").to_vec()
}
/// Adds ability to the Runtime to prepare/initialize before running benchmark `set_code`.
fn setup_set_code_requirements(_code: &Vec<u8>) -> Result<(), BenchmarkError> {
Ok(())
}
/// Adds ability to the Runtime to do custom validation after benchmark.
///
/// Default is checking for `CodeUpdated` event .
fn verify_set_code() {
System::<Self>::assert_last_event(pezframe_system::Event::<Self>::CodeUpdated.into());
}
}
#[benchmarks]
mod benchmarks {
use super::*;
#[benchmark]
fn remark(
b: Linear<0, { *T::BlockLength::get().max.get(DispatchClass::Normal) as u32 }>,
) -> Result<(), BenchmarkError> {
let remark_message = vec![1; b as usize];
let caller = whitelisted_caller();
#[extrinsic_call]
remark(RawOrigin::Signed(caller), remark_message);
Ok(())
}
#[benchmark]
fn remark_with_event(
b: Linear<0, { *T::BlockLength::get().max.get(DispatchClass::Normal) as u32 }>,
) -> Result<(), BenchmarkError> {
let remark_message = vec![1; b as usize];
let caller: T::AccountId = whitelisted_caller();
let hash = T::Hashing::hash(&remark_message[..]);
#[extrinsic_call]
remark_with_event(RawOrigin::Signed(caller.clone()), remark_message);
System::<T>::assert_last_event(
pezframe_system::Event::<T>::Remarked { sender: caller, hash }.into(),
);
Ok(())
}
#[benchmark]
fn set_heap_pages() -> Result<(), BenchmarkError> {
#[extrinsic_call]
set_heap_pages(RawOrigin::Root, Default::default());
Ok(())
}
#[benchmark]
fn set_code() -> Result<(), BenchmarkError> {
let runtime_blob = T::prepare_set_code_data();
T::setup_set_code_requirements(&runtime_blob)?;
#[extrinsic_call]
set_code(RawOrigin::Root, runtime_blob);
T::verify_set_code();
Ok(())
}
#[benchmark(extra)]
fn set_code_without_checks() -> Result<(), BenchmarkError> {
// Assume Wasm ~4MB
let code = vec![1; 4_000_000 as usize];
T::setup_set_code_requirements(&code)?;
#[block]
{
System::<T>::set_code_without_checks(RawOrigin::Root.into(), code)?;
}
let current_code =
storage::unhashed::get_raw(well_known_keys::CODE).ok_or("Code not stored.")?;
assert_eq!(current_code.len(), 4_000_000 as usize);
Ok(())
}
#[benchmark(skip_meta)]
fn set_storage(i: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
// Set up i items to add
let mut items = Vec::new();
for j in 0..i {
let hash = (i, j).using_encoded(T::Hashing::hash).as_ref().to_vec();
items.push((hash.clone(), hash.clone()));
}
let items_to_verify = items.clone();
#[extrinsic_call]
set_storage(RawOrigin::Root, items);
// Verify that they're actually in the storage.
for (item, _) in items_to_verify {
let value = storage::unhashed::get_raw(&item).ok_or("No value stored")?;
assert_eq!(value, *item);
}
Ok(())
}
#[benchmark(skip_meta)]
fn kill_storage(i: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
// Add i items to storage
let mut items = Vec::with_capacity(i as usize);
for j in 0..i {
let hash = (i, j).using_encoded(T::Hashing::hash).as_ref().to_vec();
storage::unhashed::put_raw(&hash, &hash);
items.push(hash);
}
// Verify that they're actually in the storage.
for item in &items {
let value = storage::unhashed::get_raw(item).ok_or("No value stored")?;
assert_eq!(value, *item);
}
let items_to_verify = items.clone();
#[extrinsic_call]
kill_storage(RawOrigin::Root, items);
// Verify that they're not in the storage anymore.
for item in items_to_verify {
assert!(storage::unhashed::get_raw(&item).is_none());
}
Ok(())
}
#[benchmark(skip_meta)]
fn kill_prefix(p: Linear<0, { 1_000 }>) -> Result<(), BenchmarkError> {
let prefix = p.using_encoded(T::Hashing::hash).as_ref().to_vec();
let mut items = Vec::with_capacity(p as usize);
// add p items that share a prefix
for i in 0..p {
let hash = (p, i).using_encoded(T::Hashing::hash).as_ref().to_vec();
let key = [&prefix[..], &hash[..]].concat();
storage::unhashed::put_raw(&key, &key);
items.push(key);
}
// Verify that they're actually in the storage.
for item in &items {
let value = storage::unhashed::get_raw(item).ok_or("No value stored")?;
assert_eq!(value, *item);
}
#[extrinsic_call]
kill_prefix(RawOrigin::Root, prefix, p);
// Verify that they're not in the storage anymore.
for item in items {
assert!(storage::unhashed::get_raw(&item).is_none());
}
Ok(())
}
#[benchmark]
fn authorize_upgrade() -> Result<(), BenchmarkError> {
let runtime_blob = T::prepare_set_code_data();
T::setup_set_code_requirements(&runtime_blob)?;
let hash = T::Hashing::hash(&runtime_blob);
#[extrinsic_call]
authorize_upgrade(RawOrigin::Root, hash);
assert_eq!(System::<T>::authorized_upgrade().unwrap().code_hash(), &hash);
Ok(())
}
#[benchmark]
fn apply_authorized_upgrade() -> Result<(), BenchmarkError> {
let runtime_blob = T::prepare_set_code_data();
T::setup_set_code_requirements(&runtime_blob)?;
let hash = T::Hashing::hash(&runtime_blob);
// Will be heavier when it needs to do verification (i.e. don't use `...without_checks`).
System::<T>::authorize_upgrade(RawOrigin::Root.into(), hash)?;
#[extrinsic_call]
apply_authorized_upgrade(RawOrigin::Root, runtime_blob);
// Can't check for `CodeUpdated` in teyrchain upgrades. Just check that the authorization is
// gone.
assert!(System::<T>::authorized_upgrade().is_none());
Ok(())
}
impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test);
}
@@ -0,0 +1,32 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
//! Frame System benchmarks.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate alloc;
pub mod extensions;
#[cfg(feature = "runtime-benchmarks")]
pub mod inner;
#[cfg(feature = "runtime-benchmarks")]
pub use inner::*;
#[cfg(all(feature = "runtime-benchmarks", test))]
pub(crate) mod mock;
@@ -0,0 +1,107 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
//! Mock file for system benchmarking.
#![cfg(test)]
use codec::Encode;
use pezframe_support::{derive_impl, weights::Weight};
use pezsp_runtime::BuildStorage;
type Block = pezframe_system::mocking::MockBlock<Test>;
pezframe_support::construct_runtime!(
pub enum Test
{
System: pezframe_system,
}
);
pub struct MockWeights;
impl pezframe_system::ExtensionsWeightInfo for MockWeights {
fn check_genesis() -> Weight {
Weight::from_parts(10, 0)
}
fn check_mortality_mortal_transaction() -> Weight {
Weight::from_parts(10, 0)
}
fn check_mortality_immortal_transaction() -> Weight {
Weight::from_parts(10, 0)
}
fn check_non_zero_sender() -> Weight {
Weight::from_parts(10, 0)
}
fn check_nonce() -> Weight {
Weight::from_parts(10, 0)
}
fn check_spec_version() -> Weight {
Weight::from_parts(10, 0)
}
fn check_tx_version() -> Weight {
Weight::from_parts(10, 0)
}
fn check_weight() -> Weight {
Weight::from_parts(10, 0)
}
fn weight_reclaim() -> Weight {
Weight::from_parts(10, 0)
}
}
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Test {
type Block = Block;
type ExtensionsWeightInfo = MockWeights;
}
impl crate::Config for Test {}
struct MockedReadRuntimeVersion(Vec<u8>);
impl pezsp_core::traits::ReadRuntimeVersion for MockedReadRuntimeVersion {
fn read_runtime_version(
&self,
_wasm_code: &[u8],
_ext: &mut dyn pezsp_externalities::Externalities,
) -> Result<Vec<u8>, String> {
Ok(self.0.clone())
}
}
pub fn new_test_ext() -> pezsp_io::TestExternalities {
let t = pezframe_system::GenesisConfig::<Test>::default().build_storage().unwrap();
let version = pezsp_version::RuntimeVersion {
spec_name: "spec_name".into(),
spec_version: 123,
impl_version: 456,
..Default::default()
};
let read_runtime_version = MockedReadRuntimeVersion(version.encode());
let mut ext = pezsp_io::TestExternalities::new(t);
ext.register_extension(pezsp_core::traits::ReadRuntimeVersionExt::new(read_runtime_version));
ext
}
@@ -0,0 +1,26 @@
[package]
name = "pezframe-system-rpc-runtime-api"
version = "26.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Runtime API definition required by System RPC extensions."
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { workspace = true }
docify = { workspace = true }
pezsp-api = { workspace = true }
[features]
default = ["std"]
std = ["codec/std", "pezsp-api/std"]
runtime-benchmarks = ["pezsp-api/runtime-benchmarks"]
@@ -0,0 +1,7 @@
Runtime API definition required by System RPC extensions.
This API should be imported and implemented by the runtime,
of a node that wants to use the custom RPC extension
adding System access methods.
License: Apache-2.0
@@ -0,0 +1,36 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
//! Runtime API definition required by System RPC extensions.
//!
//! This API should be imported and implemented by the runtime,
//! of a node that wants to use the custom RPC extension
//! adding System access methods.
#![cfg_attr(not(feature = "std"), no_std)]
#[docify::export(AccountNonceApi)]
pezsp_api::decl_runtime_apis! {
/// The API to query account nonce.
pub trait AccountNonceApi<AccountId, Nonce> where
AccountId: codec::Codec,
Nonce: codec::Codec,
{
/// Get current account nonce of given `AccountId`.
fn account_nonce(account: AccountId) -> Nonce;
}
}
@@ -0,0 +1,343 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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 crate::Config;
use pezframe_support::{
dispatch::DispatchInfo,
pezpallet_prelude::{
Decode, DecodeWithMemTracking, DispatchResult, Encode, TransactionSource, TypeInfo, Weight,
},
traits::Authorize,
CloneNoBound, EqNoBound, PartialEqNoBound, RuntimeDebugNoBound,
};
use pezsp_runtime::{
traits::{
AsTransactionAuthorizedOrigin, Dispatchable, Implication, PostDispatchInfoOf,
TransactionExtension, ValidateResult,
},
transaction_validity::TransactionValidityError,
};
/// A transaction extension that authorizes some calls (i.e. dispatchable functions) to be
/// included in the block.
///
/// This transaction extension use the runtime implementation of the trait
/// [`Authorize`](pezframe_support::traits::Authorize) to set the validity of the transaction.
#[derive(
Encode,
Decode,
CloneNoBound,
EqNoBound,
PartialEqNoBound,
TypeInfo,
RuntimeDebugNoBound,
DecodeWithMemTracking,
)]
#[scale_info(skip_type_params(T))]
pub struct AuthorizeCall<T>(core::marker::PhantomData<T>);
impl<T> AuthorizeCall<T> {
pub fn new() -> Self {
Self(Default::default())
}
}
impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for AuthorizeCall<T>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
{
const IDENTIFIER: &'static str = "AuthorizeCall";
type Implicit = ();
type Val = Weight;
type Pre = Weight;
fn validate(
&self,
origin: T::RuntimeOrigin,
call: &T::RuntimeCall,
_info: &DispatchInfo,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl Implication,
source: TransactionSource,
) -> ValidateResult<Self::Val, T::RuntimeCall> {
if !origin.is_transaction_authorized() {
if let Some(authorize) = call.authorize(source) {
return authorize.map(|(validity, unspent)| {
(validity, unspent, crate::Origin::<T>::Authorized.into())
});
}
}
Ok((Default::default(), Weight::zero(), origin))
}
fn prepare(
self,
val: Self::Val,
_origin: &T::RuntimeOrigin,
_call: &T::RuntimeCall,
_info: &DispatchInfo,
_len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
Ok(val)
}
fn post_dispatch_details(
pre: Self::Pre,
_info: &DispatchInfo,
_post_info: &PostDispatchInfoOf<T::RuntimeCall>,
_len: usize,
_result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
Ok(pre)
}
fn weight(&self, call: &T::RuntimeCall) -> Weight {
call.weight_of_authorize()
}
}
#[cfg(test)]
mod tests {
use crate as pezframe_system;
use codec::Encode;
use pezframe_support::{
derive_impl, dispatch::GetDispatchInfo, pezpallet_prelude::TransactionSource,
traits::OriginTrait,
};
use pezsp_runtime::{
testing::UintAuthorityId,
traits::{Applyable, Checkable, TransactionExtension as _, TxBaseImplication},
transaction_validity::{
InvalidTransaction, TransactionSource::External, TransactionValidityError,
},
BuildStorage, DispatchError,
};
#[pezframe_support::pallet]
pub mod pallet1 {
use crate as pezframe_system;
use pezframe_support::pezpallet_prelude::*;
use pezframe_system::pezpallet_prelude::*;
pub const CALL_WEIGHT: Weight = Weight::from_all(4);
pub const AUTH_WEIGHT: Weight = Weight::from_all(5);
pub fn valid_transaction() -> ValidTransaction {
ValidTransaction {
priority: 10,
provides: vec![1u8.encode()],
requires: vec![],
longevity: 1000,
propagate: true,
}
}
#[pallet::pallet]
pub struct Pallet<T>(_);
#[pallet::config]
pub trait Config: pezframe_system::Config {}
#[pallet::call]
impl<T: Config> Pallet<T> {
#[pallet::weight(CALL_WEIGHT)]
#[pallet::call_index(0)]
#[pallet::authorize(|_source, valid| if *valid {
Ok((valid_transaction(), Weight::zero()))
} else {
Err(TransactionValidityError::Invalid(InvalidTransaction::Call))
})]
#[pallet::weight_of_authorize(AUTH_WEIGHT)]
pub fn call1(origin: OriginFor<T>, valid: bool) -> DispatchResult {
crate::ensure_authorized(origin)?;
let _ = valid;
Ok(())
}
}
}
#[pezframe_support::runtime]
mod runtime {
#[runtime::runtime]
#[runtime::derive(
RuntimeCall,
RuntimeEvent,
RuntimeError,
RuntimeOrigin,
RuntimeFreezeReason,
RuntimeHoldReason,
RuntimeSlashReason,
RuntimeLockId,
RuntimeTask
)]
pub struct Runtime;
#[runtime::pezpallet_index(0)]
pub type System = pezframe_system::Pallet<Runtime>;
#[runtime::pezpallet_index(1)]
pub type Pallet1 = pallet1::Pallet<Runtime>;
}
pub type TransactionExtension = (pezframe_system::AuthorizeCall<Runtime>,);
pub type Header = pezsp_runtime::generic::Header<u32, pezsp_runtime::traits::BlakeTwo256>;
pub type Block = pezsp_runtime::generic::Block<Header, UncheckedExtrinsic>;
pub type UncheckedExtrinsic = pezsp_runtime::generic::UncheckedExtrinsic<
u64,
RuntimeCall,
UintAuthorityId,
TransactionExtension,
>;
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl pezframe_system::Config for Runtime {
type Block = Block;
}
impl pallet1::Config for Runtime {}
pub fn new_test_ext() -> pezsp_io::TestExternalities {
let t = RuntimeGenesisConfig { ..Default::default() }.build_storage().unwrap();
t.into()
}
#[test]
fn valid_transaction() {
let call = RuntimeCall::Pallet1(pallet1::Call::call1 { valid: true });
new_test_ext().execute_with(|| {
let tx_ext = (pezframe_system::AuthorizeCall::<Runtime>::new(),);
let tx = UncheckedExtrinsic::new_transaction(call, tx_ext);
let info = tx.get_dispatch_info();
let len = tx.using_encoded(|e| e.len());
let checked = Checkable::check(tx, &pezframe_system::ChainContext::<Runtime>::default())
.expect("Transaction is general so signature is good");
let valid_tx = checked
.validate::<Runtime>(TransactionSource::External, &info, len)
.expect("call valid");
let dispatch_result =
checked.apply::<Runtime>(&info, len).expect("Transaction is valid");
assert!(dispatch_result.is_ok());
let post_info = dispatch_result.unwrap_or_else(|e| e.post_info);
assert_eq!(valid_tx, pallet1::valid_transaction());
assert_eq!(info.call_weight, pallet1::CALL_WEIGHT);
assert_eq!(info.extension_weight, pallet1::AUTH_WEIGHT);
assert_eq!(post_info.actual_weight, Some(pallet1::CALL_WEIGHT + pallet1::AUTH_WEIGHT));
});
}
#[test]
fn invalid_transaction_fail_authorization() {
let call = RuntimeCall::Pallet1(pallet1::Call::call1 { valid: false });
new_test_ext().execute_with(|| {
let tx_ext = (pezframe_system::AuthorizeCall::<Runtime>::new(),);
let tx = UncheckedExtrinsic::new_transaction(call, tx_ext);
let info = tx.get_dispatch_info();
let len = tx.using_encoded(|e| e.len());
let checked = Checkable::check(tx, &pezframe_system::ChainContext::<Runtime>::default())
.expect("Transaction is general so signature is good");
let validate_err = checked
.validate::<Runtime>(TransactionSource::External, &info, len)
.expect_err("call is invalid");
let apply_err =
checked.apply::<Runtime>(&info, len).expect_err("Transaction is invalid");
assert_eq!(validate_err, TransactionValidityError::Invalid(InvalidTransaction::Call));
assert_eq!(apply_err, TransactionValidityError::Invalid(InvalidTransaction::Call));
assert_eq!(info.call_weight, pallet1::CALL_WEIGHT);
assert_eq!(info.extension_weight, pallet1::AUTH_WEIGHT);
});
}
#[test]
fn failing_transaction_invalid_origin() {
let call = RuntimeCall::Pallet1(pallet1::Call::call1 { valid: true });
new_test_ext().execute_with(|| {
let tx_ext = (pezframe_system::AuthorizeCall::<Runtime>::new(),);
let tx = UncheckedExtrinsic::new_signed(call, 42, 42.into(), tx_ext);
let info = tx.get_dispatch_info();
let len = tx.using_encoded(|e| e.len());
let checked = Checkable::check(tx, &pezframe_system::ChainContext::<Runtime>::default())
.expect("Signature is good");
checked
.validate::<Runtime>(TransactionSource::External, &info, len)
.expect("Transaction is valid, tx ext doesn't deny none");
let dispatch_res = checked
.apply::<Runtime>(&info, len)
.expect("Transaction is valid")
.expect_err("Transaction is failing, because origin is wrong");
assert_eq!(dispatch_res.error, DispatchError::BadOrigin);
assert_eq!(info.call_weight, pallet1::CALL_WEIGHT);
assert_eq!(info.extension_weight, pallet1::AUTH_WEIGHT);
});
}
#[test]
fn call_filter_preserved() {
new_test_ext().execute_with(|| {
let ext = pezframe_system::AuthorizeCall::<Runtime>::new();
let filtered_call = RuntimeCall::System(pezframe_system::Call::remark { remark: vec![] });
let origin = {
let mut o: RuntimeOrigin = crate::Origin::<Runtime>::Signed(42).into();
let filter_clone = filtered_call.clone();
o.add_filter(move |call| filter_clone != *call);
o
};
assert!(!origin.filter_call(&filtered_call));
let (_, _, new_origin) = ext
.validate(
origin,
&RuntimeCall::Pallet1(pallet1::Call::call1 { valid: true }),
&crate::DispatchInfo::default(),
Default::default(),
(),
&TxBaseImplication(()),
External,
)
.expect("valid");
assert!(!new_origin.filter_call(&filtered_call));
});
}
}
@@ -0,0 +1,70 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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 crate::{pezpallet_prelude::BlockNumberFor, Config, Pallet};
use codec::{Decode, DecodeWithMemTracking, Encode};
use scale_info::TypeInfo;
use pezsp_runtime::{
impl_tx_ext_default,
traits::{TransactionExtension, Zero},
transaction_validity::TransactionValidityError,
};
/// Genesis hash check to provide replay protection between different networks.
///
/// # Transaction Validity
///
/// Note that while a transaction with invalid `genesis_hash` will fail to be decoded,
/// the extension does not affect any other fields of `TransactionValidity` directly.
#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct CheckGenesis<T: Config + Send + Sync>(core::marker::PhantomData<T>);
impl<T: Config + Send + Sync> core::fmt::Debug for CheckGenesis<T> {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "CheckGenesis")
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
Ok(())
}
}
impl<T: Config + Send + Sync> CheckGenesis<T> {
/// Creates new `TransactionExtension` to check genesis hash.
pub fn new() -> Self {
Self(core::marker::PhantomData)
}
}
impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for CheckGenesis<T> {
const IDENTIFIER: &'static str = "CheckGenesis";
type Implicit = T::Hash;
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
Ok(<Pallet<T>>::block_hash(BlockNumberFor::<T>::zero()))
}
type Val = ();
type Pre = ();
fn weight(&self, _: &T::RuntimeCall) -> pezsp_weights::Weight {
// All transactions will always read the hash of the genesis block, so to avoid
// charging this multiple times in a block we manually set the proof size to 0.
<T::ExtensionsWeightInfo as super::WeightInfo>::check_genesis().set_proof_size(0)
}
impl_tx_ext_default!(T::RuntimeCall; validate prepare);
}
@@ -0,0 +1,166 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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 crate::{pezpallet_prelude::BlockNumberFor, BlockHash, Config, Pallet};
use codec::{Decode, DecodeWithMemTracking, Encode};
use pezframe_support::pezpallet_prelude::TransactionSource;
use scale_info::TypeInfo;
use pezsp_runtime::{
generic::Era,
impl_tx_ext_default,
traits::{DispatchInfoOf, SaturatedConversion, TransactionExtension, ValidateResult},
transaction_validity::{InvalidTransaction, TransactionValidityError, ValidTransaction},
};
/// Check for transaction mortality.
///
/// The extension adds [`Era`] to every signed extrinsic. It also contributes to the signed data, by
/// including the hash of the block at [`Era::birth`].
///
/// # Transaction Validity
///
/// The extension affects `longevity` of the transaction according to the [`Era`] definition.
#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct CheckMortality<T: Config + Send + Sync>(pub Era, core::marker::PhantomData<T>);
impl<T: Config + Send + Sync> CheckMortality<T> {
/// utility constructor. Used only in client/factory code.
pub fn from(era: Era) -> Self {
Self(era, core::marker::PhantomData)
}
}
impl<T: Config + Send + Sync> core::fmt::Debug for CheckMortality<T> {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "CheckMortality({:?})", self.0)
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
Ok(())
}
}
impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for CheckMortality<T> {
const IDENTIFIER: &'static str = "CheckMortality";
type Implicit = T::Hash;
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
let current_u64 = <Pallet<T>>::block_number().saturated_into::<u64>();
let n = self.0.birth(current_u64).saturated_into::<BlockNumberFor<T>>();
if !<BlockHash<T>>::contains_key(n) {
Err(InvalidTransaction::AncientBirthBlock.into())
} else {
Ok(<Pallet<T>>::block_hash(n))
}
}
type Pre = ();
type Val = ();
fn weight(&self, _: &T::RuntimeCall) -> pezsp_weights::Weight {
if self.0.is_immortal() {
// All immortal transactions will always read the hash of the genesis block, so to avoid
// charging this multiple times in a block we manually set the proof size to 0.
<T::ExtensionsWeightInfo as super::WeightInfo>::check_mortality_immortal_transaction()
.set_proof_size(0)
} else {
<T::ExtensionsWeightInfo as super::WeightInfo>::check_mortality_mortal_transaction()
}
}
fn validate(
&self,
origin: <T as Config>::RuntimeOrigin,
_call: &T::RuntimeCall,
_info: &DispatchInfoOf<T::RuntimeCall>,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl Encode,
_source: TransactionSource,
) -> ValidateResult<Self::Val, T::RuntimeCall> {
let current_u64 = <Pallet<T>>::block_number().saturated_into::<u64>();
let valid_till = self.0.death(current_u64);
Ok((
ValidTransaction {
longevity: valid_till.saturating_sub(current_u64),
..Default::default()
},
(),
origin,
))
}
impl_tx_ext_default!(T::RuntimeCall; prepare);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{new_test_ext, System, Test, CALL};
use pezframe_support::{
dispatch::{DispatchClass, DispatchInfo, Pays},
weights::Weight,
};
use pezsp_core::H256;
use pezsp_runtime::{
traits::DispatchTransaction, transaction_validity::TransactionSource::External,
};
#[test]
fn signed_ext_check_era_should_work() {
new_test_ext().execute_with(|| {
// future
assert_eq!(
CheckMortality::<Test>::from(Era::mortal(4, 2)).implicit().err().unwrap(),
InvalidTransaction::AncientBirthBlock.into(),
);
// correct
System::set_block_number(13);
<BlockHash<Test>>::insert(12, H256::repeat_byte(1));
assert!(CheckMortality::<Test>::from(Era::mortal(4, 12)).implicit().is_ok());
})
}
#[test]
fn signed_ext_check_era_should_change_longevity() {
new_test_ext().execute_with(|| {
let normal = DispatchInfo {
call_weight: Weight::from_parts(100, 0),
extension_weight: Weight::zero(),
class: DispatchClass::Normal,
pays_fee: Pays::Yes,
};
let len = 0_usize;
let ext = (
crate::CheckWeight::<Test>::new(),
CheckMortality::<Test>::from(Era::mortal(16, 256)),
);
System::set_block_number(17);
<BlockHash<Test>>::insert(16, H256::repeat_byte(1));
assert_eq!(
ext.validate_only(Some(1).into(), CALL, &normal, len, External, 0)
.unwrap()
.0
.longevity,
15
);
})
}
}
@@ -0,0 +1,126 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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 crate::Config;
use codec::{Decode, DecodeWithMemTracking, Encode};
use core::marker::PhantomData;
use pezframe_support::{pezpallet_prelude::TransactionSource, traits::OriginTrait, DefaultNoBound};
use scale_info::TypeInfo;
use pezsp_runtime::{
impl_tx_ext_default,
traits::{DispatchInfoOf, TransactionExtension},
transaction_validity::InvalidTransaction,
};
/// Check to ensure that the sender is not the zero address.
#[derive(Encode, Decode, DecodeWithMemTracking, DefaultNoBound, Clone, Eq, PartialEq, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct CheckNonZeroSender<T>(PhantomData<T>);
impl<T: Config + Send + Sync> core::fmt::Debug for CheckNonZeroSender<T> {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "CheckNonZeroSender")
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
Ok(())
}
}
impl<T: Config + Send + Sync> CheckNonZeroSender<T> {
/// Create new `TransactionExtension` to check runtime version.
pub fn new() -> Self {
Self(core::marker::PhantomData)
}
}
impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for CheckNonZeroSender<T> {
const IDENTIFIER: &'static str = "CheckNonZeroSender";
type Implicit = ();
type Val = ();
type Pre = ();
fn weight(&self, _: &T::RuntimeCall) -> pezsp_weights::Weight {
<T::ExtensionsWeightInfo as super::WeightInfo>::check_non_zero_sender()
}
fn validate(
&self,
origin: <T as Config>::RuntimeOrigin,
_call: &T::RuntimeCall,
_info: &DispatchInfoOf<T::RuntimeCall>,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl Encode,
_source: TransactionSource,
) -> pezsp_runtime::traits::ValidateResult<Self::Val, T::RuntimeCall> {
if let Some(who) = origin.as_signer() {
if who.using_encoded(|d| d.iter().all(|x| *x == 0)) {
return Err(InvalidTransaction::BadSigner.into());
}
}
Ok((Default::default(), (), origin))
}
impl_tx_ext_default!(T::RuntimeCall; prepare);
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{new_test_ext, Test, CALL};
use pezframe_support::{assert_ok, dispatch::DispatchInfo};
use pezsp_runtime::{
traits::{AsTransactionAuthorizedOrigin, DispatchTransaction, TxBaseImplication},
transaction_validity::{TransactionSource::External, TransactionValidityError},
};
#[test]
fn zero_account_ban_works() {
new_test_ext().execute_with(|| {
let info = DispatchInfo::default();
let len = 0_usize;
assert_eq!(
CheckNonZeroSender::<Test>::new()
.validate_only(Some(0).into(), CALL, &info, len, External, 0)
.unwrap_err(),
TransactionValidityError::from(InvalidTransaction::BadSigner)
);
assert_ok!(CheckNonZeroSender::<Test>::new().validate_only(
Some(1).into(),
CALL,
&info,
len,
External,
0,
));
})
}
#[test]
fn unsigned_origin_works() {
new_test_ext().execute_with(|| {
let info = DispatchInfo::default();
let len = 0_usize;
let (_, _, origin) = CheckNonZeroSender::<Test>::new()
.validate(None.into(), CALL, &info, len, (), &TxBaseImplication(CALL), External)
.unwrap();
assert!(!origin.is_transaction_authorized());
})
}
}
@@ -0,0 +1,447 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
extern crate alloc;
use alloc::{vec, vec::Vec};
use crate::Config;
use codec::{Decode, DecodeWithMemTracking, Encode};
use pezframe_support::{
dispatch::DispatchInfo, pezpallet_prelude::TransactionSource, RuntimeDebugNoBound,
};
use scale_info::TypeInfo;
use pezsp_runtime::{
traits::{
AsSystemOriginSigner, CheckedAdd, DispatchInfoOf, Dispatchable, One, PostDispatchInfoOf,
TransactionExtension, ValidateResult, Zero,
},
transaction_validity::{
InvalidTransaction, TransactionLongevity, TransactionValidityError, ValidTransaction,
},
DispatchResult, Saturating,
};
use pezsp_weights::Weight;
/// Nonce check and increment to give replay protection for transactions.
///
/// # Transaction Validity
///
/// This extension affects `requires` and `provides` tags of validity, but DOES NOT
/// set the `priority` field. Make sure that AT LEAST one of the transaction extension sets
/// some kind of priority upon validating transactions.
///
/// The preparation step assumes that the nonce information has not changed since the validation
/// step. This means that other extensions ahead of `CheckNonce` in the pipeline must not alter the
/// nonce during their own preparation step, or else the transaction may be rejected during dispatch
/// or lead to an inconsistent account state.
#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct CheckNonce<T: Config>(#[codec(compact)] pub T::Nonce);
/// For a valid transaction the provides and requires information related to the nonce.
pub struct ValidNonceInfo {
/// The encoded `provides` used for this transaction.
pub provides: Vec<Vec<u8>>,
/// The encoded `requires` used for this transaction.
pub requires: Vec<Vec<u8>>,
}
impl<T: Config> CheckNonce<T> {
/// utility constructor. Used only in client/factory code.
pub fn from(nonce: T::Nonce) -> Self {
Self(nonce)
}
/// In transaction extension, validate nonce for account, on success returns provides and
/// requires.
pub fn validate_nonce_for_account(
who: &T::AccountId,
nonce: T::Nonce,
) -> Result<ValidNonceInfo, TransactionValidityError> {
let account = crate::Account::<T>::get(who);
if account.providers.is_zero() && account.sufficients.is_zero() {
// Nonce storage not paid for
return Err(InvalidTransaction::Payment.into());
}
if nonce < account.nonce {
return Err(InvalidTransaction::Stale.into());
}
let provides = vec![Encode::encode(&(who.clone(), nonce))];
let requires = if account.nonce < nonce {
vec![Encode::encode(&(who.clone(), nonce.saturating_sub(One::one())))]
} else {
vec![]
};
Ok(ValidNonceInfo { provides, requires })
}
/// In transaction extension, prepare nonce for account.
pub fn prepare_nonce_for_account(
who: &T::AccountId,
mut nonce: T::Nonce,
) -> Result<(), TransactionValidityError> {
let account = crate::Account::<T>::get(who);
if nonce > account.nonce {
return Err(InvalidTransaction::Future.into());
}
nonce = nonce.checked_add(&T::Nonce::one()).unwrap_or(T::Nonce::zero());
crate::Account::<T>::mutate(who, |account| account.nonce = nonce);
Ok(())
}
}
impl<T: Config> core::fmt::Debug for CheckNonce<T> {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "CheckNonce({})", self.0)
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
Ok(())
}
}
/// Operation to perform from `validate` to `prepare` in [`CheckNonce`] transaction extension.
#[derive(RuntimeDebugNoBound)]
pub enum Val<T: Config> {
/// Account and its nonce to check for.
CheckNonce(T::AccountId),
/// Weight to refund.
Refund(Weight),
}
/// Operation to perform from `prepare` to `post_dispatch_details` in [`CheckNonce`] transaction
/// extension.
#[derive(RuntimeDebugNoBound)]
pub enum Pre {
/// The transaction extension weight should not be refunded.
NonceChecked,
/// The transaction extension weight should be refunded.
Refund(Weight),
}
impl<T: Config> TransactionExtension<T::RuntimeCall> for CheckNonce<T>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo>,
<T::RuntimeCall as Dispatchable>::RuntimeOrigin: AsSystemOriginSigner<T::AccountId> + Clone,
{
const IDENTIFIER: &'static str = "CheckNonce";
type Implicit = ();
type Val = Val<T>;
type Pre = Pre;
fn weight(&self, _: &T::RuntimeCall) -> pezsp_weights::Weight {
<T::ExtensionsWeightInfo as super::WeightInfo>::check_nonce()
}
fn validate(
&self,
origin: <T as Config>::RuntimeOrigin,
call: &T::RuntimeCall,
_info: &DispatchInfoOf<T::RuntimeCall>,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl Encode,
_source: TransactionSource,
) -> ValidateResult<Self::Val, T::RuntimeCall> {
let Some(who) = origin.as_system_origin_signer() else {
return Ok((Default::default(), Val::Refund(self.weight(call)), origin));
};
let ValidNonceInfo { provides, requires } = Self::validate_nonce_for_account(who, self.0)?;
let validity = ValidTransaction {
priority: 0,
requires,
provides,
longevity: TransactionLongevity::max_value(),
propagate: true,
};
Ok((validity, Val::CheckNonce(who.clone()), origin))
}
fn prepare(
self,
val: Self::Val,
_origin: &T::RuntimeOrigin,
_call: &T::RuntimeCall,
_info: &DispatchInfoOf<T::RuntimeCall>,
_len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
let (who, nonce) = match val {
Val::CheckNonce(who) => (who, self.0),
Val::Refund(weight) => return Ok(Pre::Refund(weight)),
};
Self::prepare_nonce_for_account(&who, nonce).map(|_| Pre::NonceChecked)
}
fn post_dispatch_details(
pre: Self::Pre,
_info: &DispatchInfo,
_post_info: &PostDispatchInfoOf<T::RuntimeCall>,
_len: usize,
_result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
match pre {
Pre::NonceChecked => Ok(Weight::zero()),
Pre::Refund(weight) => Ok(weight),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{new_test_ext, RuntimeCall, Test, CALL};
use pezframe_support::{
assert_ok, assert_storage_noop, dispatch::GetDispatchInfo, traits::OriginTrait,
};
use pezsp_runtime::{
traits::{AsTransactionAuthorizedOrigin, DispatchTransaction, TxBaseImplication},
transaction_validity::TransactionSource::External,
};
#[test]
fn signed_ext_check_nonce_works() {
new_test_ext().execute_with(|| {
crate::Account::<Test>::insert(
1,
crate::AccountInfo {
nonce: 1u64.into(),
consumers: 0,
providers: 1,
sufficients: 0,
data: 0,
},
);
let info = DispatchInfo::default();
let len = 0_usize;
// stale
assert_storage_noop!({
assert_eq!(
CheckNonce::<Test>(0u64.into())
.validate_only(Some(1).into(), CALL, &info, len, External, 0)
.unwrap_err(),
TransactionValidityError::Invalid(InvalidTransaction::Stale)
);
assert_eq!(
CheckNonce::<Test>(0u64.into())
.validate_and_prepare(Some(1).into(), CALL, &info, len, 0)
.unwrap_err(),
TransactionValidityError::Invalid(InvalidTransaction::Stale)
);
});
// correct
assert_ok!(CheckNonce::<Test>(1u64.into()).validate_only(
Some(1).into(),
CALL,
&info,
len,
External,
0,
));
assert_ok!(CheckNonce::<Test>(1u64.into()).validate_and_prepare(
Some(1).into(),
CALL,
&info,
len,
0,
));
// future
assert_ok!(CheckNonce::<Test>(5u64.into()).validate_only(
Some(1).into(),
CALL,
&info,
len,
External,
0,
));
assert_eq!(
CheckNonce::<Test>(5u64.into())
.validate_and_prepare(Some(1).into(), CALL, &info, len, 0)
.unwrap_err(),
TransactionValidityError::Invalid(InvalidTransaction::Future)
);
})
}
#[test]
fn signed_ext_check_nonce_requires_provider() {
new_test_ext().execute_with(|| {
crate::Account::<Test>::insert(
2,
crate::AccountInfo {
nonce: 1u64.into(),
consumers: 0,
providers: 1,
sufficients: 0,
data: 0,
},
);
crate::Account::<Test>::insert(
3,
crate::AccountInfo {
nonce: 1u64.into(),
consumers: 0,
providers: 0,
sufficients: 1,
data: 0,
},
);
let info = DispatchInfo::default();
let len = 0_usize;
// Both providers and sufficients zero
assert_storage_noop!({
assert_eq!(
CheckNonce::<Test>(1u64.into())
.validate_only(Some(1).into(), CALL, &info, len, External, 0)
.unwrap_err(),
TransactionValidityError::Invalid(InvalidTransaction::Payment)
);
assert_eq!(
CheckNonce::<Test>(1u64.into())
.validate_and_prepare(Some(1).into(), CALL, &info, len, 0)
.unwrap_err(),
TransactionValidityError::Invalid(InvalidTransaction::Payment)
);
});
// Non-zero providers
assert_ok!(CheckNonce::<Test>(1u64.into()).validate_only(
Some(2).into(),
CALL,
&info,
len,
External,
0,
));
assert_ok!(CheckNonce::<Test>(1u64.into()).validate_and_prepare(
Some(2).into(),
CALL,
&info,
len,
0,
));
// Non-zero sufficients
assert_ok!(CheckNonce::<Test>(1u64.into()).validate_only(
Some(3).into(),
CALL,
&info,
len,
External,
0,
));
assert_ok!(CheckNonce::<Test>(1u64.into()).validate_and_prepare(
Some(3).into(),
CALL,
&info,
len,
0,
));
})
}
#[test]
fn unsigned_check_nonce_works() {
new_test_ext().execute_with(|| {
let info = DispatchInfo::default();
let len = 0_usize;
let (_, val, origin) = CheckNonce::<Test>(1u64.into())
.validate(None.into(), CALL, &info, len, (), &TxBaseImplication(CALL), External)
.unwrap();
assert!(!origin.is_transaction_authorized());
assert_ok!(CheckNonce::<Test>(1u64.into()).prepare(val, &origin, CALL, &info, len));
})
}
#[test]
fn check_nonce_preserves_account_data() {
new_test_ext().execute_with(|| {
crate::Account::<Test>::insert(
1,
crate::AccountInfo {
nonce: 1u64.into(),
consumers: 0,
providers: 1,
sufficients: 0,
data: 0,
},
);
let info = DispatchInfo::default();
let len = 0_usize;
// run the validation step
let (_, val, origin) = CheckNonce::<Test>(1u64.into())
.validate(Some(1).into(), CALL, &info, len, (), &TxBaseImplication(CALL), External)
.unwrap();
// mutate `AccountData` for the caller
crate::Account::<Test>::mutate(1, |info| {
info.data = 42;
});
// run the preparation step
assert_ok!(CheckNonce::<Test>(1u64.into()).prepare(val, &origin, CALL, &info, len));
// only the nonce should be altered by the preparation step
let expected_info = crate::AccountInfo {
nonce: 2u64.into(),
consumers: 0,
providers: 1,
sufficients: 0,
data: 42,
};
assert_eq!(crate::Account::<Test>::get(1), expected_info);
})
}
#[test]
fn check_nonce_skipped_and_refund_for_other_origins() {
new_test_ext().execute_with(|| {
let ext = CheckNonce::<Test>(1u64.into());
let mut info = CALL.get_dispatch_info();
info.extension_weight = ext.weight(CALL);
// Ensure we test the refund.
assert!(info.extension_weight != Weight::zero());
let len = CALL.encoded_size();
let origin = crate::RawOrigin::Root.into();
let (pre, origin) = ext.validate_and_prepare(origin, CALL, &info, len, 0).unwrap();
assert!(origin.as_system_ref().unwrap().is_root());
let pd_res = Ok(());
let mut post_info = pezframe_support::dispatch::PostDispatchInfo {
actual_weight: Some(info.total_weight()),
pays_fee: Default::default(),
};
<CheckNonce<Test> as TransactionExtension<RuntimeCall>>::post_dispatch(
pre,
&info,
&mut post_info,
len,
&pd_res,
)
.unwrap();
assert_eq!(post_info.actual_weight, Some(info.call_weight));
})
}
}
@@ -0,0 +1,69 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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 crate::{Config, Pallet};
use codec::{Decode, DecodeWithMemTracking, Encode};
use scale_info::TypeInfo;
use pezsp_runtime::{
impl_tx_ext_default, traits::TransactionExtension,
transaction_validity::TransactionValidityError,
};
/// Ensure the runtime version registered in the transaction is the same as at present.
///
/// # Transaction Validity
///
/// The transaction with incorrect `spec_version` are considered invalid. The validity
/// is not affected in any other way.
#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct CheckSpecVersion<T: Config + Send + Sync>(core::marker::PhantomData<T>);
impl<T: Config + Send + Sync> core::fmt::Debug for CheckSpecVersion<T> {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "CheckSpecVersion")
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
Ok(())
}
}
impl<T: Config + Send + Sync> CheckSpecVersion<T> {
/// Create new `TransactionExtension` to check runtime version.
pub fn new() -> Self {
Self(core::marker::PhantomData)
}
}
impl<T: Config + Send + Sync> TransactionExtension<<T as Config>::RuntimeCall>
for CheckSpecVersion<T>
{
const IDENTIFIER: &'static str = "CheckSpecVersion";
type Implicit = u32;
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
Ok(<Pallet<T>>::runtime_version().spec_version)
}
type Val = ();
type Pre = ();
fn weight(&self, _: &<T as Config>::RuntimeCall) -> pezsp_weights::Weight {
<T::ExtensionsWeightInfo as super::WeightInfo>::check_spec_version()
}
impl_tx_ext_default!(<T as Config>::RuntimeCall; validate prepare);
}
@@ -0,0 +1,69 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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 crate::{Config, Pallet};
use codec::{Decode, DecodeWithMemTracking, Encode};
use scale_info::TypeInfo;
use pezsp_runtime::{
impl_tx_ext_default, traits::TransactionExtension,
transaction_validity::TransactionValidityError,
};
/// Ensure the transaction version registered in the transaction is the same as at present.
///
/// # Transaction Validity
///
/// The transaction with incorrect `transaction_version` are considered invalid. The validity
/// is not affected in any other way.
#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct CheckTxVersion<T: Config + Send + Sync>(core::marker::PhantomData<T>);
impl<T: Config + Send + Sync> core::fmt::Debug for CheckTxVersion<T> {
#[cfg(feature = "std")]
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "CheckTxVersion")
}
#[cfg(not(feature = "std"))]
fn fmt(&self, _: &mut core::fmt::Formatter) -> core::fmt::Result {
Ok(())
}
}
impl<T: Config + Send + Sync> CheckTxVersion<T> {
/// Create new `TransactionExtension` to check transaction version.
pub fn new() -> Self {
Self(core::marker::PhantomData)
}
}
impl<T: Config + Send + Sync> TransactionExtension<<T as Config>::RuntimeCall>
for CheckTxVersion<T>
{
const IDENTIFIER: &'static str = "CheckTxVersion";
type Implicit = u32;
fn implicit(&self) -> Result<Self::Implicit, TransactionValidityError> {
Ok(<Pallet<T>>::runtime_version().transaction_version)
}
type Val = ();
type Pre = ();
fn weight(&self, _: &<T as Config>::RuntimeCall) -> pezsp_weights::Weight {
<T::ExtensionsWeightInfo as super::WeightInfo>::check_tx_version()
}
impl_tx_ext_default!(<T as Config>::RuntimeCall; validate prepare);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,29 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
pub mod authorize_call;
pub mod check_genesis;
pub mod check_mortality;
pub mod check_non_zero_sender;
pub mod check_nonce;
pub mod check_spec_version;
pub mod check_tx_version;
pub mod check_weight;
pub mod weight_reclaim;
pub mod weights;
pub use weights::WeightInfo;
@@ -0,0 +1,401 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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 crate::Config;
use codec::{Decode, DecodeWithMemTracking, Encode};
use pezframe_support::dispatch::{DispatchInfo, PostDispatchInfo};
use scale_info::TypeInfo;
use pezsp_runtime::{
traits::{
DispatchInfoOf, Dispatchable, PostDispatchInfoOf, TransactionExtension, ValidateResult,
},
transaction_validity::{TransactionSource, TransactionValidityError, ValidTransaction},
DispatchResult,
};
use pezsp_weights::Weight;
/// Reclaim the unused weight using the post dispatch information
///
/// After the dispatch of the extrinsic, calculate the unused weight using the post dispatch
/// information and update the block consumed weight according to the new calculated extrinsic
/// weight.
#[derive(Encode, Decode, DecodeWithMemTracking, Clone, Eq, PartialEq, Default, TypeInfo)]
#[scale_info(skip_type_params(T))]
pub struct WeightReclaim<T: Config + Send + Sync>(core::marker::PhantomData<T>);
impl<T: Config + Send + Sync> WeightReclaim<T>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
{
/// Creates new `TransactionExtension` to recalculate the extrinsic weight after dispatch.
pub fn new() -> Self {
Self(Default::default())
}
}
impl<T: Config + Send + Sync> TransactionExtension<T::RuntimeCall> for WeightReclaim<T>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
{
const IDENTIFIER: &'static str = "WeightReclaim";
type Implicit = ();
type Pre = ();
type Val = ();
fn weight(&self, _: &T::RuntimeCall) -> Weight {
<T::ExtensionsWeightInfo as super::WeightInfo>::weight_reclaim()
}
fn validate(
&self,
origin: T::RuntimeOrigin,
_call: &T::RuntimeCall,
_info: &DispatchInfoOf<T::RuntimeCall>,
_len: usize,
_self_implicit: Self::Implicit,
_inherited_implication: &impl Encode,
_source: TransactionSource,
) -> ValidateResult<Self::Val, T::RuntimeCall> {
Ok((ValidTransaction::default(), (), origin))
}
fn prepare(
self,
_val: Self::Val,
_origin: &T::RuntimeOrigin,
_call: &T::RuntimeCall,
_info: &DispatchInfoOf<T::RuntimeCall>,
_len: usize,
) -> Result<Self::Pre, TransactionValidityError> {
Ok(())
}
fn post_dispatch_details(
_pre: Self::Pre,
info: &DispatchInfoOf<T::RuntimeCall>,
post_info: &PostDispatchInfoOf<T::RuntimeCall>,
_len: usize,
_result: &DispatchResult,
) -> Result<Weight, TransactionValidityError> {
crate::Pallet::<T>::reclaim_weight(info, post_info).map(|()| Weight::zero())
}
fn bare_validate(
_call: &T::RuntimeCall,
_info: &DispatchInfoOf<T::RuntimeCall>,
_len: usize,
) -> pezframe_support::pezpallet_prelude::TransactionValidity {
Ok(ValidTransaction::default())
}
fn bare_validate_and_prepare(
_call: &T::RuntimeCall,
_info: &DispatchInfoOf<T::RuntimeCall>,
_len: usize,
) -> Result<(), TransactionValidityError> {
Ok(())
}
fn bare_post_dispatch(
info: &DispatchInfoOf<T::RuntimeCall>,
post_info: &mut PostDispatchInfoOf<T::RuntimeCall>,
_len: usize,
_result: &DispatchResult,
) -> Result<(), TransactionValidityError> {
crate::Pallet::<T>::reclaim_weight(info, post_info)
}
}
impl<T: Config + Send + Sync> core::fmt::Debug for WeightReclaim<T>
where
T::RuntimeCall: Dispatchable<Info = DispatchInfo, PostInfo = PostDispatchInfo>,
{
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(f, "{}", Self::IDENTIFIER)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{
mock::{new_test_ext, Test},
BlockWeight, DispatchClass,
};
use pezframe_support::{assert_ok, weights::Weight};
fn block_weights() -> crate::limits::BlockWeights {
<Test as crate::Config>::BlockWeights::get()
}
#[test]
fn extrinsic_already_refunded_more_precisely() {
new_test_ext().execute_with(|| {
// This is half of the max block weight
let info =
DispatchInfo { call_weight: Weight::from_parts(512, 0), ..Default::default() };
let post_info = PostDispatchInfo {
actual_weight: Some(Weight::from_parts(128, 0)),
pays_fee: Default::default(),
};
let prior_block_weight = Weight::from_parts(64, 0);
let accurate_refund = Weight::from_parts(510, 0);
let len = 0_usize;
let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic;
// Set initial info
BlockWeight::<Test>::mutate(|current_weight| {
current_weight.set(prior_block_weight, DispatchClass::Normal);
current_weight.accrue(
base_extrinsic + info.total_weight() - accurate_refund,
DispatchClass::Normal,
);
});
crate::ExtrinsicWeightReclaimed::<Test>::put(accurate_refund);
// Do the post dispatch
assert_ok!(WeightReclaim::<Test>::post_dispatch_details(
(),
&info,
&post_info,
len,
&Ok(())
));
// Ensure the accurate refund is used
assert_eq!(crate::ExtrinsicWeightReclaimed::<Test>::get(), accurate_refund);
assert_eq!(
*BlockWeight::<Test>::get().get(DispatchClass::Normal),
info.total_weight() - accurate_refund + prior_block_weight + base_extrinsic
);
})
}
#[test]
fn extrinsic_already_refunded_less_precisely() {
new_test_ext().execute_with(|| {
// This is half of the max block weight
let info =
DispatchInfo { call_weight: Weight::from_parts(512, 0), ..Default::default() };
let post_info = PostDispatchInfo {
actual_weight: Some(Weight::from_parts(128, 0)),
pays_fee: Default::default(),
};
let prior_block_weight = Weight::from_parts(64, 0);
let inaccurate_refund = Weight::from_parts(110, 0);
let len = 0_usize;
let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic;
// Set initial info
BlockWeight::<Test>::mutate(|current_weight| {
current_weight.set(prior_block_weight, DispatchClass::Normal);
current_weight.accrue(
base_extrinsic + info.total_weight() - inaccurate_refund,
DispatchClass::Normal,
);
});
crate::ExtrinsicWeightReclaimed::<Test>::put(inaccurate_refund);
// Do the post dispatch
assert_ok!(WeightReclaim::<Test>::post_dispatch_details(
(),
&info,
&post_info,
len,
&Ok(())
));
// Ensure the accurate refund from benchmark is used
assert_eq!(
crate::ExtrinsicWeightReclaimed::<Test>::get(),
post_info.calc_unspent(&info)
);
assert_eq!(
*BlockWeight::<Test>::get().get(DispatchClass::Normal),
post_info.actual_weight.unwrap() + prior_block_weight + base_extrinsic
);
})
}
#[test]
fn extrinsic_not_refunded_before() {
new_test_ext().execute_with(|| {
// This is half of the max block weight
let info =
DispatchInfo { call_weight: Weight::from_parts(512, 0), ..Default::default() };
let post_info = PostDispatchInfo {
actual_weight: Some(Weight::from_parts(128, 0)),
pays_fee: Default::default(),
};
let prior_block_weight = Weight::from_parts(64, 0);
let len = 0_usize;
let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic;
// Set initial info
BlockWeight::<Test>::mutate(|current_weight| {
current_weight.set(prior_block_weight, DispatchClass::Normal);
current_weight.accrue(base_extrinsic + info.total_weight(), DispatchClass::Normal);
});
// Do the post dispatch
assert_ok!(WeightReclaim::<Test>::post_dispatch_details(
(),
&info,
&post_info,
len,
&Ok(())
));
// Ensure the accurate refund from benchmark is used
assert_eq!(
crate::ExtrinsicWeightReclaimed::<Test>::get(),
post_info.calc_unspent(&info)
);
assert_eq!(
*BlockWeight::<Test>::get().get(DispatchClass::Normal),
post_info.actual_weight.unwrap() + prior_block_weight + base_extrinsic
);
})
}
#[test]
fn no_actual_post_dispatch_weight() {
new_test_ext().execute_with(|| {
// This is half of the max block weight
let info =
DispatchInfo { call_weight: Weight::from_parts(512, 0), ..Default::default() };
let post_info = PostDispatchInfo { actual_weight: None, pays_fee: Default::default() };
let prior_block_weight = Weight::from_parts(64, 0);
let len = 0_usize;
let base_extrinsic = block_weights().get(DispatchClass::Normal).base_extrinsic;
// Set initial info
BlockWeight::<Test>::mutate(|current_weight| {
current_weight.set(prior_block_weight, DispatchClass::Normal);
current_weight.accrue(base_extrinsic + info.total_weight(), DispatchClass::Normal);
});
// Do the post dispatch
assert_ok!(WeightReclaim::<Test>::post_dispatch_details(
(),
&info,
&post_info,
len,
&Ok(())
));
// Ensure the accurate refund from benchmark is used
assert_eq!(
crate::ExtrinsicWeightReclaimed::<Test>::get(),
post_info.calc_unspent(&info)
);
assert_eq!(
*BlockWeight::<Test>::get().get(DispatchClass::Normal),
info.total_weight() + prior_block_weight + base_extrinsic
);
})
}
#[test]
fn different_dispatch_class() {
new_test_ext().execute_with(|| {
// This is half of the max block weight
let info = DispatchInfo {
call_weight: Weight::from_parts(512, 0),
class: DispatchClass::Operational,
..Default::default()
};
let post_info = PostDispatchInfo {
actual_weight: Some(Weight::from_parts(128, 0)),
pays_fee: Default::default(),
};
let prior_block_weight = Weight::from_parts(64, 0);
let len = 0_usize;
let base_extrinsic = block_weights().get(DispatchClass::Operational).base_extrinsic;
// Set initial info
BlockWeight::<Test>::mutate(|current_weight| {
current_weight.set(prior_block_weight, DispatchClass::Operational);
current_weight
.accrue(base_extrinsic + info.total_weight(), DispatchClass::Operational);
});
// Do the post dispatch
assert_ok!(WeightReclaim::<Test>::post_dispatch_details(
(),
&info,
&post_info,
len,
&Ok(())
));
// Ensure the accurate refund from benchmark is used
assert_eq!(
crate::ExtrinsicWeightReclaimed::<Test>::get(),
post_info.calc_unspent(&info)
);
assert_eq!(
*BlockWeight::<Test>::get().get(DispatchClass::Operational),
post_info.actual_weight.unwrap() + prior_block_weight + base_extrinsic
);
})
}
#[test]
fn bare_also_works() {
new_test_ext().execute_with(|| {
// This is half of the max block weight
let info = DispatchInfo {
call_weight: Weight::from_parts(512, 0),
class: DispatchClass::Operational,
..Default::default()
};
let post_info = PostDispatchInfo {
actual_weight: Some(Weight::from_parts(128, 0)),
pays_fee: Default::default(),
};
let prior_block_weight = Weight::from_parts(64, 0);
let len = 0_usize;
let base_extrinsic = block_weights().get(DispatchClass::Operational).base_extrinsic;
// Set initial info
BlockWeight::<Test>::mutate(|current_weight| {
current_weight.set(prior_block_weight, DispatchClass::Operational);
current_weight
.accrue(base_extrinsic + info.total_weight(), DispatchClass::Operational);
});
// Do the bare post dispatch
assert_ok!(WeightReclaim::<Test>::bare_post_dispatch(
&info,
&mut post_info.clone(),
len,
&Ok(())
));
// Ensure the accurate refund from benchmark is used
assert_eq!(
crate::ExtrinsicWeightReclaimed::<Test>::get(),
post_info.calc_unspent(&info)
);
assert_eq!(
*BlockWeight::<Test>::get().get(DispatchClass::Operational),
post_info.actual_weight.unwrap() + prior_block_weight + base_extrinsic
);
})
}
}
@@ -0,0 +1,232 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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 `pezframe_system_extensions`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION 32.0.0
//! DATE: 2024-11-08, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `runner-wiukf8gn-project-674-concurrent-0`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024`
// Executed Command:
// ./target/production/bizinikiwi-node
// benchmark
// pallet
// --chain=dev
// --steps=50
// --repeat=20
// --pallet=pezframe_system_extensions
// --no-storage-info
// --no-median-slopes
// --no-min-squares
// --extrinsic=*
// --wasm-execution=compiled
// --heap-pages=4096
// --output=./bizinikiwi/pezframe/system/src/extensions/weights.rs
// --header=./bizinikiwi/HEADER-APACHE2
// --template=./bizinikiwi/.maintain/frame-weight-template.hbs
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
use pezframe_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use core::marker::PhantomData;
/// Weight functions needed for `pezframe_system_extensions`.
pub trait WeightInfo {
fn check_genesis() -> Weight;
fn check_mortality_mortal_transaction() -> Weight;
fn check_mortality_immortal_transaction() -> Weight;
fn check_non_zero_sender() -> Weight;
fn check_nonce() -> Weight;
fn check_spec_version() -> Weight;
fn check_tx_version() -> Weight;
fn check_weight() -> Weight;
fn weight_reclaim() -> Weight;
}
/// Weights for `pezframe_system_extensions` using the Bizinikiwi node and recommended hardware.
pub struct BizinikiwiWeight<T>(PhantomData<T>);
impl<T: crate::Config> WeightInfo for BizinikiwiWeight<T> {
/// Storage: `System::BlockHash` (r:1 w:0)
/// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
fn check_genesis() -> Weight {
// Proof Size summary in bytes:
// Measured: `30`
// Estimated: `3509`
// Minimum execution time: 3_388_000 picoseconds.
Weight::from_parts(3_577_000, 3509)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `System::BlockHash` (r:1 w:0)
/// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
fn check_mortality_mortal_transaction() -> Weight {
// Proof Size summary in bytes:
// Measured: `68`
// Estimated: `3509`
// Minimum execution time: 6_442_000 picoseconds.
Weight::from_parts(6_703_000, 3509)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
/// Storage: `System::BlockHash` (r:1 w:0)
/// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
fn check_mortality_immortal_transaction() -> Weight {
// Proof Size summary in bytes:
// Measured: `68`
// Estimated: `3509`
// Minimum execution time: 6_357_000 picoseconds.
Weight::from_parts(6_605_000, 3509)
.saturating_add(T::DbWeight::get().reads(1_u64))
}
fn check_non_zero_sender() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 457_000 picoseconds.
Weight::from_parts(570_000, 0)
}
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
fn check_nonce() -> Weight {
// Proof Size summary in bytes:
// Measured: `101`
// Estimated: `3593`
// Minimum execution time: 6_936_000 picoseconds.
Weight::from_parts(7_261_000, 3593)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
fn check_spec_version() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 336_000 picoseconds.
Weight::from_parts(430_000, 0)
}
fn check_tx_version() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 348_000 picoseconds.
Weight::from_parts(455_000, 0)
}
fn check_weight() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 2_887_000 picoseconds.
Weight::from_parts(3_006_000, 0)
}
/// Storage: `System::AllExtrinsicsLen` (r:1 w:1)
/// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn weight_reclaim() -> Weight {
// Proof Size summary in bytes:
// Measured: `24`
// Estimated: `1489`
// Minimum execution time: 4_375_000 picoseconds.
Weight::from_parts(4_747_000, 1489)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
}
// For backwards compatibility and tests.
impl WeightInfo for () {
/// Storage: `System::BlockHash` (r:1 w:0)
/// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
fn check_genesis() -> Weight {
// Proof Size summary in bytes:
// Measured: `30`
// Estimated: `3509`
// Minimum execution time: 3_388_000 picoseconds.
Weight::from_parts(3_577_000, 3509)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `System::BlockHash` (r:1 w:0)
/// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
fn check_mortality_mortal_transaction() -> Weight {
// Proof Size summary in bytes:
// Measured: `68`
// Estimated: `3509`
// Minimum execution time: 6_442_000 picoseconds.
Weight::from_parts(6_703_000, 3509)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
/// Storage: `System::BlockHash` (r:1 w:0)
/// Proof: `System::BlockHash` (`max_values`: None, `max_size`: Some(44), added: 2519, mode: `MaxEncodedLen`)
fn check_mortality_immortal_transaction() -> Weight {
// Proof Size summary in bytes:
// Measured: `68`
// Estimated: `3509`
// Minimum execution time: 6_357_000 picoseconds.
Weight::from_parts(6_605_000, 3509)
.saturating_add(RocksDbWeight::get().reads(1_u64))
}
fn check_non_zero_sender() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 457_000 picoseconds.
Weight::from_parts(570_000, 0)
}
/// Storage: `System::Account` (r:1 w:1)
/// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`)
fn check_nonce() -> Weight {
// Proof Size summary in bytes:
// Measured: `101`
// Estimated: `3593`
// Minimum execution time: 6_936_000 picoseconds.
Weight::from_parts(7_261_000, 3593)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
fn check_spec_version() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 336_000 picoseconds.
Weight::from_parts(430_000, 0)
}
fn check_tx_version() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 348_000 picoseconds.
Weight::from_parts(455_000, 0)
}
fn check_weight() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 2_887_000 picoseconds.
Weight::from_parts(3_006_000, 0)
}
/// Storage: `System::AllExtrinsicsLen` (r:1 w:1)
/// Proof: `System::AllExtrinsicsLen` (`max_values`: Some(1), `max_size`: Some(4), added: 499, mode: `MaxEncodedLen`)
fn weight_reclaim() -> Weight {
// Proof Size summary in bytes:
// Measured: `24`
// Estimated: `1489`
// Minimum execution time: 4_375_000 picoseconds.
Weight::from_parts(4_747_000, 1489)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
}
File diff suppressed because it is too large Load Diff
+450
View File
@@ -0,0 +1,450 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
//! Block resource limits configuration structures.
//!
//! FRAME defines two resources that are limited within a block:
//! - Weight (execution cost/time)
//! - Length (block size)
//!
//! `pezframe_system` tracks consumption of each of these resources separately for each
//! `DispatchClass`. This module contains configuration object for both resources,
//! which should be passed to `pezframe_system` configuration when runtime is being set up.
use pezframe_support::{
dispatch::{DispatchClass, OneOrMany, PerDispatchClass},
weights::{constants, Weight},
};
use scale_info::TypeInfo;
use pezsp_runtime::{traits::Bounded, Perbill, RuntimeDebug};
/// Block length limit configuration.
#[derive(RuntimeDebug, Clone, codec::Encode, codec::Decode, TypeInfo)]
pub struct BlockLength {
/// Maximal total length in bytes for each extrinsic class.
///
/// In the worst case, the total block length is going to be:
/// `MAX(max)`
pub max: PerDispatchClass<u32>,
}
impl Default for BlockLength {
fn default() -> Self {
BlockLength::max_with_normal_ratio(5 * 1024 * 1024, DEFAULT_NORMAL_RATIO)
}
}
impl BlockLength {
/// Create new `BlockLength` with `max` for every class.
pub fn max(max: u32) -> Self {
Self { max: PerDispatchClass::new(|_| max) }
}
/// Create new `BlockLength` with `max` for `Operational` & `Mandatory`
/// and `normal * max` for `Normal`.
pub fn max_with_normal_ratio(max: u32, normal: Perbill) -> Self {
Self {
max: PerDispatchClass::new(|class| {
if class == DispatchClass::Normal {
normal * max
} else {
max
}
}),
}
}
}
#[derive(Default, RuntimeDebug)]
pub struct ValidationErrors {
pub has_errors: bool,
#[cfg(feature = "std")]
pub errors: Vec<String>,
}
macro_rules! error_assert {
($cond : expr, $err : expr, $format : expr $(, $params: expr )*$(,)*) => {
if !$cond {
$err.has_errors = true;
#[cfg(feature = "std")]
{ $err.errors.push(format!($format $(, &$params )*)); }
}
}
}
/// A result of validating `BlockWeights` correctness.
pub type ValidationResult = Result<BlockWeights, ValidationErrors>;
/// A ratio of `Normal` dispatch class within block, used as default value for
/// `BlockWeight` and `BlockLength`. The `Default` impls are provided mostly for convenience
/// to use in tests.
const DEFAULT_NORMAL_RATIO: Perbill = Perbill::from_percent(75);
/// `DispatchClass`-specific weight configuration.
#[derive(RuntimeDebug, Clone, codec::Encode, codec::Decode, TypeInfo)]
pub struct WeightsPerClass {
/// Base weight of single extrinsic of given class.
pub base_extrinsic: Weight,
/// Maximal weight of single extrinsic. Should NOT include `base_extrinsic` cost.
///
/// `None` indicates that this class of extrinsics doesn't have a limit.
pub max_extrinsic: Option<Weight>,
/// Block maximal total weight for all extrinsics of given class.
///
/// `None` indicates that weight sum of this class of extrinsics is not
/// restricted. Use this value carefully, since it might produce heavily oversized
/// blocks.
///
/// In the worst case, the total weight consumed by the class is going to be:
/// `MAX(max_total) + MAX(reserved)`.
pub max_total: Option<Weight>,
/// Block reserved allowance for all extrinsics of a particular class.
///
/// Setting to `None` indicates that extrinsics of that class are allowed
/// to go over total block weight (but at most `max_total` for that class).
/// Setting to `Some(x)` guarantees that at least `x` weight of particular class
/// is processed in every block.
pub reserved: Option<Weight>,
}
/// Block weight limits & base values configuration.
///
/// This object is responsible for defining weight limits and base weight values tracked
/// during extrinsic execution.
///
/// Each block starts with `base_block` weight being consumed right away. Next up the
/// `on_initialize` pallet callbacks are invoked and their cost is added before any extrinsic
/// is executed. This cost is tracked as `Mandatory` dispatch class.
///
/// ```text,ignore
/// | | `max_block` | |
/// | | | |
/// | | | |
/// | | | |
/// | | | #| `on_initialize`
/// | #| `base_block` | #|
/// |NOM| |NOM|
/// ||\_ Mandatory
/// |\__ Operational
/// \___ Normal
/// ```
///
/// The remaining capacity can be used to dispatch extrinsics. Note that each dispatch class
/// is being tracked separately, but the sum can't exceed `max_block` (except for `reserved`).
/// Below you can see a picture representing full block with 3 extrinsics (two `Operational` and
/// one `Normal`). Each class has it's own limit `max_total`, but also the sum cannot exceed
/// `max_block` value.
///
/// ```text,ignore
/// -- `Mandatory` limit (unlimited)
/// | # | | |
/// | # | `Ext3` | - - `Operational` limit
/// |# | `Ext2` |- - `Normal` limit
/// | # | `Ext1` | # |
/// | #| `on_initialize` | ##|
/// | #| `base_block` |###|
/// |NOM| |NOM|
/// ```
///
/// It should be obvious now that it's possible for one class to reach it's limit (say `Normal`),
/// while the block has still capacity to process more transactions (`max_block` not reached,
/// `Operational` transactions can still go in). Setting `max_total` to `None` disables the
/// per-class limit. This is generally highly recommended for `Mandatory` dispatch class, while it
/// can be dangerous for `Normal` class and should only be done with extra care and consideration.
///
/// Often it's desirable for some class of transactions to be added to the block despite it being
/// full. For instance one might want to prevent high-priority `Normal` transactions from pushing
/// out lower-priority `Operational` transactions. In such cases you might add a `reserved` capacity
/// for given class.
///
/// ```test,ignore
/// _
/// # \
/// # `Ext8` - `reserved`
/// # _/
/// | # | `Ext7 | - - `Operational` limit
/// |# | `Ext6` | |
/// |# | `Ext5` |-# - `Normal` limit
/// |# | `Ext4` |## |
/// | #| `on_initialize` |###|
/// | #| `base_block` |###|
/// |NOM| |NOM|
/// ```
///
/// In the above example, `Ext4-6` fill up the block almost up to `max_block`. `Ext7` would not fit
/// if there wasn't the extra `reserved` space for `Operational` transactions. Note that `max_total`
/// limit applies to `reserved` space as well (i.e. the sum of weights of `Ext7` & `Ext8` mustn't
/// exceed it). Setting `reserved` to `None` allows the extrinsics to always get into the block up
/// to their `max_total` limit. If `max_total` is set to `None` as well, all extrinsics witch
/// dispatchables of given class will always end up in the block (recommended for `Mandatory`
/// dispatch class).
///
/// As a consequence of `reserved` space, total consumed block weight might exceed `max_block`
/// value, so this parameter should rather be thought of as "target block weight" than a hard limit.
#[derive(RuntimeDebug, Clone, codec::Encode, codec::Decode, TypeInfo)]
pub struct BlockWeights {
/// Base weight of block execution.
pub base_block: Weight,
/// Maximal total weight consumed by all kinds of extrinsics (without `reserved` space).
pub max_block: Weight,
/// Weight limits for extrinsics of given dispatch class.
pub per_class: PerDispatchClass<WeightsPerClass>,
}
impl Default for BlockWeights {
fn default() -> Self {
Self::with_sensible_defaults(
Weight::from_parts(constants::WEIGHT_REF_TIME_PER_SECOND, u64::MAX),
DEFAULT_NORMAL_RATIO,
)
}
}
impl BlockWeights {
/// Get per-class weight settings.
pub fn get(&self, class: DispatchClass) -> &WeightsPerClass {
self.per_class.get(class)
}
/// Verifies correctness of this `BlockWeights` object.
pub fn validate(self) -> ValidationResult {
fn or_max(w: Option<Weight>) -> Weight {
w.unwrap_or_else(Weight::max_value)
}
let mut error = ValidationErrors::default();
for class in DispatchClass::all() {
let weights = self.per_class.get(*class);
let max_for_class = or_max(weights.max_total);
let base_for_class = weights.base_extrinsic;
let reserved = or_max(weights.reserved);
// Make sure that if total is set it's greater than base_block &&
// base_for_class
error_assert!(
(max_for_class.all_gt(self.base_block) && max_for_class.all_gt(base_for_class))
|| max_for_class == Weight::zero(),
&mut error,
"[{:?}] {:?} (total) has to be greater than {:?} (base block) & {:?} (base extrinsic)",
class, max_for_class, self.base_block, base_for_class,
);
// Max extrinsic can't be greater than max_for_class.
error_assert!(
weights
.max_extrinsic
.unwrap_or(Weight::zero())
.all_lte(max_for_class.saturating_sub(base_for_class)),
&mut error,
"[{:?}] {:?} (max_extrinsic) can't be greater than {:?} (max for class)",
class,
weights.max_extrinsic,
max_for_class.saturating_sub(base_for_class),
);
// Max extrinsic should not be 0
error_assert!(
weights.max_extrinsic.unwrap_or_else(Weight::max_value).all_gt(Weight::zero()),
&mut error,
"[{:?}] {:?} (max_extrinsic) must not be 0. Check base cost and average initialization cost.",
class, weights.max_extrinsic,
);
// Make sure that if reserved is set it's greater than base_for_class.
error_assert!(
reserved.all_gt(base_for_class) || reserved == Weight::zero(),
&mut error,
"[{:?}] {:?} (reserved) has to be greater than {:?} (base extrinsic) if set",
class,
reserved,
base_for_class,
);
// Make sure max block is greater than max_total if it's set.
error_assert!(
self.max_block.all_gte(weights.max_total.unwrap_or(Weight::zero())),
&mut error,
"[{:?}] {:?} (max block) has to be greater than {:?} (max for class)",
class,
self.max_block,
weights.max_total,
);
// Make sure we can fit at least one extrinsic.
error_assert!(
self.max_block.all_gt(base_for_class + self.base_block),
&mut error,
"[{:?}] {:?} (max block) must fit at least one extrinsic {:?} (base weight)",
class,
self.max_block,
base_for_class + self.base_block,
);
}
if error.has_errors {
Err(error)
} else {
Ok(self)
}
}
/// Create new weights definition, with both `Normal` and `Operational`
/// classes limited to given weight.
///
/// Note there is no reservation for `Operational` class, so this constructor
/// is not suitable for production deployments.
pub fn simple_max(block_weight: Weight) -> Self {
Self::builder()
.base_block(Weight::zero())
.for_class(DispatchClass::all(), |weights| {
weights.base_extrinsic = Weight::zero();
})
.for_class(DispatchClass::non_mandatory(), |weights| {
weights.max_total = block_weight.into();
})
.build()
.expect("We only specify max_total and leave base values as defaults; qed")
}
/// Create a sensible default weights system given only expected maximal block weight and the
/// ratio that `Normal` extrinsics should occupy.
///
/// Assumptions:
/// - Average block initialization is assumed to be `10%`.
/// - `Operational` transactions have reserved allowance (`1.0 - normal_ratio`)
pub fn with_sensible_defaults(expected_block_weight: Weight, normal_ratio: Perbill) -> Self {
let normal_weight = normal_ratio * expected_block_weight;
Self::builder()
.for_class(DispatchClass::Normal, |weights| {
weights.max_total = normal_weight.into();
})
.for_class(DispatchClass::Operational, |weights| {
weights.max_total = expected_block_weight.into();
weights.reserved = (expected_block_weight - normal_weight).into();
})
.avg_block_initialization(Perbill::from_percent(10))
.build()
.expect("Sensible defaults are tested to be valid; qed")
}
/// Start constructing new `BlockWeights` object.
///
/// By default all kinds except of `Mandatory` extrinsics are disallowed.
pub fn builder() -> BlockWeightsBuilder {
BlockWeightsBuilder {
weights: BlockWeights {
base_block: constants::BlockExecutionWeight::get(),
max_block: Weight::zero(),
per_class: PerDispatchClass::new(|class| {
let initial =
if class == DispatchClass::Mandatory { None } else { Some(Weight::zero()) };
WeightsPerClass {
base_extrinsic: constants::ExtrinsicBaseWeight::get(),
max_extrinsic: None,
max_total: initial,
reserved: initial,
}
}),
},
init_cost: None,
}
}
}
/// An opinionated builder for `Weights` object.
pub struct BlockWeightsBuilder {
weights: BlockWeights,
init_cost: Option<Perbill>,
}
impl BlockWeightsBuilder {
/// Set base block weight.
pub fn base_block(mut self, base_block: Weight) -> Self {
self.weights.base_block = base_block;
self
}
/// Average block initialization weight cost.
///
/// This value is used to derive maximal allowed extrinsic weight for each
/// class, based on the allowance.
///
/// This is to make sure that extrinsics don't stay forever in the pool,
/// because they could seemingly fit the block (since they are below `max_block`),
/// but the cost of calling `on_initialize` always prevents them from being included.
pub fn avg_block_initialization(mut self, init_cost: Perbill) -> Self {
self.init_cost = Some(init_cost);
self
}
/// Set parameters for particular class.
///
/// Note: `None` values of `max_extrinsic` will be overwritten in `build` in case
/// `avg_block_initialization` rate is set to a non-zero value.
pub fn for_class(
mut self,
class: impl OneOrMany<DispatchClass>,
action: impl Fn(&mut WeightsPerClass),
) -> Self {
for class in class.into_iter() {
action(self.weights.per_class.get_mut(class));
}
self
}
/// Construct the `BlockWeights` object.
pub fn build(self) -> ValidationResult {
// compute max extrinsic size
let Self { mut weights, init_cost } = self;
// compute max block size.
for class in DispatchClass::all() {
weights.max_block = match weights.per_class.get(*class).max_total {
Some(max) => max.max(weights.max_block),
_ => weights.max_block,
};
}
// compute max size of single extrinsic
if let Some(init_weight) = init_cost.map(|rate| rate * weights.max_block) {
for class in DispatchClass::all() {
let per_class = weights.per_class.get_mut(*class);
if per_class.max_extrinsic.is_none() && init_cost.is_some() {
per_class.max_extrinsic = per_class
.max_total
.map(|x| x.saturating_sub(init_weight))
.map(|x| x.saturating_sub(per_class.base_extrinsic));
}
}
}
// Validate the result
weights.validate()
}
/// Construct the `BlockWeights` object or panic if it's invalid.
///
/// This is a convenience method to be called whenever you construct a runtime.
pub fn build_or_panic(self) -> BlockWeights {
self.build().expect(
"Builder finished with `build_or_panic`; The panic is expected if runtime weights are not correct"
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_weights_are_valid() {
BlockWeights::default().validate().unwrap();
}
}
@@ -0,0 +1,121 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
//! Migrate the reference counting state.
use super::LOG_TARGET;
use crate::{Config, Pallet};
use codec::{Decode, Encode, FullCodec};
use pezframe_support::{
pezpallet_prelude::ValueQuery, traits::PalletInfoAccess, weights::Weight, Blake2_128Concat,
};
use pezsp_runtime::RuntimeDebug;
/// Type used to encode the number of references an account has.
type RefCount = u32;
/// Information of an account.
#[derive(Clone, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode)]
struct AccountInfo<Nonce, AccountData> {
nonce: Nonce,
consumers: RefCount,
providers: RefCount,
sufficients: RefCount,
data: AccountData,
}
/// Trait to implement to give information about types used for migration
pub trait V2ToV3 {
/// The system pallet.
type Pallet: 'static + PalletInfoAccess;
/// System config account id
type AccountId: 'static + FullCodec;
/// System config nonce
type Nonce: 'static + FullCodec + Copy;
/// System config account data
type AccountData: 'static + FullCodec;
}
#[pezframe_support::storage_alias]
type UpgradedToU32RefCount<T: Config> = StorageValue<Pallet<T>, bool, ValueQuery>;
#[pezframe_support::storage_alias]
type UpgradedToTripleRefCount<T: Config> = StorageValue<Pallet<T>, bool, ValueQuery>;
#[pezframe_support::storage_alias]
type Account<V, T: Config> = StorageMap<
Pallet<T>,
Blake2_128Concat,
<V as V2ToV3>::AccountId,
AccountInfo<<V as V2ToV3>::Nonce, <V as V2ToV3>::AccountData>,
>;
/// Migrate from unique `u8` reference counting to triple `u32` reference counting.
pub fn migrate_from_single_u8_to_triple_ref_count<V: V2ToV3, T: Config>() -> Weight {
let mut translated: usize = 0;
<Account<V, T>>::translate::<(V::Nonce, u8, V::AccountData), _>(|_key, (nonce, rc, data)| {
translated += 1;
Some(AccountInfo { nonce, consumers: rc as RefCount, providers: 1, sufficients: 0, data })
});
log::info!(
target: LOG_TARGET,
"Applied migration from single u8 to triple reference counting to {:?} elements.",
translated
);
<UpgradedToU32RefCount<T>>::put(true);
<UpgradedToTripleRefCount<T>>::put(true);
Weight::MAX
}
/// Migrate from unique `u32` reference counting to triple `u32` reference counting.
pub fn migrate_from_single_to_triple_ref_count<V: V2ToV3, T: Config>() -> Weight {
let mut translated: usize = 0;
<Account<V, T>>::translate::<(V::Nonce, RefCount, V::AccountData), _>(
|_key, (nonce, consumers, data)| {
translated += 1;
Some(AccountInfo { nonce, consumers, providers: 1, sufficients: 0, data })
},
);
log::info!(
target: LOG_TARGET,
"Applied migration from single to triple reference counting to {:?} elements.",
translated
);
<UpgradedToTripleRefCount<T>>::put(true);
Weight::MAX
}
/// Migrate from dual `u32` reference counting to triple `u32` reference counting.
pub fn migrate_from_dual_to_triple_ref_count<V: V2ToV3, T: Config>() -> Weight {
let mut translated: usize = 0;
<Account<V, T>>::translate::<(V::Nonce, RefCount, RefCount, V::AccountData), _>(
|_key, (nonce, consumers, providers, data)| {
translated += 1;
Some(AccountInfo { nonce, consumers, providers, sufficients: 0, data })
},
);
log::info!(
target: LOG_TARGET,
"Applied migration from dual to triple reference counting to {:?} elements.",
translated
);
<UpgradedToTripleRefCount<T>>::put(true);
Weight::MAX
}
+137
View File
@@ -0,0 +1,137 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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 crate::{self as pezframe_system, *};
use pezframe_support::{derive_impl, parameter_types};
use pezsp_runtime::{type_with_default::TypeWithDefault, BuildStorage, Perbill};
type Block = mocking::MockBlock<Test>;
pezframe_support::construct_runtime!(
pub enum Test
{
System: pezframe_system,
}
);
const NORMAL_DISPATCH_RATIO: Perbill = Perbill::from_percent(75);
const MAX_BLOCK_WEIGHT: Weight = Weight::from_parts(1024, u64::MAX);
parameter_types! {
pub Version: RuntimeVersion = RuntimeVersion {
spec_name: alloc::borrow::Cow::Borrowed("test"),
impl_name: alloc::borrow::Cow::Borrowed("system-test"),
authoring_version: 1,
spec_version: 1,
impl_version: 1,
apis: pezsp_version::create_apis_vec!([]),
transaction_version: 1,
system_version: 1,
};
pub const DbWeight: RuntimeDbWeight = RuntimeDbWeight {
read: 10,
write: 100,
};
pub RuntimeBlockWeights: limits::BlockWeights = limits::BlockWeights::builder()
.base_block(Weight::from_parts(10, 0))
.for_class(DispatchClass::all(), |weights| {
weights.base_extrinsic = Weight::from_parts(5, 0);
})
.for_class(DispatchClass::Normal, |weights| {
weights.max_total = Some(NORMAL_DISPATCH_RATIO * MAX_BLOCK_WEIGHT);
})
.for_class(DispatchClass::Operational, |weights| {
weights.base_extrinsic = Weight::from_parts(10, 0);
weights.max_total = Some(MAX_BLOCK_WEIGHT);
weights.reserved = Some(
MAX_BLOCK_WEIGHT - NORMAL_DISPATCH_RATIO * MAX_BLOCK_WEIGHT
);
})
.avg_block_initialization(Perbill::from_percent(0))
.build_or_panic();
pub RuntimeBlockLength: limits::BlockLength =
limits::BlockLength::max_with_normal_ratio(1024, NORMAL_DISPATCH_RATIO);
}
parameter_types! {
pub static Killed: Vec<u64> = vec![];
}
pub struct RecordKilled;
impl OnKilledAccount<u64> for RecordKilled {
fn on_killed_account(who: &u64) {
Killed::mutate(|r| r.push(*who))
}
}
#[derive(Debug, TypeInfo)]
pub struct DefaultNonceProvider;
impl Get<u64> for DefaultNonceProvider {
fn get() -> u64 {
System::block_number()
}
}
#[derive_impl(pezframe_system::config_preludes::TestDefaultConfig)]
impl Config for Test {
type BlockWeights = RuntimeBlockWeights;
type BlockLength = RuntimeBlockLength;
type Block = Block;
type Version = Version;
type AccountData = u32;
type OnKilledAccount = RecordKilled;
type MultiBlockMigrator = MockedMigrator;
type Nonce = TypeWithDefault<u64, DefaultNonceProvider>;
}
parameter_types! {
pub static Ongoing: bool = false;
}
pub struct MockedMigrator;
impl pezframe_support::migrations::MultiStepMigrator for MockedMigrator {
fn ongoing() -> bool {
Ongoing::get()
}
fn step() -> Weight {
Weight::zero()
}
}
pub type SysEvent = pezframe_system::Event<Test>;
/// A simple call, which one doesn't matter.
pub const CALL: &<Test as Config>::RuntimeCall =
&RuntimeCall::System(pezframe_system::Call::set_heap_pages { pages: 0u64 });
/// Create new externalities for `System` module tests.
pub fn new_test_ext() -> pezsp_io::TestExternalities {
// Initialize logging
pezsp_tracing::try_init_simple();
let mut ext: pezsp_io::TestExternalities =
RuntimeGenesisConfig::default().build_storage().unwrap().into();
// Add to each test the initial weight of a block
ext.execute_with(|| {
System::register_extra_weight_unchecked(
<Test as crate::Config>::BlockWeights::get().base_block,
DispatchClass::Mandatory,
)
});
ext
}
+47
View File
@@ -0,0 +1,47 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
//! Provide types to help defining a mock environment when testing pallets.
use pezsp_runtime::generic;
/// An unchecked extrinsic type to be used in tests.
pub type MockUncheckedExtrinsic<T, Signature = (), Extra = ()> = generic::UncheckedExtrinsic<
<T as crate::Config>::AccountId,
<T as crate::Config>::RuntimeCall,
Signature,
Extra,
>;
/// An implementation of `pezsp_runtime::traits::Block` to be used in tests.
pub type MockBlock<T> = generic::Block<
generic::Header<u64, pezsp_runtime::traits::BlakeTwo256>,
MockUncheckedExtrinsic<T>,
>;
/// An implementation of `pezsp_runtime::traits::Block` to be used in tests with u32 BlockNumber type.
pub type MockBlockU32<T> = generic::Block<
generic::Header<u32, pezsp_runtime::traits::BlakeTwo256>,
MockUncheckedExtrinsic<T>,
>;
/// An implementation of `pezsp_runtime::traits::Block` to be used in tests with u128 BlockNumber
/// type.
pub type MockBlockU128<T> = generic::Block<
generic::Header<u128, pezsp_runtime::traits::BlakeTwo256>,
MockUncheckedExtrinsic<T>,
>;
+843
View File
@@ -0,0 +1,843 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
//! Module helpers for off-chain calls.
//!
//! ## Overview
//!
//! This module provides transaction related helpers to:
//! - Submit a raw unsigned transaction
//! - Submit an unsigned transaction with a signed payload
//! - Submit a signed transaction.
//!
//! ## Usage
//!
//! Please refer to [`example-offchain-worker`](../../pezpallet_example_offchain_worker/index.html) for
//! a concrete example usage of this crate.
//!
//! ### Submit a raw unsigned transaction
//!
//! To submit a raw unsigned transaction, [`SubmitTransaction`](./struct.SubmitTransaction.html)
//! can be used.
//!
//! ### Signing transactions
//!
//! To be able to use signing, the following trait should be implemented:
//!
//! - [`AppCrypto`](./trait.AppCrypto.html): where an application-specific key is defined and can be
//! used by this module's helpers for signing.
//! - [`CreateSignedTransaction`](./trait.CreateSignedTransaction.html): where the manner in which
//! the transaction is constructed is defined.
//!
//! #### Submit an unsigned transaction with a signed payload
//!
//! Initially, a payload instance that implements the `SignedPayload` trait should be defined.
//! See [`PricePayload`](../../pezpallet_example_offchain_worker/struct.PricePayload.html)
//!
//! The payload type that is defined defined can then be signed and submitted onchain.
//!
//! #### Submit a signed transaction
//!
//! [`Signer`](./struct.Signer.html) can be used to sign/verify payloads
#![warn(missing_docs)]
use alloc::{boxed::Box, collections::btree_set::BTreeSet, vec::Vec};
use codec::Encode;
use scale_info::TypeInfo;
use pezsp_runtime::{
app_crypto::RuntimeAppPublic,
traits::{ExtrinsicLike, IdentifyAccount, One},
RuntimeDebug,
};
/// Marker struct used to flag using all supported keys to sign a payload.
pub struct ForAll {}
/// Marker struct used to flag using any of the supported keys to sign a payload.
pub struct ForAny {}
/// Provides the ability to directly submit signed and unsigned
/// transaction onchain.
///
/// For submitting unsigned transactions, `submit_unsigned_transaction`
/// utility function can be used. However, this struct is used by `Signer`
/// to submit a signed transactions providing the signature along with the call.
pub struct SubmitTransaction<T: CreateTransactionBase<RuntimeCall>, RuntimeCall> {
_phantom: core::marker::PhantomData<(T, RuntimeCall)>,
}
impl<T, LocalCall> SubmitTransaction<T, LocalCall>
where
T: CreateTransactionBase<LocalCall>,
{
/// A convenience method to submit an extrinsic onchain.
pub fn submit_transaction(xt: T::Extrinsic) -> Result<(), ()> {
pezsp_io::offchain::submit_transaction(xt.encode())
}
}
/// Provides an implementation for signing transaction payloads.
///
/// Keys used for signing are defined when instantiating the signer object.
/// Signing can be done using:
///
/// - All supported keys in the keystore
/// - Any of the supported keys in the keystore
/// - An intersection of in-keystore keys and the list of provided keys
///
/// The signer is then able to:
/// - Submit a unsigned transaction with a signed payload
/// - Submit a signed transaction
#[derive(RuntimeDebug)]
pub struct Signer<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>, X = ForAny> {
accounts: Option<Vec<T::Public>>,
_phantom: core::marker::PhantomData<(X, C)>,
}
impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>, X> Default for Signer<T, C, X> {
fn default() -> Self {
Self { accounts: Default::default(), _phantom: Default::default() }
}
}
impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>, X> Signer<T, C, X> {
/// Use all available keys for signing.
pub fn all_accounts() -> Signer<T, C, ForAll> {
Default::default()
}
/// Use any of the available keys for signing.
pub fn any_account() -> Signer<T, C, ForAny> {
Default::default()
}
/// Use provided `accounts` for signing.
///
/// Note that not all keys will be necessarily used. The provided
/// vector of accounts will be intersected with the supported keys
/// in the keystore and the resulting list will be used for signing.
pub fn with_filter(mut self, accounts: Vec<T::Public>) -> Self {
self.accounts = Some(accounts);
self
}
/// Check if there are any keys that could be used for signing.
pub fn can_sign(&self) -> bool {
self.accounts_from_keys().count() > 0
}
/// Return a vector of the intersection between
/// all available accounts and the provided accounts
/// in `with_filter`. If no accounts are provided,
/// use all accounts by default.
pub fn accounts_from_keys<'a>(&'a self) -> Box<dyn Iterator<Item = Account<T>> + 'a> {
let keystore_accounts = Self::keystore_accounts();
match self.accounts {
None => Box::new(keystore_accounts),
Some(ref keys) => {
let keystore_lookup: BTreeSet<<T as SigningTypes>::Public> =
keystore_accounts.map(|account| account.public).collect();
Box::new(
keys.iter()
.enumerate()
.map(|(index, key)| {
let account_id = key.clone().into_account();
Account::new(index, account_id, key.clone())
})
.filter(move |account| keystore_lookup.contains(&account.public)),
)
},
}
}
/// Return all available accounts in keystore.
pub fn keystore_accounts() -> impl Iterator<Item = Account<T>> {
C::RuntimeAppPublic::all().into_iter().enumerate().map(|(index, key)| {
let generic_public = C::GenericPublic::from(key);
let public: T::Public = generic_public.into();
let account_id = public.clone().into_account();
Account::new(index, account_id, public)
})
}
}
impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>> Signer<T, C, ForAll> {
fn for_all<F, R>(&self, f: F) -> Vec<(Account<T>, R)>
where
F: Fn(&Account<T>) -> Option<R>,
{
let accounts = self.accounts_from_keys();
accounts
.into_iter()
.filter_map(|account| f(&account).map(|res| (account, res)))
.collect()
}
}
impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>> Signer<T, C, ForAny> {
fn for_any<F, R>(&self, f: F) -> Option<(Account<T>, R)>
where
F: Fn(&Account<T>) -> Option<R>,
{
let accounts = self.accounts_from_keys();
for account in accounts.into_iter() {
let res = f(&account);
if let Some(res) = res {
return Some((account, res));
}
}
None
}
}
impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>> SignMessage<T>
for Signer<T, C, ForAll>
{
type SignatureData = Vec<(Account<T>, T::Signature)>;
fn sign_message(&self, message: &[u8]) -> Self::SignatureData {
self.for_all(|account| C::sign(message, account.public.clone()))
}
fn sign<TPayload, F>(&self, f: F) -> Self::SignatureData
where
F: Fn(&Account<T>) -> TPayload,
TPayload: SignedPayload<T>,
{
self.for_all(|account| f(account).sign::<C>())
}
}
impl<T: SigningTypes, C: AppCrypto<T::Public, T::Signature>> SignMessage<T>
for Signer<T, C, ForAny>
{
type SignatureData = Option<(Account<T>, T::Signature)>;
fn sign_message(&self, message: &[u8]) -> Self::SignatureData {
self.for_any(|account| C::sign(message, account.public.clone()))
}
fn sign<TPayload, F>(&self, f: F) -> Self::SignatureData
where
F: Fn(&Account<T>) -> TPayload,
TPayload: SignedPayload<T>,
{
self.for_any(|account| f(account).sign::<C>())
}
}
impl<
T: CreateSignedTransaction<LocalCall> + SigningTypes,
C: AppCrypto<T::Public, T::Signature>,
LocalCall,
> SendSignedTransaction<T, C, LocalCall> for Signer<T, C, ForAny>
{
type Result = Option<(Account<T>, Result<(), ()>)>;
fn send_signed_transaction(&self, f: impl Fn(&Account<T>) -> LocalCall) -> Self::Result {
self.for_any(|account| {
let call = f(account);
self.send_single_signed_transaction(account, call)
})
}
}
impl<
T: SigningTypes + CreateSignedTransaction<LocalCall>,
C: AppCrypto<T::Public, T::Signature>,
LocalCall,
> SendSignedTransaction<T, C, LocalCall> for Signer<T, C, ForAll>
{
type Result = Vec<(Account<T>, Result<(), ()>)>;
fn send_signed_transaction(&self, f: impl Fn(&Account<T>) -> LocalCall) -> Self::Result {
self.for_all(|account| {
let call = f(account);
self.send_single_signed_transaction(account, call)
})
}
}
impl<T: SigningTypes + CreateBare<LocalCall>, C: AppCrypto<T::Public, T::Signature>, LocalCall>
SendUnsignedTransaction<T, LocalCall> for Signer<T, C, ForAny>
{
type Result = Option<(Account<T>, Result<(), ()>)>;
fn send_unsigned_transaction<TPayload, F>(
&self,
f: F,
f2: impl Fn(TPayload, T::Signature) -> LocalCall,
) -> Self::Result
where
F: Fn(&Account<T>) -> TPayload,
TPayload: SignedPayload<T>,
{
self.for_any(|account| {
let payload = f(account);
let signature = payload.sign::<C>()?;
let call = f2(payload, signature);
self.submit_unsigned_transaction(call)
})
}
}
impl<T: SigningTypes + CreateBare<LocalCall>, C: AppCrypto<T::Public, T::Signature>, LocalCall>
SendUnsignedTransaction<T, LocalCall> for Signer<T, C, ForAll>
{
type Result = Vec<(Account<T>, Result<(), ()>)>;
fn send_unsigned_transaction<TPayload, F>(
&self,
f: F,
f2: impl Fn(TPayload, T::Signature) -> LocalCall,
) -> Self::Result
where
F: Fn(&Account<T>) -> TPayload,
TPayload: SignedPayload<T>,
{
self.for_all(|account| {
let payload = f(account);
let signature = payload.sign::<C>()?;
let call = f2(payload, signature);
self.submit_unsigned_transaction(call)
})
}
}
/// Details of an account for which a private key is contained in the keystore.
#[derive(RuntimeDebug, PartialEq)]
pub struct Account<T: SigningTypes> {
/// Index on the provided list of accounts or list of all accounts.
pub index: usize,
/// Runtime-specific `AccountId`.
pub id: T::AccountId,
/// A runtime-specific `Public` key for that key pair.
pub public: T::Public,
}
impl<T: SigningTypes> Account<T> {
/// Create a new Account instance
pub fn new(index: usize, id: T::AccountId, public: T::Public) -> Self {
Self { index, id, public }
}
}
impl<T: SigningTypes> Clone for Account<T>
where
T::AccountId: Clone,
T::Public: Clone,
{
fn clone(&self) -> Self {
Self { index: self.index, id: self.id.clone(), public: self.public.clone() }
}
}
/// A type binding runtime-level `Public/Signature` pair with crypto wrapped by `RuntimeAppPublic`.
///
/// Implementations of this trait should specify the app-specific public/signature types.
/// This is merely a wrapper around an existing `RuntimeAppPublic` type, but with
/// extra non-application-specific crypto type that is being wrapped (e.g. `sr25519`, `ed25519`).
/// This is needed to later on convert into runtime-specific `Public` key, which might support
/// multiple different crypto.
/// The point of this trait is to be able to easily convert between `RuntimeAppPublic`, the wrapped
/// (generic = non application-specific) crypto types and the `Public` type required by the runtime.
///
/// Example (pseudo-)implementation:
/// ```ignore
/// // im-online specific crypto
/// type RuntimeAppPublic = ImOnline(sr25519::Public);
///
/// // wrapped "raw" crypto
/// type GenericPublic = sr25519::Public;
/// type GenericSignature = sr25519::Signature;
///
/// // runtime-specific public key
/// type Public = MultiSigner: From<sr25519::Public>;
/// type Signature = MultiSignature: From<sr25519::Signature>;
/// ```
// TODO [#5662] Potentially use `IsWrappedBy` types, or find some other way to make it easy to
// obtain unwrapped crypto (and wrap it back).
pub trait AppCrypto<Public, Signature> {
/// A application-specific crypto.
type RuntimeAppPublic: RuntimeAppPublic;
/// A raw crypto public key wrapped by `RuntimeAppPublic`.
type GenericPublic: From<Self::RuntimeAppPublic>
+ Into<Self::RuntimeAppPublic>
+ TryFrom<Public>
+ Into<Public>;
/// A matching raw crypto `Signature` type.
type GenericSignature: From<<Self::RuntimeAppPublic as RuntimeAppPublic>::Signature>
+ Into<<Self::RuntimeAppPublic as RuntimeAppPublic>::Signature>
+ TryFrom<Signature>
+ Into<Signature>;
/// Sign payload with the private key to maps to the provided public key.
fn sign(payload: &[u8], public: Public) -> Option<Signature> {
let p: Self::GenericPublic = public.try_into().ok()?;
let x = Into::<Self::RuntimeAppPublic>::into(p);
x.sign(&payload)
.map(|x| {
let sig: Self::GenericSignature = x.into();
sig
})
.map(Into::into)
}
/// Verify signature against the provided public key.
fn verify(payload: &[u8], public: Public, signature: Signature) -> bool {
let p: Self::GenericPublic = match public.try_into() {
Ok(a) => a,
_ => return false,
};
let x = Into::<Self::RuntimeAppPublic>::into(p);
let signature: Self::GenericSignature = match signature.try_into() {
Ok(a) => a,
_ => return false,
};
let signature =
Into::<<Self::RuntimeAppPublic as RuntimeAppPublic>::Signature>::into(signature);
x.verify(&payload, &signature)
}
}
/// A wrapper around the types which are used for signing.
///
/// This trait adds extra bounds to `Public` and `Signature` types of the runtime
/// that are necessary to use these types for signing.
// TODO [#5663] Could this be just `T::Signature as traits::Verify>::Signer`?
// Seems that this may cause issues with bounds resolution.
pub trait SigningTypes: crate::Config {
/// A public key that is capable of identifying `AccountId`s.
///
/// Usually that's either a raw crypto public key (e.g. `sr25519::Public`) or
/// an aggregate type for multiple crypto public keys, like `MultiSigner`.
type Public: Clone
+ PartialEq
+ IdentifyAccount<AccountId = Self::AccountId>
+ core::fmt::Debug
+ codec::Codec
+ Ord
+ scale_info::TypeInfo;
/// A matching `Signature` type.
type Signature: Clone + PartialEq + core::fmt::Debug + codec::Codec + scale_info::TypeInfo;
}
/// Common interface for the `CreateTransaction` trait family to unify the `Call` type.
pub trait CreateTransactionBase<LocalCall> {
/// The extrinsic.
type Extrinsic: ExtrinsicLike + Encode;
/// The runtime's call type.
///
/// This has additional bound to be able to be created from pezpallet-local `Call` types.
type RuntimeCall: From<LocalCall> + Encode;
}
/// Interface for creating a transaction.
pub trait CreateTransaction<LocalCall>: CreateTransactionBase<LocalCall> {
/// The extension.
type Extension: TypeInfo;
/// Create a transaction using the call and the desired transaction extension.
fn create_transaction(
call: <Self as CreateTransactionBase<LocalCall>>::RuntimeCall,
extension: Self::Extension,
) -> Self::Extrinsic;
}
/// Interface for creating an old-school signed transaction.
pub trait CreateSignedTransaction<LocalCall>:
CreateTransactionBase<LocalCall> + SigningTypes
{
/// Attempt to create signed extrinsic data that encodes call from given account.
///
/// Runtime implementation is free to construct the payload to sign and the signature
/// in any way it wants.
/// Returns `None` if signed extrinsic could not be created (either because signing failed
/// or because of any other runtime-specific reason).
fn create_signed_transaction<C: AppCrypto<Self::Public, Self::Signature>>(
call: <Self as CreateTransactionBase<LocalCall>>::RuntimeCall,
public: Self::Public,
account: Self::AccountId,
nonce: Self::Nonce,
) -> Option<Self::Extrinsic>;
}
/// Interface for creating an inherent; ⚠️ **Deprecated use [`CreateBare`]**.
///
/// This is a deprecated type alias for [`CreateBare`].
///
/// Doc for [`CreateBare`]:
#[deprecated(note = "Use `CreateBare` instead")]
#[doc(inline)]
pub use CreateBare as CreateInherent;
/// Interface for creating a bare extrinsic.
///
/// Bare extrinsic are used for inherent extrinsic and unsigned transaction.
pub trait CreateBare<LocalCall>: CreateTransactionBase<LocalCall> {
/// Create a bare extrinsic.
///
/// Bare extrinsic are used for inherent extrinsic and unsigned transaction.
fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic;
/// Create an inherent.
#[deprecated(note = "Use `create_bare` instead")]
fn create_inherent(call: Self::RuntimeCall) -> Self::Extrinsic {
Self::create_bare(call)
}
}
/// A message signer.
pub trait SignMessage<T: SigningTypes> {
/// A signature data.
///
/// May contain account used for signing and the `Signature` itself.
type SignatureData;
/// Sign a message.
///
/// Implementation of this method should return
/// a result containing the signature.
fn sign_message(&self, message: &[u8]) -> Self::SignatureData;
/// Construct and sign given payload.
///
/// This method expects `f` to return a `SignedPayload`
/// object which is then used for signing.
fn sign<TPayload, F>(&self, f: F) -> Self::SignatureData
where
F: Fn(&Account<T>) -> TPayload,
TPayload: SignedPayload<T>;
}
/// Interface for creating a transaction for a call that will be authorized.
///
/// Authorized calls are calls that has some specific validation logic execute in the transaction
/// extension: [`crate::AuthorizeCall`].
/// The authorization logic is defined on the call with the attribute:
/// [`pezframe_support::pezpallet_macros::authorize`].
///
/// This trait allows the runtime to define the extension to be used when creating an authorized
/// transaction. It can be used in the offchain worker to create a transaction from a call.
pub trait CreateAuthorizedTransaction<LocalCall>: CreateTransaction<LocalCall> {
/// Create the transaction extension to be used alongside an authorized call.
///
/// For more information about authorized call see [`pezframe_support::pezpallet_prelude::authorize`].
fn create_extension() -> Self::Extension;
/// Create a new transaction for an authorized call.
///
/// For more information about authorized call see [`pezframe_support::pezpallet_prelude::authorize`].
fn create_authorized_transaction(call: Self::RuntimeCall) -> Self::Extrinsic {
Self::create_transaction(call, Self::create_extension())
}
}
/// Submit a signed transaction to the transaction pool.
pub trait SendSignedTransaction<
T: CreateSignedTransaction<LocalCall>,
C: AppCrypto<T::Public, T::Signature>,
LocalCall,
>
{
/// A submission result.
///
/// This should contain an indication of success and the account that was used for signing.
type Result;
/// Submit a signed transaction to the local pool.
///
/// Given `f` closure will be called for every requested account and expects a `Call` object
/// to be returned.
/// The call is then wrapped into a transaction (see `#CreateSignedTransaction`), signed and
/// submitted to the pool.
fn send_signed_transaction(&self, f: impl Fn(&Account<T>) -> LocalCall) -> Self::Result;
/// Wraps the call into transaction, signs using given account and submits to the pool.
fn send_single_signed_transaction(
&self,
account: &Account<T>,
call: LocalCall,
) -> Option<Result<(), ()>> {
let mut account_data = crate::Account::<T>::get(&account.id);
log::debug!(
target: "runtime::offchain",
"Creating signed transaction from account: {:?} (nonce: {:?})",
account.id,
account_data.nonce,
);
let transaction = T::create_signed_transaction::<C>(
call.into(),
account.public.clone(),
account.id.clone(),
account_data.nonce,
)?;
let res = SubmitTransaction::<T, LocalCall>::submit_transaction(transaction);
if res.is_ok() {
// increment the nonce. This is fine, since the code should always
// be running in off-chain context, so we NEVER persists data.
account_data.nonce += One::one();
crate::Account::<T>::insert(&account.id, account_data);
}
Some(res)
}
}
/// Submit an unsigned transaction onchain with a signed payload
pub trait SendUnsignedTransaction<T: SigningTypes + CreateBare<LocalCall>, LocalCall> {
/// A submission result.
///
/// Should contain the submission result and the account(s) that signed the payload.
type Result;
/// Send an unsigned transaction with a signed payload.
///
/// This method takes `f` and `f2` where:
/// - `f` is called for every account and is expected to return a `SignedPayload` object.
/// - `f2` is then called with the `SignedPayload` returned by `f` and the signature and is
/// expected to return a `Call` object to be embedded into transaction.
fn send_unsigned_transaction<TPayload, F>(
&self,
f: F,
f2: impl Fn(TPayload, T::Signature) -> LocalCall,
) -> Self::Result
where
F: Fn(&Account<T>) -> TPayload,
TPayload: SignedPayload<T>;
/// Submits an unsigned call to the transaction pool.
fn submit_unsigned_transaction(&self, call: LocalCall) -> Option<Result<(), ()>> {
let xt = T::create_bare(call.into());
Some(SubmitTransaction::<T, LocalCall>::submit_transaction(xt))
}
}
/// Utility trait to be implemented on payloads that can be signed.
pub trait SignedPayload<T: SigningTypes>: Encode {
/// Return a public key that is expected to have a matching key in the keystore,
/// which should be used to sign the payload.
fn public(&self) -> T::Public;
/// Sign the payload using the implementor's provided public key.
///
/// Returns `Some(signature)` if public key is supported.
fn sign<C: AppCrypto<T::Public, T::Signature>>(&self) -> Option<T::Signature> {
self.using_encoded(|payload| C::sign(payload, self.public()))
}
/// Verify signature against payload.
///
/// Returns a bool indicating whether the signature is valid or not.
fn verify<C: AppCrypto<T::Public, T::Signature>>(&self, signature: T::Signature) -> bool {
self.using_encoded(|payload| C::verify(payload, self.public(), signature))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mock::{RuntimeCall, Test as TestRuntime, CALL};
use codec::Decode;
use pezsp_core::offchain::{testing, TransactionPoolExt};
use pezsp_runtime::testing::{TestSignature, TestXt, UintAuthorityId};
impl SigningTypes for TestRuntime {
type Public = UintAuthorityId;
type Signature = TestSignature;
}
type Extrinsic = TestXt<RuntimeCall, ()>;
impl CreateTransactionBase<RuntimeCall> for TestRuntime {
type Extrinsic = Extrinsic;
type RuntimeCall = RuntimeCall;
}
impl CreateBare<RuntimeCall> for TestRuntime {
fn create_bare(call: Self::RuntimeCall) -> Self::Extrinsic {
Extrinsic::new_bare(call)
}
}
#[derive(codec::Encode, codec::Decode)]
struct SimplePayload {
pub public: UintAuthorityId,
pub data: Vec<u8>,
}
impl SignedPayload<TestRuntime> for SimplePayload {
fn public(&self) -> UintAuthorityId {
self.public.clone()
}
}
struct DummyAppCrypto;
// Bind together the `SigningTypes` with app-crypto and the wrapper types.
// here the implementation is pretty dummy, because we use the same type for
// both application-specific crypto and the runtime crypto, but in real-life
// runtimes it's going to use different types everywhere.
impl AppCrypto<UintAuthorityId, TestSignature> for DummyAppCrypto {
type RuntimeAppPublic = UintAuthorityId;
type GenericPublic = UintAuthorityId;
type GenericSignature = TestSignature;
}
fn assert_account(next: Option<(Account<TestRuntime>, Result<(), ()>)>, index: usize, id: u64) {
assert_eq!(next, Some((Account { index, id, public: id.into() }, Ok(()))));
}
#[test]
fn should_send_unsigned_with_signed_payload_with_all_accounts() {
let (pool, pool_state) = testing::TestTransactionPoolExt::new();
let mut t = pezsp_io::TestExternalities::default();
t.register_extension(TransactionPoolExt::new(pool));
// given
UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
t.execute_with(|| {
// when
let result = Signer::<TestRuntime, DummyAppCrypto>::all_accounts()
.send_unsigned_transaction(
|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
|_payload, _signature| CALL.clone(),
);
// then
let mut res = result.into_iter();
assert_account(res.next(), 0, 0xf0);
assert_account(res.next(), 1, 0xf1);
assert_account(res.next(), 2, 0xf2);
assert_eq!(res.next(), None);
// check the transaction pool content:
let tx1 = pool_state.write().transactions.pop().unwrap();
let _tx2 = pool_state.write().transactions.pop().unwrap();
let _tx3 = pool_state.write().transactions.pop().unwrap();
assert!(pool_state.read().transactions.is_empty());
let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
assert!(tx1.is_inherent());
});
}
#[test]
fn should_send_unsigned_with_signed_payload_with_any_account() {
let (pool, pool_state) = testing::TestTransactionPoolExt::new();
let mut t = pezsp_io::TestExternalities::default();
t.register_extension(TransactionPoolExt::new(pool));
// given
UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
t.execute_with(|| {
// when
let result = Signer::<TestRuntime, DummyAppCrypto>::any_account()
.send_unsigned_transaction(
|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
|_payload, _signature| CALL.clone(),
);
// then
let mut res = result.into_iter();
assert_account(res.next(), 0, 0xf0);
assert_eq!(res.next(), None);
// check the transaction pool content:
let tx1 = pool_state.write().transactions.pop().unwrap();
assert!(pool_state.read().transactions.is_empty());
let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
assert!(tx1.is_inherent());
});
}
#[test]
fn should_send_unsigned_with_signed_payload_with_all_account_and_filter() {
let (pool, pool_state) = testing::TestTransactionPoolExt::new();
let mut t = pezsp_io::TestExternalities::default();
t.register_extension(TransactionPoolExt::new(pool));
// given
UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
t.execute_with(|| {
// when
let result = Signer::<TestRuntime, DummyAppCrypto>::all_accounts()
.with_filter(vec![0xf2.into(), 0xf1.into()])
.send_unsigned_transaction(
|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
|_payload, _signature| CALL.clone(),
);
// then
let mut res = result.into_iter();
assert_account(res.next(), 0, 0xf2);
assert_account(res.next(), 1, 0xf1);
assert_eq!(res.next(), None);
// check the transaction pool content:
let tx1 = pool_state.write().transactions.pop().unwrap();
let _tx2 = pool_state.write().transactions.pop().unwrap();
assert!(pool_state.read().transactions.is_empty());
let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
assert!(tx1.is_inherent());
});
}
#[test]
fn should_send_unsigned_with_signed_payload_with_any_account_and_filter() {
let (pool, pool_state) = testing::TestTransactionPoolExt::new();
let mut t = pezsp_io::TestExternalities::default();
t.register_extension(TransactionPoolExt::new(pool));
// given
UintAuthorityId::set_all_keys(vec![0xf0, 0xf1, 0xf2]);
t.execute_with(|| {
// when
let result = Signer::<TestRuntime, DummyAppCrypto>::any_account()
.with_filter(vec![0xf2.into(), 0xf1.into()])
.send_unsigned_transaction(
|account| SimplePayload { data: vec![1, 2, 3], public: account.public.clone() },
|_payload, _signature| CALL.clone(),
);
// then
let mut res = result.into_iter();
assert_account(res.next(), 0, 0xf2);
assert_eq!(res.next(), None);
// check the transaction pool content:
let tx1 = pool_state.write().transactions.pop().unwrap();
assert!(pool_state.read().transactions.is_empty());
let tx1 = Extrinsic::decode(&mut &*tx1).unwrap();
assert!(tx1.is_inherent());
});
}
}
+979
View File
@@ -0,0 +1,979 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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 crate::*;
use pezframe_support::{
assert_noop, assert_ok,
dispatch::{Pays, PostDispatchInfo, WithPostDispatchInfo},
traits::{OnRuntimeUpgrade, WhitelistedStorageKeys},
};
use mock::{RuntimeOrigin, *};
use pezsp_core::{hexdisplay::HexDisplay, H256};
use pezsp_runtime::{
traits::{BlakeTwo256, Header},
DispatchError, DispatchErrorWithPostInfo,
};
use std::collections::BTreeSet;
use bizinikiwi_test_runtime_client::WasmExecutor;
#[test]
fn check_whitelist() {
let whitelist: BTreeSet<String> = AllPalletsWithSystem::whitelisted_storage_keys()
.iter()
.map(|s| HexDisplay::from(&s.key).to_string())
.collect();
// Block Number
assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac"));
// Execution Phase
assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a"));
// Event Count
assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850"));
// System Events
assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7"));
// System BlockWeight
assert!(whitelist.contains("26aa394eea5630e07c48ae0c9558cef734abf5cb34d6244378cddbf18e849d96"));
}
#[test]
fn origin_works() {
let o = RuntimeOrigin::from(RawOrigin::<u64>::Signed(1u64));
let x: Result<RawOrigin<u64>, RuntimeOrigin> = o.into();
assert_eq!(x.unwrap(), RawOrigin::<u64>::Signed(1u64));
}
#[test]
fn unique_datum_works() {
new_test_ext().execute_with(|| {
System::initialize(&1, &[0u8; 32].into(), &Default::default());
assert!(pezsp_io::storage::exists(well_known_keys::INTRABLOCK_ENTROPY));
let h1 = unique(b"");
assert_eq!(
32,
pezsp_io::storage::read(well_known_keys::INTRABLOCK_ENTROPY, &mut [], 0).unwrap()
);
let h2 = unique(b"");
assert_eq!(
32,
pezsp_io::storage::read(well_known_keys::INTRABLOCK_ENTROPY, &mut [], 0).unwrap()
);
assert_ne!(h1, h2);
let h3 = unique(b"Hello");
assert_eq!(
32,
pezsp_io::storage::read(well_known_keys::INTRABLOCK_ENTROPY, &mut [], 0).unwrap()
);
assert_ne!(h2, h3);
let h4 = unique(b"Hello");
assert_eq!(
32,
pezsp_io::storage::read(well_known_keys::INTRABLOCK_ENTROPY, &mut [], 0).unwrap()
);
assert_ne!(h3, h4);
System::finalize();
assert!(!pezsp_io::storage::exists(well_known_keys::INTRABLOCK_ENTROPY));
});
}
#[test]
fn stored_map_works() {
new_test_ext().execute_with(|| {
assert_eq!(System::inc_providers(&0), IncRefStatus::Created);
assert_ok!(System::insert(&0, 42));
assert!(!System::is_provider_required(&0));
assert_eq!(
Account::<Test>::get(0),
AccountInfo {
nonce: 0u64.into(),
providers: 1,
consumers: 0,
sufficients: 0,
data: 42
}
);
assert_ok!(System::inc_consumers(&0));
assert!(System::is_provider_required(&0));
assert_ok!(System::insert(&0, 69));
assert!(System::is_provider_required(&0));
System::dec_consumers(&0);
assert!(!System::is_provider_required(&0));
assert!(Killed::get().is_empty());
assert_ok!(System::remove(&0));
assert_ok!(System::dec_providers(&0));
assert_eq!(Killed::get(), vec![0u64]);
});
}
#[test]
fn provider_ref_handover_to_self_sufficient_ref_works() {
new_test_ext().execute_with(|| {
assert_eq!(System::inc_providers(&0), IncRefStatus::Created);
System::inc_account_nonce(&0);
assert_eq!(System::account_nonce(&0), 1u64.into());
// a second reference coming and going doesn't change anything.
assert_eq!(System::inc_sufficients(&0), IncRefStatus::Existed);
assert_eq!(System::dec_sufficients(&0), DecRefStatus::Exists);
assert_eq!(System::account_nonce(&0), 1u64.into());
// a provider reference coming and going doesn't change anything.
assert_eq!(System::inc_providers(&0), IncRefStatus::Existed);
assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Exists);
assert_eq!(System::account_nonce(&0), 1u64.into());
// decreasing the providers with a self-sufficient present should not delete the account
assert_eq!(System::inc_sufficients(&0), IncRefStatus::Existed);
assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Exists);
assert_eq!(System::account_nonce(&0), 1u64.into());
// decreasing the sufficients should delete the account
assert_eq!(System::dec_sufficients(&0), DecRefStatus::Reaped);
assert_eq!(System::account_nonce(&0), 0u64.into());
});
}
#[test]
fn dec_sufficients_does_not_undeflow() {
new_test_ext().execute_with(|| {
assert_eq!(System::inc_providers(&0), IncRefStatus::Created);
assert_eq!(System::dec_sufficients(&0), DecRefStatus::Exists);
});
}
#[test]
fn self_sufficient_ref_handover_to_provider_ref_works() {
new_test_ext().execute_with(|| {
assert_eq!(System::inc_sufficients(&0), IncRefStatus::Created);
System::inc_account_nonce(&0);
assert_eq!(System::account_nonce(&0), 1u64.into());
// a second reference coming and going doesn't change anything.
assert_eq!(System::inc_providers(&0), IncRefStatus::Existed);
assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Exists);
assert_eq!(System::account_nonce(&0), 1u64.into());
// a sufficient reference coming and going doesn't change anything.
assert_eq!(System::inc_sufficients(&0), IncRefStatus::Existed);
assert_eq!(System::dec_sufficients(&0), DecRefStatus::Exists);
assert_eq!(System::account_nonce(&0), 1u64.into());
// decreasing the sufficients with a provider present should not delete the account
assert_eq!(System::inc_providers(&0), IncRefStatus::Existed);
assert_eq!(System::dec_sufficients(&0), DecRefStatus::Exists);
assert_eq!(System::account_nonce(&0), 1u64.into());
// decreasing the providers should delete the account
assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Reaped);
assert_eq!(System::account_nonce(&0), 0u64.into());
});
}
#[test]
fn sufficient_cannot_support_consumer() {
new_test_ext().execute_with(|| {
assert_eq!(System::inc_sufficients(&0), IncRefStatus::Created);
System::inc_account_nonce(&0);
assert_eq!(System::account_nonce(&0), 1u64.into());
assert_noop!(System::inc_consumers(&0), DispatchError::NoProviders);
assert_eq!(System::inc_providers(&0), IncRefStatus::Existed);
assert_ok!(System::inc_consumers(&0));
assert_noop!(System::dec_providers(&0), DispatchError::ConsumerRemaining);
});
}
#[test]
fn provider_required_to_support_consumer() {
new_test_ext().execute_with(|| {
assert_noop!(System::inc_consumers(&0), DispatchError::NoProviders);
assert_eq!(System::inc_providers(&0), IncRefStatus::Created);
System::inc_account_nonce(&0);
assert_eq!(System::account_nonce(&0), 1u64.into());
assert_eq!(System::inc_providers(&0), IncRefStatus::Existed);
assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Exists);
assert_eq!(System::account_nonce(&0), 1u64.into());
assert_ok!(System::inc_consumers(&0));
assert_noop!(System::dec_providers(&0), DispatchError::ConsumerRemaining);
System::dec_consumers(&0);
assert_eq!(System::dec_providers(&0).unwrap(), DecRefStatus::Reaped);
assert_eq!(System::account_nonce(&0), 0u64.into());
});
}
#[test]
fn deposit_event_should_work() {
new_test_ext().execute_with(|| {
System::reset_events();
System::initialize(&1, &[0u8; 32].into(), &Default::default());
System::note_finished_extrinsics();
System::deposit_event(SysEvent::CodeUpdated);
System::finalize();
assert_eq!(
System::events(),
vec![EventRecord {
phase: Phase::Finalization,
event: SysEvent::CodeUpdated.into(),
topics: vec![],
}]
);
let normal_base = <Test as crate::Config>::BlockWeights::get()
.get(DispatchClass::Normal)
.base_extrinsic;
System::reset_events();
System::initialize(&2, &[0u8; 32].into(), &Default::default());
System::deposit_event(SysEvent::NewAccount { account: 32 });
System::note_finished_initialize();
System::deposit_event(SysEvent::KilledAccount { account: 42 });
System::note_applied_extrinsic(&Ok(().into()), Default::default());
System::note_applied_extrinsic(&Err(DispatchError::BadOrigin.into()), Default::default());
System::note_finished_extrinsics();
System::deposit_event(SysEvent::NewAccount { account: 3 });
System::finalize();
assert_eq!(
System::events(),
vec![
EventRecord {
phase: Phase::Initialization,
event: SysEvent::NewAccount { account: 32 }.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: SysEvent::KilledAccount { account: 42 }.into(),
topics: vec![]
},
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: SysEvent::ExtrinsicSuccess {
dispatch_info: DispatchEventInfo {
weight: normal_base,
..Default::default()
}
}
.into(),
topics: vec![]
},
EventRecord {
phase: Phase::ApplyExtrinsic(1),
event: SysEvent::ExtrinsicFailed {
dispatch_error: DispatchError::BadOrigin.into(),
dispatch_info: DispatchEventInfo {
weight: normal_base,
..Default::default()
}
}
.into(),
topics: vec![]
},
EventRecord {
phase: Phase::Finalization,
event: SysEvent::NewAccount { account: 3 }.into(),
topics: vec![]
},
]
);
});
}
#[test]
fn deposit_event_uses_actual_weight_and_pays_fee() {
new_test_ext().execute_with(|| {
System::reset_events();
System::initialize(&1, &[0u8; 32].into(), &Default::default());
System::note_finished_initialize();
let normal_base = <Test as crate::Config>::BlockWeights::get()
.get(DispatchClass::Normal)
.base_extrinsic;
let pre_info =
DispatchInfo { call_weight: Weight::from_parts(1000, 0), ..Default::default() };
System::note_applied_extrinsic(&Ok(from_actual_ref_time(Some(300))), pre_info);
System::note_applied_extrinsic(&Ok(from_actual_ref_time(Some(1000))), pre_info);
System::note_applied_extrinsic(
// values over the pre info should be capped at pre dispatch value
&Ok(from_actual_ref_time(Some(1200))),
pre_info,
);
System::note_applied_extrinsic(
&Ok(from_post_weight_info(Some(2_500_000), Pays::Yes)),
pre_info,
);
System::note_applied_extrinsic(&Ok(Pays::No.into()), pre_info);
System::note_applied_extrinsic(
&Ok(from_post_weight_info(Some(2_500_000), Pays::No)),
pre_info,
);
System::note_applied_extrinsic(&Ok(from_post_weight_info(Some(500), Pays::No)), pre_info);
System::note_applied_extrinsic(
&Err(DispatchError::BadOrigin.with_weight(Weight::from_parts(999, 0))),
pre_info,
);
System::note_applied_extrinsic(
&Err(DispatchErrorWithPostInfo {
post_info: PostDispatchInfo { actual_weight: None, pays_fee: Pays::Yes },
error: DispatchError::BadOrigin,
}),
pre_info,
);
System::note_applied_extrinsic(
&Err(DispatchErrorWithPostInfo {
post_info: PostDispatchInfo {
actual_weight: Some(Weight::from_parts(800, 0)),
pays_fee: Pays::Yes,
},
error: DispatchError::BadOrigin,
}),
pre_info,
);
System::note_applied_extrinsic(
&Err(DispatchErrorWithPostInfo {
post_info: PostDispatchInfo {
actual_weight: Some(Weight::from_parts(800, 0)),
pays_fee: Pays::No,
},
error: DispatchError::BadOrigin,
}),
pre_info,
);
// Also works for operational.
let operational_base = <Test as crate::Config>::BlockWeights::get()
.get(DispatchClass::Operational)
.base_extrinsic;
assert!(normal_base != operational_base, "Test pre-condition violated");
let pre_info = DispatchInfo {
call_weight: Weight::from_parts(1000, 0),
class: DispatchClass::Operational,
..Default::default()
};
System::note_applied_extrinsic(&Ok(from_actual_ref_time(Some(300))), pre_info);
let got = System::events();
let want = vec![
EventRecord {
phase: Phase::ApplyExtrinsic(0),
event: SysEvent::ExtrinsicSuccess {
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(300, 0).saturating_add(normal_base),
..Default::default()
},
}
.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(1),
event: SysEvent::ExtrinsicSuccess {
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(1000, 0).saturating_add(normal_base),
..Default::default()
},
}
.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(2),
event: SysEvent::ExtrinsicSuccess {
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(1000, 0).saturating_add(normal_base),
..Default::default()
},
}
.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(3),
event: SysEvent::ExtrinsicSuccess {
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(1000, 0).saturating_add(normal_base),
pays_fee: Pays::Yes,
class: Default::default(),
},
}
.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(4),
event: SysEvent::ExtrinsicSuccess {
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(1000, 0).saturating_add(normal_base),
pays_fee: Pays::No,
class: Default::default(),
},
}
.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(5),
event: SysEvent::ExtrinsicSuccess {
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(1000, 0).saturating_add(normal_base),
pays_fee: Pays::No,
class: Default::default(),
},
}
.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(6),
event: SysEvent::ExtrinsicSuccess {
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(500, 0).saturating_add(normal_base),
pays_fee: Pays::No,
class: Default::default(),
},
}
.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(7),
event: SysEvent::ExtrinsicFailed {
dispatch_error: DispatchError::BadOrigin.into(),
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(999, 0).saturating_add(normal_base),
..Default::default()
},
}
.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(8),
event: SysEvent::ExtrinsicFailed {
dispatch_error: DispatchError::BadOrigin.into(),
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(1000, 0).saturating_add(normal_base),
pays_fee: Pays::Yes,
class: Default::default(),
},
}
.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(9),
event: SysEvent::ExtrinsicFailed {
dispatch_error: DispatchError::BadOrigin.into(),
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(800, 0).saturating_add(normal_base),
pays_fee: Pays::Yes,
class: Default::default(),
},
}
.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(10),
event: SysEvent::ExtrinsicFailed {
dispatch_error: DispatchError::BadOrigin.into(),
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(800, 0).saturating_add(normal_base),
pays_fee: Pays::No,
class: Default::default(),
},
}
.into(),
topics: vec![],
},
EventRecord {
phase: Phase::ApplyExtrinsic(11),
event: SysEvent::ExtrinsicSuccess {
dispatch_info: DispatchEventInfo {
weight: Weight::from_parts(300, 0).saturating_add(operational_base),
class: DispatchClass::Operational,
pays_fee: Default::default(),
},
}
.into(),
topics: vec![],
},
];
for (i, event) in want.into_iter().enumerate() {
assert_eq!(got[i], event, "Event mismatch at index {}", i);
}
});
}
#[test]
fn deposit_event_topics() {
new_test_ext().execute_with(|| {
const BLOCK_NUMBER: u64 = 1;
System::reset_events();
System::initialize(&BLOCK_NUMBER, &[0u8; 32].into(), &Default::default());
System::note_finished_extrinsics();
let topics = vec![H256::repeat_byte(1), H256::repeat_byte(2), H256::repeat_byte(3)];
// We deposit a few events with different sets of topics.
System::deposit_event_indexed(&topics[0..3], SysEvent::NewAccount { account: 1 }.into());
System::deposit_event_indexed(&topics[0..1], SysEvent::NewAccount { account: 2 }.into());
System::deposit_event_indexed(&topics[1..2], SysEvent::NewAccount { account: 3 }.into());
System::finalize();
// Check that topics are reflected in the event record.
assert_eq!(
System::events(),
vec![
EventRecord {
phase: Phase::Finalization,
event: SysEvent::NewAccount { account: 1 }.into(),
topics: topics[0..3].to_vec(),
},
EventRecord {
phase: Phase::Finalization,
event: SysEvent::NewAccount { account: 2 }.into(),
topics: topics[0..1].to_vec(),
},
EventRecord {
phase: Phase::Finalization,
event: SysEvent::NewAccount { account: 3 }.into(),
topics: topics[1..2].to_vec(),
}
]
);
// Check that the topic-events mapping reflects the deposited topics.
// Note that these are indexes of the events.
assert_eq!(System::event_topics(&topics[0]), vec![(BLOCK_NUMBER, 0), (BLOCK_NUMBER, 1)]);
assert_eq!(System::event_topics(&topics[1]), vec![(BLOCK_NUMBER, 0), (BLOCK_NUMBER, 2)]);
assert_eq!(System::event_topics(&topics[2]), vec![(BLOCK_NUMBER, 0)]);
});
}
#[test]
fn event_util_functions_should_work() {
new_test_ext().execute_with(|| {
System::set_block_number(1);
System::deposit_event(SysEvent::CodeUpdated);
System::assert_has_event(SysEvent::CodeUpdated.into());
System::assert_last_event(SysEvent::CodeUpdated.into());
});
}
#[test]
fn prunes_block_hash_mappings() {
new_test_ext().execute_with(|| {
// simulate import of 15 blocks
for n in 1..=15 {
System::reset_events();
System::initialize(&n, &[n as u8 - 1; 32].into(), &Default::default());
System::finalize();
}
// first 5 block hashes are pruned
for n in 0..5 {
assert_eq!(System::block_hash(n), H256::zero());
}
// the remaining 10 are kept
for n in 5..15 {
assert_eq!(System::block_hash(n), [n as u8; 32].into());
}
})
}
#[test]
fn set_code_checks_works() {
struct ReadRuntimeVersion(Vec<u8>);
impl pezsp_core::traits::ReadRuntimeVersion for ReadRuntimeVersion {
fn read_runtime_version(
&self,
_wasm_code: &[u8],
_ext: &mut dyn pezsp_externalities::Externalities,
) -> Result<Vec<u8>, String> {
Ok(self.0.clone())
}
}
let test_data = vec![
("test", 1, 2, Err(Error::<Test>::SpecVersionNeedsToIncrease)),
("test", 1, 1, Err(Error::<Test>::SpecVersionNeedsToIncrease)),
("test2", 1, 1, Err(Error::<Test>::InvalidSpecName)),
(
"test",
2,
1,
Ok(Some(<mock::Test as pallet::Config>::BlockWeights::get().max_block).into()),
),
("test", 0, 1, Err(Error::<Test>::SpecVersionNeedsToIncrease)),
("test", 1, 0, Err(Error::<Test>::SpecVersionNeedsToIncrease)),
];
for (spec_name, spec_version, impl_version, expected) in test_data.into_iter() {
let version = RuntimeVersion {
spec_name: spec_name.into(),
spec_version,
impl_version,
..Default::default()
};
let read_runtime_version = ReadRuntimeVersion(version.encode());
let mut ext = new_test_ext();
ext.register_extension(pezsp_core::traits::ReadRuntimeVersionExt::new(read_runtime_version));
ext.execute_with(|| {
let res = System::set_code(RawOrigin::Root.into(), vec![1, 2, 3, 4]);
assert_runtime_updated_digest(if res.is_ok() { 1 } else { 0 });
assert_eq!(expected.map_err(DispatchErrorWithPostInfo::from), res);
});
}
}
fn assert_runtime_updated_digest(num: usize) {
assert_eq!(
System::digest()
.logs
.into_iter()
.filter(|item| *item == generic::DigestItem::RuntimeEnvironmentUpdated)
.count(),
num,
"Incorrect number of Runtime Updated digest items",
);
}
#[test]
fn set_code_with_real_wasm_blob() {
let executor = WasmExecutor::default();
let mut ext = new_test_ext();
ext.register_extension(pezsp_core::traits::ReadRuntimeVersionExt::new(executor));
ext.execute_with(|| {
System::set_block_number(1);
System::set_code(
RawOrigin::Root.into(),
bizinikiwi_test_runtime_client::runtime::wasm_binary_unwrap().to_vec(),
)
.unwrap();
assert_eq!(
System::events(),
vec![EventRecord {
phase: Phase::Initialization,
event: SysEvent::CodeUpdated.into(),
topics: vec![],
}],
);
});
}
#[test]
fn set_code_rejects_during_mbm() {
Ongoing::set(true);
let executor = bizinikiwi_test_runtime_client::WasmExecutor::default();
let mut ext = new_test_ext();
ext.register_extension(pezsp_core::traits::ReadRuntimeVersionExt::new(executor));
ext.execute_with(|| {
System::set_block_number(1);
let res = System::set_code(
RawOrigin::Root.into(),
bizinikiwi_test_runtime_client::runtime::wasm_binary_unwrap().to_vec(),
);
assert_eq!(
res,
Err(DispatchErrorWithPostInfo::from(Error::<Test>::MultiBlockMigrationsOngoing))
);
assert!(System::events().is_empty());
});
}
#[test]
fn set_code_via_authorization_works() {
let executor = bizinikiwi_test_runtime_client::WasmExecutor::default();
let mut ext = new_test_ext();
ext.register_extension(pezsp_core::traits::ReadRuntimeVersionExt::new(executor));
ext.execute_with(|| {
System::set_block_number(1);
assert!(System::authorized_upgrade().is_none());
let runtime = bizinikiwi_test_runtime_client::runtime::wasm_binary_unwrap().to_vec();
let hash = <mock::Test as pallet::Config>::Hashing::hash(&runtime);
// Can't apply before authorization
assert_noop!(
System::apply_authorized_upgrade(RawOrigin::None.into(), runtime.clone()),
Error::<Test>::NothingAuthorized,
);
// Can authorize
assert_ok!(System::authorize_upgrade(RawOrigin::Root.into(), hash));
System::assert_has_event(
SysEvent::UpgradeAuthorized { code_hash: hash, check_version: true }.into(),
);
assert_eq!(System::authorized_upgrade().unwrap().code_hash(), &hash);
// Can't be sneaky
let mut bad_runtime = bizinikiwi_test_runtime_client::runtime::wasm_binary_unwrap().to_vec();
bad_runtime.extend(b"sneaky");
assert_noop!(
System::apply_authorized_upgrade(RawOrigin::None.into(), bad_runtime),
Error::<Test>::Unauthorized,
);
// Can apply correct runtime
assert_ok!(System::apply_authorized_upgrade(RawOrigin::None.into(), runtime));
System::assert_has_event(SysEvent::CodeUpdated.into());
assert!(System::authorized_upgrade().is_none());
});
}
#[test]
fn runtime_upgraded_with_set_storage() {
let executor = bizinikiwi_test_runtime_client::WasmExecutor::default();
let mut ext = new_test_ext();
ext.register_extension(pezsp_core::traits::ReadRuntimeVersionExt::new(executor));
ext.execute_with(|| {
System::set_storage(
RawOrigin::Root.into(),
vec![(
well_known_keys::CODE.to_vec(),
bizinikiwi_test_runtime_client::runtime::wasm_binary_unwrap().to_vec(),
)],
)
.unwrap();
});
}
#[test]
fn events_not_emitted_during_genesis() {
new_test_ext().execute_with(|| {
// Block Number is zero at genesis
assert!(System::block_number().is_zero());
let mut account_data = AccountInfo::default();
System::on_created_account(Default::default(), &mut account_data);
// 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);
assert!(System::events().len() == 1);
});
}
#[test]
fn extrinsics_root_is_calculated_correctly() {
new_test_ext().execute_with(|| {
System::reset_events();
System::initialize(&1, &[0u8; 32].into(), &Default::default());
System::note_finished_initialize();
System::note_extrinsic(vec![1]);
System::note_applied_extrinsic(&Ok(().into()), Default::default());
System::note_extrinsic(vec![2]);
System::note_applied_extrinsic(&Err(DispatchError::BadOrigin.into()), Default::default());
System::note_finished_extrinsics();
let header = System::finalize();
let ext_root = extrinsics_data_root::<BlakeTwo256>(
vec![vec![1], vec![2]],
pezsp_core::storage::StateVersion::V0,
);
assert_eq!(ext_root, *header.extrinsics_root());
});
}
#[test]
fn runtime_updated_digest_emitted_when_heap_pages_changed() {
new_test_ext().execute_with(|| {
System::reset_events();
System::initialize(&1, &[0u8; 32].into(), &Default::default());
System::set_heap_pages(RawOrigin::Root.into(), 5).unwrap();
assert_runtime_updated_digest(1);
});
}
#[test]
fn ensure_signed_stuff_works() {
struct Members;
impl SortedMembers<u64> for Members {
fn sorted_members() -> Vec<u64> {
(0..10).collect()
}
}
let signed_origin = RuntimeOrigin::signed(0u64);
assert_ok!(<EnsureSigned<_> as EnsureOrigin<_>>::try_origin(signed_origin.clone()));
assert_ok!(<EnsureSignedBy<Members, _> as EnsureOrigin<_>>::try_origin(signed_origin));
#[cfg(feature = "runtime-benchmarks")]
{
let successful_origin: RuntimeOrigin =
<EnsureSigned<_> as EnsureOrigin<_>>::try_successful_origin()
.expect("EnsureSigned has no successful origin required for the test");
assert_ok!(<EnsureSigned<_> as EnsureOrigin<_>>::try_origin(successful_origin));
let successful_origin: RuntimeOrigin =
<EnsureSignedBy<Members, _> as EnsureOrigin<_>>::try_successful_origin()
.expect("EnsureSignedBy has no successful origin required for the test");
assert_ok!(<EnsureSignedBy<Members, _> as EnsureOrigin<_>>::try_origin(successful_origin));
}
}
pub fn from_actual_ref_time(ref_time: Option<u64>) -> PostDispatchInfo {
PostDispatchInfo {
actual_weight: ref_time.map(|t| Weight::from_all(t)),
pays_fee: Default::default(),
}
}
pub fn from_post_weight_info(ref_time: Option<u64>, pays_fee: Pays) -> PostDispatchInfo {
PostDispatchInfo { actual_weight: ref_time.map(|t| Weight::from_all(t)), pays_fee }
}
#[docify::export]
#[test]
fn last_runtime_upgrade_spec_version_usage() {
#[allow(dead_code)]
struct Migration;
impl OnRuntimeUpgrade for Migration {
fn on_runtime_upgrade() -> Weight {
// Ensure to compare the spec version against some static version to prevent applying
// the same migration multiple times.
//
// `1337` here is the spec version of the runtime running on chain. If there is maybe
// a runtime upgrade in the pipeline of being applied, you should use the spec version
// of this upgrade.
if System::last_runtime_upgrade_spec_version() > 1337 {
return Weight::zero();
}
// Do the migration.
Weight::zero()
}
}
}
#[test]
fn test_default_account_nonce() {
new_test_ext().execute_with(|| {
System::set_block_number(2);
assert_eq!(System::account_nonce(&1), 2u64.into());
System::inc_account_nonce(&1);
assert_eq!(System::account_nonce(&1), 3u64.into());
System::set_block_number(5);
assert_eq!(System::account_nonce(&1), 3u64.into());
Account::<Test>::remove(&1);
assert_eq!(System::account_nonce(&1), 5u64.into());
});
}
#[test]
fn extrinsic_weight_refunded_is_cleaned() {
new_test_ext().execute_with(|| {
crate::ExtrinsicWeightReclaimed::<Test>::put(Weight::from_parts(1, 2));
assert_eq!(crate::ExtrinsicWeightReclaimed::<Test>::get(), Weight::from_parts(1, 2));
System::note_applied_extrinsic(&Ok(().into()), Default::default());
assert_eq!(crate::ExtrinsicWeightReclaimed::<Test>::get(), Weight::zero());
crate::ExtrinsicWeightReclaimed::<Test>::put(Weight::from_parts(1, 2));
assert_eq!(crate::ExtrinsicWeightReclaimed::<Test>::get(), Weight::from_parts(1, 2));
System::note_applied_extrinsic(&Err(DispatchError::BadOrigin.into()), Default::default());
assert_eq!(crate::ExtrinsicWeightReclaimed::<Test>::get(), Weight::zero());
});
}
#[test]
fn reclaim_works() {
new_test_ext().execute_with(|| {
let info = DispatchInfo { call_weight: Weight::from_parts(100, 200), ..Default::default() };
crate::Pallet::<Test>::reclaim_weight(
&info,
&PostDispatchInfo {
actual_weight: Some(Weight::from_parts(50, 100)),
..Default::default()
},
)
.unwrap();
assert_eq!(crate::ExtrinsicWeightReclaimed::<Test>::get(), Weight::from_parts(50, 100));
crate::Pallet::<Test>::reclaim_weight(
&info,
&PostDispatchInfo {
actual_weight: Some(Weight::from_parts(25, 200)),
..Default::default()
},
)
.unwrap();
assert_eq!(crate::ExtrinsicWeightReclaimed::<Test>::get(), Weight::from_parts(75, 100));
crate::Pallet::<Test>::reclaim_weight(
&info,
&PostDispatchInfo {
actual_weight: Some(Weight::from_parts(300, 50)),
..Default::default()
},
)
.unwrap();
assert_eq!(crate::ExtrinsicWeightReclaimed::<Test>::get(), Weight::from_parts(75, 150));
crate::Pallet::<Test>::reclaim_weight(
&info,
&PostDispatchInfo {
actual_weight: Some(Weight::from_parts(300, 300)),
..Default::default()
},
)
.unwrap();
assert_eq!(crate::ExtrinsicWeightReclaimed::<Test>::get(), Weight::from_parts(75, 150));
System::note_applied_extrinsic(&Ok(().into()), Default::default());
assert_eq!(crate::ExtrinsicWeightReclaimed::<Test>::get(), Weight::zero());
});
}
#[test]
#[should_panic(expected = "Block number must be strictly increasing.")]
fn initialize_block_number_must_be_sequential() {
new_test_ext().execute_with(|| {
// Initialize block 1
System::initialize(&1, &[0u8; 32].into(), &Default::default());
System::finalize();
// Try to initialize block 3, skipping block 2 - this should panic
System::initialize(&3, &[0u8; 32].into(), &Default::default());
});
}
+311
View File
@@ -0,0 +1,311 @@
// This file is part of Bizinikiwi.
// Copyright (C) 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.
// This file is part of Bizinikiwi.
// Copyright (C) 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 `pezframe_system`
//!
//! THIS FILE WAS AUTO-GENERATED USING THE BIZINIKIWI BENCHMARK CLI VERSION 32.0.0
//! DATE: 2025-02-21, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]`
//! WORST CASE MAP SIZE: `1000000`
//! HOSTNAME: `4563561839a5`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz`
//! WASM-EXECUTION: `Compiled`, CHAIN: `None`, DB CACHE: `1024`
// Executed Command:
// frame-omni-bencher
// v1
// benchmark
// pallet
// --extrinsic=*
// --runtime=target/production/wbuild/kitchensink-runtime/kitchensink_runtime.wasm
// --pallet=pezframe_system
// --header=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/HEADER-APACHE2
// --output=/__w/pezkuwi-sdk/pezkuwi-sdk/bizinikiwi/pezframe/system/src/weights.rs
// --wasm-execution=compiled
// --steps=50
// --repeat=20
// --heap-pages=4096
// --template=bizinikiwi/.maintain/frame-weight-template.hbs
// --no-storage-info
// --no-min-squares
// --no-median-slopes
// --genesis-builder-policy=none
// --exclude-pallets=pezpallet_xcm,pezpallet_xcm_benchmarks::fungible,pezpallet_xcm_benchmarks::generic,pezpallet_nomination_pools,pezpallet_remark,pezpallet_transaction_storage,pezpallet_election_provider_multi_block,pezpallet_election_provider_multi_block::signed,pezpallet_election_provider_multi_block::unsigned,pezpallet_election_provider_multi_block::verifier
#![cfg_attr(rustfmt, rustfmt_skip)]
#![allow(unused_parens)]
#![allow(unused_imports)]
#![allow(missing_docs)]
#![allow(dead_code)]
use pezframe_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}};
use core::marker::PhantomData;
/// Weight functions needed for `pezframe_system`.
pub trait WeightInfo {
fn remark(b: u32, ) -> Weight;
fn remark_with_event(b: u32, ) -> Weight;
fn set_heap_pages() -> Weight;
fn set_code() -> Weight;
fn set_storage(i: u32, ) -> Weight;
fn kill_storage(i: u32, ) -> Weight;
fn kill_prefix(p: u32, ) -> Weight;
fn authorize_upgrade() -> Weight;
fn apply_authorized_upgrade() -> Weight;
}
/// Weights for `pezframe_system` using the Bizinikiwi node and recommended hardware.
pub struct BizinikiwiWeight<T>(PhantomData<T>);
impl<T: crate::Config> WeightInfo for BizinikiwiWeight<T> {
/// The range of component `b` is `[0, 3932160]`.
fn remark(b: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 1_518_000 picoseconds.
Weight::from_parts(1_586_000, 0)
// Standard Error: 122
.saturating_add(Weight::from_parts(10_920, 0).saturating_mul(b.into()))
}
/// The range of component `b` is `[0, 3932160]`.
fn remark_with_event(b: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 4_558_000 picoseconds.
Weight::from_parts(4_725_000, 0)
// Standard Error: 120
.saturating_add(Weight::from_parts(12_386, 0).saturating_mul(b.into()))
}
/// Storage: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1)
/// Proof: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1)
fn set_heap_pages() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 2_639_000 picoseconds.
Weight::from_parts(2_836_000, 0)
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0)
/// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`)
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
fn set_code() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `67035`
// Minimum execution time: 161_314_402_000 picoseconds.
Weight::from_parts(164_247_810_000, 67035)
.saturating_add(T::DbWeight::get().reads(1_u64))
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: `Skipped::Metadata` (r:0 w:0)
/// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `i` is `[0, 1000]`.
fn set_storage(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 1_518_000 picoseconds.
Weight::from_parts(1_565_000, 0)
// Standard Error: 2_255
.saturating_add(Weight::from_parts(733_648, 0).saturating_mul(i.into()))
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into())))
}
/// Storage: `Skipped::Metadata` (r:0 w:0)
/// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `i` is `[0, 1000]`.
fn kill_storage(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 1_583_000 picoseconds.
Weight::from_parts(1_624_000, 0)
// Standard Error: 1_360
.saturating_add(Weight::from_parts(566_903, 0).saturating_mul(i.into()))
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(i.into())))
}
/// Storage: `Skipped::Metadata` (r:0 w:0)
/// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `p` is `[0, 1000]`.
fn kill_prefix(p: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `12 + p * (69 ±0)`
// Estimated: `0 + p * (70 ±0)`
// Minimum execution time: 2_884_000 picoseconds.
Weight::from_parts(2_964_000, 0)
// Standard Error: 1_896
.saturating_add(Weight::from_parts(1_340_742, 0).saturating_mul(p.into()))
.saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(p.into())))
.saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(p.into())))
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
}
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
fn authorize_upgrade() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 12_466_000 picoseconds.
Weight::from_parts(15_570_000, 0)
.saturating_add(T::DbWeight::get().writes(1_u64))
}
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
/// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0)
/// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`)
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
fn apply_authorized_upgrade() -> Weight {
// Proof Size summary in bytes:
// Measured: `21`
// Estimated: `67035`
// Minimum execution time: 163_673_542_000 picoseconds.
Weight::from_parts(166_858_158_000, 67035)
.saturating_add(T::DbWeight::get().reads(2_u64))
.saturating_add(T::DbWeight::get().writes(2_u64))
}
}
// For backwards compatibility and tests.
impl WeightInfo for () {
/// The range of component `b` is `[0, 3932160]`.
fn remark(b: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 1_518_000 picoseconds.
Weight::from_parts(1_586_000, 0)
// Standard Error: 122
.saturating_add(Weight::from_parts(10_920, 0).saturating_mul(b.into()))
}
/// The range of component `b` is `[0, 3932160]`.
fn remark_with_event(b: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 4_558_000 picoseconds.
Weight::from_parts(4_725_000, 0)
// Standard Error: 120
.saturating_add(Weight::from_parts(12_386, 0).saturating_mul(b.into()))
}
/// Storage: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1)
/// Proof: UNKNOWN KEY `0x3a686561707061676573` (r:0 w:1)
fn set_heap_pages() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 2_639_000 picoseconds.
Weight::from_parts(2_836_000, 0)
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0)
/// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`)
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
fn set_code() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `67035`
// Minimum execution time: 161_314_402_000 picoseconds.
Weight::from_parts(164_247_810_000, 67035)
.saturating_add(RocksDbWeight::get().reads(1_u64))
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: `Skipped::Metadata` (r:0 w:0)
/// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `i` is `[0, 1000]`.
fn set_storage(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 1_518_000 picoseconds.
Weight::from_parts(1_565_000, 0)
// Standard Error: 2_255
.saturating_add(Weight::from_parts(733_648, 0).saturating_mul(i.into()))
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into())))
}
/// Storage: `Skipped::Metadata` (r:0 w:0)
/// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `i` is `[0, 1000]`.
fn kill_storage(i: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 1_583_000 picoseconds.
Weight::from_parts(1_624_000, 0)
// Standard Error: 1_360
.saturating_add(Weight::from_parts(566_903, 0).saturating_mul(i.into()))
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(i.into())))
}
/// Storage: `Skipped::Metadata` (r:0 w:0)
/// Proof: `Skipped::Metadata` (`max_values`: None, `max_size`: None, mode: `Measured`)
/// The range of component `p` is `[0, 1000]`.
fn kill_prefix(p: u32, ) -> Weight {
// Proof Size summary in bytes:
// Measured: `12 + p * (69 ±0)`
// Estimated: `0 + p * (70 ±0)`
// Minimum execution time: 2_884_000 picoseconds.
Weight::from_parts(2_964_000, 0)
// Standard Error: 1_896
.saturating_add(Weight::from_parts(1_340_742, 0).saturating_mul(p.into()))
.saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(p.into())))
.saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(p.into())))
.saturating_add(Weight::from_parts(0, 70).saturating_mul(p.into()))
}
/// Storage: `System::AuthorizedUpgrade` (r:0 w:1)
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
fn authorize_upgrade() -> Weight {
// Proof Size summary in bytes:
// Measured: `0`
// Estimated: `0`
// Minimum execution time: 12_466_000 picoseconds.
Weight::from_parts(15_570_000, 0)
.saturating_add(RocksDbWeight::get().writes(1_u64))
}
/// Storage: `System::AuthorizedUpgrade` (r:1 w:1)
/// Proof: `System::AuthorizedUpgrade` (`max_values`: Some(1), `max_size`: Some(33), added: 528, mode: `MaxEncodedLen`)
/// Storage: `MultiBlockMigrations::Cursor` (r:1 w:0)
/// Proof: `MultiBlockMigrations::Cursor` (`max_values`: Some(1), `max_size`: Some(65550), added: 66045, mode: `MaxEncodedLen`)
/// Storage: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
/// Proof: UNKNOWN KEY `0x3a636f6465` (r:0 w:1)
fn apply_authorized_upgrade() -> Weight {
// Proof Size summary in bytes:
// Measured: `21`
// Estimated: `67035`
// Minimum execution time: 163_673_542_000 picoseconds.
Weight::from_parts(166_858_158_000, 67035)
.saturating_add(RocksDbWeight::get().reads(2_u64))
.saturating_add(RocksDbWeight::get().writes(2_u64))
}
}