diff --git a/CHANGELOG.md b/CHANGELOG.md index b46129eb2e..f9212cc279 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Pallet ranked collective: max member count per rank ([runtimes#381](https://github.com/polkadot-fellows/runtimes/pull/381), [SDK v1.14 #4807](https://github.com/paritytech/polkadot-sdk/pull/4807)). - All runtimes: XcmPaymentApi and DryRunApi ([polkadot-fellows/runtimes#380](https://github.com/polkadot-fellows/runtimes/pull/380)) - All runtimes: add `LocationToAccountApi` ([polkadot-fellows/runtimes#413](https://github.com/polkadot-fellows/runtimes/pull/413)) +- Enable Agile Coretime on Polkadot ([polkadot-fellows/runtimes#401](https://github.com/polkadot-fellows/runtimes/pull/401)) #### From [#322](https://github.com/polkadot-fellows/runtimes/pull/322): diff --git a/relay/polkadot/constants/src/lib.rs b/relay/polkadot/constants/src/lib.rs index 58254291f5..3449d98884 100644 --- a/relay/polkadot/constants/src/lib.rs +++ b/relay/polkadot/constants/src/lib.rs @@ -131,9 +131,22 @@ pub mod system_parachain { pub const BRIDGE_HUB_ID: u32 = 1002; /// People parachain ID. pub const PEOPLE_ID: u32 = 1004; + /// Coretime Chain ID. + pub const BROKER_ID: u32 = 1005; // System parachains from Polkadot point of view. pub type SystemParachains = IsChildSystemParachain; + + /// Coretime constants + pub mod coretime { + /// Coretime timeslice period in blocks + /// WARNING: This constant is used across chains, so additional care should be taken + /// when changing it. + #[cfg(feature = "fast-runtime")] + pub const TIMESLICE_PERIOD: u32 = 20; + #[cfg(not(feature = "fast-runtime"))] + pub const TIMESLICE_PERIOD: u32 = 80; + } } /// Polkadot Treasury pallet instance. diff --git a/relay/polkadot/src/lib.rs b/relay/polkadot/src/lib.rs index 5a2be9c7be..1e44c81fc7 100644 --- a/relay/polkadot/src/lib.rs +++ b/relay/polkadot/src/lib.rs @@ -26,15 +26,16 @@ use polkadot_runtime_common::{ impls::{ DealWithFees, LocatableAssetConverter, VersionedLocatableAsset, VersionedLocationConverter, }, - paras_registrar, prod_or_fast, slots, BlockHashCount, BlockLength, CurrencyToVote, - SlowAdjustingFeeUpdate, + paras_registrar, prod_or_fast, slots, + traits::OnSwap, + BlockHashCount, BlockLength, CurrencyToVote, SlowAdjustingFeeUpdate, }; use runtime_parachains::{ - assigner_parachains as parachains_assigner_parachains, - configuration as parachains_configuration, + assigner_coretime as parachains_assigner_coretime, + assigner_on_demand as parachains_assigner_on_demand, configuration as parachains_configuration, configuration::ActiveConfigHrmpChannelSizeAndCapacityRatio, - disputes as parachains_disputes, + coretime, disputes as parachains_disputes, disputes::slashing as parachains_slashing, dmp as parachains_dmp, hrmp as parachains_hrmp, inclusion as parachains_inclusion, inclusion::{AggregateMessageOrigin, UmpQueueId}, @@ -62,10 +63,13 @@ use frame_support::{ parameter_types, traits::{ fungible::HoldConsideration, ConstU32, EitherOf, EitherOfDiverse, Everything, Get, - InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, PrivilegeCmp, ProcessMessage, - ProcessMessageError, WithdrawReasons, + InstanceFilter, KeyOwnerProofSystem, LinearStoragePrice, OnRuntimeUpgrade, PrivilegeCmp, + ProcessMessage, ProcessMessageError, WithdrawReasons, + }, + weights::{ + constants::{WEIGHT_PROOF_SIZE_PER_KB, WEIGHT_REF_TIME_PER_MICROS}, + ConstantMultiplier, WeightMeter, WeightToFee as _, }, - weights::{ConstantMultiplier, WeightMeter, WeightToFee as _}, PalletId, }; use frame_system::EnsureRoot; @@ -86,8 +90,9 @@ use sp_runtime::{ curve::PiecewiseLinear, generic, impl_opaque_keys, traits::{ - AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto, Extrinsic as ExtrinsicT, - IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, Verify, + AccountIdConversion, AccountIdLookup, BlakeTwo256, Block as BlockT, ConvertInto, + Extrinsic as ExtrinsicT, IdentityLookup, Keccak256, OpaqueKeys, SaturatedConversion, + Verify, }, transaction_validity::{TransactionPriority, TransactionSource, TransactionValidity}, ApplyExtrinsicResult, FixedU128, KeyTypeId, Perbill, Percent, Permill, RuntimeDebug, @@ -117,7 +122,9 @@ pub use pallet_timestamp::Call as TimestampCall; pub use sp_runtime::BuildStorage; /// Constant values used within the runtime. -use polkadot_runtime_constants::{currency::*, fee::*, time::*, TREASURY_PALLET_ID}; +use polkadot_runtime_constants::{ + currency::*, fee::*, system_parachain, time::*, TREASURY_PALLET_ID, +}; // Weights used in the runtime. mod weights; @@ -1228,7 +1235,7 @@ impl parachains_paras::Config for Runtime { type QueueFootprinter = ParaInclusion; type NextSessionRotation = Babe; type OnNewHead = Registrar; - type AssignCoretime = (); + type AssignCoretime = CoretimeAssignmentProvider; } parameter_types! { @@ -1309,16 +1316,66 @@ impl parachains_paras_inherent::Config for Runtime { } impl parachains_scheduler::Config for Runtime { - type AssignmentProvider = ParaAssignmentProvider; + // If you change this, make sure the `Assignment` type of the new provider is binary compatible, + // otherwise provide a migration. + type AssignmentProvider = CoretimeAssignmentProvider; +} + +parameter_types! { + pub const BrokerId: u32 = system_parachain::BROKER_ID; + pub const BrokerPalletId: PalletId = PalletId(*b"py/broke"); + pub MaxXcmTransactWeight: Weight = Weight::from_parts( + 250 * WEIGHT_REF_TIME_PER_MICROS, + 20 * WEIGHT_PROOF_SIZE_PER_KB + ); +} + +pub struct BrokerPot; +impl Get for BrokerPot { + fn get() -> InteriorLocation { + Junction::AccountId32 { network: None, id: BrokerPalletId::get().into_account_truncating() } + .into() + } +} + +impl coretime::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type BrokerId = BrokerId; + type WeightInfo = weights::runtime_parachains_coretime::WeightInfo; + type SendXcm = crate::xcm_config::XcmRouter; + type MaxXcmTransactWeight = MaxXcmTransactWeight; + type BrokerPotLocation = BrokerPot; + type AssetTransactor = crate::xcm_config::LocalAssetTransactor; + type AccountToLocation = xcm_builder::AliasesIntoAccountId32< + xcm_config::ThisNetwork, + ::AccountId, + >; } -impl parachains_assigner_parachains::Config for Runtime {} +parameter_types! { + pub const OnDemandTrafficDefaultValue: FixedU128 = FixedU128::from_u32(1); + pub const MaxHistoricalRevenue: BlockNumber = 2 * system_parachain::coretime::TIMESLICE_PERIOD; + pub const OnDemandPalletId: PalletId = PalletId(*b"py/ondmd"); +} + +impl parachains_assigner_on_demand::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type TrafficDefaultValue = OnDemandTrafficDefaultValue; + type WeightInfo = weights::runtime_parachains_assigner_on_demand::WeightInfo; + type MaxHistoricalRevenue = MaxHistoricalRevenue; + type PalletId = OnDemandPalletId; +} + +impl parachains_assigner_coretime::Config for Runtime {} impl parachains_initializer::Config for Runtime { type Randomness = pallet_babe::RandomnessFromOneEpochAgo; type ForceOrigin = EnsureRoot; type WeightInfo = weights::runtime_parachains_initializer::WeightInfo; - type CoretimeOnNewSession = (); + type CoretimeOnNewSession = Coretime; } impl parachains_disputes::Config for Runtime { @@ -1356,7 +1413,7 @@ impl paras_registrar::Config for Runtime { type RuntimeOrigin = RuntimeOrigin; type RuntimeEvent = RuntimeEvent; type Currency = Balances; - type OnSwap = (Crowdloan, Slots); + type OnSwap = (Crowdloan, Slots, SwapLeases); type ParaDeposit = ParaDeposit; type DataDepositPerByte = ParaDataByteDeposit; type WeightInfo = weights::polkadot_runtime_common_paras_registrar::WeightInfo; @@ -1506,6 +1563,13 @@ impl pallet_asset_rate::Config for Runtime { #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = polkadot_runtime_common::impls::benchmarks::AssetRateArguments; } +/// Notify the `coretime` pallet when a lease swap occurs. +pub struct SwapLeases; +impl OnSwap for SwapLeases { + fn on_swap(one: ParaId, other: ParaId) { + coretime::Pallet::::on_legacy_lease_swap(one, other); + } +} construct_runtime! { pub enum Runtime @@ -1588,13 +1652,15 @@ construct_runtime! { ParaSessionInfo: parachains_session_info = 61, ParasDisputes: parachains_disputes = 62, ParasSlashing: parachains_slashing = 63, - ParaAssignmentProvider: parachains_assigner_parachains = 64, + OnDemand: parachains_assigner_on_demand = 64, + CoretimeAssignmentProvider: parachains_assigner_coretime = 65, // Parachain Onboarding Pallets. Start indices at 70 to leave room. Registrar: paras_registrar = 70, Slots: slots = 71, Auctions: auctions = 72, Crowdloan: crowdloan = 73, + Coretime: coretime = 74, // State trie migration pallet, only temporary. StateTrieMigration: pallet_state_trie_migration = 98, @@ -1865,8 +1931,61 @@ pub type Migrations = (migrations::Unreleased, migrations::Permanent); /// The runtime migrations per release. #[allow(deprecated, missing_docs)] pub mod migrations { + use polkadot_runtime_common::traits::Leaser; + use super::*; + pub struct GetLegacyLeaseImpl; + impl coretime::migration::GetLegacyLease for GetLegacyLeaseImpl { + fn get_parachain_lease_in_blocks(para: ParaId) -> Option { + let now = frame_system::Pallet::::block_number(); + let lease = slots::Leases::::get(para); + if lease.is_empty() { + return None; + } + let (index, _) = + as Leaser>::lease_period_index(now)?; + Some(index.saturating_add(lease.len() as u32).saturating_mul(LeasePeriod::get())) + } + } + + /// Cancel all ongoing auctions. + /// + /// Any leases that come into existence after coretime was launched will not be served. Yet, + /// any ongoing auctions must be cancelled. + /// + /// Safety: + /// + /// - After coretime is launched, there are no auctions anymore. So if this forgotten to + /// be removed after the runtime upgrade, running this again on the next one is harmless. + /// - I am assuming scheduler `TaskName`s are unique, so removal of the scheduled entry + /// multiple times should also be fine. + pub struct CancelAuctions; + impl OnRuntimeUpgrade for CancelAuctions { + fn on_runtime_upgrade() -> Weight { + if let Err(err) = Auctions::cancel_auction(frame_system::RawOrigin::Root.into()) { + log::debug!(target: "runtime", "Cancelling auctions failed: {:?}", err); + } + // Cancel scheduled auction as well: + if let Err(err) = Scheduler::cancel_named( + pallet_custom_origins::Origin::AuctionAdmin.into(), + [ + 0x87, 0xa8, 0x71, 0xb4, 0xd6, 0x21, 0xf0, 0xb9, 0x73, 0x47, 0x5a, 0xaf, 0xcc, + 0x32, 0x61, 0x0b, 0xd7, 0x68, 0x8f, 0x15, 0x02, 0x33, 0x8a, 0xcd, 0x00, 0xee, + 0x48, 0x8a, 0xc3, 0x62, 0x0f, 0x4c, + ], + ) { + log::debug!(target: "runtime", "Cancelling scheduled auctions failed: {:?}", err); + } + use pallet_scheduler::WeightInfo as _; + use polkadot_runtime_common::auctions::WeightInfo as _; + weights::polkadot_runtime_common_auctions::WeightInfo::::cancel_auction() + .saturating_add(weights::pallet_scheduler::WeightInfo::::cancel_named( + ::MaxScheduledPerBlock::get(), + )) + } + } + /// Unreleased migrations. Add new ones here: pub type Unreleased = ( parachains_configuration::migration::v12::MigrateToV12, @@ -1881,6 +2000,13 @@ pub mod migrations { ::DbWeight, >, clear_judgement_proxies::Migration, + // Migrate from legacy lease to coretime. Needs to run after configuration v11 + coretime::migration::MigrateToCoretime< + Runtime, + crate::xcm_config::XcmRouter, + GetLegacyLeaseImpl, + >, + CancelAuctions, ); /// Migrations/checks that do not need to be versioned and can run on every update. @@ -1920,6 +2046,8 @@ mod benches { [runtime_parachains::initializer, Initializer] [runtime_parachains::paras, Paras] [runtime_parachains::paras_inherent, ParaInherent] + [runtime_parachains::assigner_on_demand, OnDemand] + [runtime_parachains::coretime, Coretime] // Substrate [pallet_bags_list, VoterList] [pallet_balances, Balances] diff --git a/relay/polkadot/src/weights/mod.rs b/relay/polkadot/src/weights/mod.rs index 61ab39cef5..190b254db2 100644 --- a/relay/polkadot/src/weights/mod.rs +++ b/relay/polkadot/src/weights/mod.rs @@ -46,7 +46,9 @@ pub mod polkadot_runtime_common_claims; pub mod polkadot_runtime_common_crowdloan; pub mod polkadot_runtime_common_paras_registrar; pub mod polkadot_runtime_common_slots; +pub mod runtime_parachains_assigner_on_demand; pub mod runtime_parachains_configuration; +pub mod runtime_parachains_coretime; pub mod runtime_parachains_disputes; pub mod runtime_parachains_disputes_slashing; pub mod runtime_parachains_hrmp; diff --git a/relay/polkadot/src/weights/runtime_parachains_assigner_on_demand.rs b/relay/polkadot/src/weights/runtime_parachains_assigner_on_demand.rs new file mode 100644 index 0000000000..2b17dc2b7a --- /dev/null +++ b/relay/polkadot/src/weights/runtime_parachains_assigner_on_demand.rs @@ -0,0 +1,89 @@ +// Copyright (C) Parity Technologies and the various Polkadot contributors, see Contributions.md +// for a list of specific contributors. +// 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 `runtime_parachains::assigner_on_demand` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ggwpez-ref-hw`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("./kusama-chain-spec.json")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/polkadot +// benchmark +// pallet +// --chain=./kusama-chain-spec.json +// --steps=50 +// --repeat=20 +// --pallet=runtime_parachains::assigner_on_demand +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./kusama-weights/ +// --header=./file_header.txt + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `runtime_parachains::assigner_on_demand`. +pub struct WeightInfo(PhantomData); +impl runtime_parachains::assigner_on_demand::WeightInfo for WeightInfo { + /// Storage: `OnDemandAssignmentProvider::SpotTraffic` (r:1 w:0) + /// Proof: `OnDemandAssignmentProvider::SpotTraffic` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::OnDemandQueue` (r:1 w:1) + /// Proof: `OnDemandAssignmentProvider::OnDemandQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `s` is `[1, 9999]`. + fn place_order_keep_alive(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `330 + s * (4 ±0)` + // Estimated: `3795 + s * (4 ±0)` + // Minimum execution time: 25_690_000 picoseconds. + Weight::from_parts(21_232_109, 0) + .saturating_add(Weight::from_parts(0, 3795)) + // Standard Error: 137 + .saturating_add(Weight::from_parts(13_682, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) + } + /// Storage: `OnDemandAssignmentProvider::SpotTraffic` (r:1 w:0) + /// Proof: `OnDemandAssignmentProvider::SpotTraffic` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Paras::ParaLifecycles` (r:1 w:0) + /// Proof: `Paras::ParaLifecycles` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `OnDemandAssignmentProvider::OnDemandQueue` (r:1 w:1) + /// Proof: `OnDemandAssignmentProvider::OnDemandQueue` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// The range of component `s` is `[1, 9999]`. + fn place_order_allow_death(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `330 + s * (4 ±0)` + // Estimated: `3795 + s * (4 ±0)` + // Minimum execution time: 25_648_000 picoseconds. + Weight::from_parts(21_383_327, 0) + .saturating_add(Weight::from_parts(0, 3795)) + // Standard Error: 136 + .saturating_add(Weight::from_parts(13_662, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + .saturating_add(Weight::from_parts(0, 4).saturating_mul(s.into())) + } +} diff --git a/relay/polkadot/src/weights/runtime_parachains_coretime.rs b/relay/polkadot/src/weights/runtime_parachains_coretime.rs new file mode 100644 index 0000000000..d141d340a3 --- /dev/null +++ b/relay/polkadot/src/weights/runtime_parachains_coretime.rs @@ -0,0 +1,92 @@ +// Copyright (C) Parity Technologies and the various Polkadot contributors, see Contributions.md +// for a list of specific contributors. +// 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 `runtime_parachains::coretime` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 32.0.0 +//! DATE: 2024-03-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `ggwpez-ref-hw`, CPU: `Intel(R) Xeon(R) CPU @ 2.60GHz` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("./kusama-chain-spec.json")`, DB CACHE: 1024 + +// Executed Command: +// ./target/production/polkadot +// benchmark +// pallet +// --chain=./kusama-chain-spec.json +// --steps=50 +// --repeat=20 +// --pallet=runtime_parachains::coretime +// --extrinsic=* +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=./kusama-weights/ +// --header=./file_header.txt + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `runtime_parachains::coretime`. +pub struct WeightInfo(PhantomData); +impl runtime_parachains::coretime::WeightInfo for WeightInfo { + fn request_revenue_at() -> Weight { + // Proof Size summary in bytes: + // Measured: `2963` + // Estimated: `6428` + // Minimum execution time: 36_613_000 picoseconds. + Weight::from_parts(37_637_000, 0) + .saturating_add(Weight::from_parts(0, 6428)) + .saturating_add(T::DbWeight::get().reads(6)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: `Configuration::PendingConfigs` (r:1 w:1) + /// Proof: `Configuration::PendingConfigs` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `Configuration::BypassConsistencyCheck` (r:1 w:0) + /// Proof: `Configuration::BypassConsistencyCheck` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + /// Storage: `ParasShared::CurrentSessionIndex` (r:1 w:0) + /// Proof: `ParasShared::CurrentSessionIndex` (`max_values`: Some(1), `max_size`: None, mode: `Measured`) + fn request_core_count() -> Weight { + // Proof Size summary in bytes: + // Measured: `155` + // Estimated: `1640` + // Minimum execution time: 7_573_000 picoseconds. + Weight::from_parts(7_884_000, 0) + .saturating_add(Weight::from_parts(0, 1640)) + .saturating_add(T::DbWeight::get().reads(3)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: `CoretimeAssignmentProvider::CoreDescriptors` (r:1 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreDescriptors` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// Storage: `CoretimeAssignmentProvider::CoreSchedules` (r:0 w:1) + /// Proof: `CoretimeAssignmentProvider::CoreSchedules` (`max_values`: None, `max_size`: None, mode: `Measured`) + /// The range of component `s` is `[1, 100]`. + fn assign_core(s: u32, ) -> Weight { + // Proof Size summary in bytes: + // Measured: `114` + // Estimated: `3579` + // Minimum execution time: 9_649_000 picoseconds. + Weight::from_parts(10_304_517, 0) + .saturating_add(Weight::from_parts(0, 3579)) + // Standard Error: 281 + .saturating_add(Weight::from_parts(13_646, 0).saturating_mul(s.into())) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(2)) + } +}