mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-01 05:41:03 +00:00
Add BEEFY capabilities to Westend and Kusama (#7591)
* runtime: add BEEFY and MMR to Westend Signed-off-by: Adrian Catangiu <adrian@parity.io> * runtime: add BEEFY and MMR to Kusama Signed-off-by: Adrian Catangiu <adrian@parity.io> * node/service: enable BEEFY for Westend and Kusama Signed-off-by: Adrian Catangiu <adrian@parity.io> * node/service: regenerate genesis keys for westend-native and kusama-native Since these keys are only used for development/local chains, also publish the secret seeds used to generate the public keys, so that developers can recover/generate the private key pairs if needed. Signed-off-by: Adrian Catangiu <adrian@parity.io> * runtime: add session keys migration to add BEEFY to Westend and Kusama * runtime: fix migration * fix try-runtime build * cargo fmt * fix parachains slashing benchmark * address review comments * Apply suggestions from code review Co-authored-by: Bastian Köcher <git@kchr.de> * runtime: fix session keys migration --------- Signed-off-by: Adrian Catangiu <adrian@parity.io> Co-authored-by: parity-processbot <> Co-authored-by: Bastian Köcher <git@kchr.de>
This commit is contained in:
@@ -20,11 +20,13 @@ smallvec = "1.8.0"
|
||||
authority-discovery-primitives = { package = "sp-authority-discovery", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
babe-primitives = { package = "sp-consensus-babe", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
beefy-primitives = { package = "sp-consensus-beefy", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
binary-merkle-tree = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
kusama-runtime-constants = { package = "kusama-runtime-constants", path = "./constants", default-features = false }
|
||||
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
offchain-primitives = { package = "sp-offchain", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
sp-std = { package = "sp-std", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
sp-application-crypto = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
sp-arithmetic = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
sp-mmr-primitives = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
@@ -42,6 +44,8 @@ pallet-authorship = { git = "https://github.com/paritytech/substrate", branch =
|
||||
pallet-babe = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-bags-list = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-balances = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-beefy = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-beefy-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-bounties = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-child-bounties = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-transaction-payment = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
@@ -61,6 +65,7 @@ pallet-im-online = { git = "https://github.com/paritytech/substrate", branch = "
|
||||
pallet-indices = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-membership = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-message-queue = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-mmr = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-multisig = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-nomination-pools = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
pallet-offences = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||
@@ -144,6 +149,8 @@ std = [
|
||||
"pallet-authorship/std",
|
||||
"pallet-bags-list/std",
|
||||
"pallet-balances/std",
|
||||
"pallet-beefy/std",
|
||||
"pallet-beefy-mmr/std",
|
||||
"pallet-bounties/std",
|
||||
"pallet-child-bounties/std",
|
||||
"pallet-transaction-payment/std",
|
||||
@@ -161,6 +168,7 @@ std = [
|
||||
"pallet-indices/std",
|
||||
"pallet-membership/std",
|
||||
"pallet-message-queue/std",
|
||||
"pallet-mmr/std",
|
||||
"pallet-multisig/std",
|
||||
"pallet-nomination-pools/std",
|
||||
"pallet-nomination-pools-runtime-api/std",
|
||||
@@ -184,6 +192,7 @@ std = [
|
||||
"pallet-whitelist/std",
|
||||
"pallet-babe/std",
|
||||
"pallet-xcm/std",
|
||||
"sp-application-crypto/std",
|
||||
"sp-mmr-primitives/std",
|
||||
"sp-runtime/std",
|
||||
"sp-staking/std",
|
||||
@@ -265,6 +274,8 @@ try-runtime = [
|
||||
"pallet-authorship/try-runtime",
|
||||
"pallet-bags-list/try-runtime",
|
||||
"pallet-balances/try-runtime",
|
||||
"pallet-beefy/try-runtime",
|
||||
"pallet-beefy-mmr/try-runtime",
|
||||
"pallet-bounties/try-runtime",
|
||||
"pallet-child-bounties/try-runtime",
|
||||
"pallet-transaction-payment/try-runtime",
|
||||
@@ -281,6 +292,7 @@ try-runtime = [
|
||||
"pallet-indices/try-runtime",
|
||||
"pallet-membership/try-runtime",
|
||||
"pallet-message-queue/try-runtime",
|
||||
"pallet-mmr/try-runtime",
|
||||
"pallet-multisig/try-runtime",
|
||||
"pallet-nomination-pools/try-runtime",
|
||||
"pallet-offences/try-runtime",
|
||||
|
||||
@@ -52,7 +52,10 @@ use runtime_parachains::{
|
||||
};
|
||||
|
||||
use authority_discovery_primitives::AuthorityId as AuthorityDiscoveryId;
|
||||
use beefy_primitives::ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature};
|
||||
use beefy_primitives::{
|
||||
ecdsa_crypto::{AuthorityId as BeefyId, Signature as BeefySignature},
|
||||
mmr::{BeefyDataProvider, MmrLeafVersion},
|
||||
};
|
||||
use frame_election_provider_support::{
|
||||
bounds::ElectionBoundsBuilder, generate_solution_type, onchain, NposSolution,
|
||||
SequentialPhragmen,
|
||||
@@ -71,13 +74,12 @@ use pallet_grandpa::{fg_primitives, AuthorityId as GrandpaId};
|
||||
use pallet_im_online::sr25519::AuthorityId as ImOnlineId;
|
||||
use pallet_session::historical as session_historical;
|
||||
use pallet_transaction_payment::{CurrencyAdapter, FeeDetails, RuntimeDispatchInfo};
|
||||
use sp_core::{ConstU128, OpaqueMetadata};
|
||||
use sp_mmr_primitives as mmr;
|
||||
use sp_core::{ConstU128, OpaqueMetadata, H256};
|
||||
use sp_runtime::{
|
||||
create_runtime_str, generic, impl_opaque_keys,
|
||||
traits::{
|
||||
AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto, Extrinsic as ExtrinsicT,
|
||||
OpaqueKeys, SaturatedConversion, Verify,
|
||||
Keccak256, OpaqueKeys, SaturatedConversion, Verify,
|
||||
},
|
||||
transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity},
|
||||
ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill,
|
||||
@@ -316,6 +318,81 @@ impl pallet_balances::Config for Runtime {
|
||||
type MaxHolds = ConstU32<1>;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub BeefySetIdSessionEntries: u32 = BondingDuration::get() * SessionsPerEra::get();
|
||||
}
|
||||
|
||||
impl pallet_beefy::Config for Runtime {
|
||||
type BeefyId = BeefyId;
|
||||
type MaxAuthorities = MaxAuthorities;
|
||||
type MaxNominators = MaxNominatorRewardedPerValidator;
|
||||
type MaxSetIdSessionEntries = BeefySetIdSessionEntries;
|
||||
type OnNewValidatorSet = BeefyMmrLeaf;
|
||||
type WeightInfo = ();
|
||||
type KeyOwnerProof = <Historical as KeyOwnerProofSystem<(KeyTypeId, BeefyId)>>::Proof;
|
||||
type EquivocationReportSystem =
|
||||
pallet_beefy::EquivocationReportSystem<Self, Offences, Historical, ReportLongevity>;
|
||||
}
|
||||
|
||||
impl pallet_mmr::Config for Runtime {
|
||||
const INDEXING_PREFIX: &'static [u8] = mmr::INDEXING_PREFIX;
|
||||
type Hashing = Keccak256;
|
||||
type OnNewRoot = pallet_beefy_mmr::DepositBeefyDigest<Runtime>;
|
||||
type WeightInfo = ();
|
||||
type LeafData = pallet_beefy_mmr::Pallet<Runtime>;
|
||||
}
|
||||
|
||||
/// MMR helper types.
|
||||
mod mmr {
|
||||
use super::Runtime;
|
||||
pub use pallet_mmr::primitives::*;
|
||||
|
||||
pub type Leaf = <<Runtime as pallet_mmr::Config>::LeafData as LeafDataProvider>::LeafData;
|
||||
pub type Hashing = <Runtime as pallet_mmr::Config>::Hashing;
|
||||
pub type Hash = <Hashing as sp_runtime::traits::Hash>::Output;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
/// Version of the produced MMR leaf.
|
||||
///
|
||||
/// The version consists of two parts;
|
||||
/// - `major` (3 bits)
|
||||
/// - `minor` (5 bits)
|
||||
///
|
||||
/// `major` should be updated only if decoding the previous MMR Leaf format from the payload
|
||||
/// is not possible (i.e. backward incompatible change).
|
||||
/// `minor` should be updated if fields are added to the previous MMR Leaf, which given SCALE
|
||||
/// encoding does not prevent old leafs from being decoded.
|
||||
///
|
||||
/// Hence we expect `major` to be changed really rarely (think never).
|
||||
/// See [`MmrLeafVersion`] type documentation for more details.
|
||||
pub LeafVersion: MmrLeafVersion = MmrLeafVersion::new(0, 0);
|
||||
}
|
||||
|
||||
/// A BEEFY data provider that merkelizes all the parachain heads at the current block
|
||||
/// (sorted by their parachain id).
|
||||
pub struct ParaHeadsRootProvider;
|
||||
impl BeefyDataProvider<H256> for ParaHeadsRootProvider {
|
||||
fn extra_data() -> H256 {
|
||||
let mut para_heads: Vec<(u32, Vec<u8>)> = Paras::parachains()
|
||||
.into_iter()
|
||||
.filter_map(|id| Paras::para_head(&id).map(|head| (id.into(), head.0)))
|
||||
.collect();
|
||||
para_heads.sort_by_key(|k| k.0);
|
||||
binary_merkle_tree::merkle_root::<mmr::Hashing, _>(
|
||||
para_heads.into_iter().map(|pair| pair.encode()),
|
||||
)
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl pallet_beefy_mmr::Config for Runtime {
|
||||
type LeafVersion = LeafVersion;
|
||||
type BeefyAuthorityToMerkleLeaf = pallet_beefy_mmr::BeefyEcdsaToEthereum;
|
||||
type LeafExtra = H256;
|
||||
type BeefyDataProvider = ParaHeadsRootProvider;
|
||||
}
|
||||
|
||||
parameter_types! {
|
||||
pub const TransactionByteFee: Balance = 10 * MILLICENTS;
|
||||
/// This value increases the priority of `Operational` transactions by adding
|
||||
@@ -347,6 +424,17 @@ impl pallet_authorship::Config for Runtime {
|
||||
type EventHandler = (Staking, ImOnline);
|
||||
}
|
||||
|
||||
impl_opaque_keys! {
|
||||
pub struct OldSessionKeys {
|
||||
pub grandpa: Grandpa,
|
||||
pub babe: Babe,
|
||||
pub im_online: ImOnline,
|
||||
pub para_validator: Initializer,
|
||||
pub para_assignment: ParaSessionInfo,
|
||||
pub authority_discovery: AuthorityDiscovery,
|
||||
}
|
||||
}
|
||||
|
||||
impl_opaque_keys! {
|
||||
pub struct SessionKeys {
|
||||
pub grandpa: Grandpa,
|
||||
@@ -355,6 +443,33 @@ impl_opaque_keys! {
|
||||
pub para_validator: Initializer,
|
||||
pub para_assignment: ParaSessionInfo,
|
||||
pub authority_discovery: AuthorityDiscovery,
|
||||
pub beefy: Beefy,
|
||||
}
|
||||
}
|
||||
|
||||
// remove this when removing `OldSessionKeys`
|
||||
fn transform_session_keys(v: AccountId, old: OldSessionKeys) -> SessionKeys {
|
||||
SessionKeys {
|
||||
grandpa: old.grandpa,
|
||||
babe: old.babe,
|
||||
im_online: old.im_online,
|
||||
para_validator: old.para_validator,
|
||||
para_assignment: old.para_assignment,
|
||||
authority_discovery: old.authority_discovery,
|
||||
beefy: {
|
||||
// From Session::upgrade_keys():
|
||||
//
|
||||
// Care should be taken that the raw versions of the
|
||||
// added keys are unique for every `ValidatorId, KeyTypeId` combination.
|
||||
// This is an invariant that the session pallet typically maintains internally.
|
||||
//
|
||||
// So, produce a dummy value that's unique for the `ValidatorId, KeyTypeId` combination.
|
||||
let mut id: BeefyId = sp_application_crypto::ecdsa::Public::from_raw([0u8; 33]).into();
|
||||
let id_raw: &mut [u8] = id.as_mut();
|
||||
id_raw[1..33].copy_from_slice(v.as_ref());
|
||||
id_raw[0..4].copy_from_slice(b"beef");
|
||||
id
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1390,6 +1505,14 @@ construct_runtime! {
|
||||
Staking: pallet_staking::{Pallet, Call, Storage, Config<T>, Event<T>} = 6,
|
||||
Offences: pallet_offences::{Pallet, Storage, Event} = 7,
|
||||
Historical: session_historical::{Pallet} = 34,
|
||||
|
||||
// BEEFY Bridges support.
|
||||
Beefy: pallet_beefy::{Pallet, Call, Storage, Config<T>, ValidateUnsigned} = 200,
|
||||
// MMR leaf construction must be before session in order to have leaf contents
|
||||
// refer to block<N-1> consistently. see substrate issue #11797 for details.
|
||||
Mmr: pallet_mmr::{Pallet, Storage} = 201,
|
||||
BeefyMmrLeaf: pallet_beefy_mmr::{Pallet, Storage} = 202,
|
||||
|
||||
Session: pallet_session::{Pallet, Call, Storage, Event, Config<T>} = 8,
|
||||
Grandpa: pallet_grandpa::{Pallet, Call, Storage, Config<T>, Event, ValidateUnsigned} = 10,
|
||||
ImOnline: pallet_im_online::{Pallet, Call, Storage, Event<T>, ValidateUnsigned, Config<T>} = 11,
|
||||
@@ -1527,7 +1650,7 @@ impl Get<Perbill> for NominationPoolsMigrationV4OldPallet {
|
||||
///
|
||||
/// This contains the combined migrations of the last 10 releases. It allows to skip runtime
|
||||
/// upgrades in case governance decides to do so. THE ORDER IS IMPORTANT.
|
||||
pub type Migrations = (migrations::Unreleased,);
|
||||
pub type Migrations = migrations::Unreleased;
|
||||
|
||||
/// The runtime migrations per release.
|
||||
#[allow(deprecated, missing_docs)]
|
||||
@@ -1579,6 +1702,16 @@ pub mod migrations {
|
||||
type PalletName = TipsPalletName;
|
||||
}
|
||||
|
||||
/// Upgrade Session keys to include BEEFY key.
|
||||
/// When this is removed, should also remove `OldSessionKeys`.
|
||||
pub struct UpgradeSessionKeys;
|
||||
impl frame_support::traits::OnRuntimeUpgrade for UpgradeSessionKeys {
|
||||
fn on_runtime_upgrade() -> Weight {
|
||||
Session::upgrade_keys::<OldSessionKeys, _>(transform_session_keys);
|
||||
Perbill::from_percent(50) * BlockWeights::get().max_block
|
||||
}
|
||||
}
|
||||
|
||||
/// Unreleased migrations. Add new ones here:
|
||||
pub type Unreleased = (
|
||||
init_state_migration::InitMigrate,
|
||||
@@ -1606,6 +1739,8 @@ pub mod migrations {
|
||||
frame_support::migrations::RemovePallet<TechnicalMembershipPalletName, <Runtime as frame_system::Config>::DbWeight>,
|
||||
frame_support::migrations::RemovePallet<TipsPalletName, <Runtime as frame_system::Config>::DbWeight>,
|
||||
|
||||
// Upgrade SessionKeys to include BEEFY key
|
||||
UpgradeSessionKeys,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1886,62 +2021,94 @@ sp_api::impl_runtime_apis! {
|
||||
|
||||
impl beefy_primitives::BeefyApi<Block, BeefyId> for Runtime {
|
||||
fn beefy_genesis() -> Option<BlockNumber> {
|
||||
// dummy implementation due to lack of BEEFY pallet.
|
||||
None
|
||||
Beefy::genesis_block()
|
||||
}
|
||||
|
||||
fn validator_set() -> Option<beefy_primitives::ValidatorSet<BeefyId>> {
|
||||
// dummy implementation due to lack of BEEFY pallet.
|
||||
None
|
||||
Beefy::validator_set()
|
||||
}
|
||||
|
||||
fn submit_report_equivocation_unsigned_extrinsic(
|
||||
_equivocation_proof: beefy_primitives::EquivocationProof<
|
||||
equivocation_proof: beefy_primitives::EquivocationProof<
|
||||
BlockNumber,
|
||||
BeefyId,
|
||||
BeefySignature,
|
||||
>,
|
||||
_key_owner_proof: beefy_primitives::OpaqueKeyOwnershipProof,
|
||||
key_owner_proof: beefy_primitives::OpaqueKeyOwnershipProof,
|
||||
) -> Option<()> {
|
||||
None
|
||||
let key_owner_proof = key_owner_proof.decode()?;
|
||||
|
||||
Beefy::submit_unsigned_equivocation_report(
|
||||
equivocation_proof,
|
||||
key_owner_proof,
|
||||
)
|
||||
}
|
||||
|
||||
fn generate_key_ownership_proof(
|
||||
_set_id: beefy_primitives::ValidatorSetId,
|
||||
_authority_id: BeefyId,
|
||||
authority_id: BeefyId,
|
||||
) -> Option<beefy_primitives::OpaqueKeyOwnershipProof> {
|
||||
None
|
||||
use parity_scale_codec::Encode;
|
||||
|
||||
Historical::prove((beefy_primitives::KEY_TYPE, authority_id))
|
||||
.map(|p| p.encode())
|
||||
.map(beefy_primitives::OpaqueKeyOwnershipProof::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl mmr::MmrApi<Block, Hash, BlockNumber> for Runtime {
|
||||
fn mmr_root() -> Result<Hash, mmr::Error> {
|
||||
Err(mmr::Error::PalletNotIncluded)
|
||||
fn mmr_root() -> Result<mmr::Hash, mmr::Error> {
|
||||
Ok(Mmr::mmr_root())
|
||||
}
|
||||
|
||||
fn mmr_leaf_count() -> Result<mmr::LeafIndex, mmr::Error> {
|
||||
Err(mmr::Error::PalletNotIncluded)
|
||||
Ok(Mmr::mmr_leaves())
|
||||
}
|
||||
|
||||
fn generate_proof(
|
||||
_block_numbers: Vec<BlockNumber>,
|
||||
_best_known_block_number: Option<BlockNumber>,
|
||||
) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::Proof<Hash>), mmr::Error> {
|
||||
Err(mmr::Error::PalletNotIncluded)
|
||||
block_numbers: Vec<BlockNumber>,
|
||||
best_known_block_number: Option<BlockNumber>,
|
||||
) -> Result<(Vec<mmr::EncodableOpaqueLeaf>, mmr::Proof<mmr::Hash>), mmr::Error> {
|
||||
Mmr::generate_proof(block_numbers, best_known_block_number).map(
|
||||
|(leaves, proof)| {
|
||||
(
|
||||
leaves
|
||||
.into_iter()
|
||||
.map(|leaf| mmr::EncodableOpaqueLeaf::from_leaf(&leaf))
|
||||
.collect(),
|
||||
proof,
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn verify_proof(_leaves: Vec<mmr::EncodableOpaqueLeaf>, _proof: mmr::Proof<Hash>)
|
||||
fn verify_proof(leaves: Vec<mmr::EncodableOpaqueLeaf>, proof: mmr::Proof<mmr::Hash>)
|
||||
-> Result<(), mmr::Error>
|
||||
{
|
||||
Err(mmr::Error::PalletNotIncluded)
|
||||
let leaves = leaves.into_iter().map(|leaf|
|
||||
leaf.into_opaque_leaf()
|
||||
.try_decode()
|
||||
.ok_or(mmr::Error::Verify)).collect::<Result<Vec<mmr::Leaf>, mmr::Error>>()?;
|
||||
Mmr::verify_leaves(leaves, proof)
|
||||
}
|
||||
|
||||
fn verify_proof_stateless(
|
||||
_root: Hash,
|
||||
_leaves: Vec<mmr::EncodableOpaqueLeaf>,
|
||||
_proof: mmr::Proof<Hash>
|
||||
root: mmr::Hash,
|
||||
leaves: Vec<mmr::EncodableOpaqueLeaf>,
|
||||
proof: mmr::Proof<mmr::Hash>
|
||||
) -> Result<(), mmr::Error> {
|
||||
Err(mmr::Error::PalletNotIncluded)
|
||||
let nodes = leaves.into_iter().map(|leaf|mmr::DataOrHash::Data(leaf.into_opaque_leaf())).collect();
|
||||
pallet_mmr::verify_leaves_proof::<mmr::Hashing, _>(root, nodes, proof)
|
||||
}
|
||||
}
|
||||
|
||||
impl pallet_beefy_mmr::BeefyMmrApi<Block, Hash> for RuntimeApi {
|
||||
fn authority_set_proof() -> beefy_primitives::mmr::BeefyAuthoritySet<Hash> {
|
||||
BeefyMmrLeaf::authority_set_proof()
|
||||
}
|
||||
|
||||
fn next_authority_set_proof() -> beefy_primitives::mmr::BeefyNextAuthoritySet<Hash> {
|
||||
BeefyMmrLeaf::next_authority_set_proof()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,7 @@ fn nominator_limit() {
|
||||
|
||||
#[test]
|
||||
fn call_size() {
|
||||
RuntimeCall::assert_size_under(230);
|
||||
RuntimeCall::assert_size_under(256);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user