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
@@ -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))
}
}