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:
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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>,
|
||||
>;
|
||||
@@ -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());
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
});
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user