diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 647511373cc31146c4f1ffae2c8522ca115a5dd2..53f370a9306262825663246b46cde74a05de6cba 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -5581,7 +5581,9 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "pallet-balances", + "pallet-preimage", "pallet-scheduler", "parity-scale-codec", "scale-info", @@ -5899,6 +5901,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "pallet-balances", "parity-scale-codec", "scale-info", @@ -6065,6 +6068,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "pallet-balances", "parity-scale-codec", "scale-info", diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 4898312f9608f19ead8ae340204569fc8574d455..d10448cc2d183a1e944771b9e186fc849e7b266f 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -340,8 +340,6 @@ impl pallet_proxy::Config for Runtime { parameter_types! { pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * RuntimeBlockWeights::get().max_block; - // Retry a scheduled item every 10 blocks (1 minute) until the preimage exists. - pub const NoPreimagePostponement: Option<u32> = Some(10); } impl pallet_scheduler::Config for Runtime { @@ -351,11 +349,10 @@ impl pallet_scheduler::Config for Runtime { type RuntimeCall = RuntimeCall; type MaximumWeight = MaximumSchedulerWeight; type ScheduleOrigin = EnsureRoot<AccountId>; - type MaxScheduledPerBlock = ConstU32<50>; + type MaxScheduledPerBlock = ConstU32<512>; type WeightInfo = pallet_scheduler::weights::SubstrateWeight<Runtime>; type OriginPrivilegeCmp = EqualPrivilegeOnly; - type PreimageProvider = Preimage; - type NoPreimagePostponement = NoPreimagePostponement; + type Preimages = Preimage; } parameter_types! { @@ -370,7 +367,6 @@ impl pallet_preimage::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type ManagerOrigin = EnsureRoot<AccountId>; - type MaxSize = PreimageMaxSize; type BaseDeposit = PreimageBaseDeposit; type ByteDeposit = PreimageByteDeposit; } @@ -862,6 +858,7 @@ impl pallet_referenda::Config for Runtime { type UndecidingTimeout = UndecidingTimeout; type AlarmInterval = AlarmInterval; type Tracks = TracksInfo; + type Preimages = Preimage; } impl pallet_referenda::Config<pallet_referenda::Instance2> for Runtime { @@ -881,6 +878,7 @@ impl pallet_referenda::Config<pallet_referenda::Instance2> for Runtime { type UndecidingTimeout = UndecidingTimeout; type AlarmInterval = AlarmInterval; type Tracks = TracksInfo; + type Preimages = Preimage; } impl pallet_ranked_collective::Config for Runtime { @@ -909,7 +907,6 @@ parameter_types! { } impl pallet_democracy::Config for Runtime { - type Proposal = RuntimeCall; type RuntimeEvent = RuntimeEvent; type Currency = Balances; type EnactmentPeriod = EnactmentPeriod; @@ -949,14 +946,15 @@ impl pallet_democracy::Config for Runtime { // only do it once and it lasts only for the cool-off period. type VetoOrigin = pallet_collective::EnsureMember<AccountId, TechnicalCollective>; type CooloffPeriod = CooloffPeriod; - type PreimageByteDeposit = PreimageByteDeposit; - type OperationalPreimageOrigin = pallet_collective::EnsureMember<AccountId, CouncilCollective>; type Slash = Treasury; type Scheduler = Scheduler; type PalletsOrigin = OriginCaller; type MaxVotes = ConstU32<100>; type WeightInfo = pallet_democracy::weights::SubstrateWeight<Runtime>; type MaxProposals = MaxProposals; + type Preimages = Preimage; + type MaxDeposits = ConstU32<100>; + type MaxBlacklisted = ConstU32<100>; } parameter_types! { diff --git a/substrate/frame/bounties/src/lib.rs b/substrate/frame/bounties/src/lib.rs index b95940a2835ceb5d5f3b24b998d2508167ba8f4d..d947226f87fa066694f2a90f13deb749990b2534 100644 --- a/substrate/frame/bounties/src/lib.rs +++ b/substrate/frame/bounties/src/lib.rs @@ -819,7 +819,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { value: BalanceOf<T, I>, ) -> DispatchResult { let bounded_description: BoundedVec<_, _> = - description.try_into().map_err(|()| Error::<T, I>::ReasonTooBig)?; + description.try_into().map_err(|_| Error::<T, I>::ReasonTooBig)?; ensure!(value >= T::BountyValueMinimum::get(), Error::<T, I>::InvalidValue); let index = Self::bounty_count(); diff --git a/substrate/frame/contracts/src/storage.rs b/substrate/frame/contracts/src/storage.rs index c3fc0840d8649b509e4c164a909bd31bdd6509c7..cf10c3225c9202fe0db5def75e91994521ccd82d 100644 --- a/substrate/frame/contracts/src/storage.rs +++ b/substrate/frame/contracts/src/storage.rs @@ -340,7 +340,7 @@ where let queue: Vec<DeletedContract> = (0..T::DeletionQueueDepth::get()) .map(|_| DeletedContract { trie_id: TrieId::default() }) .collect(); - let bounded: BoundedVec<_, _> = queue.try_into().unwrap(); + let bounded: BoundedVec<_, _> = queue.try_into().map_err(|_| ()).unwrap(); <DeletionQueue<T>>::put(bounded); } } diff --git a/substrate/frame/conviction-voting/src/lib.rs b/substrate/frame/conviction-voting/src/lib.rs index 534941d6f7f66fd97f6e90d8b1a26e7b34104161..b876a9354ee59eab10e11479ecf913f432684a0c 100644 --- a/substrate/frame/conviction-voting/src/lib.rs +++ b/substrate/frame/conviction-voting/src/lib.rs @@ -400,7 +400,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { Err(i) => { votes .try_insert(i, (poll_index, vote)) - .map_err(|()| Error::<T, I>::MaxVotesReached)?; + .map_err(|_| Error::<T, I>::MaxVotesReached)?; }, } // Shouldn't be possible to fail, but we handle it gracefully. diff --git a/substrate/frame/democracy/Cargo.toml b/substrate/frame/democracy/Cargo.toml index f0ab3162c892b630887f0c470fd6ba4c0ab5ce40..e50d39ff769026eb5c626022cf460231a9143262 100644 --- a/substrate/frame/democracy/Cargo.toml +++ b/substrate/frame/democracy/Cargo.toml @@ -24,11 +24,13 @@ frame-system = { version = "4.0.0-dev", default-features = false, path = "../sys sp-io = { version = "6.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } +sp-core = { version = "6.0.0", default-features = false, path = "../../primitives/core" } +log = { version = "0.4.17", default-features = false } [dev-dependencies] pallet-balances = { version = "4.0.0-dev", path = "../balances" } pallet-scheduler = { version = "4.0.0-dev", path = "../scheduler" } -sp-core = { version = "6.0.0", path = "../../primitives/core" } +pallet-preimage = { version = "4.0.0-dev", path = "../preimage" } [features] default = ["std"] @@ -42,6 +44,7 @@ std = [ "sp-io/std", "sp-runtime/std", "sp-std/std", + "sp-core/std", ] runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", @@ -49,4 +52,4 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] -try-runtime = ["frame-support/try-runtime"] +try-runtime = ["frame-support/try-runtime",] diff --git a/substrate/frame/democracy/src/benchmarking.rs b/substrate/frame/democracy/src/benchmarking.rs index ab7ee3331e319ccf4be3f60c785f88864e1bb0ec..424192e2521da69e81daa48f952d7a9cc54d4f7c 100644 --- a/substrate/frame/democracy/src/benchmarking.rs +++ b/substrate/frame/democracy/src/benchmarking.rs @@ -22,24 +22,16 @@ use super::*; use frame_benchmarking::{account, benchmarks, whitelist_account}; use frame_support::{ assert_noop, assert_ok, - codec::Decode, - traits::{ - schedule::DispatchTime, Currency, EnsureOrigin, Get, OnInitialize, UnfilteredDispatchable, - }, + traits::{Currency, EnsureOrigin, Get, OnInitialize, UnfilteredDispatchable}, }; -use frame_system::{Pallet as System, RawOrigin}; -use sp_runtime::traits::{BadOrigin, Bounded, One}; +use frame_system::RawOrigin; +use sp_core::H256; +use sp_runtime::{traits::Bounded, BoundedVec}; use crate::Pallet as Democracy; +const REFERENDUM_COUNT_HINT: u32 = 10; const SEED: u32 = 0; -const MAX_REFERENDUMS: u32 = 99; -const MAX_SECONDERS: u32 = 100; -const MAX_BYTES: u32 = 16_384; - -fn assert_last_event<T: Config>(generic_event: <T as Config>::RuntimeEvent) { - frame_system::Pallet::<T>::assert_last_event(generic_event.into()); -} fn funded_account<T: Config>(name: &'static str, index: u32) -> T::AccountId { let caller: T::AccountId = account(name, index, SEED); @@ -49,37 +41,32 @@ fn funded_account<T: Config>(name: &'static str, index: u32) -> T::AccountId { caller } -fn add_proposal<T: Config>(n: u32) -> Result<T::Hash, &'static str> { +fn make_proposal<T: Config>(n: u32) -> BoundedCallOf<T> { + let call: CallOf<T> = frame_system::Call::remark { remark: n.encode() }.into(); + <T as Config>::Preimages::bound(call).unwrap() +} + +fn add_proposal<T: Config>(n: u32) -> Result<H256, &'static str> { let other = funded_account::<T>("proposer", n); let value = T::MinimumDeposit::get(); - let proposal_hash: T::Hash = T::Hashing::hash_of(&n); - - Democracy::<T>::propose(RawOrigin::Signed(other).into(), proposal_hash, value)?; - - Ok(proposal_hash) + let proposal = make_proposal::<T>(n); + Democracy::<T>::propose(RawOrigin::Signed(other).into(), proposal.clone(), value)?; + Ok(proposal.hash()) } -fn add_referendum<T: Config>(n: u32) -> Result<ReferendumIndex, &'static str> { - let proposal_hash: T::Hash = T::Hashing::hash_of(&n); +fn add_referendum<T: Config>(n: u32) -> (ReferendumIndex, H256) { let vote_threshold = VoteThreshold::SimpleMajority; - - Democracy::<T>::inject_referendum( - T::LaunchPeriod::get(), - proposal_hash, - vote_threshold, - 0u32.into(), - ); - let referendum_index: ReferendumIndex = ReferendumCount::<T>::get() - 1; - T::Scheduler::schedule_named( - (DEMOCRACY_ID, referendum_index).encode(), - DispatchTime::At(2u32.into()), - None, - 63, - frame_system::RawOrigin::Root.into(), - Call::enact_proposal { proposal_hash, index: referendum_index }.into(), + let proposal = make_proposal::<T>(n); + let hash = proposal.hash(); + ( + Democracy::<T>::inject_referendum( + T::LaunchPeriod::get(), + proposal, + vote_threshold, + 0u32.into(), + ), + hash, ) - .map_err(|_| "failed to schedule named")?; - Ok(referendum_index) } fn account_vote<T: Config>(b: BalanceOf<T>) -> AccountVote<BalanceOf<T>> { @@ -97,95 +84,90 @@ benchmarks! { } let caller = funded_account::<T>("caller", 0); - let proposal_hash: T::Hash = T::Hashing::hash_of(&0); + let proposal = make_proposal::<T>(0); let value = T::MinimumDeposit::get(); whitelist_account!(caller); - }: _(RawOrigin::Signed(caller), proposal_hash, value) + }: _(RawOrigin::Signed(caller), proposal, value) verify { assert_eq!(Democracy::<T>::public_props().len(), p as usize, "Proposals not created."); } second { - let s in 0 .. MAX_SECONDERS; - let caller = funded_account::<T>("caller", 0); - let proposal_hash = add_proposal::<T>(s)?; + add_proposal::<T>(0)?; // Create s existing "seconds" - for i in 0 .. s { + // we must reserve one deposit for the `proposal` and one for our benchmarked `second` call. + for i in 0 .. T::MaxDeposits::get() - 2 { let seconder = funded_account::<T>("seconder", i); - Democracy::<T>::second(RawOrigin::Signed(seconder).into(), 0, u32::MAX)?; + Democracy::<T>::second(RawOrigin::Signed(seconder).into(), 0)?; } let deposits = Democracy::<T>::deposit_of(0).ok_or("Proposal not created")?; - assert_eq!(deposits.0.len(), (s + 1) as usize, "Seconds not recorded"); + assert_eq!(deposits.0.len(), (T::MaxDeposits::get() - 1) as usize, "Seconds not recorded"); whitelist_account!(caller); - }: _(RawOrigin::Signed(caller), 0, u32::MAX) + }: _(RawOrigin::Signed(caller), 0) verify { let deposits = Democracy::<T>::deposit_of(0).ok_or("Proposal not created")?; - assert_eq!(deposits.0.len(), (s + 2) as usize, "`second` benchmark did not work"); + assert_eq!(deposits.0.len(), (T::MaxDeposits::get()) as usize, "`second` benchmark did not work"); } vote_new { - let r in 1 .. MAX_REFERENDUMS; - let caller = funded_account::<T>("caller", 0); let account_vote = account_vote::<T>(100u32.into()); // We need to create existing direct votes - for i in 0 .. r { - let ref_idx = add_referendum::<T>(i)?; - Democracy::<T>::vote(RawOrigin::Signed(caller.clone()).into(), ref_idx, account_vote)?; + for i in 0 .. T::MaxVotes::get() - 1 { + let ref_index = add_referendum::<T>(i).0; + Democracy::<T>::vote(RawOrigin::Signed(caller.clone()).into(), ref_index, account_vote)?; } let votes = match VotingOf::<T>::get(&caller) { Voting::Direct { votes, .. } => votes, _ => return Err("Votes are not direct".into()), }; - assert_eq!(votes.len(), r as usize, "Votes were not recorded."); + assert_eq!(votes.len(), (T::MaxVotes::get() - 1) as usize, "Votes were not recorded."); - let referendum_index = add_referendum::<T>(r)?; + let ref_index = add_referendum::<T>(T::MaxVotes::get() - 1).0; whitelist_account!(caller); - }: vote(RawOrigin::Signed(caller.clone()), referendum_index, account_vote) + }: vote(RawOrigin::Signed(caller.clone()), ref_index, account_vote) verify { let votes = match VotingOf::<T>::get(&caller) { Voting::Direct { votes, .. } => votes, _ => return Err("Votes are not direct".into()), }; - assert_eq!(votes.len(), (r + 1) as usize, "Vote was not recorded."); + assert_eq!(votes.len(), T::MaxVotes::get() as usize, "Vote was not recorded."); } vote_existing { - let r in 1 .. MAX_REFERENDUMS; - let caller = funded_account::<T>("caller", 0); let account_vote = account_vote::<T>(100u32.into()); // We need to create existing direct votes - for i in 0 ..=r { - let ref_idx = add_referendum::<T>(i)?; - Democracy::<T>::vote(RawOrigin::Signed(caller.clone()).into(), ref_idx, account_vote)?; + for i in 0..T::MaxVotes::get() { + let ref_index = add_referendum::<T>(i).0; + Democracy::<T>::vote(RawOrigin::Signed(caller.clone()).into(), ref_index, account_vote)?; } let votes = match VotingOf::<T>::get(&caller) { Voting::Direct { votes, .. } => votes, _ => return Err("Votes are not direct".into()), }; - assert_eq!(votes.len(), (r + 1) as usize, "Votes were not recorded."); + assert_eq!(votes.len(), T::MaxVotes::get() as usize, "Votes were not recorded."); // Change vote from aye to nay let nay = Vote { aye: false, conviction: Conviction::Locked1x }; let new_vote = AccountVote::Standard { vote: nay, balance: 1000u32.into() }; - let referendum_index = Democracy::<T>::referendum_count() - 1; + let ref_index = Democracy::<T>::referendum_count() - 1; // This tests when a user changes a vote whitelist_account!(caller); - }: vote(RawOrigin::Signed(caller.clone()), referendum_index, new_vote) + }: vote(RawOrigin::Signed(caller.clone()), ref_index, new_vote) verify { let votes = match VotingOf::<T>::get(&caller) { Voting::Direct { votes, .. } => votes, _ => return Err("Votes are not direct".into()), }; - assert_eq!(votes.len(), (r + 1) as usize, "Vote was incorrectly added"); - let referendum_info = Democracy::<T>::referendum_info(referendum_index) + assert_eq!(votes.len(), T::MaxVotes::get() as usize, "Vote was incorrectly added"); + let referendum_info = Democracy::<T>::referendum_info(ref_index) .ok_or("referendum doesn't exist")?; let tally = match referendum_info { ReferendumInfo::Ongoing(r) => r.tally, @@ -196,61 +178,55 @@ benchmarks! { emergency_cancel { let origin = T::CancellationOrigin::successful_origin(); - let referendum_index = add_referendum::<T>(0)?; - assert_ok!(Democracy::<T>::referendum_status(referendum_index)); - }: _<T::RuntimeOrigin>(origin, referendum_index) + let ref_index = add_referendum::<T>(0).0; + assert_ok!(Democracy::<T>::referendum_status(ref_index)); + }: _<T::RuntimeOrigin>(origin, ref_index) verify { // Referendum has been canceled assert_noop!( - Democracy::<T>::referendum_status(referendum_index), + Democracy::<T>::referendum_status(ref_index), Error::<T>::ReferendumInvalid, ); } blacklist { - let p in 1 .. T::MaxProposals::get(); - // Place our proposal at the end to make sure it's worst case. - for i in 0 .. p - 1 { + for i in 0 .. T::MaxProposals::get() - 1 { add_proposal::<T>(i)?; } // We should really add a lot of seconds here, but we're not doing it elsewhere. + // Add a referendum of our proposal. + let (ref_index, hash) = add_referendum::<T>(0); + assert_ok!(Democracy::<T>::referendum_status(ref_index)); // Place our proposal in the external queue, too. - let hash = T::Hashing::hash_of(&0); assert_ok!( - Democracy::<T>::external_propose(T::ExternalOrigin::successful_origin(), hash) + Democracy::<T>::external_propose(T::ExternalOrigin::successful_origin(), make_proposal::<T>(0)) ); let origin = T::BlacklistOrigin::successful_origin(); - // Add a referendum of our proposal. - let referendum_index = add_referendum::<T>(0)?; - assert_ok!(Democracy::<T>::referendum_status(referendum_index)); - }: _<T::RuntimeOrigin>(origin, hash, Some(referendum_index)) + }: _<T::RuntimeOrigin>(origin, hash, Some(ref_index)) verify { // Referendum has been canceled assert_noop!( - Democracy::<T>::referendum_status(referendum_index), + Democracy::<T>::referendum_status(ref_index), Error::<T>::ReferendumInvalid ); } // Worst case scenario, we external propose a previously blacklisted proposal external_propose { - let v in 1 .. MAX_VETOERS as u32; - let origin = T::ExternalOrigin::successful_origin(); - let proposal_hash = T::Hashing::hash_of(&0); + let proposal = make_proposal::<T>(0); // Add proposal to blacklist with block number 0 - let addresses = (0..v) + let addresses: BoundedVec<_, _> = (0..(T::MaxBlacklisted::get() - 1)) .into_iter() .map(|i| account::<T::AccountId>("blacklist", i, SEED)) - .collect::<Vec<_>>(); - Blacklist::<T>::insert( - proposal_hash, - (T::BlockNumber::zero(), addresses), - ); - }: _<T::RuntimeOrigin>(origin, proposal_hash) + .collect::<Vec<_>>() + .try_into() + .unwrap(); + Blacklist::<T>::insert(proposal.hash(), (T::BlockNumber::zero(), addresses)); + }: _<T::RuntimeOrigin>(origin, proposal) verify { // External proposal created ensure!(<NextExternal<T>>::exists(), "External proposal didn't work"); @@ -258,8 +234,8 @@ benchmarks! { external_propose_majority { let origin = T::ExternalMajorityOrigin::successful_origin(); - let proposal_hash = T::Hashing::hash_of(&0); - }: _<T::RuntimeOrigin>(origin, proposal_hash) + let proposal = make_proposal::<T>(0); + }: _<T::RuntimeOrigin>(origin, proposal) verify { // External proposal created ensure!(<NextExternal<T>>::exists(), "External proposal didn't work"); @@ -267,8 +243,8 @@ benchmarks! { external_propose_default { let origin = T::ExternalDefaultOrigin::successful_origin(); - let proposal_hash = T::Hashing::hash_of(&0); - }: _<T::RuntimeOrigin>(origin, proposal_hash) + let proposal = make_proposal::<T>(0); + }: _<T::RuntimeOrigin>(origin, proposal) verify { // External proposal created ensure!(<NextExternal<T>>::exists(), "External proposal didn't work"); @@ -276,8 +252,9 @@ benchmarks! { fast_track { let origin_propose = T::ExternalDefaultOrigin::successful_origin(); - let proposal_hash: T::Hash = T::Hashing::hash_of(&0); - Democracy::<T>::external_propose_default(origin_propose, proposal_hash)?; + let proposal = make_proposal::<T>(0); + let proposal_hash = proposal.hash(); + Democracy::<T>::external_propose_default(origin_propose, proposal)?; // NOTE: Instant origin may invoke a little bit more logic, but may not always succeed. let origin_fast_track = T::FastTrackOrigin::successful_origin(); @@ -289,17 +266,15 @@ benchmarks! { } veto_external { - // Existing veto-ers - let v in 0 .. MAX_VETOERS as u32; - - let proposal_hash: T::Hash = T::Hashing::hash_of(&v); + let proposal = make_proposal::<T>(0); + let proposal_hash = proposal.hash(); let origin_propose = T::ExternalDefaultOrigin::successful_origin(); - Democracy::<T>::external_propose_default(origin_propose, proposal_hash)?; + Democracy::<T>::external_propose_default(origin_propose, proposal)?; - let mut vetoers: Vec<T::AccountId> = Vec::new(); - for i in 0 .. v { - vetoers.push(account::<T::AccountId>("vetoer", i, SEED)); + let mut vetoers: BoundedVec<T::AccountId, _> = Default::default(); + for i in 0 .. (T::MaxBlacklisted::get() - 1) { + vetoers.try_push(account::<T::AccountId>("vetoer", i, SEED)).unwrap(); } vetoers.sort(); Blacklist::<T>::insert(proposal_hash, (T::BlockNumber::zero(), vetoers)); @@ -310,42 +285,27 @@ benchmarks! { verify { assert!(NextExternal::<T>::get().is_none()); let (_, new_vetoers) = <Blacklist<T>>::get(&proposal_hash).ok_or("no blacklist")?; - assert_eq!(new_vetoers.len(), (v + 1) as usize, "vetoers not added"); + assert_eq!(new_vetoers.len(), T::MaxBlacklisted::get() as usize, "vetoers not added"); } cancel_proposal { - let p in 1 .. T::MaxProposals::get(); - // Place our proposal at the end to make sure it's worst case. - for i in 0 .. p { + for i in 0 .. T::MaxProposals::get() { add_proposal::<T>(i)?; } - let cancel_origin = T::CancelProposalOrigin::successful_origin(); }: _<T::RuntimeOrigin>(cancel_origin, 0) cancel_referendum { - let referendum_index = add_referendum::<T>(0)?; - }: _(RawOrigin::Root, referendum_index) - - cancel_queued { - let r in 1 .. MAX_REFERENDUMS; - - for i in 0..r { - add_referendum::<T>(i)?; // This add one element in the scheduler - } - - let referendum_index = add_referendum::<T>(r)?; - }: _(RawOrigin::Root, referendum_index) + let ref_index = add_referendum::<T>(0).0; + }: _(RawOrigin::Root, ref_index) - // This measures the path of `launch_next` external. Not currently used as we simply - // assume the weight is `MaxBlockWeight` when executing. #[extra] on_initialize_external { - let r in 0 .. MAX_REFERENDUMS; + let r in 0 .. REFERENDUM_COUNT_HINT; for i in 0..r { - add_referendum::<T>(i)?; + add_referendum::<T>(i); } assert_eq!(Democracy::<T>::referendum_count(), r, "referenda not created"); @@ -354,8 +314,8 @@ benchmarks! { LastTabledWasExternal::<T>::put(false); let origin = T::ExternalMajorityOrigin::successful_origin(); - let proposal_hash = T::Hashing::hash_of(&r); - let call = Call::<T>::external_propose_majority { proposal_hash }; + let proposal = make_proposal::<T>(r); + let call = Call::<T>::external_propose_majority { proposal }; call.dispatch_bypass_filter(origin)?; // External proposal created ensure!(<NextExternal<T>>::exists(), "External proposal didn't work"); @@ -379,14 +339,12 @@ benchmarks! { } } - // This measures the path of `launch_next` public. Not currently used as we simply - // assume the weight is `MaxBlockWeight` when executing. #[extra] on_initialize_public { - let r in 1 .. MAX_REFERENDUMS; + let r in 0 .. (T::MaxVotes::get() - 1); for i in 0..r { - add_referendum::<T>(i)?; + add_referendum::<T>(i); } assert_eq!(Democracy::<T>::referendum_count(), r, "referenda not created"); @@ -415,10 +373,10 @@ benchmarks! { // No launch no maturing referenda. on_initialize_base { - let r in 1 .. MAX_REFERENDUMS; + let r in 0 .. (T::MaxVotes::get() - 1); for i in 0..r { - add_referendum::<T>(i)?; + add_referendum::<T>(i); } for (key, mut info) in ReferendumInfoOf::<T>::iter() { @@ -445,10 +403,10 @@ benchmarks! { } on_initialize_base_with_launch_period { - let r in 1 .. MAX_REFERENDUMS; + let r in 0 .. (T::MaxVotes::get() - 1); for i in 0..r { - add_referendum::<T>(i)?; + add_referendum::<T>(i); } for (key, mut info) in ReferendumInfoOf::<T>::iter() { @@ -477,7 +435,7 @@ benchmarks! { } delegate { - let r in 1 .. MAX_REFERENDUMS; + let r in 0 .. (T::MaxVotes::get() - 1); let initial_balance: BalanceOf<T> = 100u32.into(); let delegated_balance: BalanceOf<T> = 1000u32.into(); @@ -504,8 +462,8 @@ benchmarks! { let account_vote = account_vote::<T>(initial_balance); // We need to create existing direct votes for the `new_delegate` for i in 0..r { - let ref_idx = add_referendum::<T>(i)?; - Democracy::<T>::vote(RawOrigin::Signed(new_delegate.clone()).into(), ref_idx, account_vote)?; + let ref_index = add_referendum::<T>(i).0; + Democracy::<T>::vote(RawOrigin::Signed(new_delegate.clone()).into(), ref_index, account_vote)?; } let votes = match VotingOf::<T>::get(&new_delegate) { Voting::Direct { votes, .. } => votes, @@ -529,7 +487,7 @@ benchmarks! { } undelegate { - let r in 1 .. MAX_REFERENDUMS; + let r in 0 .. (T::MaxVotes::get() - 1); let initial_balance: BalanceOf<T> = 100u32.into(); let delegated_balance: BalanceOf<T> = 1000u32.into(); @@ -553,10 +511,10 @@ benchmarks! { // We need to create votes direct votes for the `delegate` let account_vote = account_vote::<T>(initial_balance); for i in 0..r { - let ref_idx = add_referendum::<T>(i)?; + let ref_index = add_referendum::<T>(i).0; Democracy::<T>::vote( RawOrigin::Signed(the_delegate.clone()).into(), - ref_idx, + ref_index, account_vote )?; } @@ -580,71 +538,9 @@ benchmarks! { }: _(RawOrigin::Root) - note_preimage { - // Num of bytes in encoded proposal - let b in 0 .. MAX_BYTES; - - let caller = funded_account::<T>("caller", 0); - let encoded_proposal = vec![1; b as usize]; - whitelist_account!(caller); - }: _(RawOrigin::Signed(caller), encoded_proposal.clone()) - verify { - let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); - match Preimages::<T>::get(proposal_hash) { - Some(PreimageStatus::Available { .. }) => (), - _ => return Err("preimage not available".into()) - } - } - - note_imminent_preimage { - // Num of bytes in encoded proposal - let b in 0 .. MAX_BYTES; - - // d + 1 to include the one we are testing - let encoded_proposal = vec![1; b as usize]; - let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); - let block_number = T::BlockNumber::one(); - Preimages::<T>::insert(&proposal_hash, PreimageStatus::Missing(block_number)); - - let caller = funded_account::<T>("caller", 0); - let encoded_proposal = vec![1; b as usize]; - whitelist_account!(caller); - }: _(RawOrigin::Signed(caller), encoded_proposal.clone()) - verify { - let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); - match Preimages::<T>::get(proposal_hash) { - Some(PreimageStatus::Available { .. }) => (), - _ => return Err("preimage not available".into()) - } - } - - reap_preimage { - // Num of bytes in encoded proposal - let b in 0 .. MAX_BYTES; - - let encoded_proposal = vec![1; b as usize]; - let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); - - let submitter = funded_account::<T>("submitter", b); - Democracy::<T>::note_preimage(RawOrigin::Signed(submitter).into(), encoded_proposal.clone())?; - - // We need to set this otherwise we get `Early` error. - let block_number = T::VotingPeriod::get() + T::EnactmentPeriod::get() + T::BlockNumber::one(); - System::<T>::set_block_number(block_number); - - assert!(Preimages::<T>::contains_key(proposal_hash)); - - let caller = funded_account::<T>("caller", 0); - whitelist_account!(caller); - }: _(RawOrigin::Signed(caller), proposal_hash, u32::MAX) - verify { - let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); - assert!(!Preimages::<T>::contains_key(proposal_hash)); - } - // Test when unlock will remove locks unlock_remove { - let r in 1 .. MAX_REFERENDUMS; + let r in 0 .. (T::MaxVotes::get() - 1); let locker = funded_account::<T>("locker", 0); let locker_lookup = T::Lookup::unlookup(locker.clone()); @@ -653,9 +549,9 @@ benchmarks! { let small_vote = account_vote::<T>(base_balance); // Vote and immediately unvote for i in 0 .. r { - let ref_idx = add_referendum::<T>(i)?; - Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), ref_idx, small_vote)?; - Democracy::<T>::remove_vote(RawOrigin::Signed(locker.clone()).into(), ref_idx)?; + let ref_index = add_referendum::<T>(i).0; + Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), ref_index, small_vote)?; + Democracy::<T>::remove_vote(RawOrigin::Signed(locker.clone()).into(), ref_index)?; } let caller = funded_account::<T>("caller", 0); @@ -669,7 +565,7 @@ benchmarks! { // Test when unlock will set a new value unlock_set { - let r in 1 .. MAX_REFERENDUMS; + let r in 0 .. (T::MaxVotes::get() - 1); let locker = funded_account::<T>("locker", 0); let locker_lookup = T::Lookup::unlookup(locker.clone()); @@ -677,14 +573,14 @@ benchmarks! { let base_balance: BalanceOf<T> = 100u32.into(); let small_vote = account_vote::<T>(base_balance); for i in 0 .. r { - let ref_idx = add_referendum::<T>(i)?; - Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), ref_idx, small_vote)?; + let ref_index = add_referendum::<T>(i).0; + Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), ref_index, small_vote)?; } // Create a big vote so lock increases let big_vote = account_vote::<T>(base_balance * 10u32.into()); - let referendum_index = add_referendum::<T>(r)?; - Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), referendum_index, big_vote)?; + let ref_index = add_referendum::<T>(r).0; + Democracy::<T>::vote(RawOrigin::Signed(locker.clone()).into(), ref_index, big_vote)?; let votes = match VotingOf::<T>::get(&locker) { Voting::Direct { votes, .. } => votes, @@ -695,7 +591,7 @@ benchmarks! { let voting = VotingOf::<T>::get(&locker); assert_eq!(voting.locked_balance(), base_balance * 10u32.into()); - Democracy::<T>::remove_vote(RawOrigin::Signed(locker.clone()).into(), referendum_index)?; + Democracy::<T>::remove_vote(RawOrigin::Signed(locker.clone()).into(), ref_index)?; let caller = funded_account::<T>("caller", 0); whitelist_account!(caller); @@ -709,18 +605,18 @@ benchmarks! { let voting = VotingOf::<T>::get(&locker); // Note that we may want to add a `get_lock` api to actually verify - assert_eq!(voting.locked_balance(), base_balance); + assert_eq!(voting.locked_balance(), if r > 0 { base_balance } else { 0u32.into() }); } remove_vote { - let r in 1 .. MAX_REFERENDUMS; + let r in 1 .. T::MaxVotes::get(); let caller = funded_account::<T>("caller", 0); let account_vote = account_vote::<T>(100u32.into()); for i in 0 .. r { - let ref_idx = add_referendum::<T>(i)?; - Democracy::<T>::vote(RawOrigin::Signed(caller.clone()).into(), ref_idx, account_vote)?; + let ref_index = add_referendum::<T>(i).0; + Democracy::<T>::vote(RawOrigin::Signed(caller.clone()).into(), ref_index, account_vote)?; } let votes = match VotingOf::<T>::get(&caller) { @@ -729,9 +625,9 @@ benchmarks! { }; assert_eq!(votes.len(), r as usize, "Votes not created"); - let referendum_index = r - 1; + let ref_index = r - 1; whitelist_account!(caller); - }: _(RawOrigin::Signed(caller.clone()), referendum_index) + }: _(RawOrigin::Signed(caller.clone()), ref_index) verify { let votes = match VotingOf::<T>::get(&caller) { Voting::Direct { votes, .. } => votes, @@ -742,15 +638,15 @@ benchmarks! { // Worst case is when target == caller and referendum is ongoing remove_other_vote { - let r in 1 .. MAX_REFERENDUMS; + let r in 1 .. T::MaxVotes::get(); let caller = funded_account::<T>("caller", r); let caller_lookup = T::Lookup::unlookup(caller.clone()); let account_vote = account_vote::<T>(100u32.into()); for i in 0 .. r { - let ref_idx = add_referendum::<T>(i)?; - Democracy::<T>::vote(RawOrigin::Signed(caller.clone()).into(), ref_idx, account_vote)?; + let ref_index = add_referendum::<T>(i).0; + Democracy::<T>::vote(RawOrigin::Signed(caller.clone()).into(), ref_index, account_vote)?; } let votes = match VotingOf::<T>::get(&caller) { @@ -759,9 +655,9 @@ benchmarks! { }; assert_eq!(votes.len(), r as usize, "Votes not created"); - let referendum_index = r - 1; + let ref_index = r - 1; whitelist_account!(caller); - }: _(RawOrigin::Signed(caller.clone()), caller_lookup, referendum_index) + }: _(RawOrigin::Signed(caller.clone()), caller_lookup, ref_index) verify { let votes = match VotingOf::<T>::get(&caller) { Voting::Direct { votes, .. } => votes, @@ -770,54 +666,6 @@ benchmarks! { assert_eq!(votes.len(), (r - 1) as usize, "Vote was not removed"); } - #[extra] - enact_proposal_execute { - // Num of bytes in encoded proposal - let b in 0 .. MAX_BYTES; - - let proposer = funded_account::<T>("proposer", 0); - let raw_call = Call::note_preimage { encoded_proposal: vec![1; b as usize] }; - let generic_call: T::Proposal = raw_call.into(); - let encoded_proposal = generic_call.encode(); - let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); - Democracy::<T>::note_preimage(RawOrigin::Signed(proposer).into(), encoded_proposal)?; - - match Preimages::<T>::get(proposal_hash) { - Some(PreimageStatus::Available { .. }) => (), - _ => return Err("preimage not available".into()) - } - }: enact_proposal(RawOrigin::Root, proposal_hash, 0) - verify { - // Fails due to mismatched origin - assert_last_event::<T>(Event::<T>::Executed { ref_index: 0, result: Err(BadOrigin.into()) }.into()); - } - - #[extra] - enact_proposal_slash { - // Num of bytes in encoded proposal - let b in 0 .. MAX_BYTES; - - let proposer = funded_account::<T>("proposer", 0); - // Random invalid bytes - let encoded_proposal = vec![200; b as usize]; - let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); - Democracy::<T>::note_preimage(RawOrigin::Signed(proposer).into(), encoded_proposal)?; - - match Preimages::<T>::get(proposal_hash) { - Some(PreimageStatus::Available { .. }) => (), - _ => return Err("preimage not available".into()) - } - let origin = RawOrigin::Root.into(); - let call = Call::<T>::enact_proposal { proposal_hash, index: 0 }.encode(); - }: { - assert_eq!( - <Call<T> as Decode>::decode(&mut &*call) - .expect("call is encoded above, encoding must be correct") - .dispatch_bypass_filter(origin), - Err(Error::<T>::PreimageInvalid.into()) - ); - } - impl_benchmark_test_suite!( Democracy, crate::tests::new_test_ext(), diff --git a/substrate/frame/democracy/src/conviction.rs b/substrate/frame/democracy/src/conviction.rs index 57d631e8c1f4cfb13b4e22c6085756940ac008c1..a938d8a4e6852afe0551cb8c78abf584f692d085 100644 --- a/substrate/frame/democracy/src/conviction.rs +++ b/substrate/frame/democracy/src/conviction.rs @@ -18,7 +18,7 @@ //! The conviction datatype. use crate::types::Delegations; -use codec::{Decode, Encode}; +use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{ traits::{Bounded, CheckedDiv, CheckedMul, Zero}, @@ -27,7 +27,19 @@ use sp_runtime::{ use sp_std::{prelude::*, result::Result}; /// A value denoting the strength of conviction of a vote. -#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo)] +#[derive( + Encode, + MaxEncodedLen, + Decode, + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + RuntimeDebug, + TypeInfo, +)] pub enum Conviction { /// 0.1x votes, unlocked. None, diff --git a/substrate/frame/democracy/src/lib.rs b/substrate/frame/democracy/src/lib.rs index 3c1be1910399839776734602fb6dc04fb9ef3d4a..cf954d4800eee6c8e24eb6df5f8e92b1022e7620 100644 --- a/substrate/frame/democracy/src/lib.rs +++ b/substrate/frame/democracy/src/lib.rs @@ -152,21 +152,20 @@ #![recursion_limit = "256"] #![cfg_attr(not(feature = "std"), no_std)] -use codec::{Decode, Encode, Input}; +use codec::{Decode, Encode}; use frame_support::{ ensure, traits::{ defensive_prelude::*, - schedule::{DispatchTime, Named as ScheduleNamed}, - BalanceStatus, Currency, Get, LockIdentifier, LockableCurrency, OnUnbalanced, - ReservableCurrency, WithdrawReasons, + schedule::{v3::Named as ScheduleNamed, DispatchTime}, + Bounded, Currency, Get, LockIdentifier, LockableCurrency, OnUnbalanced, QueryPreimage, + ReservableCurrency, StorePreimage, WithdrawReasons, }, weights::Weight, }; -use scale_info::TypeInfo; use sp_runtime::{ - traits::{Bounded, Dispatchable, Hash, Saturating, StaticLookup, Zero}, - ArithmeticError, DispatchError, DispatchResult, RuntimeDebug, + traits::{Bounded as ArithBounded, One, Saturating, StaticLookup, Zero}, + ArithmeticError, DispatchError, DispatchResult, }; use sp_std::prelude::*; @@ -188,12 +187,9 @@ mod tests; #[cfg(feature = "runtime-benchmarks")] pub mod benchmarking; -const DEMOCRACY_ID: LockIdentifier = *b"democrac"; +pub mod migrations; -/// The maximum number of vetoers on a single proposal used to compute Weight. -/// -/// NOTE: This is not enforced by any logic. -pub const MAX_VETOERS: u32 = 100; +const DEMOCRACY_ID: LockIdentifier = *b"democrac"; /// A proposal index. pub type PropIndex = u32; @@ -206,58 +202,36 @@ type BalanceOf<T> = type NegativeImbalanceOf<T> = <<T as Config>::Currency as Currency< <T as frame_system::Config>::AccountId, >>::NegativeImbalance; +pub type CallOf<T> = <T as frame_system::Config>::RuntimeCall; +pub type BoundedCallOf<T> = Bounded<CallOf<T>>; type AccountIdLookupOf<T> = <<T as frame_system::Config>::Lookup as StaticLookup>::Source; -#[derive(Clone, Encode, Decode, RuntimeDebug, TypeInfo)] -pub enum PreimageStatus<AccountId, Balance, BlockNumber> { - /// The preimage is imminently needed at the argument. - Missing(BlockNumber), - /// The preimage is available. - Available { - data: Vec<u8>, - provider: AccountId, - deposit: Balance, - since: BlockNumber, - /// None if it's not imminent. - expiry: Option<BlockNumber>, - }, -} - -impl<AccountId, Balance, BlockNumber> PreimageStatus<AccountId, Balance, BlockNumber> { - fn to_missing_expiry(self) -> Option<BlockNumber> { - match self { - PreimageStatus::Missing(expiry) => Some(expiry), - _ => None, - } - } -} - -// A value placed in storage that represents the current version of the Democracy storage. -// This value is used by the `on_runtime_upgrade` logic to determine whether we run -// storage migration logic. -#[derive(Encode, Decode, Clone, Copy, PartialEq, Eq, RuntimeDebug, TypeInfo)] -enum Releases { - V1, -} - #[frame_support::pallet] pub mod pallet { use super::{DispatchResult, *}; use frame_support::pallet_prelude::*; use frame_system::pallet_prelude::*; + use sp_core::H256; + + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] - #[pallet::without_storage_info] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet<T>(_); #[pallet::config] pub trait Config: frame_system::Config + Sized { - type Proposal: Parameter - + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin> - + From<Call<Self>>; + type WeightInfo: WeightInfo; type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; + /// The Scheduler. + type Scheduler: ScheduleNamed<Self::BlockNumber, CallOf<Self>, Self::PalletsOrigin>; + + /// The Preimage provider. + type Preimages: QueryPreimage + StorePreimage; + /// Currency type for this pallet. type Currency: ReservableCurrency<Self::AccountId> + LockableCurrency<Self::AccountId, Moment = Self::BlockNumber>; @@ -289,6 +263,39 @@ pub mod pallet { #[pallet::constant] type MinimumDeposit: Get<BalanceOf<Self>>; + /// Indicator for whether an emergency origin is even allowed to happen. Some chains may + /// want to set this permanently to `false`, others may want to condition it on things such + /// as an upgrade having happened recently. + #[pallet::constant] + type InstantAllowed: Get<bool>; + + /// Minimum voting period allowed for a fast-track referendum. + #[pallet::constant] + type FastTrackVotingPeriod: Get<Self::BlockNumber>; + + /// Period in blocks where an external proposal may not be re-submitted after being vetoed. + #[pallet::constant] + type CooloffPeriod: Get<Self::BlockNumber>; + + /// The maximum number of votes for an account. + /// + /// Also used to compute weight, an overly big value can + /// lead to extrinsic with very big weight: see `delegate` for instance. + #[pallet::constant] + type MaxVotes: Get<u32>; + + /// The maximum number of public proposals that can exist at any time. + #[pallet::constant] + type MaxProposals: Get<u32>; + + /// The maximum number of deposits a public proposal may have at any time. + #[pallet::constant] + type MaxDeposits: Get<u32>; + + /// The maximum number of items which can be blacklisted. + #[pallet::constant] + type MaxBlacklisted: Get<u32>; + /// Origin from which the next tabled referendum may be forced. This is a normal /// "super-majority-required" referendum. type ExternalOrigin: EnsureOrigin<Self::RuntimeOrigin>; @@ -311,16 +318,6 @@ pub mod pallet { /// origin. It retains its threshold method. type InstantOrigin: EnsureOrigin<Self::RuntimeOrigin>; - /// Indicator for whether an emergency origin is even allowed to happen. Some chains may - /// want to set this permanently to `false`, others may want to condition it on things such - /// as an upgrade having happened recently. - #[pallet::constant] - type InstantAllowed: Get<bool>; - - /// Minimum voting period allowed for a fast-track referendum. - #[pallet::constant] - type FastTrackVotingPeriod: Get<Self::BlockNumber>; - /// Origin from which any referendum may be cancelled in an emergency. type CancellationOrigin: EnsureOrigin<Self::RuntimeOrigin>; @@ -331,79 +328,39 @@ pub mod pallet { type CancelProposalOrigin: EnsureOrigin<Self::RuntimeOrigin>; /// Origin for anyone able to veto proposals. - /// - /// # Warning - /// - /// The number of Vetoers for a proposal must be small, extrinsics are weighted according to - /// [MAX_VETOERS](./const.MAX_VETOERS.html) type VetoOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>; - /// Period in blocks where an external proposal may not be re-submitted after being vetoed. - #[pallet::constant] - type CooloffPeriod: Get<Self::BlockNumber>; - - /// The amount of balance that must be deposited per byte of preimage stored. - #[pallet::constant] - type PreimageByteDeposit: Get<BalanceOf<Self>>; - - /// An origin that can provide a preimage using operational extrinsics. - type OperationalPreimageOrigin: EnsureOrigin<Self::RuntimeOrigin, Success = Self::AccountId>; - - /// Handler for the unbalanced reduction when slashing a preimage deposit. - type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>; - - /// The Scheduler. - type Scheduler: ScheduleNamed<Self::BlockNumber, Self::Proposal, Self::PalletsOrigin>; - /// Overarching type of all pallets origins. type PalletsOrigin: From<frame_system::RawOrigin<Self::AccountId>>; - /// The maximum number of votes for an account. - /// - /// Also used to compute weight, an overly big value can - /// lead to extrinsic with very big weight: see `delegate` for instance. - #[pallet::constant] - type MaxVotes: Get<u32>; - - /// Weight information for extrinsics in this pallet. - type WeightInfo: WeightInfo; - - /// The maximum number of public proposals that can exist at any time. - #[pallet::constant] - type MaxProposals: Get<u32>; + /// Handler for the unbalanced reduction when slashing a preimage deposit. + type Slash: OnUnbalanced<NegativeImbalanceOf<Self>>; } - // TODO: Refactor public proposal queue into its own pallet. - // https://github.com/paritytech/substrate/issues/5322 /// The number of (public) proposals that have been made so far. #[pallet::storage] #[pallet::getter(fn public_prop_count)] pub type PublicPropCount<T> = StorageValue<_, PropIndex, ValueQuery>; - /// The public proposals. Unsorted. The second item is the proposal's hash. + /// The public proposals. Unsorted. The second item is the proposal. #[pallet::storage] #[pallet::getter(fn public_props)] - pub type PublicProps<T: Config> = - StorageValue<_, Vec<(PropIndex, T::Hash, T::AccountId)>, ValueQuery>; + pub type PublicProps<T: Config> = StorageValue< + _, + BoundedVec<(PropIndex, BoundedCallOf<T>, T::AccountId), T::MaxProposals>, + ValueQuery, + >; /// Those who have locked a deposit. /// /// TWOX-NOTE: Safe, as increasing integer keys are safe. #[pallet::storage] #[pallet::getter(fn deposit_of)] - pub type DepositOf<T: Config> = - StorageMap<_, Twox64Concat, PropIndex, (Vec<T::AccountId>, BalanceOf<T>)>; - - /// Map of hashes to the proposal preimage, along with who registered it and their deposit. - /// The block number is the block at which it was deposited. - // TODO: Refactor Preimages into its own pallet. - // https://github.com/paritytech/substrate/issues/5322 - #[pallet::storage] - pub type Preimages<T: Config> = StorageMap< + pub type DepositOf<T: Config> = StorageMap< _, - Identity, - T::Hash, - PreimageStatus<T::AccountId, BalanceOf<T>, T::BlockNumber>, + Twox64Concat, + PropIndex, + (BoundedVec<T::AccountId, T::MaxDeposits>, BalanceOf<T>), >; /// The next free referendum index, aka the number of referenda started so far. @@ -426,7 +383,7 @@ pub mod pallet { _, Twox64Concat, ReferendumIndex, - ReferendumInfo<T::BlockNumber, T::Hash, BalanceOf<T>>, + ReferendumInfo<T::BlockNumber, BoundedCallOf<T>, BalanceOf<T>>, >; /// All votes for a particular voter. We store the balance for the number of votes that we @@ -438,14 +395,12 @@ pub mod pallet { _, Twox64Concat, T::AccountId, - Voting<BalanceOf<T>, T::AccountId, T::BlockNumber>, + Voting<BalanceOf<T>, T::AccountId, T::BlockNumber, T::MaxVotes>, ValueQuery, >; /// True if the last referendum tabled was submitted externally. False if it was a public /// proposal. - // TODO: There should be any number of tabling origins, not just public and "external" - // (council). https://github.com/paritytech/substrate/issues/5322 #[pallet::storage] pub type LastTabledWasExternal<T> = StorageValue<_, bool, ValueQuery>; @@ -454,23 +409,21 @@ pub mod pallet { /// - `LastTabledWasExternal` is `false`; or /// - `PublicProps` is empty. #[pallet::storage] - pub type NextExternal<T: Config> = StorageValue<_, (T::Hash, VoteThreshold)>; + pub type NextExternal<T: Config> = StorageValue<_, (BoundedCallOf<T>, VoteThreshold)>; /// A record of who vetoed what. Maps proposal hash to a possible existent block number /// (until when it may not be resubmitted) and who vetoed it. #[pallet::storage] - pub type Blacklist<T: Config> = - StorageMap<_, Identity, T::Hash, (T::BlockNumber, Vec<T::AccountId>)>; + pub type Blacklist<T: Config> = StorageMap< + _, + Identity, + H256, + (T::BlockNumber, BoundedVec<T::AccountId, T::MaxBlacklisted>), + >; /// Record of all proposals that have been subject to emergency cancellation. #[pallet::storage] - pub type Cancellations<T: Config> = StorageMap<_, Identity, T::Hash, bool, ValueQuery>; - - /// Storage version of the pallet. - /// - /// New networks start with last version. - #[pallet::storage] - pub(crate) type StorageVersion<T> = StorageValue<_, Releases>; + pub type Cancellations<T: Config> = StorageMap<_, Identity, H256, bool, ValueQuery>; #[pallet::genesis_config] pub struct GenesisConfig<T: Config> { @@ -490,7 +443,6 @@ pub mod pallet { PublicPropCount::<T>::put(0 as PropIndex); ReferendumCount::<T>::put(0 as ReferendumIndex); LowestUnbaked::<T>::put(0 as ReferendumIndex); - StorageVersion::<T>::put(Releases::V1); } } @@ -500,7 +452,7 @@ pub mod pallet { /// A motion has been proposed by a public account. Proposed { proposal_index: PropIndex, deposit: BalanceOf<T> }, /// A public proposal has been tabled for referendum vote. - Tabled { proposal_index: PropIndex, deposit: BalanceOf<T>, depositors: Vec<T::AccountId> }, + Tabled { proposal_index: PropIndex, deposit: BalanceOf<T> }, /// An external proposal has been tabled. ExternalTabled, /// A referendum has begun. @@ -511,31 +463,14 @@ pub mod pallet { NotPassed { ref_index: ReferendumIndex }, /// A referendum has been cancelled. Cancelled { ref_index: ReferendumIndex }, - /// A proposal has been enacted. - Executed { ref_index: ReferendumIndex, result: DispatchResult }, /// An account has delegated their vote to another account. Delegated { who: T::AccountId, target: T::AccountId }, /// An account has cancelled a previous delegation operation. Undelegated { account: T::AccountId }, /// An external proposal has been vetoed. - Vetoed { who: T::AccountId, proposal_hash: T::Hash, until: T::BlockNumber }, - /// A proposal's preimage was noted, and the deposit taken. - PreimageNoted { proposal_hash: T::Hash, who: T::AccountId, deposit: BalanceOf<T> }, - /// A proposal preimage was removed and used (the deposit was returned). - PreimageUsed { proposal_hash: T::Hash, provider: T::AccountId, deposit: BalanceOf<T> }, - /// A proposal could not be executed because its preimage was invalid. - PreimageInvalid { proposal_hash: T::Hash, ref_index: ReferendumIndex }, - /// A proposal could not be executed because its preimage was missing. - PreimageMissing { proposal_hash: T::Hash, ref_index: ReferendumIndex }, - /// A registered preimage was removed and the deposit collected by the reaper. - PreimageReaped { - proposal_hash: T::Hash, - provider: T::AccountId, - deposit: BalanceOf<T>, - reaper: T::AccountId, - }, + Vetoed { who: T::AccountId, proposal_hash: H256, until: T::BlockNumber }, /// A proposal_hash has been blacklisted permanently. - Blacklisted { proposal_hash: T::Hash }, + Blacklisted { proposal_hash: H256 }, /// An account has voted in a referendum Voted { voter: T::AccountId, ref_index: ReferendumIndex, vote: AccountVote<BalanceOf<T>> }, /// An account has secconded a proposal @@ -564,20 +499,8 @@ pub mod pallet { NoProposal, /// Identity may not veto a proposal twice AlreadyVetoed, - /// Preimage already noted - DuplicatePreimage, - /// Not imminent - NotImminent, - /// Too early - TooEarly, - /// Imminent - Imminent, - /// Preimage not found - PreimageMissing, /// Vote given for invalid referendum ReferendumInvalid, - /// Invalid preimage - PreimageInvalid, /// No proposals waiting NoneWaiting, /// The given account did not vote on the referendum. @@ -601,8 +524,8 @@ pub mod pallet { WrongUpperBound, /// Maximum number of votes reached. MaxVotesReached, - /// Maximum number of proposals reached. - TooManyProposals, + /// Maximum number of items reached. + TooMany, /// Voting period too low VotingPeriodLow, } @@ -626,12 +549,10 @@ pub mod pallet { /// - `value`: The amount of deposit (must be at least `MinimumDeposit`). /// /// Emits `Proposed`. - /// - /// Weight: `O(p)` #[pallet::weight(T::WeightInfo::propose())] pub fn propose( origin: OriginFor<T>, - proposal_hash: T::Hash, + proposal: BoundedCallOf<T>, #[pallet::compact] value: BalanceOf<T>, ) -> DispatchResult { let who = ensure_signed(origin)?; @@ -640,7 +561,8 @@ pub mod pallet { let index = Self::public_prop_count(); let real_prop_count = PublicProps::<T>::decode_len().unwrap_or(0) as u32; let max_proposals = T::MaxProposals::get(); - ensure!(real_prop_count < max_proposals, Error::<T>::TooManyProposals); + ensure!(real_prop_count < max_proposals, Error::<T>::TooMany); + let proposal_hash = proposal.hash(); if let Some((until, _)) = <Blacklist<T>>::get(proposal_hash) { ensure!( @@ -650,10 +572,14 @@ pub mod pallet { } T::Currency::reserve(&who, value)?; + + let depositors = BoundedVec::<_, T::MaxDeposits>::truncate_from(vec![who.clone()]); + DepositOf::<T>::insert(index, (depositors, value)); + PublicPropCount::<T>::put(index + 1); - <DepositOf<T>>::insert(index, (&[&who][..], value)); - <PublicProps<T>>::append((index, proposal_hash, who)); + PublicProps::<T>::try_append((index, proposal, who)) + .map_err(|_| Error::<T>::TooMany)?; Self::deposit_event(Event::<T>::Proposed { proposal_index: index, deposit: value }); Ok(()) @@ -665,23 +591,19 @@ pub mod pallet { /// must have funds to cover the deposit, equal to the original deposit. /// /// - `proposal`: The index of the proposal to second. - /// - `seconds_upper_bound`: an upper bound on the current number of seconds on this - /// proposal. Extrinsic is weighted according to this value with no refund. - /// - /// Weight: `O(S)` where S is the number of seconds a proposal already has. - #[pallet::weight(T::WeightInfo::second(*seconds_upper_bound))] + #[pallet::weight(T::WeightInfo::second())] pub fn second( origin: OriginFor<T>, #[pallet::compact] proposal: PropIndex, - #[pallet::compact] seconds_upper_bound: u32, ) -> DispatchResult { let who = ensure_signed(origin)?; let seconds = Self::len_of_deposit_of(proposal).ok_or(Error::<T>::ProposalMissing)?; - ensure!(seconds <= seconds_upper_bound, Error::<T>::WrongUpperBound); + ensure!(seconds < T::MaxDeposits::get(), Error::<T>::TooMany); let mut deposit = Self::deposit_of(proposal).ok_or(Error::<T>::ProposalMissing)?; T::Currency::reserve(&who, deposit.1)?; - deposit.0.push(who.clone()); + let ok = deposit.0.try_push(who.clone()).is_ok(); + debug_assert!(ok, "`seconds` is below static limit; `try_insert` should succeed; qed"); <DepositOf<T>>::insert(proposal, deposit); Self::deposit_event(Event::<T>::Seconded { seconder: who, prop_index: proposal }); Ok(()) @@ -694,12 +616,7 @@ pub mod pallet { /// /// - `ref_index`: The index of the referendum to vote for. /// - `vote`: The vote configuration. - /// - /// Weight: `O(R)` where R is the number of referendums the voter has voted on. - #[pallet::weight( - T::WeightInfo::vote_new(T::MaxVotes::get()) - .max(T::WeightInfo::vote_existing(T::MaxVotes::get())) - )] + #[pallet::weight(T::WeightInfo::vote_new().max(T::WeightInfo::vote_existing()))] pub fn vote( origin: OriginFor<T>, #[pallet::compact] ref_index: ReferendumIndex, @@ -725,7 +642,7 @@ pub mod pallet { T::CancellationOrigin::ensure_origin(origin)?; let status = Self::referendum_status(ref_index)?; - let h = status.proposal_hash; + let h = status.proposal.hash(); ensure!(!<Cancellations<T>>::contains_key(h), Error::<T>::AlreadyCanceled); <Cancellations<T>>::insert(h, true); @@ -739,20 +656,20 @@ pub mod pallet { /// The dispatch origin of this call must be `ExternalOrigin`. /// /// - `proposal_hash`: The preimage hash of the proposal. - /// - /// Weight: `O(V)` with V number of vetoers in the blacklist of proposal. - /// Decoding vec of length V. Charged as maximum - #[pallet::weight(T::WeightInfo::external_propose(MAX_VETOERS))] - pub fn external_propose(origin: OriginFor<T>, proposal_hash: T::Hash) -> DispatchResult { + #[pallet::weight(T::WeightInfo::external_propose())] + pub fn external_propose( + origin: OriginFor<T>, + proposal: BoundedCallOf<T>, + ) -> DispatchResult { T::ExternalOrigin::ensure_origin(origin)?; ensure!(!<NextExternal<T>>::exists(), Error::<T>::DuplicateProposal); - if let Some((until, _)) = <Blacklist<T>>::get(proposal_hash) { + if let Some((until, _)) = <Blacklist<T>>::get(proposal.hash()) { ensure!( <frame_system::Pallet<T>>::block_number() >= until, Error::<T>::ProposalBlacklisted, ); } - <NextExternal<T>>::put((proposal_hash, VoteThreshold::SuperMajorityApprove)); + <NextExternal<T>>::put((proposal, VoteThreshold::SuperMajorityApprove)); Ok(()) } @@ -770,10 +687,10 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::external_propose_majority())] pub fn external_propose_majority( origin: OriginFor<T>, - proposal_hash: T::Hash, + proposal: BoundedCallOf<T>, ) -> DispatchResult { T::ExternalMajorityOrigin::ensure_origin(origin)?; - <NextExternal<T>>::put((proposal_hash, VoteThreshold::SimpleMajority)); + <NextExternal<T>>::put((proposal, VoteThreshold::SimpleMajority)); Ok(()) } @@ -791,10 +708,10 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::external_propose_default())] pub fn external_propose_default( origin: OriginFor<T>, - proposal_hash: T::Hash, + proposal: BoundedCallOf<T>, ) -> DispatchResult { T::ExternalDefaultOrigin::ensure_origin(origin)?; - <NextExternal<T>>::put((proposal_hash, VoteThreshold::SuperMajorityAgainst)); + <NextExternal<T>>::put((proposal, VoteThreshold::SuperMajorityAgainst)); Ok(()) } @@ -805,7 +722,7 @@ pub mod pallet { /// The dispatch of this call must be `FastTrackOrigin`. /// /// - `proposal_hash`: The hash of the current external proposal. - /// - `voting_period`: The period that is allowed for voting on this proposal. + /// - `voting_period`: The period that is allowed for voting on this proposal. Increased to /// Must be always greater than zero. /// For `FastTrackOrigin` must be equal or greater than `FastTrackVotingPeriod`. /// - `delay`: The number of block after voting has ended in approval and this should be @@ -817,7 +734,7 @@ pub mod pallet { #[pallet::weight(T::WeightInfo::fast_track())] pub fn fast_track( origin: OriginFor<T>, - proposal_hash: T::Hash, + proposal_hash: H256, voting_period: T::BlockNumber, delay: T::BlockNumber, ) -> DispatchResult { @@ -836,20 +753,21 @@ pub mod pallet { T::InstantOrigin::ensure_origin(ensure_instant)?; ensure!(T::InstantAllowed::get(), Error::<T>::InstantNotAllowed); } + ensure!(voting_period > T::BlockNumber::zero(), Error::<T>::VotingPeriodLow); - let (e_proposal_hash, threshold) = + let (ext_proposal, threshold) = <NextExternal<T>>::get().ok_or(Error::<T>::ProposalMissing)?; ensure!( threshold != VoteThreshold::SuperMajorityApprove, Error::<T>::NotSimpleMajority, ); - ensure!(proposal_hash == e_proposal_hash, Error::<T>::InvalidHash); + ensure!(proposal_hash == ext_proposal.hash(), Error::<T>::InvalidHash); <NextExternal<T>>::kill(); let now = <frame_system::Pallet<T>>::block_number(); Self::inject_referendum( now.saturating_add(voting_period), - proposal_hash, + ext_proposal, threshold, delay, ); @@ -865,22 +783,24 @@ pub mod pallet { /// Emits `Vetoed`. /// /// Weight: `O(V + log(V))` where V is number of `existing vetoers` - #[pallet::weight(T::WeightInfo::veto_external(MAX_VETOERS))] - pub fn veto_external(origin: OriginFor<T>, proposal_hash: T::Hash) -> DispatchResult { + #[pallet::weight(T::WeightInfo::veto_external())] + pub fn veto_external(origin: OriginFor<T>, proposal_hash: H256) -> DispatchResult { let who = T::VetoOrigin::ensure_origin(origin)?; - if let Some((e_proposal_hash, _)) = <NextExternal<T>>::get() { - ensure!(proposal_hash == e_proposal_hash, Error::<T>::ProposalMissing); + if let Some((ext_proposal, _)) = NextExternal::<T>::get() { + ensure!(proposal_hash == ext_proposal.hash(), Error::<T>::ProposalMissing); } else { return Err(Error::<T>::NoProposal.into()) } let mut existing_vetoers = - <Blacklist<T>>::get(&proposal_hash).map(|pair| pair.1).unwrap_or_else(Vec::new); + <Blacklist<T>>::get(&proposal_hash).map(|pair| pair.1).unwrap_or_default(); let insert_position = existing_vetoers.binary_search(&who).err().ok_or(Error::<T>::AlreadyVetoed)?; + existing_vetoers + .try_insert(insert_position, who.clone()) + .map_err(|_| Error::<T>::TooMany)?; - existing_vetoers.insert(insert_position, who.clone()); let until = <frame_system::Pallet<T>>::block_number().saturating_add(T::CooloffPeriod::get()); <Blacklist<T>>::insert(&proposal_hash, (until, existing_vetoers)); @@ -907,21 +827,6 @@ pub mod pallet { Ok(()) } - /// Cancel a proposal queued for enactment. - /// - /// The dispatch origin of this call must be _Root_. - /// - /// - `which`: The index of the referendum to cancel. - /// - /// Weight: `O(D)` where `D` is the items in the dispatch queue. Weighted as `D = 10`. - #[pallet::weight((T::WeightInfo::cancel_queued(10), DispatchClass::Operational))] - pub fn cancel_queued(origin: OriginFor<T>, which: ReferendumIndex) -> DispatchResult { - ensure_root(origin)?; - T::Scheduler::cancel_named((DEMOCRACY_ID, which).encode()) - .map_err(|_| Error::<T>::ProposalMissing)?; - Ok(()) - } - /// Delegate the voting power (with some given conviction) of the sending account. /// /// The balance delegated is locked for as long as it's delegated, and thereafter for the @@ -991,135 +896,6 @@ pub mod pallet { Ok(()) } - /// Register the preimage for an upcoming proposal. This doesn't require the proposal to be - /// in the dispatch queue but does require a deposit, returned once enacted. - /// - /// The dispatch origin of this call must be _Signed_. - /// - /// - `encoded_proposal`: The preimage of a proposal. - /// - /// Emits `PreimageNoted`. - /// - /// Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit). - #[pallet::weight(T::WeightInfo::note_preimage(encoded_proposal.len() as u32))] - pub fn note_preimage(origin: OriginFor<T>, encoded_proposal: Vec<u8>) -> DispatchResult { - Self::note_preimage_inner(ensure_signed(origin)?, encoded_proposal)?; - Ok(()) - } - - /// Same as `note_preimage` but origin is `OperationalPreimageOrigin`. - #[pallet::weight(( - T::WeightInfo::note_preimage(encoded_proposal.len() as u32), - DispatchClass::Operational, - ))] - pub fn note_preimage_operational( - origin: OriginFor<T>, - encoded_proposal: Vec<u8>, - ) -> DispatchResult { - let who = T::OperationalPreimageOrigin::ensure_origin(origin)?; - Self::note_preimage_inner(who, encoded_proposal)?; - Ok(()) - } - - /// Register the preimage for an upcoming proposal. This requires the proposal to be - /// in the dispatch queue. No deposit is needed. When this call is successful, i.e. - /// the preimage has not been uploaded before and matches some imminent proposal, - /// no fee is paid. - /// - /// The dispatch origin of this call must be _Signed_. - /// - /// - `encoded_proposal`: The preimage of a proposal. - /// - /// Emits `PreimageNoted`. - /// - /// Weight: `O(E)` with E size of `encoded_proposal` (protected by a required deposit). - #[pallet::weight(T::WeightInfo::note_imminent_preimage(encoded_proposal.len() as u32))] - pub fn note_imminent_preimage( - origin: OriginFor<T>, - encoded_proposal: Vec<u8>, - ) -> DispatchResultWithPostInfo { - Self::note_imminent_preimage_inner(ensure_signed(origin)?, encoded_proposal)?; - // We check that this preimage was not uploaded before in - // `note_imminent_preimage_inner`, thus this call can only be successful once. If - // successful, user does not pay a fee. - Ok(Pays::No.into()) - } - - /// Same as `note_imminent_preimage` but origin is `OperationalPreimageOrigin`. - #[pallet::weight(( - T::WeightInfo::note_imminent_preimage(encoded_proposal.len() as u32), - DispatchClass::Operational, - ))] - pub fn note_imminent_preimage_operational( - origin: OriginFor<T>, - encoded_proposal: Vec<u8>, - ) -> DispatchResultWithPostInfo { - let who = T::OperationalPreimageOrigin::ensure_origin(origin)?; - Self::note_imminent_preimage_inner(who, encoded_proposal)?; - // We check that this preimage was not uploaded before in - // `note_imminent_preimage_inner`, thus this call can only be successful once. If - // successful, user does not pay a fee. - Ok(Pays::No.into()) - } - - /// Remove an expired proposal preimage and collect the deposit. - /// - /// The dispatch origin of this call must be _Signed_. - /// - /// - `proposal_hash`: The preimage hash of a proposal. - /// - `proposal_length_upper_bound`: an upper bound on length of the proposal. Extrinsic is - /// weighted according to this value with no refund. - /// - /// This will only work after `VotingPeriod` blocks from the time that the preimage was - /// noted, if it's the same account doing it. If it's a different account, then it'll only - /// work an additional `EnactmentPeriod` later. - /// - /// Emits `PreimageReaped`. - /// - /// Weight: `O(D)` where D is length of proposal. - #[pallet::weight(T::WeightInfo::reap_preimage(*proposal_len_upper_bound))] - pub fn reap_preimage( - origin: OriginFor<T>, - proposal_hash: T::Hash, - #[pallet::compact] proposal_len_upper_bound: u32, - ) -> DispatchResult { - let who = ensure_signed(origin)?; - - ensure!( - Self::pre_image_data_len(proposal_hash)? <= proposal_len_upper_bound, - Error::<T>::WrongUpperBound, - ); - - let (provider, deposit, since, expiry) = <Preimages<T>>::get(&proposal_hash) - .and_then(|m| match m { - PreimageStatus::Available { provider, deposit, since, expiry, .. } => - Some((provider, deposit, since, expiry)), - _ => None, - }) - .ok_or(Error::<T>::PreimageMissing)?; - - let now = <frame_system::Pallet<T>>::block_number(); - let (voting, enactment) = (T::VotingPeriod::get(), T::EnactmentPeriod::get()); - let additional = if who == provider { Zero::zero() } else { enactment }; - ensure!( - now >= since.saturating_add(voting).saturating_add(additional), - Error::<T>::TooEarly - ); - ensure!(expiry.map_or(true, |e| now > e), Error::<T>::Imminent); - - let res = - T::Currency::repatriate_reserved(&provider, &who, deposit, BalanceStatus::Free); - debug_assert!(res.is_ok()); - <Preimages<T>>::remove(&proposal_hash); - Self::deposit_event(Event::<T>::PreimageReaped { - proposal_hash, - provider, - deposit, - reaper: who, - }); - Ok(()) - } - /// Unlock tokens that have an expired lock. /// /// The dispatch origin of this call must be _Signed_. @@ -1127,10 +903,7 @@ pub mod pallet { /// - `target`: The account to remove the lock on. /// /// Weight: `O(R)` with R number of vote of target. - #[pallet::weight( - T::WeightInfo::unlock_set(T::MaxVotes::get()) - .max(T::WeightInfo::unlock_remove(T::MaxVotes::get())) - )] + #[pallet::weight(T::WeightInfo::unlock_set(T::MaxVotes::get()).max(T::WeightInfo::unlock_remove(T::MaxVotes::get())))] pub fn unlock(origin: OriginFor<T>, target: AccountIdLookupOf<T>) -> DispatchResult { ensure_signed(origin)?; let target = T::Lookup::lookup(target)?; @@ -1199,17 +972,6 @@ pub mod pallet { Ok(()) } - /// Enact a proposal from a referendum. For now we just make the weight be the maximum. - #[pallet::weight(T::BlockWeights::get().max_block)] - pub fn enact_proposal( - origin: OriginFor<T>, - proposal_hash: T::Hash, - index: ReferendumIndex, - ) -> DispatchResult { - ensure_root(origin)?; - Self::do_enact_proposal(proposal_hash, index) - } - /// Permanently place a proposal into the blacklist. This prevents it from ever being /// proposed again. /// @@ -1225,21 +987,21 @@ pub mod pallet { /// /// Weight: `O(p)` (though as this is an high-privilege dispatch, we assume it has a /// reasonable value). - #[pallet::weight((T::WeightInfo::blacklist(T::MaxProposals::get()), DispatchClass::Operational))] + #[pallet::weight((T::WeightInfo::blacklist(), DispatchClass::Operational))] pub fn blacklist( origin: OriginFor<T>, - proposal_hash: T::Hash, + proposal_hash: H256, maybe_ref_index: Option<ReferendumIndex>, ) -> DispatchResult { T::BlacklistOrigin::ensure_origin(origin)?; // Insert the proposal into the blacklist. - let permanent = (T::BlockNumber::max_value(), Vec::<T::AccountId>::new()); + let permanent = (T::BlockNumber::max_value(), BoundedVec::<T::AccountId, _>::default()); Blacklist::<T>::insert(&proposal_hash, permanent); // Remove the queued proposal, if it's there. PublicProps::<T>::mutate(|props| { - if let Some(index) = props.iter().position(|p| p.1 == proposal_hash) { + if let Some(index) = props.iter().position(|p| p.1.hash() == proposal_hash) { let (prop_index, ..) = props.remove(index); if let Some((whos, amount)) = DepositOf::<T>::take(prop_index) { for who in whos.into_iter() { @@ -1250,14 +1012,14 @@ pub mod pallet { }); // Remove the external queued referendum, if it's there. - if matches!(NextExternal::<T>::get(), Some((h, ..)) if h == proposal_hash) { + if matches!(NextExternal::<T>::get(), Some((p, ..)) if p.hash() == proposal_hash) { NextExternal::<T>::kill(); } // Remove the referendum, if it's there. if let Some(ref_index) = maybe_ref_index { if let Ok(status) = Self::referendum_status(ref_index) { - if status.proposal_hash == proposal_hash { + if status.proposal.hash() == proposal_hash { Self::internal_cancel_referendum(ref_index); } } @@ -1274,7 +1036,7 @@ pub mod pallet { /// - `prop_index`: The index of the proposal to cancel. /// /// Weight: `O(p)` where `p = PublicProps::<T>::decode_len()` - #[pallet::weight(T::WeightInfo::cancel_proposal(T::MaxProposals::get()))] + #[pallet::weight(T::WeightInfo::cancel_proposal())] pub fn cancel_proposal( origin: OriginFor<T>, #[pallet::compact] prop_index: PropIndex, @@ -1294,6 +1056,25 @@ pub mod pallet { } } +pub trait EncodeInto: Encode { + fn encode_into<T: AsMut<[u8]> + Default>(&self) -> T { + let mut t = T::default(); + self.using_encoded(|data| { + if data.len() <= t.as_mut().len() { + t.as_mut()[0..data.len()].copy_from_slice(data); + } else { + // encoded self is too big to fit into a T. hash it and use the first bytes of that + // instead. + let hash = sp_io::hashing::blake2_256(data); + let l = t.as_mut().len().min(hash.len()); + t.as_mut()[0..l].copy_from_slice(&hash[0..l]); + } + }); + t + } +} +impl<T: Encode> EncodeInto for T {} + impl<T: Config> Pallet<T> { // exposed immutables. @@ -1306,7 +1087,7 @@ impl<T: Config> Pallet<T> { /// Get all referenda ready for tally at block `n`. pub fn maturing_referenda_at( n: T::BlockNumber, - ) -> Vec<(ReferendumIndex, ReferendumStatus<T::BlockNumber, T::Hash, BalanceOf<T>>)> { + ) -> Vec<(ReferendumIndex, ReferendumStatus<T::BlockNumber, BoundedCallOf<T>, BalanceOf<T>>)> { let next = Self::lowest_unbaked(); let last = Self::referendum_count(); Self::maturing_referenda_at_inner(n, next..last) @@ -1315,7 +1096,7 @@ impl<T: Config> Pallet<T> { fn maturing_referenda_at_inner( n: T::BlockNumber, range: core::ops::Range<PropIndex>, - ) -> Vec<(ReferendumIndex, ReferendumStatus<T::BlockNumber, T::Hash, BalanceOf<T>>)> { + ) -> Vec<(ReferendumIndex, ReferendumStatus<T::BlockNumber, BoundedCallOf<T>, BalanceOf<T>>)> { range .into_iter() .map(|i| (i, Self::referendum_info(i))) @@ -1331,13 +1112,13 @@ impl<T: Config> Pallet<T> { /// Start a referendum. pub fn internal_start_referendum( - proposal_hash: T::Hash, + proposal: BoundedCallOf<T>, threshold: VoteThreshold, delay: T::BlockNumber, ) -> ReferendumIndex { <Pallet<T>>::inject_referendum( <frame_system::Pallet<T>>::block_number().saturating_add(T::VotingPeriod::get()), - proposal_hash, + proposal, threshold, delay, ) @@ -1353,8 +1134,8 @@ impl<T: Config> Pallet<T> { /// Ok if the given referendum is active, Err otherwise fn ensure_ongoing( - r: ReferendumInfo<T::BlockNumber, T::Hash, BalanceOf<T>>, - ) -> Result<ReferendumStatus<T::BlockNumber, T::Hash, BalanceOf<T>>, DispatchError> { + r: ReferendumInfo<T::BlockNumber, BoundedCallOf<T>, BalanceOf<T>>, + ) -> Result<ReferendumStatus<T::BlockNumber, BoundedCallOf<T>, BalanceOf<T>>, DispatchError> { match r { ReferendumInfo::Ongoing(s) => Ok(s), _ => Err(Error::<T>::ReferendumInvalid.into()), @@ -1363,7 +1144,7 @@ impl<T: Config> Pallet<T> { fn referendum_status( ref_index: ReferendumIndex, - ) -> Result<ReferendumStatus<T::BlockNumber, T::Hash, BalanceOf<T>>, DispatchError> { + ) -> Result<ReferendumStatus<T::BlockNumber, BoundedCallOf<T>, BalanceOf<T>>, DispatchError> { let info = ReferendumInfoOf::<T>::get(ref_index).ok_or(Error::<T>::ReferendumInvalid)?; Self::ensure_ongoing(info) } @@ -1388,11 +1169,9 @@ impl<T: Config> Pallet<T> { votes[i].1 = vote; }, Err(i) => { - ensure!( - votes.len() as u32 <= T::MaxVotes::get(), - Error::<T>::MaxVotesReached - ); - votes.insert(i, (ref_index, vote)); + votes + .try_insert(i, (ref_index, vote)) + .map_err(|_| Error::<T>::MaxVotesReached)?; }, } Self::deposit_event(Event::<T>::Voted { voter: who.clone(), ref_index, vote }); @@ -1606,14 +1385,14 @@ impl<T: Config> Pallet<T> { /// Start a referendum fn inject_referendum( end: T::BlockNumber, - proposal_hash: T::Hash, + proposal: BoundedCallOf<T>, threshold: VoteThreshold, delay: T::BlockNumber, ) -> ReferendumIndex { let ref_index = Self::referendum_count(); ReferendumCount::<T>::put(ref_index + 1); let status = - ReferendumStatus { end, proposal_hash, threshold, delay, tally: Default::default() }; + ReferendumStatus { end, proposal, threshold, delay, tally: Default::default() }; let item = ReferendumInfo::Ongoing(status); <ReferendumInfoOf<T>>::insert(ref_index, item); Self::deposit_event(Event::<T>::Started { ref_index, threshold }); @@ -1659,14 +1438,10 @@ impl<T: Config> Pallet<T> { if let Some((depositors, deposit)) = <DepositOf<T>>::take(prop_index) { // refund depositors - for d in &depositors { + for d in depositors.iter() { T::Currency::unreserve(d, deposit); } - Self::deposit_event(Event::<T>::Tabled { - proposal_index: prop_index, - deposit, - depositors, - }); + Self::deposit_event(Event::<T>::Tabled { proposal_index: prop_index, deposit }); Self::inject_referendum( now.saturating_add(T::VotingPeriod::get()), proposal, @@ -1680,71 +1455,35 @@ impl<T: Config> Pallet<T> { } } - fn do_enact_proposal(proposal_hash: T::Hash, index: ReferendumIndex) -> DispatchResult { - let preimage = <Preimages<T>>::take(&proposal_hash); - if let Some(PreimageStatus::Available { data, provider, deposit, .. }) = preimage { - if let Ok(proposal) = T::Proposal::decode(&mut &data[..]) { - let err_amount = T::Currency::unreserve(&provider, deposit); - debug_assert!(err_amount.is_zero()); - Self::deposit_event(Event::<T>::PreimageUsed { proposal_hash, provider, deposit }); - - let res = proposal - .dispatch(frame_system::RawOrigin::Root.into()) - .map(|_| ()) - .map_err(|e| e.error); - Self::deposit_event(Event::<T>::Executed { ref_index: index, result: res }); - - Ok(()) - } else { - T::Slash::on_unbalanced(T::Currency::slash_reserved(&provider, deposit).0); - Self::deposit_event(Event::<T>::PreimageInvalid { - proposal_hash, - ref_index: index, - }); - Err(Error::<T>::PreimageInvalid.into()) - } - } else { - Self::deposit_event(Event::<T>::PreimageMissing { proposal_hash, ref_index: index }); - Err(Error::<T>::PreimageMissing.into()) - } - } - fn bake_referendum( now: T::BlockNumber, index: ReferendumIndex, - status: ReferendumStatus<T::BlockNumber, T::Hash, BalanceOf<T>>, + status: ReferendumStatus<T::BlockNumber, BoundedCallOf<T>, BalanceOf<T>>, ) -> bool { let total_issuance = T::Currency::total_issuance(); let approved = status.threshold.approved(status.tally, total_issuance); if approved { Self::deposit_event(Event::<T>::Passed { ref_index: index }); - if status.delay.is_zero() { - let _ = Self::do_enact_proposal(status.proposal_hash, index); - } else { - let when = now.saturating_add(status.delay); - // Note that we need the preimage now. - Preimages::<T>::mutate_exists( - &status.proposal_hash, - |maybe_pre| match *maybe_pre { - Some(PreimageStatus::Available { ref mut expiry, .. }) => - *expiry = Some(when), - ref mut a => *a = Some(PreimageStatus::Missing(when)), - }, - ); - - if T::Scheduler::schedule_named( - (DEMOCRACY_ID, index).encode(), - DispatchTime::At(when), - None, - 63, - frame_system::RawOrigin::Root.into(), - Call::enact_proposal { proposal_hash: status.proposal_hash, index }.into(), - ) - .is_err() - { - frame_support::print("LOGIC ERROR: bake_referendum/schedule_named failed"); - } + // Actually `hold` the proposal now since we didn't hold it when it came in via the + // submit extrinsic and we now know that it will be needed. This will be reversed by + // Scheduler pallet once it is executed which assumes that we will already have placed + // a `hold` on it. + T::Preimages::hold(&status.proposal); + + // Earliest it can be scheduled for is next block. + let when = now.saturating_add(status.delay.max(One::one())); + if T::Scheduler::schedule_named( + (DEMOCRACY_ID, index).encode_into(), + DispatchTime::At(when), + None, + 63, + frame_system::RawOrigin::Root.into(), + status.proposal, + ) + .is_err() + { + frame_support::print("LOGIC ERROR: bake_referendum/schedule_named failed"); } } else { Self::deposit_event(Event::<T>::NotPassed { ref_index: index }); @@ -1780,11 +1519,10 @@ impl<T: Config> Pallet<T> { if Self::launch_next(now).is_ok() { weight = max_block_weight; } else { - weight = - weight.saturating_add(T::WeightInfo::on_initialize_base_with_launch_period(r)); + weight.saturating_accrue(T::WeightInfo::on_initialize_base_with_launch_period(r)); } } else { - weight = weight.saturating_add(T::WeightInfo::on_initialize_base(r)); + weight.saturating_accrue(T::WeightInfo::on_initialize_base(r)); } // tally up votes for any expiring referenda. @@ -1795,8 +1533,8 @@ impl<T: Config> Pallet<T> { } // Notes: - // * We don't consider the lowest unbaked to be the last maturing in case some refendum have - // longer voting period than others. + // * We don't consider the lowest unbaked to be the last maturing in case some referenda + // have a longer voting period than others. // * The iteration here shouldn't trigger any storage read that are not in cache, due to // `maturing_referenda_at_inner` having already read them. // * We shouldn't iterate more than `LaunchPeriod/VotingPeriod + 1` times because the number @@ -1822,116 +1560,6 @@ impl<T: Config> Pallet<T> { // `Compact<u32>`. decode_compact_u32_at(&<DepositOf<T>>::hashed_key_for(proposal)) } - - /// Check that pre image exists and its value is variant `PreimageStatus::Missing`. - /// - /// This check is done without getting the complete value in the runtime to avoid copying a big - /// value in the runtime. - fn check_pre_image_is_missing(proposal_hash: T::Hash) -> DispatchResult { - // To decode the enum variant we only need the first byte. - let mut buf = [0u8; 1]; - let key = <Preimages<T>>::hashed_key_for(proposal_hash); - let bytes = sp_io::storage::read(&key, &mut buf, 0).ok_or(Error::<T>::NotImminent)?; - // The value may be smaller that 1 byte. - let mut input = &buf[0..buf.len().min(bytes as usize)]; - - match input.read_byte() { - Ok(0) => Ok(()), // PreimageStatus::Missing is variant 0 - Ok(1) => Err(Error::<T>::DuplicatePreimage.into()), - _ => { - sp_runtime::print("Failed to decode `PreimageStatus` variant"); - Err(Error::<T>::NotImminent.into()) - }, - } - } - - /// Check that pre image exists, its value is variant `PreimageStatus::Available` and decode - /// the length of `data: Vec<u8>` fields. - /// - /// This check is done without getting the complete value in the runtime to avoid copying a big - /// value in the runtime. - /// - /// If the pre image is missing variant or doesn't exist then the error `PreimageMissing` is - /// returned. - fn pre_image_data_len(proposal_hash: T::Hash) -> Result<u32, DispatchError> { - // To decode the `data` field of Available variant we need: - // * one byte for the variant - // * at most 5 bytes to decode a `Compact<u32>` - let mut buf = [0u8; 6]; - let key = <Preimages<T>>::hashed_key_for(proposal_hash); - let bytes = sp_io::storage::read(&key, &mut buf, 0).ok_or(Error::<T>::PreimageMissing)?; - // The value may be smaller that 6 bytes. - let mut input = &buf[0..buf.len().min(bytes as usize)]; - - match input.read_byte() { - Ok(1) => (), // Check that input exists and is second variant. - Ok(0) => return Err(Error::<T>::PreimageMissing.into()), - _ => { - sp_runtime::print("Failed to decode `PreimageStatus` variant"); - return Err(Error::<T>::PreimageMissing.into()) - }, - } - - // Decode the length of the vector. - let len = codec::Compact::<u32>::decode(&mut input) - .map_err(|_| { - sp_runtime::print("Failed to decode `PreimageStatus` variant"); - DispatchError::from(Error::<T>::PreimageMissing) - })? - .0; - - Ok(len) - } - - // See `note_preimage` - fn note_preimage_inner(who: T::AccountId, encoded_proposal: Vec<u8>) -> DispatchResult { - let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); - ensure!(!<Preimages<T>>::contains_key(&proposal_hash), Error::<T>::DuplicatePreimage); - - let deposit = <BalanceOf<T>>::from(encoded_proposal.len() as u32) - .saturating_mul(T::PreimageByteDeposit::get()); - T::Currency::reserve(&who, deposit)?; - - let now = <frame_system::Pallet<T>>::block_number(); - let a = PreimageStatus::Available { - data: encoded_proposal, - provider: who.clone(), - deposit, - since: now, - expiry: None, - }; - <Preimages<T>>::insert(proposal_hash, a); - - Self::deposit_event(Event::<T>::PreimageNoted { proposal_hash, who, deposit }); - - Ok(()) - } - - // See `note_imminent_preimage` - fn note_imminent_preimage_inner( - who: T::AccountId, - encoded_proposal: Vec<u8>, - ) -> DispatchResult { - let proposal_hash = T::Hashing::hash(&encoded_proposal[..]); - Self::check_pre_image_is_missing(proposal_hash)?; - let status = Preimages::<T>::get(&proposal_hash).ok_or(Error::<T>::NotImminent)?; - let expiry = status.to_missing_expiry().ok_or(Error::<T>::DuplicatePreimage)?; - - let now = <frame_system::Pallet<T>>::block_number(); - let free = <BalanceOf<T>>::zero(); - let a = PreimageStatus::Available { - data: encoded_proposal, - provider: who.clone(), - deposit: Zero::zero(), - since: now, - expiry: Some(expiry), - }; - <Preimages<T>>::insert(proposal_hash, a); - - Self::deposit_event(Event::<T>::PreimageNoted { proposal_hash, who, deposit: free }); - - Ok(()) - } } /// Decode `Compact<u32>` from the trie at given key. diff --git a/substrate/frame/democracy/src/migrations.rs b/substrate/frame/democracy/src/migrations.rs new file mode 100644 index 0000000000000000000000000000000000000000..3ec249c1d981c8d525988d731864de0303ffbc56 --- /dev/null +++ b/substrate/frame/democracy/src/migrations.rs @@ -0,0 +1,236 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 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. + +//! Storage migrations for the preimage pallet. + +use super::*; +use frame_support::{pallet_prelude::*, storage_alias, traits::OnRuntimeUpgrade, BoundedVec}; +use sp_core::H256; + +/// The log target. +const TARGET: &'static str = "runtime::democracy::migration::v1"; + +/// The original data layout of the democracy pallet without a specific version number. +mod v0 { + use super::*; + + #[storage_alias] + pub type PublicProps<T: Config> = StorageValue< + Pallet<T>, + Vec<(PropIndex, <T as frame_system::Config>::Hash, <T as frame_system::Config>::AccountId)>, + ValueQuery, + >; + + #[storage_alias] + pub type NextExternal<T: Config> = + StorageValue<Pallet<T>, (<T as frame_system::Config>::Hash, VoteThreshold)>; + + #[cfg(feature = "try-runtime")] + #[storage_alias] + pub type ReferendumInfoOf<T: Config> = StorageMap< + Pallet<T>, + frame_support::Twox64Concat, + ReferendumIndex, + ReferendumInfo< + <T as frame_system::Config>::BlockNumber, + <T as frame_system::Config>::Hash, + BalanceOf<T>, + >, + >; +} + +pub mod v1 { + use super::*; + + /// Migration for translating bare `Hash`es into `Bounded<Call>`s. + pub struct Migration<T>(sp_std::marker::PhantomData<T>); + + impl<T: Config + frame_system::Config<Hash = H256>> OnRuntimeUpgrade for Migration<T> { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<Vec<u8>, &'static str> { + assert_eq!(StorageVersion::get::<Pallet<T>>(), 0, "can only upgrade from version 0"); + + let props_count = v0::PublicProps::<T>::get().len(); + log::info!(target: TARGET, "{} public proposals will be migrated.", props_count,); + ensure!(props_count <= T::MaxProposals::get() as usize, "too many proposals"); + + let referenda_count = v0::ReferendumInfoOf::<T>::iter().count(); + log::info!(target: TARGET, "{} referenda will be migrated.", referenda_count); + + Ok((props_count as u32, referenda_count as u32).encode()) + } + + #[allow(deprecated)] + fn on_runtime_upgrade() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + if StorageVersion::get::<Pallet<T>>() != 0 { + log::warn!( + target: TARGET, + "skipping on_runtime_upgrade: executed on wrong storage version.\ + Expected version 0" + ); + return weight + } + + ReferendumInfoOf::<T>::translate( + |index, old: ReferendumInfo<T::BlockNumber, T::Hash, BalanceOf<T>>| { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + log::info!(target: TARGET, "migrating referendum #{:?}", &index); + Some(match old { + ReferendumInfo::Ongoing(status) => + ReferendumInfo::Ongoing(ReferendumStatus { + end: status.end, + proposal: Bounded::from_legacy_hash(status.proposal), + threshold: status.threshold, + delay: status.delay, + tally: status.tally, + }), + ReferendumInfo::Finished { approved, end } => + ReferendumInfo::Finished { approved, end }, + }) + }, + ); + + let props = v0::PublicProps::<T>::take() + .into_iter() + .map(|(i, hash, a)| (i, Bounded::from_legacy_hash(hash), a)) + .collect::<Vec<_>>(); + let bounded = BoundedVec::<_, T::MaxProposals>::truncate_from(props.clone()); + PublicProps::<T>::put(bounded); + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + + if props.len() as u32 > T::MaxProposals::get() { + log::error!( + target: TARGET, + "truncated {} public proposals to {}; continuing", + props.len(), + T::MaxProposals::get() + ); + } + + if let Some((hash, threshold)) = v0::NextExternal::<T>::take() { + log::info!(target: TARGET, "migrating next external proposal"); + NextExternal::<T>::put((Bounded::from_legacy_hash(hash), threshold)); + } + + StorageVersion::new(1).put::<Pallet<T>>(); + + weight.saturating_add(T::DbWeight::get().reads_writes(1, 2)) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { + assert_eq!(StorageVersion::get::<Pallet<T>>(), 1, "must upgrade"); + + let (old_props_count, old_ref_count): (u32, u32) = + Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed"); + let new_props_count = crate::PublicProps::<T>::get().len() as u32; + assert_eq!(new_props_count, old_props_count, "must migrate all public proposals"); + let new_ref_count = crate::ReferendumInfoOf::<T>::iter().count() as u32; + assert_eq!(new_ref_count, old_ref_count, "must migrate all referenda"); + + log::info!( + target: TARGET, + "{} public proposals migrated, {} referenda migrated", + new_props_count, + new_ref_count, + ); + Ok(()) + } + } +} + +#[cfg(test)] +#[cfg(feature = "try-runtime")] +mod test { + use super::*; + use crate::{ + tests::{Test as T, *}, + types::*, + }; + use frame_support::bounded_vec; + + #[allow(deprecated)] + #[test] + fn migration_works() { + new_test_ext().execute_with(|| { + assert_eq!(StorageVersion::get::<Pallet<T>>(), 0); + // Insert some values into the v0 storage: + + // Case 1: Ongoing referendum + let hash = H256::repeat_byte(1); + let status = ReferendumStatus { + end: 1u32.into(), + proposal: hash.clone(), + threshold: VoteThreshold::SuperMajorityApprove, + delay: 1u32.into(), + tally: Tally { ayes: 1u32.into(), nays: 1u32.into(), turnout: 1u32.into() }, + }; + v0::ReferendumInfoOf::<T>::insert(1u32, ReferendumInfo::Ongoing(status)); + + // Case 2: Finished referendum + v0::ReferendumInfoOf::<T>::insert( + 2u32, + ReferendumInfo::Finished { approved: true, end: 123u32.into() }, + ); + + // Case 3: Public proposals + let hash2 = H256::repeat_byte(2); + v0::PublicProps::<T>::put(vec![ + (3u32, hash.clone(), 123u64), + (4u32, hash2.clone(), 123u64), + ]); + + // Case 4: Next external + v0::NextExternal::<T>::put((hash.clone(), VoteThreshold::SuperMajorityApprove)); + + // Migrate. + let state = v1::Migration::<T>::pre_upgrade().unwrap(); + let _weight = v1::Migration::<T>::on_runtime_upgrade(); + v1::Migration::<T>::post_upgrade(state).unwrap(); + // Check that all values got migrated. + + // Case 1: Ongoing referendum + assert_eq!( + ReferendumInfoOf::<T>::get(1u32), + Some(ReferendumInfo::Ongoing(ReferendumStatus { + end: 1u32.into(), + proposal: Bounded::from_legacy_hash(hash), + threshold: VoteThreshold::SuperMajorityApprove, + delay: 1u32.into(), + tally: Tally { ayes: 1u32.into(), nays: 1u32.into(), turnout: 1u32.into() }, + })) + ); + // Case 2: Finished referendum + assert_eq!( + ReferendumInfoOf::<T>::get(2u32), + Some(ReferendumInfo::Finished { approved: true, end: 123u32.into() }) + ); + // Case 3: Public proposals + let props: BoundedVec<_, <Test as Config>::MaxProposals> = bounded_vec![ + (3u32, Bounded::from_legacy_hash(hash), 123u64), + (4u32, Bounded::from_legacy_hash(hash2), 123u64) + ]; + assert_eq!(PublicProps::<T>::get(), props); + // Case 4: Next external + assert_eq!( + NextExternal::<T>::get(), + Some((Bounded::from_legacy_hash(hash), VoteThreshold::SuperMajorityApprove)) + ); + }); + } +} diff --git a/substrate/frame/democracy/src/tests.rs b/substrate/frame/democracy/src/tests.rs index 03d7216fd5aaa38f2cd53c104349d364af3edf43..eceb1a3400bbac97d080322aee8177549ef8e6c6 100644 --- a/substrate/frame/democracy/src/tests.rs +++ b/substrate/frame/democracy/src/tests.rs @@ -19,11 +19,11 @@ use super::*; use crate as pallet_democracy; -use codec::Encode; use frame_support::{ assert_noop, assert_ok, ord_parameter_types, parameter_types, traits::{ - ConstU32, ConstU64, Contains, EqualPrivilegeOnly, GenesisBuild, OnInitialize, SortedMembers, + ConstU32, ConstU64, Contains, EqualPrivilegeOnly, GenesisBuild, OnInitialize, + SortedMembers, StorePreimage, }, weights::Weight, }; @@ -35,14 +35,12 @@ use sp_runtime::{ traits::{BadOrigin, BlakeTwo256, IdentityLookup}, Perbill, }; - mod cancellation; mod decoders; mod delegation; mod external_proposing; mod fast_tracking; mod lock_voting; -mod preimage; mod public_proposals; mod scheduling; mod voting; @@ -63,6 +61,7 @@ frame_support::construct_runtime!( { System: frame_system::{Pallet, Call, Config, Storage, Event<T>}, Balances: pallet_balances::{Pallet, Call, Storage, Config<T>, Event<T>}, + Preimage: pallet_preimage, Scheduler: pallet_scheduler::{Pallet, Call, Storage, Event<T>}, Democracy: pallet_democracy::{Pallet, Call, Storage, Config<T>, Event<T>}, } @@ -78,13 +77,11 @@ impl Contains<RuntimeCall> for BaseFilter { parameter_types! { pub BlockWeights: frame_system::limits::BlockWeights = - frame_system::limits::BlockWeights::simple_max( - Weight::from_ref_time(1_000_000).set_proof_size(u64::MAX), - ); + frame_system::limits::BlockWeights::simple_max(frame_support::weights::constants::WEIGHT_PER_SECOND.set_proof_size(u64::MAX)); } impl frame_system::Config for Test { type BaseCallFilter = BaseFilter; - type BlockWeights = (); + type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = (); type RuntimeOrigin = RuntimeOrigin; @@ -111,6 +108,16 @@ impl frame_system::Config for Test { parameter_types! { pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; } + +impl pallet_preimage::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); + type Currency = Balances; + type ManagerOrigin = EnsureRoot<u64>; + type BaseDeposit = ConstU64<0>; + type ByteDeposit = ConstU64<0>; +} + impl pallet_scheduler::Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; @@ -118,11 +125,10 @@ impl pallet_scheduler::Config for Test { type RuntimeCall = RuntimeCall; type MaximumWeight = MaximumSchedulerWeight; type ScheduleOrigin = EnsureRoot<u64>; - type MaxScheduledPerBlock = (); + type MaxScheduledPerBlock = ConstU32<100>; type WeightInfo = (); type OriginPrivilegeCmp = EqualPrivilegeOnly; - type PreimageProvider = (); - type NoPreimagePostponement = (); + type Preimages = (); } impl pallet_balances::Config for Test { @@ -158,7 +164,6 @@ impl SortedMembers<u64> for OneToFive { } impl Config for Test { - type Proposal = RuntimeCall; type RuntimeEvent = RuntimeEvent; type Currency = pallet_balances::Pallet<Self>; type EnactmentPeriod = ConstU64<2>; @@ -167,6 +172,8 @@ impl Config for Test { type VoteLockingPeriod = ConstU64<3>; type FastTrackVotingPeriod = ConstU64<2>; type MinimumDeposit = ConstU64<1>; + type MaxDeposits = ConstU32<1000>; + type MaxBlacklisted = ConstU32<5>; type ExternalOrigin = EnsureSignedBy<Two, u64>; type ExternalMajorityOrigin = EnsureSignedBy<Three, u64>; type ExternalDefaultOrigin = EnsureSignedBy<One, u64>; @@ -176,16 +183,15 @@ impl Config for Test { type CancelProposalOrigin = EnsureRoot<u64>; type VetoOrigin = EnsureSignedBy<OneToFive, u64>; type CooloffPeriod = ConstU64<2>; - type PreimageByteDeposit = PreimageByteDeposit; type Slash = (); type InstantOrigin = EnsureSignedBy<Six, u64>; type InstantAllowed = InstantAllowed; type Scheduler = Scheduler; type MaxVotes = ConstU32<100>; - type OperationalPreimageOrigin = EnsureSignedBy<Six, u64>; type PalletsOrigin = OriginCaller; type WeightInfo = (); type MaxProposals = ConstU32<100>; + type Preimages = Preimage; } pub fn new_test_ext() -> sp_io::TestExternalities { @@ -203,12 +209,6 @@ pub fn new_test_ext() -> sp_io::TestExternalities { ext } -/// Execute the function two times, with `true` and with `false`. -pub fn new_test_ext_execute_with_cond(execute: impl FnOnce(bool) -> () + Clone) { - new_test_ext().execute_with(|| (execute.clone())(false)); - new_test_ext().execute_with(|| execute(true)); -} - #[test] fn params_should_work() { new_test_ext().execute_with(|| { @@ -218,44 +218,22 @@ fn params_should_work() { }); } -fn set_balance_proposal(value: u64) -> Vec<u8> { - RuntimeCall::Balances(pallet_balances::Call::set_balance { - who: 42, - new_free: value, - new_reserved: 0, - }) - .encode() +fn set_balance_proposal(value: u64) -> BoundedCallOf<Test> { + let inner = pallet_balances::Call::set_balance { who: 42, new_free: value, new_reserved: 0 }; + let outer = RuntimeCall::Balances(inner); + Preimage::bound(outer).unwrap() } #[test] fn set_balance_proposal_is_correctly_filtered_out() { for i in 0..10 { - let call = RuntimeCall::decode(&mut &set_balance_proposal(i)[..]).unwrap(); + let call = Preimage::realize(&set_balance_proposal(i)).unwrap().0; assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call)); } } -fn set_balance_proposal_hash(value: u64) -> H256 { - BlakeTwo256::hash(&set_balance_proposal(value)[..]) -} - -fn set_balance_proposal_hash_and_note(value: u64) -> H256 { - let p = set_balance_proposal(value); - let h = BlakeTwo256::hash(&p[..]); - match Democracy::note_preimage(RuntimeOrigin::signed(6), p) { - Ok(_) => (), - Err(x) if x == Error::<Test>::DuplicatePreimage.into() => (), - Err(x) => panic!("{:?}", x), - } - h -} - fn propose_set_balance(who: u64, value: u64, delay: u64) -> DispatchResult { - Democracy::propose(RuntimeOrigin::signed(who), set_balance_proposal_hash(value), delay) -} - -fn propose_set_balance_and_note(who: u64, value: u64, delay: u64) -> DispatchResult { - Democracy::propose(RuntimeOrigin::signed(who), set_balance_proposal_hash_and_note(value), delay) + Democracy::propose(RuntimeOrigin::signed(who), set_balance_proposal(value), delay) } fn next_block() { @@ -272,7 +250,7 @@ fn fast_forward_to(n: u64) { fn begin_referendum() -> ReferendumIndex { System::set_block_number(0); - assert_ok!(propose_set_balance_and_note(1, 2, 1)); + assert_ok!(propose_set_balance(1, 2, 1)); fast_forward_to(2); 0 } diff --git a/substrate/frame/democracy/src/tests/cancellation.rs b/substrate/frame/democracy/src/tests/cancellation.rs index b98e51aa3d4d1e30c97727762abe6e2484f78fce..ff046d612c026f2b95068e17df9e588e1f122193 100644 --- a/substrate/frame/democracy/src/tests/cancellation.rs +++ b/substrate/frame/democracy/src/tests/cancellation.rs @@ -24,7 +24,7 @@ fn cancel_referendum_should_work() { new_test_ext().execute_with(|| { let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0, ); @@ -42,37 +42,13 @@ fn cancel_referendum_should_work() { }); } -#[test] -fn cancel_queued_should_work() { - new_test_ext().execute_with(|| { - System::set_block_number(0); - assert_ok!(propose_set_balance_and_note(1, 2, 1)); - - // start of 2 => next referendum scheduled. - fast_forward_to(2); - - assert_ok!(Democracy::vote(RuntimeOrigin::signed(1), 0, aye(1))); - - fast_forward_to(4); - - assert!(pallet_scheduler::Agenda::<Test>::get(6)[0].is_some()); - - assert_noop!( - Democracy::cancel_queued(RuntimeOrigin::root(), 1), - Error::<Test>::ProposalMissing - ); - assert_ok!(Democracy::cancel_queued(RuntimeOrigin::root(), 0)); - assert!(pallet_scheduler::Agenda::<Test>::get(6)[0].is_none()); - }); -} - #[test] fn emergency_cancel_should_work() { new_test_ext().execute_with(|| { System::set_block_number(0); let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 2, ); @@ -86,7 +62,7 @@ fn emergency_cancel_should_work() { let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 2, ); diff --git a/substrate/frame/democracy/src/tests/decoders.rs b/substrate/frame/democracy/src/tests/decoders.rs index 1fbb88060549b90038e1a5cbb2a1471975e20a86..1c8b9c3d980f9002606cc8d513a7ea4b54d2d443 100644 --- a/substrate/frame/democracy/src/tests/decoders.rs +++ b/substrate/frame/democracy/src/tests/decoders.rs @@ -18,7 +18,10 @@ //! The for various partial storage decoders use super::*; -use frame_support::storage::{migration, unhashed}; +use frame_support::{ + storage::{migration, unhashed}, + BoundedVec, +}; #[test] fn test_decode_compact_u32_at() { @@ -42,7 +45,8 @@ fn test_decode_compact_u32_at() { fn len_of_deposit_of() { new_test_ext().execute_with(|| { for l in vec![0, 1, 200, 1000] { - let value: (Vec<u64>, u64) = ((0..l).map(|_| Default::default()).collect(), 3u64); + let value: (BoundedVec<u64, _>, u64) = + ((0..l).map(|_| Default::default()).collect::<Vec<_>>().try_into().unwrap(), 3u64); DepositOf::<Test>::insert(2, value); assert_eq!(Democracy::len_of_deposit_of(2), Some(l)); } @@ -51,35 +55,3 @@ fn len_of_deposit_of() { assert_eq!(Democracy::len_of_deposit_of(2), None); }) } - -#[test] -fn pre_image() { - new_test_ext().execute_with(|| { - let key = Default::default(); - let missing = PreimageStatus::Missing(0); - Preimages::<Test>::insert(key, missing); - assert_noop!(Democracy::pre_image_data_len(key), Error::<Test>::PreimageMissing); - assert_eq!(Democracy::check_pre_image_is_missing(key), Ok(())); - - Preimages::<Test>::remove(key); - assert_noop!(Democracy::pre_image_data_len(key), Error::<Test>::PreimageMissing); - assert_noop!(Democracy::check_pre_image_is_missing(key), Error::<Test>::NotImminent); - - for l in vec![0, 10, 100, 1000u32] { - let available = PreimageStatus::Available { - data: (0..l).map(|i| i as u8).collect(), - provider: 0, - deposit: 0, - since: 0, - expiry: None, - }; - - Preimages::<Test>::insert(key, available); - assert_eq!(Democracy::pre_image_data_len(key), Ok(l)); - assert_noop!( - Democracy::check_pre_image_is_missing(key), - Error::<Test>::DuplicatePreimage - ); - } - }) -} diff --git a/substrate/frame/democracy/src/tests/delegation.rs b/substrate/frame/democracy/src/tests/delegation.rs index 4c5ee792860556fac6b2eabaee1fe375dbfe315f..bca7cb95241120d0311ccebde29d1dab4ee0d93c 100644 --- a/substrate/frame/democracy/src/tests/delegation.rs +++ b/substrate/frame/democracy/src/tests/delegation.rs @@ -24,7 +24,7 @@ fn single_proposal_should_work_with_delegation() { new_test_ext().execute_with(|| { System::set_block_number(0); - assert_ok!(propose_set_balance_and_note(1, 2, 1)); + assert_ok!(propose_set_balance(1, 2, 1)); fast_forward_to(2); @@ -75,7 +75,7 @@ fn cyclic_delegation_should_unwind() { new_test_ext().execute_with(|| { System::set_block_number(0); - assert_ok!(propose_set_balance_and_note(1, 2, 1)); + assert_ok!(propose_set_balance(1, 2, 1)); fast_forward_to(2); @@ -100,7 +100,7 @@ fn single_proposal_should_work_with_vote_and_delegation() { new_test_ext().execute_with(|| { System::set_block_number(0); - assert_ok!(propose_set_balance_and_note(1, 2, 1)); + assert_ok!(propose_set_balance(1, 2, 1)); fast_forward_to(2); @@ -122,7 +122,7 @@ fn single_proposal_should_work_with_undelegation() { new_test_ext().execute_with(|| { System::set_block_number(0); - assert_ok!(propose_set_balance_and_note(1, 2, 1)); + assert_ok!(propose_set_balance(1, 2, 1)); // Delegate and undelegate vote. assert_ok!(Democracy::delegate(RuntimeOrigin::signed(2), 1, Conviction::None, 20)); diff --git a/substrate/frame/democracy/src/tests/external_proposing.rs b/substrate/frame/democracy/src/tests/external_proposing.rs index fda555b9c345999bd0255523f5da545c54031a6f..4cfdd2aa74a3d4e1a884dbff7be7aea1eb6f0661 100644 --- a/substrate/frame/democracy/src/tests/external_proposing.rs +++ b/substrate/frame/democracy/src/tests/external_proposing.rs @@ -23,35 +23,29 @@ use super::*; fn veto_external_works() { new_test_ext().execute_with(|| { System::set_block_number(0); - assert_ok!(Democracy::external_propose( - RuntimeOrigin::signed(2), - set_balance_proposal_hash_and_note(2), - )); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2),)); assert!(<NextExternal<Test>>::exists()); - let h = set_balance_proposal_hash_and_note(2); + let h = set_balance_proposal(2).hash(); assert_ok!(Democracy::veto_external(RuntimeOrigin::signed(3), h)); // cancelled. assert!(!<NextExternal<Test>>::exists()); // fails - same proposal can't be resubmitted. assert_noop!( - Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal_hash(2),), + Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2),), Error::<Test>::ProposalBlacklisted ); fast_forward_to(1); // fails as we're still in cooloff period. assert_noop!( - Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal_hash(2),), + Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2),), Error::<Test>::ProposalBlacklisted ); fast_forward_to(2); // works; as we're out of the cooloff period. - assert_ok!(Democracy::external_propose( - RuntimeOrigin::signed(2), - set_balance_proposal_hash_and_note(2), - )); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2),)); assert!(<NextExternal<Test>>::exists()); // 3 can't veto the same thing twice. @@ -68,14 +62,11 @@ fn veto_external_works() { fast_forward_to(3); // same proposal fails as we're still in cooloff assert_noop!( - Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal_hash(2),), + Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2)), Error::<Test>::ProposalBlacklisted ); // different proposal works fine. - assert_ok!(Democracy::external_propose( - RuntimeOrigin::signed(2), - set_balance_proposal_hash_and_note(3), - )); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(3),)); }); } @@ -84,22 +75,16 @@ fn external_blacklisting_should_work() { new_test_ext().execute_with(|| { System::set_block_number(0); - assert_ok!(Democracy::external_propose( - RuntimeOrigin::signed(2), - set_balance_proposal_hash_and_note(2), - )); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2),)); - let hash = set_balance_proposal_hash(2); + let hash = set_balance_proposal(2).hash(); assert_ok!(Democracy::blacklist(RuntimeOrigin::root(), hash, None)); fast_forward_to(2); assert_noop!(Democracy::referendum_status(0), Error::<Test>::ReferendumInvalid); assert_noop!( - Democracy::external_propose( - RuntimeOrigin::signed(2), - set_balance_proposal_hash_and_note(2), - ), + Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2)), Error::<Test>::ProposalBlacklisted, ); }); @@ -110,15 +95,12 @@ fn external_referendum_works() { new_test_ext().execute_with(|| { System::set_block_number(0); assert_noop!( - Democracy::external_propose(RuntimeOrigin::signed(1), set_balance_proposal_hash(2),), + Democracy::external_propose(RuntimeOrigin::signed(1), set_balance_proposal(2),), BadOrigin, ); - assert_ok!(Democracy::external_propose( - RuntimeOrigin::signed(2), - set_balance_proposal_hash_and_note(2), - )); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2),)); assert_noop!( - Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal_hash(1),), + Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(1),), Error::<Test>::DuplicateProposal ); fast_forward_to(2); @@ -126,7 +108,7 @@ fn external_referendum_works() { Democracy::referendum_status(0), Ok(ReferendumStatus { end: 4, - proposal_hash: set_balance_proposal_hash(2), + proposal: set_balance_proposal(2), threshold: VoteThreshold::SuperMajorityApprove, delay: 2, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, @@ -140,22 +122,19 @@ fn external_majority_referendum_works() { new_test_ext().execute_with(|| { System::set_block_number(0); assert_noop!( - Democracy::external_propose_majority( - RuntimeOrigin::signed(1), - set_balance_proposal_hash(2) - ), + Democracy::external_propose_majority(RuntimeOrigin::signed(1), set_balance_proposal(2)), BadOrigin, ); assert_ok!(Democracy::external_propose_majority( RuntimeOrigin::signed(3), - set_balance_proposal_hash_and_note(2) + set_balance_proposal(2) )); fast_forward_to(2); assert_eq!( Democracy::referendum_status(0), Ok(ReferendumStatus { end: 4, - proposal_hash: set_balance_proposal_hash(2), + proposal: set_balance_proposal(2), threshold: VoteThreshold::SimpleMajority, delay: 2, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, @@ -169,22 +148,19 @@ fn external_default_referendum_works() { new_test_ext().execute_with(|| { System::set_block_number(0); assert_noop!( - Democracy::external_propose_default( - RuntimeOrigin::signed(3), - set_balance_proposal_hash(2) - ), + Democracy::external_propose_default(RuntimeOrigin::signed(3), set_balance_proposal(2)), BadOrigin, ); assert_ok!(Democracy::external_propose_default( RuntimeOrigin::signed(1), - set_balance_proposal_hash_and_note(2) + set_balance_proposal(2) )); fast_forward_to(2); assert_eq!( Democracy::referendum_status(0), Ok(ReferendumStatus { end: 4, - proposal_hash: set_balance_proposal_hash(2), + proposal: set_balance_proposal(2), threshold: VoteThreshold::SuperMajorityAgainst, delay: 2, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, @@ -197,11 +173,8 @@ fn external_default_referendum_works() { fn external_and_public_interleaving_works() { new_test_ext().execute_with(|| { System::set_block_number(0); - assert_ok!(Democracy::external_propose( - RuntimeOrigin::signed(2), - set_balance_proposal_hash_and_note(1), - )); - assert_ok!(propose_set_balance_and_note(6, 2, 2)); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(1),)); + assert_ok!(propose_set_balance(6, 2, 2)); fast_forward_to(2); @@ -210,17 +183,14 @@ fn external_and_public_interleaving_works() { Democracy::referendum_status(0), Ok(ReferendumStatus { end: 4, - proposal_hash: set_balance_proposal_hash_and_note(1), + proposal: set_balance_proposal(1), threshold: VoteThreshold::SuperMajorityApprove, delay: 2, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, }) ); // replenish external - assert_ok!(Democracy::external_propose( - RuntimeOrigin::signed(2), - set_balance_proposal_hash_and_note(3), - )); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(3),)); fast_forward_to(4); @@ -229,7 +199,7 @@ fn external_and_public_interleaving_works() { Democracy::referendum_status(1), Ok(ReferendumStatus { end: 6, - proposal_hash: set_balance_proposal_hash_and_note(2), + proposal: set_balance_proposal(2), threshold: VoteThreshold::SuperMajorityApprove, delay: 2, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, @@ -244,17 +214,14 @@ fn external_and_public_interleaving_works() { Democracy::referendum_status(2), Ok(ReferendumStatus { end: 8, - proposal_hash: set_balance_proposal_hash_and_note(3), + proposal: set_balance_proposal(3), threshold: VoteThreshold::SuperMajorityApprove, delay: 2, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, }) ); // replenish external - assert_ok!(Democracy::external_propose( - RuntimeOrigin::signed(2), - set_balance_proposal_hash_and_note(5), - )); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(5),)); fast_forward_to(8); @@ -263,18 +230,15 @@ fn external_and_public_interleaving_works() { Democracy::referendum_status(3), Ok(ReferendumStatus { end: 10, - proposal_hash: set_balance_proposal_hash_and_note(5), + proposal: set_balance_proposal(5), threshold: VoteThreshold::SuperMajorityApprove, delay: 2, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, }) ); // replenish both - assert_ok!(Democracy::external_propose( - RuntimeOrigin::signed(2), - set_balance_proposal_hash_and_note(7), - )); - assert_ok!(propose_set_balance_and_note(6, 4, 2)); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(7),)); + assert_ok!(propose_set_balance(6, 4, 2)); fast_forward_to(10); @@ -283,16 +247,16 @@ fn external_and_public_interleaving_works() { Democracy::referendum_status(4), Ok(ReferendumStatus { end: 12, - proposal_hash: set_balance_proposal_hash_and_note(4), + proposal: set_balance_proposal(4), threshold: VoteThreshold::SuperMajorityApprove, delay: 2, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, }) ); // replenish public again - assert_ok!(propose_set_balance_and_note(6, 6, 2)); + assert_ok!(propose_set_balance(6, 6, 2)); // cancel external - let h = set_balance_proposal_hash_and_note(7); + let h = set_balance_proposal(7).hash(); assert_ok!(Democracy::veto_external(RuntimeOrigin::signed(3), h)); fast_forward_to(12); @@ -302,7 +266,7 @@ fn external_and_public_interleaving_works() { Democracy::referendum_status(5), Ok(ReferendumStatus { end: 14, - proposal_hash: set_balance_proposal_hash_and_note(6), + proposal: set_balance_proposal(6), threshold: VoteThreshold::SuperMajorityApprove, delay: 2, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, diff --git a/substrate/frame/democracy/src/tests/fast_tracking.rs b/substrate/frame/democracy/src/tests/fast_tracking.rs index 8fef985c8561c30fbb8ad94b6e8671b2beaeb6f0..97bb7a63908ab2249872c280f7e3d4e939bbdada 100644 --- a/substrate/frame/democracy/src/tests/fast_tracking.rs +++ b/substrate/frame/democracy/src/tests/fast_tracking.rs @@ -23,14 +23,14 @@ use super::*; fn fast_track_referendum_works() { new_test_ext().execute_with(|| { System::set_block_number(0); - let h = set_balance_proposal_hash_and_note(2); + let h = set_balance_proposal(2).hash(); assert_noop!( Democracy::fast_track(RuntimeOrigin::signed(5), h, 3, 2), Error::<Test>::ProposalMissing ); assert_ok!(Democracy::external_propose_majority( RuntimeOrigin::signed(3), - set_balance_proposal_hash_and_note(2) + set_balance_proposal(2) )); assert_noop!(Democracy::fast_track(RuntimeOrigin::signed(1), h, 3, 2), BadOrigin); assert_ok!(Democracy::fast_track(RuntimeOrigin::signed(5), h, 2, 0)); @@ -38,7 +38,7 @@ fn fast_track_referendum_works() { Democracy::referendum_status(0), Ok(ReferendumStatus { end: 2, - proposal_hash: set_balance_proposal_hash_and_note(2), + proposal: set_balance_proposal(2), threshold: VoteThreshold::SimpleMajority, delay: 0, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, @@ -51,14 +51,14 @@ fn fast_track_referendum_works() { fn instant_referendum_works() { new_test_ext().execute_with(|| { System::set_block_number(0); - let h = set_balance_proposal_hash_and_note(2); + let h = set_balance_proposal(2).hash(); assert_noop!( Democracy::fast_track(RuntimeOrigin::signed(5), h, 3, 2), Error::<Test>::ProposalMissing ); assert_ok!(Democracy::external_propose_majority( RuntimeOrigin::signed(3), - set_balance_proposal_hash_and_note(2) + set_balance_proposal(2) )); assert_noop!(Democracy::fast_track(RuntimeOrigin::signed(1), h, 3, 2), BadOrigin); assert_noop!(Democracy::fast_track(RuntimeOrigin::signed(5), h, 1, 0), BadOrigin); @@ -76,7 +76,7 @@ fn instant_referendum_works() { Democracy::referendum_status(0), Ok(ReferendumStatus { end: 1, - proposal_hash: set_balance_proposal_hash_and_note(2), + proposal: set_balance_proposal(2), threshold: VoteThreshold::SimpleMajority, delay: 0, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, @@ -93,7 +93,7 @@ fn instant_next_block_referendum_backed() { let majority_origin_id = 3; let instant_origin_id = 6; let voting_period = 1; - let proposal_hash = set_balance_proposal_hash_and_note(2); + let proposal = set_balance_proposal(2); let delay = 2; // has no effect on test // init @@ -103,13 +103,13 @@ fn instant_next_block_referendum_backed() { // propose with majority origin assert_ok!(Democracy::external_propose_majority( RuntimeOrigin::signed(majority_origin_id), - proposal_hash + proposal.clone() )); // fast track with instant origin and voting period pointing to the next block assert_ok!(Democracy::fast_track( RuntimeOrigin::signed(instant_origin_id), - proposal_hash, + proposal.hash(), voting_period, delay )); @@ -119,7 +119,7 @@ fn instant_next_block_referendum_backed() { Democracy::referendum_status(0), Ok(ReferendumStatus { end: start_block_number + voting_period, - proposal_hash, + proposal, threshold: VoteThreshold::SimpleMajority, delay, tally: Tally { ayes: 0, nays: 0, turnout: 0 }, @@ -143,11 +143,8 @@ fn instant_next_block_referendum_backed() { fn fast_track_referendum_fails_when_no_simple_majority() { new_test_ext().execute_with(|| { System::set_block_number(0); - let h = set_balance_proposal_hash_and_note(2); - assert_ok!(Democracy::external_propose( - RuntimeOrigin::signed(2), - set_balance_proposal_hash_and_note(2) - )); + let h = set_balance_proposal(2).hash(); + assert_ok!(Democracy::external_propose(RuntimeOrigin::signed(2), set_balance_proposal(2))); assert_noop!( Democracy::fast_track(RuntimeOrigin::signed(5), h, 3, 2), Error::<Test>::NotSimpleMajority diff --git a/substrate/frame/democracy/src/tests/lock_voting.rs b/substrate/frame/democracy/src/tests/lock_voting.rs index de1137f03fd3838a48f93e3af603c90fe39d0d20..540198ecf33a166debe021825070852fa9427231 100644 --- a/substrate/frame/democracy/src/tests/lock_voting.rs +++ b/substrate/frame/democracy/src/tests/lock_voting.rs @@ -43,7 +43,7 @@ fn lock_voting_should_work() { System::set_block_number(0); let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0, ); @@ -59,7 +59,7 @@ fn lock_voting_should_work() { assert_eq!(Balances::locks(i), vec![the_lock(i * 10)]); } - fast_forward_to(2); + fast_forward_to(3); // Referendum passed; 1 and 5 didn't get their way and can now reap and unlock. assert_ok!(Democracy::remove_vote(RuntimeOrigin::signed(1), r)); @@ -126,13 +126,13 @@ fn no_locks_without_conviction_should_work() { System::set_block_number(0); let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0, ); assert_ok!(Democracy::vote(RuntimeOrigin::signed(1), r, aye(0, 10))); - fast_forward_to(2); + fast_forward_to(3); assert_eq!(Balances::free_balance(42), 2); assert_ok!(Democracy::remove_other_vote(RuntimeOrigin::signed(2), 1, r)); @@ -146,7 +146,7 @@ fn lock_voting_should_work_with_delegation() { new_test_ext().execute_with(|| { let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0, ); @@ -167,28 +167,16 @@ fn lock_voting_should_work_with_delegation() { fn setup_three_referenda() -> (u32, u32, u32) { System::set_block_number(0); - let r1 = Democracy::inject_referendum( - 2, - set_balance_proposal_hash_and_note(2), - VoteThreshold::SimpleMajority, - 0, - ); + let r1 = + Democracy::inject_referendum(2, set_balance_proposal(2), VoteThreshold::SimpleMajority, 0); assert_ok!(Democracy::vote(RuntimeOrigin::signed(5), r1, aye(4, 10))); - let r2 = Democracy::inject_referendum( - 2, - set_balance_proposal_hash_and_note(2), - VoteThreshold::SimpleMajority, - 0, - ); + let r2 = + Democracy::inject_referendum(2, set_balance_proposal(2), VoteThreshold::SimpleMajority, 0); assert_ok!(Democracy::vote(RuntimeOrigin::signed(5), r2, aye(3, 20))); - let r3 = Democracy::inject_referendum( - 2, - set_balance_proposal_hash_and_note(2), - VoteThreshold::SimpleMajority, - 0, - ); + let r3 = + Democracy::inject_referendum(2, set_balance_proposal(2), VoteThreshold::SimpleMajority, 0); assert_ok!(Democracy::vote(RuntimeOrigin::signed(5), r3, aye(2, 50))); fast_forward_to(2); @@ -306,7 +294,7 @@ fn locks_should_persist_from_voting_to_delegation() { System::set_block_number(0); let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SimpleMajority, 0, ); diff --git a/substrate/frame/democracy/src/tests/preimage.rs b/substrate/frame/democracy/src/tests/preimage.rs deleted file mode 100644 index 39536eab8009bfabc76a31ed112863b412c5bbd5..0000000000000000000000000000000000000000 --- a/substrate/frame/democracy/src/tests/preimage.rs +++ /dev/null @@ -1,237 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2017-2022 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. - -//! The preimage tests. - -use super::*; - -#[test] -fn missing_preimage_should_fail() { - new_test_ext().execute_with(|| { - let r = Democracy::inject_referendum( - 2, - set_balance_proposal_hash(2), - VoteThreshold::SuperMajorityApprove, - 0, - ); - assert_ok!(Democracy::vote(RuntimeOrigin::signed(1), r, aye(1))); - - next_block(); - next_block(); - - assert_eq!(Balances::free_balance(42), 0); - }); -} - -#[test] -fn preimage_deposit_should_be_required_and_returned() { - new_test_ext_execute_with_cond(|operational| { - // fee of 100 is too much. - PREIMAGE_BYTE_DEPOSIT.with(|v| *v.borrow_mut() = 100); - assert_noop!( - if operational { - Democracy::note_preimage_operational(RuntimeOrigin::signed(6), vec![0; 500]) - } else { - Democracy::note_preimage(RuntimeOrigin::signed(6), vec![0; 500]) - }, - BalancesError::<Test, _>::InsufficientBalance, - ); - // fee of 1 is reasonable. - PREIMAGE_BYTE_DEPOSIT.with(|v| *v.borrow_mut() = 1); - let r = Democracy::inject_referendum( - 2, - set_balance_proposal_hash_and_note(2), - VoteThreshold::SuperMajorityApprove, - 0, - ); - assert_ok!(Democracy::vote(RuntimeOrigin::signed(1), r, aye(1))); - - assert_eq!(Balances::reserved_balance(6), 12); - - next_block(); - next_block(); - - assert_eq!(Balances::reserved_balance(6), 0); - assert_eq!(Balances::free_balance(6), 60); - assert_eq!(Balances::free_balance(42), 2); - }); -} - -#[test] -fn preimage_deposit_should_be_reapable_earlier_by_owner() { - new_test_ext_execute_with_cond(|operational| { - PREIMAGE_BYTE_DEPOSIT.with(|v| *v.borrow_mut() = 1); - assert_ok!(if operational { - Democracy::note_preimage_operational(RuntimeOrigin::signed(6), set_balance_proposal(2)) - } else { - Democracy::note_preimage(RuntimeOrigin::signed(6), set_balance_proposal(2)) - }); - - assert_eq!(Balances::reserved_balance(6), 12); - - next_block(); - assert_noop!( - Democracy::reap_preimage( - RuntimeOrigin::signed(6), - set_balance_proposal_hash(2), - u32::MAX - ), - Error::<Test>::TooEarly - ); - next_block(); - assert_ok!(Democracy::reap_preimage( - RuntimeOrigin::signed(6), - set_balance_proposal_hash(2), - u32::MAX - )); - - assert_eq!(Balances::free_balance(6), 60); - assert_eq!(Balances::reserved_balance(6), 0); - }); -} - -#[test] -fn preimage_deposit_should_be_reapable() { - new_test_ext_execute_with_cond(|operational| { - assert_noop!( - Democracy::reap_preimage( - RuntimeOrigin::signed(5), - set_balance_proposal_hash(2), - u32::MAX - ), - Error::<Test>::PreimageMissing - ); - - PREIMAGE_BYTE_DEPOSIT.with(|v| *v.borrow_mut() = 1); - assert_ok!(if operational { - Democracy::note_preimage_operational(RuntimeOrigin::signed(6), set_balance_proposal(2)) - } else { - Democracy::note_preimage(RuntimeOrigin::signed(6), set_balance_proposal(2)) - }); - assert_eq!(Balances::reserved_balance(6), 12); - - next_block(); - next_block(); - next_block(); - assert_noop!( - Democracy::reap_preimage( - RuntimeOrigin::signed(5), - set_balance_proposal_hash(2), - u32::MAX - ), - Error::<Test>::TooEarly - ); - - next_block(); - assert_ok!(Democracy::reap_preimage( - RuntimeOrigin::signed(5), - set_balance_proposal_hash(2), - u32::MAX - )); - assert_eq!(Balances::reserved_balance(6), 0); - assert_eq!(Balances::free_balance(6), 48); - assert_eq!(Balances::free_balance(5), 62); - }); -} - -#[test] -fn noting_imminent_preimage_for_free_should_work() { - new_test_ext_execute_with_cond(|operational| { - PREIMAGE_BYTE_DEPOSIT.with(|v| *v.borrow_mut() = 1); - - let r = Democracy::inject_referendum( - 2, - set_balance_proposal_hash(2), - VoteThreshold::SuperMajorityApprove, - 1, - ); - assert_ok!(Democracy::vote(RuntimeOrigin::signed(1), r, aye(1))); - - assert_noop!( - if operational { - Democracy::note_imminent_preimage_operational( - RuntimeOrigin::signed(6), - set_balance_proposal(2), - ) - } else { - Democracy::note_imminent_preimage(RuntimeOrigin::signed(6), set_balance_proposal(2)) - }, - Error::<Test>::NotImminent - ); - - next_block(); - - // Now we're in the dispatch queue it's all good. - assert_ok!(Democracy::note_imminent_preimage( - RuntimeOrigin::signed(6), - set_balance_proposal(2) - )); - - next_block(); - - assert_eq!(Balances::free_balance(42), 2); - }); -} - -#[test] -fn reaping_imminent_preimage_should_fail() { - new_test_ext().execute_with(|| { - let h = set_balance_proposal_hash_and_note(2); - let r = Democracy::inject_referendum(3, h, VoteThreshold::SuperMajorityApprove, 1); - assert_ok!(Democracy::vote(RuntimeOrigin::signed(1), r, aye(1))); - next_block(); - next_block(); - assert_noop!( - Democracy::reap_preimage(RuntimeOrigin::signed(6), h, u32::MAX), - Error::<Test>::Imminent - ); - }); -} - -#[test] -fn note_imminent_preimage_can_only_be_successful_once() { - new_test_ext().execute_with(|| { - PREIMAGE_BYTE_DEPOSIT.with(|v| *v.borrow_mut() = 1); - - let r = Democracy::inject_referendum( - 2, - set_balance_proposal_hash(2), - VoteThreshold::SuperMajorityApprove, - 1, - ); - assert_ok!(Democracy::vote(RuntimeOrigin::signed(1), r, aye(1))); - next_block(); - - // First time works - assert_ok!(Democracy::note_imminent_preimage( - RuntimeOrigin::signed(6), - set_balance_proposal(2) - )); - - // Second time fails - assert_noop!( - Democracy::note_imminent_preimage(RuntimeOrigin::signed(6), set_balance_proposal(2)), - Error::<Test>::DuplicatePreimage - ); - - // Fails from any user - assert_noop!( - Democracy::note_imminent_preimage(RuntimeOrigin::signed(5), set_balance_proposal(2)), - Error::<Test>::DuplicatePreimage - ); - }); -} diff --git a/substrate/frame/democracy/src/tests/public_proposals.rs b/substrate/frame/democracy/src/tests/public_proposals.rs index c52533e46ccc576d298112c6a9087ba688634ed9..f48824dc95c5d84b6e59de5757730fb23d3e4564 100644 --- a/substrate/frame/democracy/src/tests/public_proposals.rs +++ b/substrate/frame/democracy/src/tests/public_proposals.rs @@ -22,9 +22,9 @@ use super::*; #[test] fn backing_for_should_work() { new_test_ext().execute_with(|| { - assert_ok!(propose_set_balance_and_note(1, 2, 2)); - assert_ok!(propose_set_balance_and_note(1, 4, 4)); - assert_ok!(propose_set_balance_and_note(1, 3, 3)); + assert_ok!(propose_set_balance(1, 2, 2)); + assert_ok!(propose_set_balance(1, 4, 4)); + assert_ok!(propose_set_balance(1, 3, 3)); assert_eq!(Democracy::backing_for(0), Some(2)); assert_eq!(Democracy::backing_for(1), Some(4)); assert_eq!(Democracy::backing_for(2), Some(3)); @@ -34,11 +34,11 @@ fn backing_for_should_work() { #[test] fn deposit_for_proposals_should_be_taken() { new_test_ext().execute_with(|| { - assert_ok!(propose_set_balance_and_note(1, 2, 5)); - assert_ok!(Democracy::second(RuntimeOrigin::signed(2), 0, u32::MAX)); - assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0, u32::MAX)); - assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0, u32::MAX)); - assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0, u32::MAX)); + assert_ok!(propose_set_balance(1, 2, 5)); + assert_ok!(Democracy::second(RuntimeOrigin::signed(2), 0)); + assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0)); + assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0)); + assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0)); assert_eq!(Balances::free_balance(1), 5); assert_eq!(Balances::free_balance(2), 15); assert_eq!(Balances::free_balance(5), 35); @@ -48,11 +48,11 @@ fn deposit_for_proposals_should_be_taken() { #[test] fn deposit_for_proposals_should_be_returned() { new_test_ext().execute_with(|| { - assert_ok!(propose_set_balance_and_note(1, 2, 5)); - assert_ok!(Democracy::second(RuntimeOrigin::signed(2), 0, u32::MAX)); - assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0, u32::MAX)); - assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0, u32::MAX)); - assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0, u32::MAX)); + assert_ok!(propose_set_balance(1, 2, 5)); + assert_ok!(Democracy::second(RuntimeOrigin::signed(2), 0)); + assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0)); + assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0)); + assert_ok!(Democracy::second(RuntimeOrigin::signed(5), 0)); fast_forward_to(3); assert_eq!(Balances::free_balance(1), 10); assert_eq!(Balances::free_balance(2), 20); @@ -77,30 +77,19 @@ fn poor_proposer_should_not_work() { #[test] fn poor_seconder_should_not_work() { new_test_ext().execute_with(|| { - assert_ok!(propose_set_balance_and_note(2, 2, 11)); + assert_ok!(propose_set_balance(2, 2, 11)); assert_noop!( - Democracy::second(RuntimeOrigin::signed(1), 0, u32::MAX), + Democracy::second(RuntimeOrigin::signed(1), 0), BalancesError::<Test, _>::InsufficientBalance ); }); } -#[test] -fn invalid_seconds_upper_bound_should_not_work() { - new_test_ext().execute_with(|| { - assert_ok!(propose_set_balance_and_note(1, 2, 5)); - assert_noop!( - Democracy::second(RuntimeOrigin::signed(2), 0, 0), - Error::<Test>::WrongUpperBound - ); - }); -} - #[test] fn cancel_proposal_should_work() { new_test_ext().execute_with(|| { - assert_ok!(propose_set_balance_and_note(1, 2, 2)); - assert_ok!(propose_set_balance_and_note(1, 4, 4)); + assert_ok!(propose_set_balance(1, 2, 2)); + assert_ok!(propose_set_balance(1, 4, 4)); assert_noop!(Democracy::cancel_proposal(RuntimeOrigin::signed(1), 0), BadOrigin); assert_ok!(Democracy::cancel_proposal(RuntimeOrigin::root(), 0)); System::assert_last_event(crate::Event::ProposalCanceled { prop_index: 0 }.into()); @@ -113,10 +102,10 @@ fn cancel_proposal_should_work() { fn blacklisting_should_work() { new_test_ext().execute_with(|| { System::set_block_number(0); - let hash = set_balance_proposal_hash(2); + let hash = set_balance_proposal(2).hash(); - assert_ok!(propose_set_balance_and_note(1, 2, 2)); - assert_ok!(propose_set_balance_and_note(1, 4, 4)); + assert_ok!(propose_set_balance(1, 2, 2)); + assert_ok!(propose_set_balance(1, 4, 4)); assert_noop!(Democracy::blacklist(RuntimeOrigin::signed(1), hash, None), BadOrigin); assert_ok!(Democracy::blacklist(RuntimeOrigin::root(), hash, None)); @@ -124,11 +113,11 @@ fn blacklisting_should_work() { assert_eq!(Democracy::backing_for(0), None); assert_eq!(Democracy::backing_for(1), Some(4)); - assert_noop!(propose_set_balance_and_note(1, 2, 2), Error::<Test>::ProposalBlacklisted); + assert_noop!(propose_set_balance(1, 2, 2), Error::<Test>::ProposalBlacklisted); fast_forward_to(2); - let hash = set_balance_proposal_hash(4); + let hash = set_balance_proposal(4).hash(); assert_ok!(Democracy::referendum_status(0)); assert_ok!(Democracy::blacklist(RuntimeOrigin::root(), hash, Some(0))); assert_noop!(Democracy::referendum_status(0), Error::<Test>::ReferendumInvalid); @@ -139,9 +128,9 @@ fn blacklisting_should_work() { fn runners_up_should_come_after() { new_test_ext().execute_with(|| { System::set_block_number(0); - assert_ok!(propose_set_balance_and_note(1, 2, 2)); - assert_ok!(propose_set_balance_and_note(1, 4, 4)); - assert_ok!(propose_set_balance_and_note(1, 3, 3)); + assert_ok!(propose_set_balance(1, 2, 2)); + assert_ok!(propose_set_balance(1, 4, 4)); + assert_ok!(propose_set_balance(1, 3, 3)); fast_forward_to(2); assert_ok!(Democracy::vote(RuntimeOrigin::signed(1), 0, aye(1))); fast_forward_to(4); diff --git a/substrate/frame/democracy/src/tests/scheduling.rs b/substrate/frame/democracy/src/tests/scheduling.rs index 4b5fe8dd9c1c3d94ded21d762ef0272b3654ec43..5e133f38945d6c8e3968f2f05ab18e93ed858b69 100644 --- a/substrate/frame/democracy/src/tests/scheduling.rs +++ b/substrate/frame/democracy/src/tests/scheduling.rs @@ -24,7 +24,7 @@ fn simple_passing_should_work() { new_test_ext().execute_with(|| { let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0, ); @@ -43,7 +43,7 @@ fn simple_failing_should_work() { new_test_ext().execute_with(|| { let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0, ); @@ -62,13 +62,13 @@ fn ooo_inject_referendums_should_work() { new_test_ext().execute_with(|| { let r1 = Democracy::inject_referendum( 3, - set_balance_proposal_hash_and_note(3), + set_balance_proposal(3), VoteThreshold::SuperMajorityApprove, 0, ); let r2 = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0, ); @@ -77,11 +77,13 @@ fn ooo_inject_referendums_should_work() { assert_eq!(tally(r2), Tally { ayes: 1, nays: 0, turnout: 10 }); next_block(); - assert_eq!(Balances::free_balance(42), 2); assert_ok!(Democracy::vote(RuntimeOrigin::signed(1), r1, aye(1))); assert_eq!(tally(r1), Tally { ayes: 1, nays: 0, turnout: 10 }); + next_block(); + assert_eq!(Balances::free_balance(42), 2); + next_block(); assert_eq!(Balances::free_balance(42), 3); }); @@ -92,7 +94,7 @@ fn delayed_enactment_should_work() { new_test_ext().execute_with(|| { let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 1, ); @@ -118,19 +120,19 @@ fn lowest_unbaked_should_be_sensible() { new_test_ext().execute_with(|| { let r1 = Democracy::inject_referendum( 3, - set_balance_proposal_hash_and_note(1), + set_balance_proposal(1), VoteThreshold::SuperMajorityApprove, 0, ); let r2 = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0, ); let r3 = Democracy::inject_referendum( 10, - set_balance_proposal_hash_and_note(3), + set_balance_proposal(3), VoteThreshold::SuperMajorityApprove, 0, ); @@ -141,16 +143,19 @@ fn lowest_unbaked_should_be_sensible() { assert_eq!(Democracy::lowest_unbaked(), 0); next_block(); - - // r2 is approved - assert_eq!(Balances::free_balance(42), 2); + // r2 ends with approval assert_eq!(Democracy::lowest_unbaked(), 0); next_block(); - - // r1 is approved - assert_eq!(Balances::free_balance(42), 1); + // r1 ends with approval assert_eq!(Democracy::lowest_unbaked(), 3); assert_eq!(Democracy::lowest_unbaked(), Democracy::referendum_count()); + + // r2 is executed + assert_eq!(Balances::free_balance(42), 2); + + next_block(); + // r1 is executed + assert_eq!(Balances::free_balance(42), 1); }); } diff --git a/substrate/frame/democracy/src/tests/voting.rs b/substrate/frame/democracy/src/tests/voting.rs index 59d0cd6bc50efd0449318971a3f0e8f4be71024f..482cd430e0e7f1a907e884aa23eb15c2ce13d9f2 100644 --- a/substrate/frame/democracy/src/tests/voting.rs +++ b/substrate/frame/democracy/src/tests/voting.rs @@ -63,7 +63,7 @@ fn split_vote_cancellation_should_work() { fn single_proposal_should_work() { new_test_ext().execute_with(|| { System::set_block_number(0); - assert_ok!(propose_set_balance_and_note(1, 2, 1)); + assert_ok!(propose_set_balance(1, 2, 1)); let r = 0; assert!(Democracy::referendum_info(r).is_none()); @@ -76,7 +76,7 @@ fn single_proposal_should_work() { Democracy::referendum_status(0), Ok(ReferendumStatus { end: 4, - proposal_hash: set_balance_proposal_hash_and_note(2), + proposal: set_balance_proposal(2), threshold: VoteThreshold::SuperMajorityApprove, delay: 2, tally: Tally { ayes: 1, nays: 0, turnout: 10 }, @@ -106,7 +106,7 @@ fn controversial_voting_should_work() { new_test_ext().execute_with(|| { let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0, ); @@ -132,7 +132,7 @@ fn controversial_low_turnout_voting_should_work() { new_test_ext().execute_with(|| { let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0, ); @@ -156,7 +156,7 @@ fn passing_low_turnout_voting_should_work() { let r = Democracy::inject_referendum( 2, - set_balance_proposal_hash_and_note(2), + set_balance_proposal(2), VoteThreshold::SuperMajorityApprove, 0, ); diff --git a/substrate/frame/democracy/src/types.rs b/substrate/frame/democracy/src/types.rs index 52ab8a40eb3e33647151b055ed6d2eff317f5124..4b7f1a0fac45c2d435bd7fd4702304446c2f582f 100644 --- a/substrate/frame/democracy/src/types.rs +++ b/substrate/frame/democracy/src/types.rs @@ -18,7 +18,7 @@ //! Miscellaneous additional datatypes. use crate::{AccountVote, Conviction, Vote, VoteThreshold}; -use codec::{Decode, Encode}; +use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{ traits::{Bounded, CheckedAdd, CheckedDiv, CheckedMul, CheckedSub, Saturating, Zero}, @@ -26,7 +26,7 @@ use sp_runtime::{ }; /// Info regarding an ongoing referendum. -#[derive(Encode, Decode, Default, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +#[derive(Encode, MaxEncodedLen, Decode, Default, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] pub struct Tally<Balance> { /// The number of aye votes, expressed in terms of post-conviction lock-vote. pub ayes: Balance, @@ -37,7 +37,9 @@ pub struct Tally<Balance> { } /// Amount of votes and capital placed in delegation for an account. -#[derive(Encode, Decode, Default, Copy, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +#[derive( + Encode, MaxEncodedLen, Decode, Default, Copy, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, +)] pub struct Delegations<Balance> { /// The number of votes (this is post-conviction). pub votes: Balance, @@ -160,12 +162,12 @@ impl< } /// Info regarding an ongoing referendum. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub struct ReferendumStatus<BlockNumber, Hash, Balance> { +#[derive(Encode, MaxEncodedLen, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub struct ReferendumStatus<BlockNumber, Proposal, Balance> { /// When voting on this referendum will end. pub end: BlockNumber, - /// The hash of the proposal being voted on. - pub proposal_hash: Hash, + /// The proposal being voted on. + pub proposal: Proposal, /// The thresholding mechanism to determine whether it passed. pub threshold: VoteThreshold, /// The delay (in blocks) to wait after a successful referendum before deploying. @@ -175,23 +177,23 @@ pub struct ReferendumStatus<BlockNumber, Hash, Balance> { } /// Info regarding a referendum, present or past. -#[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] -pub enum ReferendumInfo<BlockNumber, Hash, Balance> { +#[derive(Encode, MaxEncodedLen, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo)] +pub enum ReferendumInfo<BlockNumber, Proposal, Balance> { /// Referendum is happening, the arg is the block number at which it will end. - Ongoing(ReferendumStatus<BlockNumber, Hash, Balance>), + Ongoing(ReferendumStatus<BlockNumber, Proposal, Balance>), /// Referendum finished at `end`, and has been `approved` or rejected. Finished { approved: bool, end: BlockNumber }, } -impl<BlockNumber, Hash, Balance: Default> ReferendumInfo<BlockNumber, Hash, Balance> { +impl<BlockNumber, Proposal, Balance: Default> ReferendumInfo<BlockNumber, Proposal, Balance> { /// Create a new instance. pub fn new( end: BlockNumber, - proposal_hash: Hash, + proposal: Proposal, threshold: VoteThreshold, delay: BlockNumber, ) -> Self { - let s = ReferendumStatus { end, proposal_hash, threshold, delay, tally: Tally::default() }; + let s = ReferendumStatus { end, proposal, threshold, delay, tally: Tally::default() }; ReferendumInfo::Ongoing(s) } } diff --git a/substrate/frame/democracy/src/vote.rs b/substrate/frame/democracy/src/vote.rs index c74623d4dfeb8923a8a9e79e3c8e94befcda3ada..122f54febd8cf1df1db1d6cae1517ed3e8144a90 100644 --- a/substrate/frame/democracy/src/vote.rs +++ b/substrate/frame/democracy/src/vote.rs @@ -18,11 +18,12 @@ //! The vote datatype. use crate::{Conviction, Delegations, ReferendumIndex}; -use codec::{Decode, Encode, EncodeLike, Input, Output}; +use codec::{Decode, Encode, EncodeLike, Input, MaxEncodedLen, Output}; +use frame_support::traits::Get; use scale_info::TypeInfo; use sp_runtime::{ traits::{Saturating, Zero}, - RuntimeDebug, + BoundedVec, RuntimeDebug, }; use sp_std::prelude::*; @@ -39,6 +40,12 @@ impl Encode for Vote { } } +impl MaxEncodedLen for Vote { + fn max_encoded_len() -> usize { + 1 + } +} + impl EncodeLike for Vote {} impl Decode for Vote { @@ -66,7 +73,7 @@ impl TypeInfo for Vote { } /// A vote for a referendum of a particular account. -#[derive(Encode, Decode, Copy, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] +#[derive(Encode, MaxEncodedLen, Decode, Copy, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] pub enum AccountVote<Balance> { /// A standard vote, one-way (approve or reject) with a given amount of conviction. Standard { vote: Vote, balance: Balance }, @@ -107,7 +114,18 @@ impl<Balance: Saturating> AccountVote<Balance> { /// A "prior" lock, i.e. a lock for some now-forgotten reason. #[derive( - Encode, Decode, Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, RuntimeDebug, TypeInfo, + Encode, + MaxEncodedLen, + Decode, + Default, + Copy, + Clone, + Eq, + PartialEq, + Ord, + PartialOrd, + RuntimeDebug, + TypeInfo, )] pub struct PriorLock<BlockNumber, Balance>(BlockNumber, Balance); @@ -131,13 +149,15 @@ impl<BlockNumber: Ord + Copy + Zero, Balance: Ord + Copy + Zero> PriorLock<Block } /// An indicator for what an account is doing; it can either be delegating or voting. -#[derive(Encode, Decode, Clone, Eq, PartialEq, RuntimeDebug, TypeInfo)] -pub enum Voting<Balance, AccountId, BlockNumber> { +#[derive(Clone, Encode, Decode, Eq, MaxEncodedLen, PartialEq, RuntimeDebug, TypeInfo)] +#[codec(mel_bound(skip_type_params(MaxVotes)))] +#[scale_info(skip_type_params(MaxVotes))] +pub enum Voting<Balance, AccountId, BlockNumber, MaxVotes: Get<u32>> { /// The account is voting directly. `delegations` is the total amount of post-conviction voting /// weight that it controls from those that have delegated to it. Direct { /// The current votes of the account. - votes: Vec<(ReferendumIndex, AccountVote<Balance>)>, + votes: BoundedVec<(ReferendumIndex, AccountVote<Balance>), MaxVotes>, /// The total amount of delegations that this account has received. delegations: Delegations<Balance>, /// Any pre-existing locks from past voting/delegating activity. @@ -155,20 +175,24 @@ pub enum Voting<Balance, AccountId, BlockNumber> { }, } -impl<Balance: Default, AccountId, BlockNumber: Zero> Default - for Voting<Balance, AccountId, BlockNumber> +impl<Balance: Default, AccountId, BlockNumber: Zero, MaxVotes: Get<u32>> Default + for Voting<Balance, AccountId, BlockNumber, MaxVotes> { fn default() -> Self { Voting::Direct { - votes: Vec::new(), + votes: Default::default(), delegations: Default::default(), prior: PriorLock(Zero::zero(), Default::default()), } } } -impl<Balance: Saturating + Ord + Zero + Copy, BlockNumber: Ord + Copy + Zero, AccountId> - Voting<Balance, AccountId, BlockNumber> +impl< + Balance: Saturating + Ord + Zero + Copy, + BlockNumber: Ord + Copy + Zero, + AccountId, + MaxVotes: Get<u32>, + > Voting<Balance, AccountId, BlockNumber, MaxVotes> { pub fn rejig(&mut self, now: BlockNumber) { match self { diff --git a/substrate/frame/democracy/src/vote_threshold.rs b/substrate/frame/democracy/src/vote_threshold.rs index 443d6b116619803afdfe911d32e7453113496fd2..e8ef91def9820fd558171da75439ea7ddf14db06 100644 --- a/substrate/frame/democracy/src/vote_threshold.rs +++ b/substrate/frame/democracy/src/vote_threshold.rs @@ -18,7 +18,7 @@ //! Voting thresholds. use crate::Tally; -use codec::{Decode, Encode}; +use codec::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; #[cfg(feature = "std")] use serde::{Deserialize, Serialize}; @@ -26,7 +26,9 @@ use sp_runtime::traits::{IntegerSquareRoot, Zero}; use sp_std::ops::{Add, Div, Mul, Rem}; /// A means of determining if a vote is past pass threshold. -#[derive(Clone, Copy, PartialEq, Eq, Encode, Decode, sp_runtime::RuntimeDebug, TypeInfo)] +#[derive( + Clone, Copy, PartialEq, Eq, Encode, MaxEncodedLen, Decode, sp_runtime::RuntimeDebug, TypeInfo, +)] #[cfg_attr(feature = "std", derive(Serialize, Deserialize))] pub enum VoteThreshold { /// A supermajority of approvals is needed to pass this vote. diff --git a/substrate/frame/democracy/src/weights.rs b/substrate/frame/democracy/src/weights.rs index 272921ed3a15d3f17cfac4091f536afc25432e98..0a3b717938022aabfcb5d037ded244f63f4d8868 100644 --- a/substrate/frame/democracy/src/weights.rs +++ b/substrate/frame/democracy/src/weights.rs @@ -18,22 +18,24 @@ //! Autogenerated weights for pallet_democracy //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-05-23, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-10-03, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// /home/benchbot/cargo_target_dir/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_democracy // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --template=./.maintain/frame-weight-template.hbs +// --heap-pages=4096 +// --pallet=pallet_democracy +// --chain=dev // --output=./frame/democracy/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -45,27 +47,23 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_democracy. pub trait WeightInfo { fn propose() -> Weight; - fn second(s: u32, ) -> Weight; - fn vote_new(r: u32, ) -> Weight; - fn vote_existing(r: u32, ) -> Weight; + fn second() -> Weight; + fn vote_new() -> Weight; + fn vote_existing() -> Weight; fn emergency_cancel() -> Weight; - fn blacklist(p: u32, ) -> Weight; - fn external_propose(v: u32, ) -> Weight; + fn blacklist() -> Weight; + fn external_propose() -> Weight; fn external_propose_majority() -> Weight; fn external_propose_default() -> Weight; fn fast_track() -> Weight; - fn veto_external(v: u32, ) -> Weight; - fn cancel_proposal(p: u32, ) -> Weight; + fn veto_external() -> Weight; + fn cancel_proposal() -> Weight; fn cancel_referendum() -> Weight; - fn cancel_queued(r: u32, ) -> Weight; fn on_initialize_base(r: u32, ) -> Weight; fn on_initialize_base_with_launch_period(r: u32, ) -> Weight; fn delegate(r: u32, ) -> Weight; fn undelegate(r: u32, ) -> Weight; fn clear_public_proposals() -> Weight; - fn note_preimage(b: u32, ) -> Weight; - fn note_imminent_preimage(b: u32, ) -> Weight; - fn reap_preimage(b: u32, ) -> Weight; fn unlock_remove(r: u32, ) -> Weight; fn unlock_set(r: u32, ) -> Weight; fn remove_vote(r: u32, ) -> Weight; @@ -80,125 +78,103 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Storage: Democracy Blacklist (r:1 w:0) // Storage: Democracy DepositOf (r:0 w:1) fn propose() -> Weight { - Weight::from_ref_time(48_328_000 as u64) + Weight::from_ref_time(57_410_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: Democracy DepositOf (r:1 w:1) - fn second(s: u32, ) -> Weight { - Weight::from_ref_time(30_923_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(142_000 as u64).saturating_mul(s as u64)) + fn second() -> Weight { + Weight::from_ref_time(49_224_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy VotingOf (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vote_new(r: u32, ) -> Weight { - Weight::from_ref_time(40_345_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(140_000 as u64).saturating_mul(r as u64)) + fn vote_new() -> Weight { + Weight::from_ref_time(60_933_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy VotingOf (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vote_existing(r: u32, ) -> Weight { - Weight::from_ref_time(39_853_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(150_000 as u64).saturating_mul(r as u64)) + fn vote_existing() -> Weight { + Weight::from_ref_time(60_393_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy Cancellations (r:1 w:1) fn emergency_cancel() -> Weight { - Weight::from_ref_time(19_364_000 as u64) + Weight::from_ref_time(24_588_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Democracy PublicProps (r:1 w:1) + // Storage: Democracy DepositOf (r:1 w:1) + // Storage: System Account (r:1 w:1) // Storage: Democracy NextExternal (r:1 w:1) // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy Blacklist (r:0 w:1) - // Storage: Democracy DepositOf (r:1 w:1) - // Storage: System Account (r:1 w:1) - fn blacklist(p: u32, ) -> Weight { - Weight::from_ref_time(57_708_000 as u64) - // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(192_000 as u64).saturating_mul(p as u64)) + fn blacklist() -> Weight { + Weight::from_ref_time(91_226_000 as u64) .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().writes(6 as u64)) } // Storage: Democracy NextExternal (r:1 w:1) // Storage: Democracy Blacklist (r:1 w:0) - fn external_propose(v: u32, ) -> Weight { - Weight::from_ref_time(10_714_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(33_000 as u64).saturating_mul(v as u64)) + fn external_propose() -> Weight { + Weight::from_ref_time(18_898_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Democracy NextExternal (r:0 w:1) fn external_propose_majority() -> Weight { - Weight::from_ref_time(3_697_000 as u64) + Weight::from_ref_time(5_136_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Democracy NextExternal (r:0 w:1) fn external_propose_default() -> Weight { - Weight::from_ref_time(3_831_000 as u64) + Weight::from_ref_time(5_243_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Democracy NextExternal (r:1 w:1) // Storage: Democracy ReferendumCount (r:1 w:1) // Storage: Democracy ReferendumInfoOf (r:0 w:1) fn fast_track() -> Weight { - Weight::from_ref_time(20_271_000 as u64) + Weight::from_ref_time(24_275_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: Democracy NextExternal (r:1 w:1) // Storage: Democracy Blacklist (r:1 w:1) - fn veto_external(v: u32, ) -> Weight { - Weight::from_ref_time(21_319_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(52_000 as u64).saturating_mul(v as u64)) + fn veto_external() -> Weight { + Weight::from_ref_time(30_988_000 as u64) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Democracy PublicProps (r:1 w:1) // Storage: Democracy DepositOf (r:1 w:1) // Storage: System Account (r:1 w:1) - fn cancel_proposal(p: u32, ) -> Weight { - Weight::from_ref_time(43_960_000 as u64) - // Standard Error: 2_000 - .saturating_add(Weight::from_ref_time(184_000 as u64).saturating_mul(p as u64)) + fn cancel_proposal() -> Weight { + Weight::from_ref_time(78_515_000 as u64) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: Democracy ReferendumInfoOf (r:0 w:1) fn cancel_referendum() -> Weight { - Weight::from_ref_time(13_475_000 as u64) + Weight::from_ref_time(16_155_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) - fn cancel_queued(r: u32, ) -> Weight { - Weight::from_ref_time(24_320_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(560_000 as u64).saturating_mul(r as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - } // Storage: Democracy LowestUnbaked (r:1 w:1) // Storage: Democracy ReferendumCount (r:1 w:0) - // Storage: Democracy ReferendumInfoOf (r:1 w:0) + // Storage: Democracy ReferendumInfoOf (r:2 w:0) + /// The range of component `r` is `[0, 99]`. fn on_initialize_base(r: u32, ) -> Weight { - Weight::from_ref_time(3_428_000 as u64) - // Standard Error: 2_000 - .saturating_add(Weight::from_ref_time(3_171_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(7_007_000 as u64) + // Standard Error: 2_686 + .saturating_add(Weight::from_ref_time(2_288_781 as u64).saturating_mul(r as u64)) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(r as u64))) .saturating_add(T::DbWeight::get().writes(1 as u64)) @@ -208,33 +184,36 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // Storage: Democracy LastTabledWasExternal (r:1 w:0) // Storage: Democracy NextExternal (r:1 w:0) // Storage: Democracy PublicProps (r:1 w:0) - // Storage: Democracy ReferendumInfoOf (r:1 w:0) + // Storage: Democracy ReferendumInfoOf (r:2 w:0) + /// The range of component `r` is `[0, 99]`. fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { - Weight::from_ref_time(7_867_000 as u64) - // Standard Error: 2_000 - .saturating_add(Weight::from_ref_time(3_177_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(9_528_000 as u64) + // Standard Error: 2_521 + .saturating_add(Weight::from_ref_time(2_291_780 as u64).saturating_mul(r as u64)) .saturating_add(T::DbWeight::get().reads(5 as u64)) .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(r as u64))) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Democracy VotingOf (r:3 w:3) - // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Balances Locks (r:1 w:1) + // Storage: Democracy ReferendumInfoOf (r:2 w:2) + /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { - Weight::from_ref_time(37_902_000 as u64) - // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(4_335_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(46_787_000 as u64) + // Standard Error: 2_943 + .saturating_add(Weight::from_ref_time(3_460_194 as u64).saturating_mul(r as u64)) .saturating_add(T::DbWeight::get().reads(4 as u64)) .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(r as u64))) .saturating_add(T::DbWeight::get().writes(4 as u64)) .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(r as u64))) } // Storage: Democracy VotingOf (r:2 w:2) - // Storage: Democracy ReferendumInfoOf (r:1 w:1) + // Storage: Democracy ReferendumInfoOf (r:2 w:2) + /// The range of component `r` is `[0, 99]`. fn undelegate(r: u32, ) -> Weight { - Weight::from_ref_time(21_272_000 as u64) - // Standard Error: 3_000 - .saturating_add(Weight::from_ref_time(4_351_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(29_789_000 as u64) + // Standard Error: 2_324 + .saturating_add(Weight::from_ref_time(3_360_918 as u64).saturating_mul(r as u64)) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(r as u64))) .saturating_add(T::DbWeight::get().writes(2 as u64)) @@ -242,69 +221,48 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { } // Storage: Democracy PublicProps (r:0 w:1) fn clear_public_proposals() -> Weight { - Weight::from_ref_time(4_913_000 as u64) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Democracy Preimages (r:1 w:1) - fn note_preimage(b: u32, ) -> Weight { - Weight::from_ref_time(27_986_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(b as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Democracy Preimages (r:1 w:1) - fn note_imminent_preimage(b: u32, ) -> Weight { - Weight::from_ref_time(20_058_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(b as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - } - // Storage: Democracy Preimages (r:1 w:1) - // Storage: System Account (r:1 w:0) - fn reap_preimage(b: u32, ) -> Weight { - Weight::from_ref_time(28_619_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_000 as u64).saturating_mul(b as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + Weight::from_ref_time(6_519_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Democracy VotingOf (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) + /// The range of component `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { - Weight::from_ref_time(26_619_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(56_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(28_884_000 as u64) + // Standard Error: 2_631 + .saturating_add(Weight::from_ref_time(163_516 as u64).saturating_mul(r as u64)) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: Democracy VotingOf (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) + /// The range of component `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { - Weight::from_ref_time(25_373_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(142_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(33_498_000 as u64) + // Standard Error: 622 + .saturating_add(Weight::from_ref_time(133_421 as u64).saturating_mul(r as u64)) .saturating_add(T::DbWeight::get().reads(3 as u64)) .saturating_add(T::DbWeight::get().writes(3 as u64)) } // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy VotingOf (r:1 w:1) + /// The range of component `r` is `[1, 100]`. fn remove_vote(r: u32, ) -> Weight { - Weight::from_ref_time(15_961_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(115_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(18_201_000 as u64) + // Standard Error: 1_007 + .saturating_add(Weight::from_ref_time(152_699 as u64).saturating_mul(r as u64)) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy VotingOf (r:1 w:1) + /// The range of component `r` is `[1, 100]`. fn remove_other_vote(r: u32, ) -> Weight { - Weight::from_ref_time(15_992_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(113_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(18_455_000 as u64) + // Standard Error: 951 + .saturating_add(Weight::from_ref_time(150_907 as u64).saturating_mul(r as u64)) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -317,125 +275,103 @@ impl WeightInfo for () { // Storage: Democracy Blacklist (r:1 w:0) // Storage: Democracy DepositOf (r:0 w:1) fn propose() -> Weight { - Weight::from_ref_time(48_328_000 as u64) + Weight::from_ref_time(57_410_000 as u64) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } // Storage: Democracy DepositOf (r:1 w:1) - fn second(s: u32, ) -> Weight { - Weight::from_ref_time(30_923_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(142_000 as u64).saturating_mul(s as u64)) + fn second() -> Weight { + Weight::from_ref_time(49_224_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy VotingOf (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vote_new(r: u32, ) -> Weight { - Weight::from_ref_time(40_345_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(140_000 as u64).saturating_mul(r as u64)) + fn vote_new() -> Weight { + Weight::from_ref_time(60_933_000 as u64) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy VotingOf (r:1 w:1) // Storage: Balances Locks (r:1 w:1) - fn vote_existing(r: u32, ) -> Weight { - Weight::from_ref_time(39_853_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(150_000 as u64).saturating_mul(r as u64)) + fn vote_existing() -> Weight { + Weight::from_ref_time(60_393_000 as u64) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy Cancellations (r:1 w:1) fn emergency_cancel() -> Weight { - Weight::from_ref_time(19_364_000 as u64) + Weight::from_ref_time(24_588_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Democracy PublicProps (r:1 w:1) + // Storage: Democracy DepositOf (r:1 w:1) + // Storage: System Account (r:1 w:1) // Storage: Democracy NextExternal (r:1 w:1) // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy Blacklist (r:0 w:1) - // Storage: Democracy DepositOf (r:1 w:1) - // Storage: System Account (r:1 w:1) - fn blacklist(p: u32, ) -> Weight { - Weight::from_ref_time(57_708_000 as u64) - // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(192_000 as u64).saturating_mul(p as u64)) + fn blacklist() -> Weight { + Weight::from_ref_time(91_226_000 as u64) .saturating_add(RocksDbWeight::get().reads(5 as u64)) .saturating_add(RocksDbWeight::get().writes(6 as u64)) } // Storage: Democracy NextExternal (r:1 w:1) // Storage: Democracy Blacklist (r:1 w:0) - fn external_propose(v: u32, ) -> Weight { - Weight::from_ref_time(10_714_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(33_000 as u64).saturating_mul(v as u64)) + fn external_propose() -> Weight { + Weight::from_ref_time(18_898_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Democracy NextExternal (r:0 w:1) fn external_propose_majority() -> Weight { - Weight::from_ref_time(3_697_000 as u64) + Weight::from_ref_time(5_136_000 as u64) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Democracy NextExternal (r:0 w:1) fn external_propose_default() -> Weight { - Weight::from_ref_time(3_831_000 as u64) + Weight::from_ref_time(5_243_000 as u64) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Democracy NextExternal (r:1 w:1) // Storage: Democracy ReferendumCount (r:1 w:1) // Storage: Democracy ReferendumInfoOf (r:0 w:1) fn fast_track() -> Weight { - Weight::from_ref_time(20_271_000 as u64) + Weight::from_ref_time(24_275_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } // Storage: Democracy NextExternal (r:1 w:1) // Storage: Democracy Blacklist (r:1 w:1) - fn veto_external(v: u32, ) -> Weight { - Weight::from_ref_time(21_319_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(52_000 as u64).saturating_mul(v as u64)) + fn veto_external() -> Weight { + Weight::from_ref_time(30_988_000 as u64) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Democracy PublicProps (r:1 w:1) // Storage: Democracy DepositOf (r:1 w:1) // Storage: System Account (r:1 w:1) - fn cancel_proposal(p: u32, ) -> Weight { - Weight::from_ref_time(43_960_000 as u64) - // Standard Error: 2_000 - .saturating_add(Weight::from_ref_time(184_000 as u64).saturating_mul(p as u64)) + fn cancel_proposal() -> Weight { + Weight::from_ref_time(78_515_000 as u64) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } // Storage: Democracy ReferendumInfoOf (r:0 w:1) fn cancel_referendum() -> Weight { - Weight::from_ref_time(13_475_000 as u64) + Weight::from_ref_time(16_155_000 as u64) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } - // Storage: Scheduler Lookup (r:1 w:1) - // Storage: Scheduler Agenda (r:1 w:1) - fn cancel_queued(r: u32, ) -> Weight { - Weight::from_ref_time(24_320_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(560_000 as u64).saturating_mul(r as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - } // Storage: Democracy LowestUnbaked (r:1 w:1) // Storage: Democracy ReferendumCount (r:1 w:0) - // Storage: Democracy ReferendumInfoOf (r:1 w:0) + // Storage: Democracy ReferendumInfoOf (r:2 w:0) + /// The range of component `r` is `[0, 99]`. fn on_initialize_base(r: u32, ) -> Weight { - Weight::from_ref_time(3_428_000 as u64) - // Standard Error: 2_000 - .saturating_add(Weight::from_ref_time(3_171_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(7_007_000 as u64) + // Standard Error: 2_686 + .saturating_add(Weight::from_ref_time(2_288_781 as u64).saturating_mul(r as u64)) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(r as u64))) .saturating_add(RocksDbWeight::get().writes(1 as u64)) @@ -445,33 +381,36 @@ impl WeightInfo for () { // Storage: Democracy LastTabledWasExternal (r:1 w:0) // Storage: Democracy NextExternal (r:1 w:0) // Storage: Democracy PublicProps (r:1 w:0) - // Storage: Democracy ReferendumInfoOf (r:1 w:0) + // Storage: Democracy ReferendumInfoOf (r:2 w:0) + /// The range of component `r` is `[0, 99]`. fn on_initialize_base_with_launch_period(r: u32, ) -> Weight { - Weight::from_ref_time(7_867_000 as u64) - // Standard Error: 2_000 - .saturating_add(Weight::from_ref_time(3_177_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(9_528_000 as u64) + // Standard Error: 2_521 + .saturating_add(Weight::from_ref_time(2_291_780 as u64).saturating_mul(r as u64)) .saturating_add(RocksDbWeight::get().reads(5 as u64)) .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(r as u64))) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Democracy VotingOf (r:3 w:3) - // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Balances Locks (r:1 w:1) + // Storage: Democracy ReferendumInfoOf (r:2 w:2) + /// The range of component `r` is `[0, 99]`. fn delegate(r: u32, ) -> Weight { - Weight::from_ref_time(37_902_000 as u64) - // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(4_335_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(46_787_000 as u64) + // Standard Error: 2_943 + .saturating_add(Weight::from_ref_time(3_460_194 as u64).saturating_mul(r as u64)) .saturating_add(RocksDbWeight::get().reads(4 as u64)) .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(r as u64))) .saturating_add(RocksDbWeight::get().writes(4 as u64)) .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(r as u64))) } // Storage: Democracy VotingOf (r:2 w:2) - // Storage: Democracy ReferendumInfoOf (r:1 w:1) + // Storage: Democracy ReferendumInfoOf (r:2 w:2) + /// The range of component `r` is `[0, 99]`. fn undelegate(r: u32, ) -> Weight { - Weight::from_ref_time(21_272_000 as u64) - // Standard Error: 3_000 - .saturating_add(Weight::from_ref_time(4_351_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(29_789_000 as u64) + // Standard Error: 2_324 + .saturating_add(Weight::from_ref_time(3_360_918 as u64).saturating_mul(r as u64)) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(r as u64))) .saturating_add(RocksDbWeight::get().writes(2 as u64)) @@ -479,69 +418,48 @@ impl WeightInfo for () { } // Storage: Democracy PublicProps (r:0 w:1) fn clear_public_proposals() -> Weight { - Weight::from_ref_time(4_913_000 as u64) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Democracy Preimages (r:1 w:1) - fn note_preimage(b: u32, ) -> Weight { - Weight::from_ref_time(27_986_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(b as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Democracy Preimages (r:1 w:1) - fn note_imminent_preimage(b: u32, ) -> Weight { - Weight::from_ref_time(20_058_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(b as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - } - // Storage: Democracy Preimages (r:1 w:1) - // Storage: System Account (r:1 w:0) - fn reap_preimage(b: u32, ) -> Weight { - Weight::from_ref_time(28_619_000 as u64) - // Standard Error: 0 - .saturating_add(Weight::from_ref_time(1_000 as u64).saturating_mul(b as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + Weight::from_ref_time(6_519_000 as u64) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Democracy VotingOf (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) + /// The range of component `r` is `[0, 99]`. fn unlock_remove(r: u32, ) -> Weight { - Weight::from_ref_time(26_619_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(56_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(28_884_000 as u64) + // Standard Error: 2_631 + .saturating_add(Weight::from_ref_time(163_516 as u64).saturating_mul(r as u64)) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } // Storage: Democracy VotingOf (r:1 w:1) // Storage: Balances Locks (r:1 w:1) // Storage: System Account (r:1 w:1) + /// The range of component `r` is `[0, 99]`. fn unlock_set(r: u32, ) -> Weight { - Weight::from_ref_time(25_373_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(142_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(33_498_000 as u64) + // Standard Error: 622 + .saturating_add(Weight::from_ref_time(133_421 as u64).saturating_mul(r as u64)) .saturating_add(RocksDbWeight::get().reads(3 as u64)) .saturating_add(RocksDbWeight::get().writes(3 as u64)) } // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy VotingOf (r:1 w:1) + /// The range of component `r` is `[1, 100]`. fn remove_vote(r: u32, ) -> Weight { - Weight::from_ref_time(15_961_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(115_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(18_201_000 as u64) + // Standard Error: 1_007 + .saturating_add(Weight::from_ref_time(152_699 as u64).saturating_mul(r as u64)) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Democracy ReferendumInfoOf (r:1 w:1) // Storage: Democracy VotingOf (r:1 w:1) + /// The range of component `r` is `[1, 100]`. fn remove_other_vote(r: u32, ) -> Weight { - Weight::from_ref_time(15_992_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(113_000 as u64).saturating_mul(r as u64)) + Weight::from_ref_time(18_455_000 as u64) + // Standard Error: 951 + .saturating_add(Weight::from_ref_time(150_907 as u64).saturating_mul(r as u64)) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } diff --git a/substrate/frame/identity/src/tests.rs b/substrate/frame/identity/src/tests.rs index 747d1e9f271066ece26883c8e414ad2eb82d51d8..20628da50937a73da759b4fd64df8542b62aa1d9 100644 --- a/substrate/frame/identity/src/tests.rs +++ b/substrate/frame/identity/src/tests.rs @@ -268,7 +268,7 @@ fn registration_should_work() { let mut three_fields = ten(); three_fields.additional.try_push(Default::default()).unwrap(); three_fields.additional.try_push(Default::default()).unwrap(); - assert_eq!(three_fields.additional.try_push(Default::default()), Err(())); + assert!(three_fields.additional.try_push(Default::default()).is_err()); assert_ok!(Identity::set_identity(RuntimeOrigin::signed(10), Box::new(ten()))); assert_eq!(Identity::identity(10).unwrap().info, ten()); assert_eq!(Balances::free_balance(10), 90); diff --git a/substrate/frame/multisig/Cargo.toml b/substrate/frame/multisig/Cargo.toml index 6c8b5fbaa7362f7b3dc97fa592f0c7c5152bfcf5..bfd0870d30c22c5a67e0486060871c0581dc169e 100644 --- a/substrate/frame/multisig/Cargo.toml +++ b/substrate/frame/multisig/Cargo.toml @@ -22,6 +22,9 @@ sp-io = { version = "6.0.0", default-features = false, path = "../../primitives/ sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } +# third party +log = { version = "0.4.17", default-features = false } + [dev-dependencies] pallet-balances = { version = "4.0.0-dev", path = "../balances" } sp-core = { version = "6.0.0", path = "../../primitives/core" } diff --git a/substrate/frame/multisig/src/benchmarking.rs b/substrate/frame/multisig/src/benchmarking.rs index c0b0097b0723609faedb7e1f8e966ac94916bec4..d949414e31cb37a7be2a0060f4cbe359f620bc1c 100644 --- a/substrate/frame/multisig/src/benchmarking.rs +++ b/substrate/frame/multisig/src/benchmarking.rs @@ -31,7 +31,7 @@ const SEED: u32 = 0; fn setup_multi<T: Config>( s: u32, z: u32, -) -> Result<(Vec<T::AccountId>, OpaqueCall<T>), &'static str> { +) -> Result<(Vec<T::AccountId>, Box<<T as Config>::RuntimeCall>), &'static str> { let mut signatories: Vec<T::AccountId> = Vec::new(); for i in 0..s { let signatory = account("signatory", i, SEED); @@ -44,8 +44,7 @@ fn setup_multi<T: Config>( // Must first convert to runtime call type. let call: <T as Config>::RuntimeCall = frame_system::Call::<T>::remark { remark: vec![0; z as usize] }.into(); - let call_data = OpaqueCall::<T>::from_encoded(call.encode()); - Ok((signatories, call_data)) + Ok((signatories, Box::new(call))) } benchmarks! { @@ -74,35 +73,15 @@ benchmarks! { // Transaction Length let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::<T>(s, z)?; - let call_hash = blake2_256(call.encoded()); - let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap()); - let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - // Whitelist caller account from further DB operations. - let caller_key = frame_system::Account::<T>::hashed_key_for(&caller); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, false, Weight::zero()) - verify { - assert!(Multisigs::<T>::contains_key(multi_account_id, call_hash)); - assert!(!Calls::<T>::contains_key(call_hash)); - } - - as_multi_create_store { - // Signatories, need at least 2 total people - let s in 2 .. T::MaxSignatories::get() as u32; - // Transaction Length - let z in 0 .. 10_000; - let (mut signatories, call) = setup_multi::<T>(s, z)?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value()); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::<T>::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, true, Weight::zero()) + }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, Weight::zero()) verify { assert!(Multisigs::<T>::contains_key(multi_account_id, call_hash)); - assert!(Calls::<T>::contains_key(call_hash)); } as_multi_approve { @@ -111,49 +90,22 @@ benchmarks! { // Transaction Length let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::<T>(s, z)?; - let call_hash = blake2_256(call.encoded()); - let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap()); - let mut signatories2 = signatories.clone(); - let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - // before the call, get the timepoint - let timepoint = Multisig::<T>::timepoint(); - // Create the multi, storing for worst case - Multisig::<T>::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), true, Weight::zero())?; - assert!(Calls::<T>::contains_key(call_hash)); - let caller2 = signatories2.remove(0); - // Whitelist caller account from further DB operations. - let caller_key = frame_system::Account::<T>::hashed_key_for(&caller2); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, false, Weight::zero()) - verify { - let multisig = Multisigs::<T>::get(multi_account_id, call_hash).ok_or("multisig not created")?; - assert_eq!(multisig.approvals.len(), 2); - } - - as_multi_approve_store { - // Signatories, need at least 3 people (so we don't complete the multisig) - let s in 3 .. T::MaxSignatories::get() as u32; - // Transaction Length - let z in 0 .. 10_000; - let (mut signatories, call) = setup_multi::<T>(s, z)?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap()); let mut signatories2 = signatories.clone(); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; // before the call, get the timepoint let timepoint = Multisig::<T>::timepoint(); - // Create the multi, not storing - Multisig::<T>::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), false, Weight::zero())?; - assert!(!Calls::<T>::contains_key(call_hash)); + // Create the multi + Multisig::<T>::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), Weight::zero())?; let caller2 = signatories2.remove(0); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::<T>::hashed_key_for(&caller2); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, true, Weight::zero()) + }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, Weight::zero()) verify { let multisig = Multisigs::<T>::get(multi_account_id, call_hash).ok_or("multisig not created")?; assert_eq!(multisig.approvals.len(), 2); - assert!(Calls::<T>::contains_key(call_hash)); } as_multi_complete { @@ -162,27 +114,27 @@ benchmarks! { // Transaction Length let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::<T>(s, z)?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap()); let mut signatories2 = signatories.clone(); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; // before the call, get the timepoint let timepoint = Multisig::<T>::timepoint(); - // Create the multi, storing it for worst case - Multisig::<T>::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), true, Weight::zero())?; + // Create the multi + Multisig::<T>::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), Weight::zero())?; // Everyone except the first person approves for i in 1 .. s - 1 { let mut signatories_loop = signatories2.clone(); let caller_loop = signatories_loop.remove(i as usize); let o = RawOrigin::Signed(caller_loop).into(); - Multisig::<T>::as_multi(o, s as u16, signatories_loop, Some(timepoint), call.clone(), false, Weight::zero())?; + Multisig::<T>::as_multi(o, s as u16, signatories_loop, Some(timepoint), call.clone(), Weight::zero())?; } let caller2 = signatories2.remove(0); assert!(Multisigs::<T>::contains_key(&multi_account_id, call_hash)); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::<T>::hashed_key_for(&caller2); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, false, Weight::MAX) + }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, Weight::MAX) verify { assert!(!Multisigs::<T>::contains_key(&multi_account_id, call_hash)); } @@ -195,7 +147,7 @@ benchmarks! { let (mut signatories, call) = setup_multi::<T>(s, z)?; let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::<T>::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); @@ -214,7 +166,7 @@ benchmarks! { let mut signatories2 = signatories.clone(); let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); // before the call, get the timepoint let timepoint = Multisig::<T>::timepoint(); // Create the multi @@ -224,7 +176,6 @@ benchmarks! { signatories, None, call, - false, Weight::zero() )?; let caller2 = signatories2.remove(0); @@ -237,45 +188,6 @@ benchmarks! { assert_eq!(multisig.approvals.len(), 2); } - approve_as_multi_complete { - // Signatories, need at least 2 people - let s in 2 .. T::MaxSignatories::get() as u32; - // Transaction Length, not a component - let z = 10_000; - let (mut signatories, call) = setup_multi::<T>(s, z)?; - let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap()); - let mut signatories2 = signatories.clone(); - let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - T::Currency::make_free_balance_be(&caller, BalanceOf::<T>::max_value()); - let call_hash = blake2_256(call.encoded()); - // before the call, get the timepoint - let timepoint = Multisig::<T>::timepoint(); - // Create the multi - Multisig::<T>::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), true, Weight::zero())?; - // Everyone except the first person approves - for i in 1 .. s - 1 { - let mut signatories_loop = signatories2.clone(); - let caller_loop = signatories_loop.remove(i as usize); - let o = RawOrigin::Signed(caller_loop).into(); - Multisig::<T>::as_multi(o, s as u16, signatories_loop, Some(timepoint), call.clone(), false, Weight::zero())?; - } - let caller2 = signatories2.remove(0); - assert!(Multisigs::<T>::contains_key(&multi_account_id, call_hash)); - // Whitelist caller account from further DB operations. - let caller_key = frame_system::Account::<T>::hashed_key_for(&caller2); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: approve_as_multi( - RawOrigin::Signed(caller2), - s as u16, - signatories2, - Some(timepoint), - call_hash, - Weight::MAX - ) - verify { - assert!(!Multisigs::<T>::contains_key(multi_account_id, call_hash)); - } - cancel_as_multi { // Signatories, need at least 2 people let s in 2 .. T::MaxSignatories::get() as u32; @@ -284,20 +196,18 @@ benchmarks! { let (mut signatories, call) = setup_multi::<T>(s, z)?; let multi_account_id = Multisig::<T>::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); let timepoint = Multisig::<T>::timepoint(); // Create the multi let o = RawOrigin::Signed(caller.clone()).into(); - Multisig::<T>::as_multi(o, s as u16, signatories.clone(), None, call, true, Weight::zero())?; + Multisig::<T>::as_multi(o, s as u16, signatories.clone(), None, call, Weight::zero())?; assert!(Multisigs::<T>::contains_key(&multi_account_id, call_hash)); - assert!(Calls::<T>::contains_key(call_hash)); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::<T>::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: _(RawOrigin::Signed(caller), s as u16, signatories, timepoint, call_hash) verify { assert!(!Multisigs::<T>::contains_key(multi_account_id, call_hash)); - assert!(!Calls::<T>::contains_key(call_hash)); } impl_benchmark_test_suite!(Multisig, crate::tests::new_test_ext(), crate::tests::Test); diff --git a/substrate/frame/multisig/src/lib.rs b/substrate/frame/multisig/src/lib.rs index 3bdb47ffc4568c88d17a615e4dd0d626dede8dbf..e3031cc830209d4c19be1f6f4da313cd82e00478 100644 --- a/substrate/frame/multisig/src/lib.rs +++ b/substrate/frame/multisig/src/lib.rs @@ -47,6 +47,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; +pub mod migrations; mod tests; pub mod weights; @@ -57,7 +58,7 @@ use frame_support::{ PostDispatchInfo, }, ensure, - traits::{Currency, Get, ReservableCurrency, WrapperKeepOpaque}, + traits::{Currency, Get, ReservableCurrency}, weights::Weight, RuntimeDebug, }; @@ -73,6 +74,20 @@ pub use weights::WeightInfo; pub use pallet::*; +/// The log target of this pallet. +pub const LOG_TARGET: &'static str = "runtime::multisig"; + +// syntactic sugar for logging. +#[macro_export] +macro_rules! log { + ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { + log::$level!( + target: crate::LOG_TARGET, + concat!("[{:?}] âœï¸ ", $patter), <frame_system::Pallet<T>>::block_number() $(, $values)* + ) + }; +} + type BalanceOf<T> = <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance; @@ -100,12 +115,10 @@ pub struct Multisig<BlockNumber, Balance, AccountId> { approvals: Vec<AccountId>, } -type OpaqueCall<T> = WrapperKeepOpaque<<T as Config>::RuntimeCall>; - type CallHash = [u8; 32]; enum CallOrHash<T: Config> { - Call(OpaqueCall<T>, bool), + Call(<T as Config>::RuntimeCall), Hash([u8; 32]), } @@ -152,9 +165,13 @@ pub mod pallet { type WeightInfo: WeightInfo; } + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] #[pallet::without_storage_info] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet<T>(_); /// The set of open multisig operations. @@ -168,10 +185,6 @@ pub mod pallet { Multisig<T::BlockNumber, BalanceOf<T>, T::AccountId>, >; - #[pallet::storage] - pub type Calls<T: Config> = - StorageMap<_, Identity, [u8; 32], (OpaqueCall<T>, T::AccountId, BalanceOf<T>)>; - #[pallet::error] pub enum Error<T> { /// Threshold must be 2 or greater. @@ -343,13 +356,13 @@ pub mod pallet { /// taken for its lifetime of `DepositBase + threshold * DepositFactor`. /// ------------------------------- /// - DB Weight: - /// - Reads: Multisig Storage, [Caller Account], Calls (if `store_call`) - /// - Writes: Multisig Storage, [Caller Account], Calls (if `store_call`) + /// - Reads: Multisig Storage, [Caller Account] + /// - Writes: Multisig Storage, [Caller Account] /// - Plus Call Weight /// # </weight> #[pallet::weight({ let s = other_signatories.len() as u32; - let z = call.encoded_len() as u32; + let z = call.using_encoded(|d| d.len()) as u32; T::WeightInfo::as_multi_create(s, z) .max(T::WeightInfo::as_multi_create_store(s, z)) @@ -362,8 +375,7 @@ pub mod pallet { threshold: u16, other_signatories: Vec<T::AccountId>, maybe_timepoint: Option<Timepoint<T::BlockNumber>>, - call: OpaqueCall<T>, - store_call: bool, + call: Box<<T as Config>::RuntimeCall>, max_weight: Weight, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; @@ -372,7 +384,7 @@ pub mod pallet { threshold, other_signatories, maybe_timepoint, - CallOrHash::Call(call, store_call), + CallOrHash::Call(*call), max_weight, ) } @@ -462,8 +474,8 @@ pub mod pallet { /// - Storage: removes one item. /// ---------------------------------- /// - DB Weight: - /// - Read: Multisig Storage, [Caller Account], Refund Account, Calls - /// - Write: Multisig Storage, [Caller Account], Refund Account, Calls + /// - Read: Multisig Storage, [Caller Account], Refund Account + /// - Write: Multisig Storage, [Caller Account], Refund Account /// # </weight> #[pallet::weight(T::WeightInfo::cancel_as_multi(other_signatories.len() as u32))] pub fn cancel_as_multi( @@ -489,7 +501,6 @@ pub mod pallet { let err_amount = T::Currency::unreserve(&m.depositor, m.deposit); debug_assert!(err_amount.is_zero()); <Multisigs<T>>::remove(&id, &call_hash); - Self::clear_call(&call_hash); Self::deposit_event(Event::MultisigCancelled { cancelling: who, @@ -531,13 +542,12 @@ impl<T: Config> Pallet<T> { let id = Self::multi_account_id(&signatories, threshold); // Threshold > 1; this means it's a multi-step operation. We extract the `call_hash`. - let (call_hash, call_len, maybe_call, store) = match call_or_hash { - CallOrHash::Call(call, should_store) => { - let call_hash = blake2_256(call.encoded()); - let call_len = call.encoded_len(); - (call_hash, call_len, Some(call), should_store) + let (call_hash, call_len, maybe_call) = match call_or_hash { + CallOrHash::Call(call) => { + let (call_hash, call_len) = call.using_encoded(|d| (blake2_256(d), d.len())); + (call_hash, call_len, Some(call)) }, - CallOrHash::Hash(h) => (h, 0, None, false), + CallOrHash::Hash(h) => (h, 0, None), }; // Branch on whether the operation has already started or not. @@ -556,13 +566,7 @@ impl<T: Config> Pallet<T> { } // We only bother fetching/decoding call if we know that we're ready to execute. - let maybe_approved_call = if approvals >= threshold { - Self::get_call(&call_hash, maybe_call.as_ref()) - } else { - None - }; - - if let Some((call, call_len)) = maybe_approved_call { + if let Some(call) = maybe_call.filter(|_| approvals >= threshold) { // verify weight ensure!( call.get_dispatch_info().weight.all_lte(max_weight), @@ -572,7 +576,6 @@ impl<T: Config> Pallet<T> { // Clean up storage before executing call to avoid an possibility of reentrancy // attack. <Multisigs<T>>::remove(&id, call_hash); - Self::clear_call(&call_hash); T::Currency::unreserve(&m.depositor, m.deposit); let result = call.dispatch(RawOrigin::Signed(id.clone()).into()); @@ -596,19 +599,6 @@ impl<T: Config> Pallet<T> { // We cannot dispatch the call now; either it isn't available, or it is, but we // don't have threshold approvals even with our signature. - // Store the call if desired. - let stored = if let Some(data) = maybe_call.filter(|_| store) { - Self::store_call_and_reserve( - who.clone(), - &call_hash, - data, - BalanceOf::<T>::zero(), - )?; - true - } else { - false - }; - if let Some(pos) = maybe_pos { // Record approval. m.approvals.insert(pos, who.clone()); @@ -622,17 +612,11 @@ impl<T: Config> Pallet<T> { } else { // If we already approved and didn't store the Call, then this was useless and // we report an error. - ensure!(stored, Error::<T>::AlreadyApproved); + Err(Error::<T>::AlreadyApproved)? } - let final_weight = if stored { - T::WeightInfo::as_multi_approve_store( - other_signatories_len as u32, - call_len as u32, - ) - } else { - T::WeightInfo::as_multi_approve(other_signatories_len as u32, call_len as u32) - }; + let final_weight = + T::WeightInfo::as_multi_approve(other_signatories_len as u32, call_len as u32); // Call is not made, so the actual weight does not include call Ok(Some(final_weight).into()) } @@ -643,14 +627,7 @@ impl<T: Config> Pallet<T> { // Just start the operation by recording it in storage. let deposit = T::DepositBase::get() + T::DepositFactor::get() * threshold.into(); - // Store the call if desired. - let stored = if let Some(data) = maybe_call.filter(|_| store) { - Self::store_call_and_reserve(who.clone(), &call_hash, data, deposit)?; - true - } else { - T::Currency::reserve(&who, deposit)?; - false - }; + T::Currency::reserve(&who, deposit)?; <Multisigs<T>>::insert( &id, @@ -664,58 +641,13 @@ impl<T: Config> Pallet<T> { ); Self::deposit_event(Event::NewMultisig { approving: who, multisig: id, call_hash }); - let final_weight = if stored { - T::WeightInfo::as_multi_create_store(other_signatories_len as u32, call_len as u32) - } else { - T::WeightInfo::as_multi_create(other_signatories_len as u32, call_len as u32) - }; + let final_weight = + T::WeightInfo::as_multi_create(other_signatories_len as u32, call_len as u32); // Call is not made, so the actual weight does not include call Ok(Some(final_weight).into()) } } - /// Place a call's encoded data in storage, reserving funds as appropriate. - /// - /// We store `data` here because storing `call` would result in needing another `.encode`. - /// - /// Returns a `bool` indicating whether the data did end up being stored. - fn store_call_and_reserve( - who: T::AccountId, - hash: &[u8; 32], - data: OpaqueCall<T>, - other_deposit: BalanceOf<T>, - ) -> DispatchResult { - ensure!(!Calls::<T>::contains_key(hash), Error::<T>::AlreadyStored); - let deposit = other_deposit + - T::DepositBase::get() + - T::DepositFactor::get() * - BalanceOf::<T>::from(((data.encoded_len() + 31) / 32) as u32); - T::Currency::reserve(&who, deposit)?; - Calls::<T>::insert(&hash, (data, who, deposit)); - Ok(()) - } - - /// Attempt to decode and return the call, provided by the user or from storage. - fn get_call( - hash: &[u8; 32], - maybe_known: Option<&OpaqueCall<T>>, - ) -> Option<(<T as Config>::RuntimeCall, usize)> { - maybe_known.map_or_else( - || { - Calls::<T>::get(hash) - .and_then(|(data, ..)| Some((data.try_decode()?, data.encoded_len()))) - }, - |data| Some((data.try_decode()?, data.encoded_len())), - ) - } - - /// Attempt to remove a call from storage, returning any deposit on it to the owner. - fn clear_call(hash: &[u8; 32]) { - if let Some((_, who, deposit)) = Calls::<T>::take(hash) { - T::Currency::unreserve(&who, deposit); - } - } - /// The current `Timepoint`. pub fn timepoint() -> Timepoint<T::BlockNumber> { Timepoint { diff --git a/substrate/frame/multisig/src/migrations.rs b/substrate/frame/multisig/src/migrations.rs new file mode 100644 index 0000000000000000000000000000000000000000..5085297cde433f38e1016c24b5a4896e498afbcf --- /dev/null +++ b/substrate/frame/multisig/src/migrations.rs @@ -0,0 +1,86 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 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. + +// Migrations for Multisig Pallet + +use super::*; +use frame_support::{ + dispatch::GetStorageVersion, + traits::{OnRuntimeUpgrade, WrapperKeepOpaque}, + Identity, +}; + +#[cfg(feature = "try-runtime")] +use frame_support::ensure; + +pub mod v1 { + use super::*; + + type OpaqueCall<T> = WrapperKeepOpaque<<T as Config>::RuntimeCall>; + + #[frame_support::storage_alias] + type Calls<T: Config> = StorageMap< + Pallet<T>, + Identity, + [u8; 32], + (OpaqueCall<T>, <T as frame_system::Config>::AccountId, BalanceOf<T>), + >; + + pub struct MigrateToV1<T>(sp_std::marker::PhantomData<T>); + impl<T: Config> OnRuntimeUpgrade for MigrateToV1<T> { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<Vec<u8>, &'static str> { + let onchain = Pallet::<T>::on_chain_storage_version(); + + ensure!(onchain < 1, "this migration can be deleted"); + + log!(info, "Number of calls to refund and delete: {}", Calls::<T>::iter().count()); + + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> Weight { + let current = Pallet::<T>::current_storage_version(); + let onchain = Pallet::<T>::on_chain_storage_version(); + + if onchain > 0 { + log!(info, "MigrateToV1 should be removed"); + return T::DbWeight::get().reads(1) + } + + Calls::<T>::drain().for_each(|(_call_hash, (_data, caller, deposit))| { + T::Currency::unreserve(&caller, deposit); + }); + + current.put::<Pallet<T>>(); + + <T as frame_system::Config>::BlockWeights::get().max_block + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec<u8>) -> Result<(), &'static str> { + let onchain = Pallet::<T>::on_chain_storage_version(); + ensure!(onchain < 2, "this migration needs to be removed"); + ensure!(onchain == 1, "this migration needs to be run"); + ensure!( + Calls::<T>::iter().count() == 0, + "there are some dangling calls that need to be destroyed and refunded" + ); + Ok(()) + } + } +} diff --git a/substrate/frame/multisig/src/tests.rs b/substrate/frame/multisig/src/tests.rs index b24a06f454368edbae2f4f67b0627c937e54b719..f753b6f386c562f18567f37d767bd2605d7bc998 100644 --- a/substrate/frame/multisig/src/tests.rs +++ b/substrate/frame/multisig/src/tests.rs @@ -34,7 +34,6 @@ use sp_runtime::{ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>; type Block = frame_system::mocking::MockBlock<Test>; -type OpaqueCall = super::OpaqueCall<Test>; frame_support::construct_runtime!( pub enum Test where @@ -130,8 +129,8 @@ fn now() -> Timepoint<u64> { Multisig::timepoint() } -fn call_transfer(dest: u64, value: u64) -> RuntimeCall { - RuntimeCall::Balances(BalancesCall::transfer { dest, value }) +fn call_transfer(dest: u64, value: u64) -> Box<RuntimeCall> { + Box::new(RuntimeCall::Balances(BalancesCall::transfer { dest, value })) } #[test] @@ -144,14 +143,12 @@ fn multisig_deposit_is_taken_and_returned() { let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3], None, - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), Weight::zero() )); assert_eq!(Balances::free_balance(1), 2); @@ -162,8 +159,7 @@ fn multisig_deposit_is_taken_and_returned() { 2, vec![1, 3], Some(now()), - OpaqueCall::from_encoded(data), - false, + call, call_weight )); assert_eq!(Balances::free_balance(1), 5); @@ -171,96 +167,6 @@ fn multisig_deposit_is_taken_and_returned() { }); } -#[test] -fn multisig_deposit_is_taken_and_returned_with_call_storage() { - new_test_ext().execute_with(|| { - let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); - - let call = call_transfer(6, 15); - let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); - assert_ok!(Multisig::as_multi( - RuntimeOrigin::signed(1), - 2, - vec![2, 3], - None, - OpaqueCall::from_encoded(data), - true, - Weight::zero() - )); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 5); - - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(2), - 2, - vec![1, 3], - Some(now()), - hash, - call_weight - )); - assert_eq!(Balances::free_balance(1), 5); - assert_eq!(Balances::reserved_balance(1), 0); - }); -} - -#[test] -fn multisig_deposit_is_taken_and_returned_with_alt_call_storage() { - new_test_ext().execute_with(|| { - let multi = Multisig::multi_account_id(&[1, 2, 3][..], 3); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); - - let call = call_transfer(6, 15); - let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); - - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(1), - 3, - vec![2, 3], - None, - hash, - Weight::zero() - )); - assert_eq!(Balances::free_balance(1), 1); - assert_eq!(Balances::reserved_balance(1), 4); - - assert_ok!(Multisig::as_multi( - RuntimeOrigin::signed(2), - 3, - vec![1, 3], - Some(now()), - OpaqueCall::from_encoded(data), - true, - Weight::zero() - )); - assert_eq!(Balances::free_balance(2), 3); - assert_eq!(Balances::reserved_balance(2), 2); - assert_eq!(Balances::free_balance(1), 1); - assert_eq!(Balances::reserved_balance(1), 4); - - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(3), - 3, - vec![1, 2], - Some(now()), - hash, - call_weight - )); - assert_eq!(Balances::free_balance(1), 5); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::free_balance(2), 5); - assert_eq!(Balances::reserved_balance(2), 0); - }); -} - #[test] fn cancel_multisig_returns_deposit() { new_test_ext().execute_with(|| { @@ -298,8 +204,8 @@ fn timepoint_checking_works() { assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); - let call = call_transfer(6, 15).encode(); - let hash = blake2_256(&call); + let call = call_transfer(6, 15); + let hash = blake2_256(&call.encode()); assert_noop!( Multisig::approve_as_multi( @@ -328,8 +234,7 @@ fn timepoint_checking_works() { 2, vec![1, 3], None, - OpaqueCall::from_encoded(call.clone()), - false, + call.clone(), Weight::zero() ), Error::<Test>::NoTimepoint, @@ -341,8 +246,7 @@ fn timepoint_checking_works() { 2, vec![1, 3], Some(later), - OpaqueCall::from_encoded(call), - false, + call, Weight::zero() ), Error::<Test>::WrongTimepoint, @@ -350,41 +254,6 @@ fn timepoint_checking_works() { }); } -#[test] -fn multisig_2_of_3_works_with_call_storing() { - new_test_ext().execute_with(|| { - let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); - - let call = call_transfer(6, 15); - let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); - assert_ok!(Multisig::as_multi( - RuntimeOrigin::signed(1), - 2, - vec![2, 3], - None, - OpaqueCall::from_encoded(data), - true, - Weight::zero() - )); - assert_eq!(Balances::free_balance(6), 0); - - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(2), - 2, - vec![1, 3], - Some(now()), - hash, - call_weight - )); - assert_eq!(Balances::free_balance(6), 15); - }); -} - #[test] fn multisig_2_of_3_works() { new_test_ext().execute_with(|| { @@ -395,8 +264,7 @@ fn multisig_2_of_3_works() { let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); + let hash = blake2_256(&call.encode()); assert_ok!(Multisig::approve_as_multi( RuntimeOrigin::signed(1), 2, @@ -412,8 +280,7 @@ fn multisig_2_of_3_works() { 2, vec![1, 3], Some(now()), - OpaqueCall::from_encoded(data), - false, + call, call_weight )); assert_eq!(Balances::free_balance(6), 15); @@ -430,8 +297,7 @@ fn multisig_3_of_3_works() { let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); + let hash = blake2_256(&call.encode()); assert_ok!(Multisig::approve_as_multi( RuntimeOrigin::signed(1), 3, @@ -455,8 +321,7 @@ fn multisig_3_of_3_works() { 3, vec![1, 2], Some(now()), - OpaqueCall::from_encoded(data), - false, + call, call_weight )); assert_eq!(Balances::free_balance(6), 15); @@ -492,68 +357,6 @@ fn cancel_multisig_works() { }); } -#[test] -fn cancel_multisig_with_call_storage_works() { - new_test_ext().execute_with(|| { - let call = call_transfer(6, 15).encode(); - let hash = blake2_256(&call); - assert_ok!(Multisig::as_multi( - RuntimeOrigin::signed(1), - 3, - vec![2, 3], - None, - OpaqueCall::from_encoded(call), - true, - Weight::zero() - )); - assert_eq!(Balances::free_balance(1), 4); - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(2), - 3, - vec![1, 3], - Some(now()), - hash, - Weight::zero() - )); - assert_noop!( - Multisig::cancel_as_multi(RuntimeOrigin::signed(2), 3, vec![1, 3], now(), hash), - Error::<Test>::NotOwner, - ); - assert_ok!(Multisig::cancel_as_multi(RuntimeOrigin::signed(1), 3, vec![2, 3], now(), hash),); - assert_eq!(Balances::free_balance(1), 10); - }); -} - -#[test] -fn cancel_multisig_with_alt_call_storage_works() { - new_test_ext().execute_with(|| { - let call = call_transfer(6, 15).encode(); - let hash = blake2_256(&call); - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(1), - 3, - vec![2, 3], - None, - hash, - Weight::zero() - )); - assert_eq!(Balances::free_balance(1), 6); - assert_ok!(Multisig::as_multi( - RuntimeOrigin::signed(2), - 3, - vec![1, 3], - Some(now()), - OpaqueCall::from_encoded(call), - true, - Weight::zero() - )); - assert_eq!(Balances::free_balance(2), 8); - assert_ok!(Multisig::cancel_as_multi(RuntimeOrigin::signed(1), 3, vec![2, 3], now(), hash)); - assert_eq!(Balances::free_balance(1), 10); - assert_eq!(Balances::free_balance(2), 10); - }); -} - #[test] fn multisig_2_of_3_as_multi_works() { new_test_ext().execute_with(|| { @@ -564,14 +367,12 @@ fn multisig_2_of_3_as_multi_works() { let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3], None, - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), Weight::zero() )); assert_eq!(Balances::free_balance(6), 0); @@ -581,8 +382,7 @@ fn multisig_2_of_3_as_multi_works() { 2, vec![1, 3], Some(now()), - OpaqueCall::from_encoded(data), - false, + call, call_weight )); assert_eq!(Balances::free_balance(6), 15); @@ -599,18 +399,15 @@ fn multisig_2_of_3_as_multi_with_many_calls_works() { let call1 = call_transfer(6, 10); let call1_weight = call1.get_dispatch_info().weight; - let data1 = call1.encode(); let call2 = call_transfer(7, 5); let call2_weight = call2.get_dispatch_info().weight; - let data2 = call2.encode(); assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3], None, - OpaqueCall::from_encoded(data1.clone()), - false, + call1.clone(), Weight::zero() )); assert_ok!(Multisig::as_multi( @@ -618,8 +415,7 @@ fn multisig_2_of_3_as_multi_with_many_calls_works() { 2, vec![1, 3], None, - OpaqueCall::from_encoded(data2.clone()), - false, + call2.clone(), Weight::zero() )); assert_ok!(Multisig::as_multi( @@ -627,8 +423,7 @@ fn multisig_2_of_3_as_multi_with_many_calls_works() { 2, vec![1, 2], Some(now()), - OpaqueCall::from_encoded(data1), - false, + call1, call1_weight )); assert_ok!(Multisig::as_multi( @@ -636,8 +431,7 @@ fn multisig_2_of_3_as_multi_with_many_calls_works() { 2, vec![1, 2], Some(now()), - OpaqueCall::from_encoded(data2), - false, + call2, call2_weight )); @@ -656,15 +450,13 @@ fn multisig_2_of_3_cannot_reissue_same_call() { let call = call_transfer(6, 10); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); + let hash = blake2_256(&call.encode()); assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3], None, - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), Weight::zero() )); assert_ok!(Multisig::as_multi( @@ -672,8 +464,7 @@ fn multisig_2_of_3_cannot_reissue_same_call() { 2, vec![1, 3], Some(now()), - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), call_weight )); assert_eq!(Balances::free_balance(multi), 5); @@ -683,8 +474,7 @@ fn multisig_2_of_3_cannot_reissue_same_call() { 2, vec![2, 3], None, - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), Weight::zero() )); assert_ok!(Multisig::as_multi( @@ -692,8 +482,7 @@ fn multisig_2_of_3_cannot_reissue_same_call() { 2, vec![1, 2], Some(now()), - OpaqueCall::from_encoded(data), - false, + call.clone(), call_weight )); @@ -714,15 +503,14 @@ fn multisig_2_of_3_cannot_reissue_same_call() { #[test] fn minimum_threshold_check_works() { new_test_ext().execute_with(|| { - let call = call_transfer(6, 15).encode(); + let call = call_transfer(6, 15); assert_noop!( Multisig::as_multi( RuntimeOrigin::signed(1), 0, vec![2], None, - OpaqueCall::from_encoded(call.clone()), - false, + call.clone(), Weight::zero() ), Error::<Test>::MinimumThreshold, @@ -733,8 +521,7 @@ fn minimum_threshold_check_works() { 1, vec![2], None, - OpaqueCall::from_encoded(call.clone()), - false, + call.clone(), Weight::zero() ), Error::<Test>::MinimumThreshold, @@ -745,15 +532,14 @@ fn minimum_threshold_check_works() { #[test] fn too_many_signatories_fails() { new_test_ext().execute_with(|| { - let call = call_transfer(6, 15).encode(); + let call = call_transfer(6, 15); assert_noop!( Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3, 4], None, - OpaqueCall::from_encoded(call), - false, + call.clone(), Weight::zero() ), Error::<Test>::TooManySignatories, @@ -815,8 +601,8 @@ fn multisig_1_of_3_works() { assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); - let call = call_transfer(6, 15).encode(); - let hash = blake2_256(&call); + let call = call_transfer(6, 15); + let hash = blake2_256(&call.encode()); assert_noop!( Multisig::approve_as_multi( RuntimeOrigin::signed(1), @@ -834,17 +620,15 @@ fn multisig_1_of_3_works() { 1, vec![2, 3], None, - OpaqueCall::from_encoded(call), - false, + call.clone(), Weight::zero() ), Error::<Test>::MinimumThreshold, ); - let boxed_call = Box::new(call_transfer(6, 15)); assert_ok!(Multisig::as_multi_threshold_1( RuntimeOrigin::signed(1), vec![2, 3], - boxed_call + call_transfer(6, 15) )); assert_eq!(Balances::free_balance(6), 15); @@ -871,14 +655,12 @@ fn weight_check_works() { assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); - let data = call.encode(); assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3], None, - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), Weight::zero() )); assert_eq!(Balances::free_balance(6), 0); @@ -889,8 +671,7 @@ fn weight_check_works() { 2, vec![1, 3], Some(now()), - OpaqueCall::from_encoded(data), - false, + call, Weight::zero() ), Error::<Test>::MaxWeightTooLow, @@ -911,8 +692,7 @@ fn multisig_handles_no_preimage_after_all_approve() { let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); + let hash = blake2_256(&call.encode()); assert_ok!(Multisig::approve_as_multi( RuntimeOrigin::signed(1), 3, @@ -944,8 +724,7 @@ fn multisig_handles_no_preimage_after_all_approve() { 3, vec![1, 2], Some(now()), - OpaqueCall::from_encoded(data), - false, + call, call_weight )); assert_eq!(Balances::free_balance(6), 15); diff --git a/substrate/frame/nicks/src/lib.rs b/substrate/frame/nicks/src/lib.rs index 953cf39cd12db581333cae703e8d396264c7d089..b3238630d31745e03ad24108dc6723403af6aa08 100644 --- a/substrate/frame/nicks/src/lib.rs +++ b/substrate/frame/nicks/src/lib.rs @@ -140,7 +140,7 @@ pub mod pallet { let sender = ensure_signed(origin)?; let bounded_name: BoundedVec<_, _> = - name.try_into().map_err(|()| Error::<T>::TooLong)?; + name.try_into().map_err(|_| Error::<T>::TooLong)?; ensure!(bounded_name.len() >= T::MinLength::get() as usize, Error::<T>::TooShort); let deposit = if let Some((_, deposit)) = <NameOf<T>>::get(&sender) { @@ -229,7 +229,7 @@ pub mod pallet { T::ForceOrigin::ensure_origin(origin)?; let bounded_name: BoundedVec<_, _> = - name.try_into().map_err(|()| Error::<T>::TooLong)?; + name.try_into().map_err(|_| Error::<T>::TooLong)?; let target = T::Lookup::lookup(target)?; let deposit = <NameOf<T>>::get(&target).map(|x| x.1).unwrap_or_else(Zero::zero); <NameOf<T>>::insert(&target, (bounded_name, deposit)); diff --git a/substrate/frame/preimage/Cargo.toml b/substrate/frame/preimage/Cargo.toml index 9a5cc186cca64f13c9d3254f4c97cf202ad20f07..77046f4fb58b6bee69ad682043d8b7a6a15e62f2 100644 --- a/substrate/frame/preimage/Cargo.toml +++ b/substrate/frame/preimage/Cargo.toml @@ -19,6 +19,7 @@ sp-core = { version = "6.0.0", default-features = false, optional = true, path = sp-io = { version = "6.0.0", default-features = false, path = "../../primitives/io" } sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } +log = { version = "0.4.17", default-features = false } [dev-dependencies] pallet-balances = { version = "4.0.0-dev", path = "../balances" } @@ -36,10 +37,13 @@ std = [ "frame-benchmarking?/std", "frame-support/std", "frame-system/std", + "log/std", "scale-info/std", "sp-core/std", "sp-io/std", "sp-runtime/std", "sp-std/std", ] -try-runtime = ["frame-support/try-runtime"] +try-runtime = [ + "frame-support/try-runtime", +] diff --git a/substrate/frame/preimage/src/benchmarking.rs b/substrate/frame/preimage/src/benchmarking.rs index 3c7be9db573f45c2f25f2945f56bff32a08eaaa4..8a61d7d780bfd788068c4a8e1d0cd016930005be 100644 --- a/substrate/frame/preimage/src/benchmarking.rs +++ b/substrate/frame/preimage/src/benchmarking.rs @@ -35,7 +35,7 @@ fn funded_account<T: Config>(name: &'static str, index: u32) -> T::AccountId { } fn preimage_and_hash<T: Config>() -> (Vec<u8>, T::Hash) { - sized_preimage_and_hash::<T>(T::MaxSize::get()) + sized_preimage_and_hash::<T>(MAX_SIZE) } fn sized_preimage_and_hash<T: Config>(size: u32) -> (Vec<u8>, T::Hash) { @@ -48,7 +48,7 @@ fn sized_preimage_and_hash<T: Config>(size: u32) -> (Vec<u8>, T::Hash) { benchmarks! { // Expensive note - will reserve. note_preimage { - let s in 0 .. T::MaxSize::get(); + let s in 0 .. MAX_SIZE; let caller = funded_account::<T>("caller", 0); whitelist_account!(caller); let (preimage, hash) = sized_preimage_and_hash::<T>(s); @@ -58,7 +58,7 @@ benchmarks! { } // Cheap note - will not reserve since it was requested. note_requested_preimage { - let s in 0 .. T::MaxSize::get(); + let s in 0 .. MAX_SIZE; let caller = funded_account::<T>("caller", 0); whitelist_account!(caller); let (preimage, hash) = sized_preimage_and_hash::<T>(s); @@ -69,7 +69,7 @@ benchmarks! { } // Cheap note - will not reserve since it's the manager. note_no_deposit_preimage { - let s in 0 .. T::MaxSize::get(); + let s in 0 .. MAX_SIZE; let (preimage, hash) = sized_preimage_and_hash::<T>(s); assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash)); }: note_preimage<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), preimage) @@ -101,10 +101,12 @@ benchmarks! { let (preimage, hash) = preimage_and_hash::<T>(); let noter = funded_account::<T>("noter", 0); whitelist_account!(noter); - assert_ok!(Preimage::<T>::note_preimage(RawOrigin::Signed(noter).into(), preimage)); + assert_ok!(Preimage::<T>::note_preimage(RawOrigin::Signed(noter.clone()).into(), preimage)); }: _<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), hash) verify { - assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1))); + let deposit = T::BaseDeposit::get() + T::ByteDeposit::get() * MAX_SIZE.into(); + let s = RequestStatus::Requested { deposit: Some((noter, deposit)), count: 1, len: Some(MAX_SIZE) }; + assert_eq!(StatusFor::<T>::get(&hash), Some(s)); } // Cheap request - would unreserve the deposit but none was held. request_no_deposit_preimage { @@ -112,14 +114,16 @@ benchmarks! { assert_ok!(Preimage::<T>::note_preimage(T::ManagerOrigin::successful_origin(), preimage)); }: request_preimage<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), hash) verify { - assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1))); + let s = RequestStatus::Requested { deposit: None, count: 2, len: Some(MAX_SIZE) }; + assert_eq!(StatusFor::<T>::get(&hash), Some(s)); } // Cheap request - the preimage is not yet noted, so deposit to unreserve. request_unnoted_preimage { let (_, hash) = preimage_and_hash::<T>(); }: request_preimage<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), hash) verify { - assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1))); + let s = RequestStatus::Requested { deposit: None, count: 1, len: None }; + assert_eq!(StatusFor::<T>::get(&hash), Some(s)); } // Cheap request - the preimage is already requested, so just a counter bump. request_requested_preimage { @@ -127,7 +131,8 @@ benchmarks! { assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash)); }: request_preimage<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), hash) verify { - assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(2))); + let s = RequestStatus::Requested { deposit: None, count: 2, len: None }; + assert_eq!(StatusFor::<T>::get(&hash), Some(s)); } // Expensive unrequest - last reference and it's noted, so will destroy the preimage. @@ -154,7 +159,8 @@ benchmarks! { assert_ok!(Preimage::<T>::request_preimage(T::ManagerOrigin::successful_origin(), hash)); }: unrequest_preimage<T::RuntimeOrigin>(T::ManagerOrigin::successful_origin(), hash) verify { - assert_eq!(StatusFor::<T>::get(&hash), Some(RequestStatus::Requested(1))); + let s = RequestStatus::Requested { deposit: None, count: 1, len: None }; + assert_eq!(StatusFor::<T>::get(&hash), Some(s)); } impl_benchmark_test_suite!(Preimage, crate::mock::new_test_ext(), crate::mock::Test); diff --git a/substrate/frame/preimage/src/lib.rs b/substrate/frame/preimage/src/lib.rs index 90f5ac175f54027d16b83af256959c93434ea06a..e899d3643dbbffb89d7ab9e5bd1b6d140c5c3690 100644 --- a/substrate/frame/preimage/src/lib.rs +++ b/substrate/frame/preimage/src/lib.rs @@ -30,6 +30,7 @@ #[cfg(feature = "runtime-benchmarks")] mod benchmarking; +pub mod migration; #[cfg(test)] mod mock; #[cfg(test)] @@ -37,15 +38,18 @@ mod tests; pub mod weights; use sp_runtime::traits::{BadOrigin, Hash, Saturating}; -use sp_std::prelude::*; +use sp_std::{borrow::Cow, prelude::*}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ dispatch::Pays, ensure, pallet_prelude::Get, - traits::{Currency, PreimageProvider, PreimageRecipient, ReservableCurrency}, - BoundedVec, + traits::{ + Currency, Defensive, FetchResult, Hash as PreimageHash, PreimageProvider, + PreimageRecipient, QueryPreimage, ReservableCurrency, StorePreimage, + }, + BoundedSlice, BoundedVec, }; use scale_info::TypeInfo; pub use weights::WeightInfo; @@ -59,20 +63,27 @@ pub use pallet::*; #[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)] pub enum RequestStatus<AccountId, Balance> { /// The associated preimage has not yet been requested by the system. The given deposit (if - /// some) is being held until either it becomes requested or the user retracts the primage. - Unrequested(Option<(AccountId, Balance)>), + /// some) is being held until either it becomes requested or the user retracts the preimage. + Unrequested { deposit: (AccountId, Balance), len: u32 }, /// There are a non-zero number of outstanding requests for this hash by this chain. If there - /// is a preimage registered, then it may be removed iff this counter becomes zero. - Requested(u32), + /// is a preimage registered, then `len` is `Some` and it may be removed iff this counter + /// becomes zero. + Requested { deposit: Option<(AccountId, Balance)>, count: u32, len: Option<u32> }, } type BalanceOf<T> = <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance; +/// Maximum size of preimage we can store is 4mb. +const MAX_SIZE: u32 = 4 * 1024 * 1024; + #[frame_support::pallet] pub mod pallet { use super::*; + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::config] pub trait Config: frame_system::Config { /// The overarching event type. @@ -88,9 +99,6 @@ pub mod pallet { /// manage existing preimages. type ManagerOrigin: EnsureOrigin<Self::RuntimeOrigin>; - /// Max size allowed for a preimage. - type MaxSize: Get<u32>; - /// The base deposit for placing a preimage on chain. type BaseDeposit: Get<BalanceOf<Self>>; @@ -100,6 +108,7 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet<T>(PhantomData<T>); #[pallet::event] @@ -116,7 +125,7 @@ pub mod pallet { #[pallet::error] pub enum Error<T> { /// Preimage is too large to store on-chain. - TooLarge, + TooBig, /// Preimage has already been noted on-chain. AlreadyNoted, /// The user is not authorized to perform this action. @@ -134,10 +143,9 @@ pub mod pallet { pub(super) type StatusFor<T: Config> = StorageMap<_, Identity, T::Hash, RequestStatus<T::AccountId, BalanceOf<T>>>; - /// The preimages stored by this pallet. #[pallet::storage] pub(super) type PreimageFor<T: Config> = - StorageMap<_, Identity, T::Hash, BoundedVec<u8, T::MaxSize>>; + StorageMap<_, Identity, (T::Hash, u32), BoundedVec<u8, ConstU32<MAX_SIZE>>>; #[pallet::call] impl<T: Config> Pallet<T> { @@ -150,9 +158,7 @@ pub mod pallet { // We accept a signed origin which will pay a deposit, or a root origin where a deposit // is not taken. let maybe_sender = Self::ensure_signed_or_manager(origin)?; - let bounded_vec = - BoundedVec::<u8, T::MaxSize>::try_from(bytes).map_err(|()| Error::<T>::TooLarge)?; - let system_requested = Self::note_bytes(bounded_vec, maybe_sender.as_ref())?; + let (system_requested, _) = Self::note_bytes(bytes.into(), maybe_sender.as_ref())?; if system_requested || maybe_sender.is_none() { Ok(Pays::No.into()) } else { @@ -161,6 +167,11 @@ pub mod pallet { } /// Clear an unrequested preimage from the runtime storage. + /// + /// If `len` is provided, then it will be a much cheaper operation. + /// + /// - `hash`: The hash of the preimage to be removed from the store. + /// - `len`: The length of the preimage of `hash`. #[pallet::weight(T::WeightInfo::unnote_preimage())] pub fn unnote_preimage(origin: OriginFor<T>, hash: T::Hash) -> DispatchResult { let maybe_sender = Self::ensure_signed_or_manager(origin)?; @@ -203,41 +214,46 @@ impl<T: Config> Pallet<T> { /// Store some preimage on chain. /// + /// If `maybe_depositor` is `None` then it is also requested. If `Some`, then it is not. + /// /// We verify that the preimage is within the bounds of what the pallet supports. /// /// If the preimage was requested to be uploaded, then the user pays no deposits or tx fees. fn note_bytes( - preimage: BoundedVec<u8, T::MaxSize>, + preimage: Cow<[u8]>, maybe_depositor: Option<&T::AccountId>, - ) -> Result<bool, DispatchError> { + ) -> Result<(bool, T::Hash), DispatchError> { let hash = T::Hashing::hash(&preimage); - ensure!(!PreimageFor::<T>::contains_key(hash), Error::<T>::AlreadyNoted); + let len = preimage.len() as u32; + ensure!(len <= MAX_SIZE, Error::<T>::TooBig); - // We take a deposit only if there is a provided depositor, and the preimage was not + // We take a deposit only if there is a provided depositor and the preimage was not // previously requested. This also allows the tx to pay no fee. - let was_requested = match (StatusFor::<T>::get(hash), maybe_depositor) { - (Some(RequestStatus::Requested(..)), _) => true, - (Some(RequestStatus::Unrequested(..)), _) => + let status = match (StatusFor::<T>::get(hash), maybe_depositor) { + (Some(RequestStatus::Requested { count, deposit, .. }), _) => + RequestStatus::Requested { count, deposit, len: Some(len) }, + (Some(RequestStatus::Unrequested { .. }), Some(_)) => return Err(Error::<T>::AlreadyNoted.into()), - (None, None) => { - StatusFor::<T>::insert(hash, RequestStatus::Unrequested(None)); - false - }, + (Some(RequestStatus::Unrequested { len, deposit }), None) => + RequestStatus::Requested { deposit: Some(deposit), count: 1, len: Some(len) }, + (None, None) => RequestStatus::Requested { count: 1, len: Some(len), deposit: None }, (None, Some(depositor)) => { let length = preimage.len() as u32; let deposit = T::BaseDeposit::get() .saturating_add(T::ByteDeposit::get().saturating_mul(length.into())); T::Currency::reserve(depositor, deposit)?; - let status = RequestStatus::Unrequested(Some((depositor.clone(), deposit))); - StatusFor::<T>::insert(hash, status); - false + RequestStatus::Unrequested { deposit: (depositor.clone(), deposit), len } }, }; + let was_requested = matches!(status, RequestStatus::Requested { .. }); + StatusFor::<T>::insert(hash, status); + + let _ = Self::insert(&hash, preimage) + .defensive_proof("Unable to insert. Logic error in `note_bytes`?"); - PreimageFor::<T>::insert(hash, preimage); Self::deposit_event(Event::Noted { hash }); - Ok(was_requested) + Ok((was_requested, hash)) } // This function will add a hash to the list of requested preimages. @@ -245,19 +261,15 @@ impl<T: Config> Pallet<T> { // If the preimage already exists before the request is made, the deposit for the preimage is // returned to the user, and removed from their management. fn do_request_preimage(hash: &T::Hash) { - let count = StatusFor::<T>::get(hash).map_or(1, |x| match x { - RequestStatus::Requested(mut count) => { - count.saturating_inc(); - count - }, - RequestStatus::Unrequested(None) => 1, - RequestStatus::Unrequested(Some((owner, deposit))) => { - // Return the deposit - the preimage now has outstanding requests. - T::Currency::unreserve(&owner, deposit); - 1 - }, - }); - StatusFor::<T>::insert(hash, RequestStatus::Requested(count)); + let (count, len, deposit) = + StatusFor::<T>::get(hash).map_or((1, None, None), |x| match x { + RequestStatus::Requested { mut count, len, deposit } => { + count.saturating_inc(); + (count, len, deposit) + }, + RequestStatus::Unrequested { deposit, len } => (1, Some(len), Some(deposit)), + }); + StatusFor::<T>::insert(hash, RequestStatus::Requested { count, len, deposit }); if count == 1 { Self::deposit_event(Event::Requested { hash: *hash }); } @@ -265,6 +277,8 @@ impl<T: Config> Pallet<T> { // Clear a preimage from the storage of the chain, returning any deposit that may be reserved. // + // If `len` is provided, it will be a much cheaper operation. + // // If `maybe_owner` is provided, we verify that it is the correct owner before clearing the // data. fn do_unnote_preimage( @@ -272,51 +286,101 @@ impl<T: Config> Pallet<T> { maybe_check_owner: Option<T::AccountId>, ) -> DispatchResult { match StatusFor::<T>::get(hash).ok_or(Error::<T>::NotNoted)? { - RequestStatus::Unrequested(Some((owner, deposit))) => { + RequestStatus::Requested { deposit: Some((owner, deposit)), count, len } => { ensure!(maybe_check_owner.map_or(true, |c| c == owner), Error::<T>::NotAuthorized); T::Currency::unreserve(&owner, deposit); + StatusFor::<T>::insert( + hash, + RequestStatus::Requested { deposit: None, count, len }, + ); + Ok(()) }, - RequestStatus::Unrequested(None) => { + RequestStatus::Requested { deposit: None, .. } => { ensure!(maybe_check_owner.is_none(), Error::<T>::NotAuthorized); + Self::do_unrequest_preimage(hash) + }, + RequestStatus::Unrequested { deposit: (owner, deposit), len } => { + ensure!(maybe_check_owner.map_or(true, |c| c == owner), Error::<T>::NotAuthorized); + T::Currency::unreserve(&owner, deposit); + StatusFor::<T>::remove(hash); + + Self::remove(hash, len); + Self::deposit_event(Event::Cleared { hash: *hash }); + Ok(()) }, - RequestStatus::Requested(_) => return Err(Error::<T>::Requested.into()), } - StatusFor::<T>::remove(hash); - PreimageFor::<T>::remove(hash); - Self::deposit_event(Event::Cleared { hash: *hash }); - Ok(()) } /// Clear a preimage request. fn do_unrequest_preimage(hash: &T::Hash) -> DispatchResult { match StatusFor::<T>::get(hash).ok_or(Error::<T>::NotRequested)? { - RequestStatus::Requested(mut count) if count > 1 => { + RequestStatus::Requested { mut count, len, deposit } if count > 1 => { count.saturating_dec(); - StatusFor::<T>::insert(hash, RequestStatus::Requested(count)); + StatusFor::<T>::insert(hash, RequestStatus::Requested { count, len, deposit }); }, - RequestStatus::Requested(count) => { + RequestStatus::Requested { count, len, deposit } => { debug_assert!(count == 1, "preimage request counter at zero?"); - PreimageFor::<T>::remove(hash); - StatusFor::<T>::remove(hash); - Self::deposit_event(Event::Cleared { hash: *hash }); + match (len, deposit) { + // Preimage was never noted. + (None, _) => StatusFor::<T>::remove(hash), + // Preimage was noted without owner - just remove it. + (Some(len), None) => { + Self::remove(hash, len); + StatusFor::<T>::remove(hash); + Self::deposit_event(Event::Cleared { hash: *hash }); + }, + // Preimage was noted with owner - move to unrequested so they can get refund. + (Some(len), Some(deposit)) => { + StatusFor::<T>::insert(hash, RequestStatus::Unrequested { deposit, len }); + }, + } }, - RequestStatus::Unrequested(_) => return Err(Error::<T>::NotRequested.into()), + RequestStatus::Unrequested { .. } => return Err(Error::<T>::NotRequested.into()), } Ok(()) } + + fn insert(hash: &T::Hash, preimage: Cow<[u8]>) -> Result<(), ()> { + BoundedSlice::<u8, ConstU32<MAX_SIZE>>::try_from(preimage.as_ref()) + .map(|s| PreimageFor::<T>::insert((hash, s.len() as u32), s)) + } + + fn remove(hash: &T::Hash, len: u32) { + PreimageFor::<T>::remove((hash, len)) + } + + fn have(hash: &T::Hash) -> bool { + Self::len(hash).is_some() + } + + fn len(hash: &T::Hash) -> Option<u32> { + use RequestStatus::*; + match StatusFor::<T>::get(hash) { + Some(Requested { len: Some(len), .. }) | Some(Unrequested { len, .. }) => Some(len), + _ => None, + } + } + + fn fetch(hash: &T::Hash, len: Option<u32>) -> FetchResult { + let len = len.or_else(|| Self::len(hash)).ok_or(DispatchError::Unavailable)?; + PreimageFor::<T>::get((hash, len)) + .map(|p| p.into_inner()) + .map(Into::into) + .ok_or(DispatchError::Unavailable) + } } impl<T: Config> PreimageProvider<T::Hash> for Pallet<T> { fn have_preimage(hash: &T::Hash) -> bool { - PreimageFor::<T>::contains_key(hash) + Self::have(hash) } fn preimage_requested(hash: &T::Hash) -> bool { - matches!(StatusFor::<T>::get(hash), Some(RequestStatus::Requested(..))) + matches!(StatusFor::<T>::get(hash), Some(RequestStatus::Requested { .. })) } fn get_preimage(hash: &T::Hash) -> Option<Vec<u8>> { - PreimageFor::<T>::get(hash).map(|preimage| preimage.to_vec()) + Self::fetch(hash, None).ok().map(Cow::into_owned) } fn request_preimage(hash: &T::Hash) { @@ -330,15 +394,60 @@ impl<T: Config> PreimageProvider<T::Hash> for Pallet<T> { } impl<T: Config> PreimageRecipient<T::Hash> for Pallet<T> { - type MaxSize = T::MaxSize; + type MaxSize = ConstU32<MAX_SIZE>; // 2**22 fn note_preimage(bytes: BoundedVec<u8, Self::MaxSize>) { // We don't really care if this fails, since that's only the case if someone else has // already noted it. - let _ = Self::note_bytes(bytes, None); + let _ = Self::note_bytes(bytes.into_inner().into(), None); } fn unnote_preimage(hash: &T::Hash) { + // Should never fail if authorization check is skipped. + let res = Self::do_unrequest_preimage(hash); + debug_assert!(res.is_ok(), "unnote_preimage failed - request outstanding?"); + } +} + +impl<T: Config<Hash = PreimageHash>> QueryPreimage for Pallet<T> { + fn len(hash: &T::Hash) -> Option<u32> { + Pallet::<T>::len(hash) + } + + fn fetch(hash: &T::Hash, len: Option<u32>) -> FetchResult { + Pallet::<T>::fetch(hash, len) + } + + fn is_requested(hash: &T::Hash) -> bool { + matches!(StatusFor::<T>::get(hash), Some(RequestStatus::Requested { .. })) + } + + fn request(hash: &T::Hash) { + Self::do_request_preimage(hash) + } + + fn unrequest(hash: &T::Hash) { + let res = Self::do_unrequest_preimage(hash); + debug_assert!(res.is_ok(), "do_unrequest_preimage failed - counter underflow?"); + } +} + +impl<T: Config<Hash = PreimageHash>> StorePreimage for Pallet<T> { + const MAX_LENGTH: usize = MAX_SIZE as usize; + + fn note(bytes: Cow<[u8]>) -> Result<T::Hash, DispatchError> { + // We don't really care if this fails, since that's only the case if someone else has + // already noted it. + let maybe_hash = Self::note_bytes(bytes, None).map(|(_, h)| h); + // Map to the correct trait error. + if maybe_hash == Err(DispatchError::from(Error::<T>::TooBig)) { + Err(DispatchError::Exhausted) + } else { + maybe_hash + } + } + + fn unnote(hash: &T::Hash) { // Should never fail if authorization check is skipped. let res = Self::do_unnote_preimage(hash, None); debug_assert!(res.is_ok(), "unnote_preimage failed - request outstanding?"); diff --git a/substrate/frame/preimage/src/migration.rs b/substrate/frame/preimage/src/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..a5d15c23c758a448c12afc7f23a82221775c6db6 --- /dev/null +++ b/substrate/frame/preimage/src/migration.rs @@ -0,0 +1,263 @@ +// This file is part of Substrate. + +// Copyright (C) 2022 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. + +//! Storage migrations for the preimage pallet. + +use super::*; +use frame_support::{ + storage_alias, + traits::{ConstU32, OnRuntimeUpgrade}, +}; +use sp_std::collections::btree_map::BTreeMap; + +/// The log target. +const TARGET: &'static str = "runtime::preimage::migration::v1"; + +/// The original data layout of the preimage pallet without a specific version number. +mod v0 { + use super::*; + + #[derive(Clone, Eq, PartialEq, Encode, Decode, TypeInfo, MaxEncodedLen, RuntimeDebug)] + pub enum RequestStatus<AccountId, Balance> { + Unrequested(Option<(AccountId, Balance)>), + Requested(u32), + } + + #[storage_alias] + pub type PreimageFor<T: Config> = StorageMap< + Pallet<T>, + Identity, + <T as frame_system::Config>::Hash, + BoundedVec<u8, ConstU32<MAX_SIZE>>, + >; + + #[storage_alias] + pub type StatusFor<T: Config> = StorageMap< + Pallet<T>, + Identity, + <T as frame_system::Config>::Hash, + RequestStatus<<T as frame_system::Config>::AccountId, BalanceOf<T>>, + >; + + /// Returns the number of images or `None` if the storage is corrupted. + #[cfg(feature = "try-runtime")] + pub fn image_count<T: Config>() -> Option<u32> { + let images = v0::PreimageFor::<T>::iter_values().count() as u32; + let status = v0::StatusFor::<T>::iter_values().count() as u32; + + if images == status { + Some(images) + } else { + None + } + } +} + +pub mod v1 { + use super::*; + + /// Migration for moving preimage from V0 to V1 storage. + /// + /// Note: This needs to be run with the same hashing algorithm as before + /// since it is not re-hashing the preimages. + pub struct Migration<T>(sp_std::marker::PhantomData<T>); + + impl<T: Config> OnRuntimeUpgrade for Migration<T> { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<Vec<u8>, &'static str> { + assert_eq!(StorageVersion::get::<Pallet<T>>(), 0, "can only upgrade from version 0"); + + let images = v0::image_count::<T>().expect("v0 storage corrupted"); + log::info!(target: TARGET, "Migrating {} images", &images); + Ok((images as u32).encode()) + } + + fn on_runtime_upgrade() -> Weight { + let mut weight = T::DbWeight::get().reads(1); + if StorageVersion::get::<Pallet<T>>() != 0 { + log::warn!( + target: TARGET, + "skipping MovePreimagesIntoBuckets: executed on wrong storage version.\ + Expected version 0" + ); + return weight + } + + let status = v0::StatusFor::<T>::drain().collect::<Vec<_>>(); + weight.saturating_accrue(T::DbWeight::get().reads(status.len() as u64)); + + let preimages = v0::PreimageFor::<T>::drain().collect::<BTreeMap<_, _>>(); + weight.saturating_accrue(T::DbWeight::get().reads(preimages.len() as u64)); + + for (hash, status) in status.into_iter() { + let preimage = if let Some(preimage) = preimages.get(&hash) { + preimage + } else { + log::error!(target: TARGET, "preimage not found for hash {:?}", &hash); + continue + }; + let len = preimage.len() as u32; + if len > MAX_SIZE { + log::error!( + target: TARGET, + "preimage too large for hash {:?}, len: {}", + &hash, + len + ); + continue + } + + let status = match status { + v0::RequestStatus::Unrequested(deposit) => match deposit { + Some(deposit) => RequestStatus::Unrequested { deposit, len }, + // `None` depositor becomes system-requested. + None => + RequestStatus::Requested { deposit: None, count: 1, len: Some(len) }, + }, + v0::RequestStatus::Requested(count) if count == 0 => { + log::error!(target: TARGET, "preimage has counter of zero: {:?}", hash); + continue + }, + v0::RequestStatus::Requested(count) => + RequestStatus::Requested { deposit: None, count, len: Some(len) }, + }; + log::trace!(target: TARGET, "Moving preimage {:?} with len {}", hash, len); + + crate::StatusFor::<T>::insert(hash, status); + crate::PreimageFor::<T>::insert(&(hash, len), preimage); + + weight.saturating_accrue(T::DbWeight::get().writes(2)); + } + StorageVersion::new(1).put::<Pallet<T>>(); + + weight.saturating_add(T::DbWeight::get().writes(1)) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { + let old_images: u32 = + Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed"); + let new_images = image_count::<T>().expect("V1 storage corrupted"); + + if new_images != old_images { + log::error!( + target: TARGET, + "migrated {} images, expected {}", + new_images, + old_images + ); + } + assert_eq!(StorageVersion::get::<Pallet<T>>(), 1, "must upgrade"); + Ok(()) + } + } + + /// Returns the number of images or `None` if the storage is corrupted. + #[cfg(feature = "try-runtime")] + pub fn image_count<T: Config>() -> Option<u32> { + // Use iter_values() to ensure that the values are decodable. + let images = crate::PreimageFor::<T>::iter_values().count() as u32; + let status = crate::StatusFor::<T>::iter_values().count() as u32; + + if images == status { + Some(images) + } else { + None + } + } +} + +#[cfg(test)] +#[cfg(feature = "try-runtime")] +mod test { + use super::*; + use crate::mock::{Test as T, *}; + + use frame_support::bounded_vec; + + #[test] + fn migration_works() { + new_test_ext().execute_with(|| { + assert_eq!(StorageVersion::get::<Pallet<T>>(), 0); + // Insert some preimages into the v0 storage: + + // Case 1: Unrequested without deposit + let (p, h) = preimage::<T>(128); + v0::PreimageFor::<T>::insert(h, p); + v0::StatusFor::<T>::insert(h, v0::RequestStatus::Unrequested(None)); + // Case 2: Unrequested with deposit + let (p, h) = preimage::<T>(1024); + v0::PreimageFor::<T>::insert(h, p); + v0::StatusFor::<T>::insert(h, v0::RequestStatus::Unrequested(Some((1, 1)))); + // Case 3: Requested by 0 (invalid) + let (p, h) = preimage::<T>(8192); + v0::PreimageFor::<T>::insert(h, p); + v0::StatusFor::<T>::insert(h, v0::RequestStatus::Requested(0)); + // Case 4: Requested by 10 + let (p, h) = preimage::<T>(65536); + v0::PreimageFor::<T>::insert(h, p); + v0::StatusFor::<T>::insert(h, v0::RequestStatus::Requested(10)); + + assert_eq!(v0::image_count::<T>(), Some(4)); + assert_eq!(v1::image_count::<T>(), None, "V1 storage should be corrupted"); + + let state = v1::Migration::<T>::pre_upgrade().unwrap(); + let _w = v1::Migration::<T>::on_runtime_upgrade(); + v1::Migration::<T>::post_upgrade(state).unwrap(); + + // V0 and V1 share the same prefix, so `iter_values` still counts the same. + assert_eq!(v0::image_count::<T>(), Some(3)); + assert_eq!(v1::image_count::<T>(), Some(3)); // One gets skipped therefore 3. + assert_eq!(StorageVersion::get::<Pallet<T>>(), 1); + + // Case 1: Unrequested without deposit becomes system-requested + let (p, h) = preimage::<T>(128); + assert_eq!(crate::PreimageFor::<T>::get(&(h, 128)), Some(p)); + assert_eq!( + crate::StatusFor::<T>::get(h), + Some(RequestStatus::Requested { deposit: None, count: 1, len: Some(128) }) + ); + // Case 2: Unrequested with deposit becomes unrequested + let (p, h) = preimage::<T>(1024); + assert_eq!(crate::PreimageFor::<T>::get(&(h, 1024)), Some(p)); + assert_eq!( + crate::StatusFor::<T>::get(h), + Some(RequestStatus::Unrequested { deposit: (1, 1), len: 1024 }) + ); + // Case 3: Requested by 0 should be skipped + let (_, h) = preimage::<T>(8192); + assert_eq!(crate::PreimageFor::<T>::get(&(h, 8192)), None); + assert_eq!(crate::StatusFor::<T>::get(h), None); + // Case 4: Requested by 10 becomes requested by 10 + let (p, h) = preimage::<T>(65536); + assert_eq!(crate::PreimageFor::<T>::get(&(h, 65536)), Some(p)); + assert_eq!( + crate::StatusFor::<T>::get(h), + Some(RequestStatus::Requested { deposit: None, count: 10, len: Some(65536) }) + ); + }); + } + + /// Returns a preimage with a given size and its hash. + fn preimage<T: Config>( + len: usize, + ) -> (BoundedVec<u8, ConstU32<MAX_SIZE>>, <T as frame_system::Config>::Hash) { + let p = bounded_vec![1; len]; + let h = <T as frame_system::Config>::Hashing::hash_of(&p); + (p, h) + } +} diff --git a/substrate/frame/preimage/src/mock.rs b/substrate/frame/preimage/src/mock.rs index e12598a35b4bbec4d8a2b2943f2b9b2d8bffe9e0..ce74ea65bd8aa1f93e8c41342873258f3137a8f5 100644 --- a/substrate/frame/preimage/src/mock.rs +++ b/substrate/frame/preimage/src/mock.rs @@ -105,7 +105,6 @@ impl Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type ManagerOrigin = EnsureSignedBy<One, u64>; - type MaxSize = ConstU32<1024>; type BaseDeposit = ConstU64<2>; type ByteDeposit = ConstU64<1>; } diff --git a/substrate/frame/preimage/src/tests.rs b/substrate/frame/preimage/src/tests.rs index e6b64ae16dd8c9115b107a377fdd003e3b6b7906..f480b9c36b6705634bcf308baae9183eeb2e4f13 100644 --- a/substrate/frame/preimage/src/tests.rs +++ b/substrate/frame/preimage/src/tests.rs @@ -17,11 +17,35 @@ //! # Scheduler tests. +#![cfg(test)] + use super::*; use crate::mock::*; -use frame_support::{assert_noop, assert_ok}; +use frame_support::{ + assert_err, assert_noop, assert_ok, assert_storage_noop, bounded_vec, + traits::{Bounded, BoundedInline, Hash as PreimageHash}, + StorageNoopGuard, +}; use pallet_balances::Error as BalancesError; +use sp_core::{blake2_256, H256}; + +/// Returns one `Inline`, `Lookup` and `Legacy` item each with different data and hash. +pub fn make_bounded_values() -> (Bounded<Vec<u8>>, Bounded<Vec<u8>>, Bounded<Vec<u8>>) { + let data: BoundedInline = bounded_vec![1]; + let inline = Bounded::<Vec<u8>>::Inline(data); + + let data = vec![1, 2]; + let hash: H256 = blake2_256(&data[..]).into(); + let len = data.len() as u32; + let lookup = Bounded::<Vec<u8>>::unrequested(hash, len); + + let data = vec![1, 2, 3]; + let hash: H256 = blake2_256(&data[..]).into(); + let legacy = Bounded::<Vec<u8>>::Legacy { hash, dummy: Default::default() }; + + (inline, lookup, legacy) +} #[test] fn user_note_preimage_works() { @@ -56,10 +80,7 @@ fn manager_note_preimage_works() { assert!(Preimage::have_preimage(&h)); assert_eq!(Preimage::get_preimage(&h), Some(vec![1])); - assert_noop!( - Preimage::note_preimage(RuntimeOrigin::signed(1), vec![1]), - Error::<Test>::AlreadyNoted - ); + assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![1])); }); } @@ -130,14 +151,16 @@ fn requested_then_noted_preimage_cannot_be_unnoted() { new_test_ext().execute_with(|| { assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![1])); assert_ok!(Preimage::request_preimage(RuntimeOrigin::signed(1), hashed([1]))); - assert_noop!( - Preimage::unnote_preimage(RuntimeOrigin::signed(1), hashed([1])), - Error::<Test>::Requested - ); + assert_ok!(Preimage::unnote_preimage(RuntimeOrigin::signed(1), hashed([1]))); + // it's still here. let h = hashed([1]); assert!(Preimage::have_preimage(&h)); assert_eq!(Preimage::get_preimage(&h), Some(vec![1])); + + // now it's gone + assert_ok!(Preimage::unrequest_preimage(RuntimeOrigin::signed(1), hashed([1]))); + assert!(!Preimage::have_preimage(&hashed([1]))); }); } @@ -145,15 +168,16 @@ fn requested_then_noted_preimage_cannot_be_unnoted() { fn request_note_order_makes_no_difference() { let one_way = new_test_ext().execute_with(|| { assert_ok!(Preimage::request_preimage(RuntimeOrigin::signed(1), hashed([1]))); - assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![1])); + assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(2), vec![1])); ( StatusFor::<Test>::iter().collect::<Vec<_>>(), PreimageFor::<Test>::iter().collect::<Vec<_>>(), ) }); new_test_ext().execute_with(|| { - assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![1])); + assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(2), vec![1])); assert_ok!(Preimage::request_preimage(RuntimeOrigin::signed(1), hashed([1]))); + assert_ok!(Preimage::unnote_preimage(RuntimeOrigin::signed(2), hashed([1]))); let other_way = ( StatusFor::<Test>::iter().collect::<Vec<_>>(), PreimageFor::<Test>::iter().collect::<Vec<_>>(), @@ -189,6 +213,7 @@ fn request_user_note_order_makes_no_difference() { new_test_ext().execute_with(|| { assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(2), vec![1])); assert_ok!(Preimage::request_preimage(RuntimeOrigin::signed(1), hashed([1]))); + assert_ok!(Preimage::unnote_preimage(RuntimeOrigin::signed(2), hashed([1]))); let other_way = ( StatusFor::<Test>::iter().collect::<Vec<_>>(), PreimageFor::<Test>::iter().collect::<Vec<_>>(), @@ -226,8 +251,240 @@ fn user_noted_then_requested_preimage_is_refunded_once_only() { assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(2), vec![1])); assert_ok!(Preimage::request_preimage(RuntimeOrigin::signed(1), hashed([1]))); assert_ok!(Preimage::unrequest_preimage(RuntimeOrigin::signed(1), hashed([1]))); + assert_ok!(Preimage::unnote_preimage(RuntimeOrigin::signed(2), hashed([1]))); // Still have reserve from `vec[1; 3]`. assert_eq!(Balances::reserved_balance(2), 5); assert_eq!(Balances::free_balance(2), 95); }); } + +#[test] +fn noted_preimage_use_correct_map() { + new_test_ext().execute_with(|| { + // Add one preimage per bucket... + for i in 0..7 { + assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![0; 128 << (i * 2)])); + } + assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(1), vec![0; MAX_SIZE as usize])); + assert_eq!(PreimageFor::<Test>::iter().count(), 8); + + // All are present + assert_eq!(StatusFor::<Test>::iter().count(), 8); + + // Now start removing them again... + for i in 0..7 { + assert_ok!(Preimage::unnote_preimage( + RuntimeOrigin::signed(1), + hashed(vec![0; 128 << (i * 2)]) + )); + } + assert_eq!(PreimageFor::<Test>::iter().count(), 1); + assert_ok!(Preimage::unnote_preimage( + RuntimeOrigin::signed(1), + hashed(vec![0; MAX_SIZE as usize]) + )); + assert_eq!(PreimageFor::<Test>::iter().count(), 0); + + // All are gone + assert_eq!(StatusFor::<Test>::iter().count(), 0); + }); +} + +/// The `StorePreimage` and `QueryPreimage` traits work together. +#[test] +fn query_and_store_preimage_workflow() { + new_test_ext().execute_with(|| { + let _guard = StorageNoopGuard::default(); + let data: Vec<u8> = vec![1; 512]; + let encoded = data.encode(); + + // Bound an unbound value. + let bound = Preimage::bound(data.clone()).unwrap(); + let (len, hash) = (bound.len().unwrap(), bound.hash()); + + assert_eq!(hash, blake2_256(&encoded).into()); + assert_eq!(bound.len(), Some(len)); + assert!(bound.lookup_needed(), "Should not be Inlined"); + assert_eq!(bound.lookup_len(), Some(len)); + + // The value is requested and available. + assert!(Preimage::is_requested(&hash)); + assert!(<Preimage as QueryPreimage>::have(&bound)); + assert_eq!(Preimage::len(&hash), Some(len)); + + // It can be fetched with length. + assert_eq!(Preimage::fetch(&hash, Some(len)).unwrap(), encoded); + // ... and without length. + assert_eq!(Preimage::fetch(&hash, None).unwrap(), encoded); + // ... but not with wrong length. + assert_err!(Preimage::fetch(&hash, Some(0)), DispatchError::Unavailable); + + // It can be peeked and decoded correctly. + assert_eq!(Preimage::peek::<Vec<u8>>(&bound).unwrap(), (data.clone(), Some(len))); + // Request it two more times. + assert_eq!(Preimage::pick::<Vec<u8>>(hash, len), bound); + Preimage::request(&hash); + // It is requested thrice. + assert!(matches!( + StatusFor::<Test>::get(&hash).unwrap(), + RequestStatus::Requested { count: 3, .. } + )); + + // It can be realized and decoded correctly. + assert_eq!(Preimage::realize::<Vec<u8>>(&bound).unwrap(), (data.clone(), Some(len))); + assert!(matches!( + StatusFor::<Test>::get(&hash).unwrap(), + RequestStatus::Requested { count: 2, .. } + )); + // Dropping should unrequest. + Preimage::drop(&bound); + assert!(matches!( + StatusFor::<Test>::get(&hash).unwrap(), + RequestStatus::Requested { count: 1, .. } + )); + + // Is still available. + assert!(<Preimage as QueryPreimage>::have(&bound)); + // Manually unnote it. + Preimage::unnote(&hash); + // Is not available anymore. + assert!(!<Preimage as QueryPreimage>::have(&bound)); + assert_err!(Preimage::fetch(&hash, Some(len)), DispatchError::Unavailable); + // And not requested since the traits assume permissioned origin. + assert!(!Preimage::is_requested(&hash)); + + // No storage changes remain. Checked by `StorageNoopGuard`. + }); +} + +/// The request function behaves as expected. +#[test] +fn query_preimage_request_works() { + new_test_ext().execute_with(|| { + let _guard = StorageNoopGuard::default(); + let data: Vec<u8> = vec![1; 10]; + let hash: PreimageHash = blake2_256(&data[..]).into(); + + // Request the preimage. + <Preimage as QueryPreimage>::request(&hash); + + // The preimage is requested with unknown length and cannot be fetched. + assert!(<Preimage as QueryPreimage>::is_requested(&hash)); + assert!(<Preimage as QueryPreimage>::len(&hash).is_none()); + assert_noop!(<Preimage as QueryPreimage>::fetch(&hash, None), DispatchError::Unavailable); + + // Request again. + <Preimage as QueryPreimage>::request(&hash); + // The preimage is still requested. + assert!(<Preimage as QueryPreimage>::is_requested(&hash)); + assert!(<Preimage as QueryPreimage>::len(&hash).is_none()); + assert_noop!(<Preimage as QueryPreimage>::fetch(&hash, None), DispatchError::Unavailable); + // But there is only one entry in the map. + assert_eq!(StatusFor::<Test>::iter().count(), 1); + + // Un-request the preimage. + <Preimage as QueryPreimage>::unrequest(&hash); + // It is still requested. + assert!(<Preimage as QueryPreimage>::is_requested(&hash)); + // Un-request twice. + <Preimage as QueryPreimage>::unrequest(&hash); + // It is not requested anymore. + assert!(!<Preimage as QueryPreimage>::is_requested(&hash)); + // And there is no entry in the map. + assert_eq!(StatusFor::<Test>::iter().count(), 0); + }); +} + +/// The `QueryPreimage` functions can be used together with `Bounded` values. +#[test] +fn query_preimage_hold_and_drop_work() { + new_test_ext().execute_with(|| { + let _guard = StorageNoopGuard::default(); + let (inline, lookup, legacy) = make_bounded_values(); + + // `hold` does nothing for `Inline` values. + assert_storage_noop!(<Preimage as QueryPreimage>::hold(&inline)); + // `hold` requests `Lookup` values. + <Preimage as QueryPreimage>::hold(&lookup); + assert!(<Preimage as QueryPreimage>::is_requested(&lookup.hash())); + // `hold` requests `Legacy` values. + <Preimage as QueryPreimage>::hold(&legacy); + assert!(<Preimage as QueryPreimage>::is_requested(&legacy.hash())); + + // There are two values requested in total. + assert_eq!(StatusFor::<Test>::iter().count(), 2); + + // Cleanup by dropping both. + <Preimage as QueryPreimage>::drop(&lookup); + assert!(!<Preimage as QueryPreimage>::is_requested(&lookup.hash())); + <Preimage as QueryPreimage>::drop(&legacy); + assert!(!<Preimage as QueryPreimage>::is_requested(&legacy.hash())); + + // There are no values requested anymore. + assert_eq!(StatusFor::<Test>::iter().count(), 0); + }); +} + +/// The `StorePreimage` trait works as expected. +#[test] +fn store_preimage_basic_works() { + new_test_ext().execute_with(|| { + let _guard = StorageNoopGuard::default(); + let data: Vec<u8> = vec![1; 512]; // Too large to inline. + let encoded = Cow::from(data.encode()); + + // Bound the data. + let bound = <Preimage as StorePreimage>::bound(data.clone()).unwrap(); + // The preimage can be peeked. + assert_ok!(<Preimage as QueryPreimage>::peek(&bound)); + // Un-note the preimage. + <Preimage as StorePreimage>::unnote(&bound.hash()); + // The preimage cannot be peeked anymore. + assert_err!(<Preimage as QueryPreimage>::peek(&bound), DispatchError::Unavailable); + // Noting the wrong pre-image does not make it peek-able. + assert_ok!(<Preimage as StorePreimage>::note(Cow::Borrowed(&data))); + assert_err!(<Preimage as QueryPreimage>::peek(&bound), DispatchError::Unavailable); + + // Manually note the preimage makes it peek-able again. + assert_ok!(<Preimage as StorePreimage>::note(encoded.clone())); + // Noting again works. + assert_ok!(<Preimage as StorePreimage>::note(encoded)); + assert_ok!(<Preimage as QueryPreimage>::peek(&bound)); + + // Cleanup. + <Preimage as StorePreimage>::unnote(&bound.hash()); + let data_hash = blake2_256(&data); + <Preimage as StorePreimage>::unnote(&data_hash.into()); + + // No storage changes remain. Checked by `StorageNoopGuard`. + }); +} + +#[test] +fn store_preimage_note_too_large_errors() { + new_test_ext().execute_with(|| { + // Works with `MAX_LENGTH`. + let len = <Preimage as StorePreimage>::MAX_LENGTH; + let data = vec![0u8; len]; + assert_ok!(<Preimage as StorePreimage>::note(data.into())); + + // Errors with `MAX_LENGTH+1`. + let data = vec![0u8; len + 1]; + assert_err!(<Preimage as StorePreimage>::note(data.into()), DispatchError::Exhausted); + }); +} + +#[test] +fn store_preimage_bound_too_large_errors() { + new_test_ext().execute_with(|| { + // Using `MAX_LENGTH` number of bytes in a vector does not work + // since SCALE prepends the length. + let len = <Preimage as StorePreimage>::MAX_LENGTH; + let data: Vec<u8> = vec![0; len]; + assert_err!(<Preimage as StorePreimage>::bound(data.clone()), DispatchError::Exhausted); + + // Works with `MAX_LENGTH-4`. + let data: Vec<u8> = vec![0; len - 4]; + assert_ok!(<Preimage as StorePreimage>::bound(data.clone())); + }); +} diff --git a/substrate/frame/preimage/src/weights.rs b/substrate/frame/preimage/src/weights.rs index ad9e3e569e733e8263cbb811226e3d45889a72d6..186c41b798c6b9cd061a1f7067f512bb20aa5b55 100644 --- a/substrate/frame/preimage/src/weights.rs +++ b/substrate/frame/preimage/src/weights.rs @@ -18,22 +18,24 @@ //! Autogenerated weights for pallet_preimage //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-05-23, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-10-03, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// /home/benchbot/cargo_target_dir/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_preimage // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --template=./.maintain/frame-weight-template.hbs +// --heap-pages=4096 +// --pallet=pallet_preimage +// --chain=dev // --output=./frame/preimage/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -61,88 +63,90 @@ pub trait WeightInfo { /// Weights for pallet_preimage using the Substrate node and recommended hardware. pub struct SubstrateWeight<T>(PhantomData<T>); impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { - // Storage: Preimage PreimageFor (r:1 w:1) // Storage: Preimage StatusFor (r:1 w:1) + // Storage: Preimage PreimageFor (r:0 w:1) + /// The range of component `s` is `[0, 4194304]`. fn note_preimage(s: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_ref_time(32_591_000 as u64) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) + .saturating_add(Weight::from_ref_time(1_680 as u64).saturating_mul(s as u64)) + .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:0) + // Storage: Preimage StatusFor (r:1 w:1) + // Storage: Preimage PreimageFor (r:0 w:1) + /// The range of component `s` is `[0, 4194304]`. fn note_requested_preimage(s: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_ref_time(23_350_000 as u64) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + .saturating_add(Weight::from_ref_time(1_681 as u64).saturating_mul(s as u64)) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) } - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:0) + // Storage: Preimage StatusFor (r:1 w:1) + // Storage: Preimage PreimageFor (r:0 w:1) + /// The range of component `s` is `[0, 4194304]`. fn note_no_deposit_preimage(s: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_ref_time(21_436_000 as u64) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + .saturating_add(Weight::from_ref_time(1_680 as u64).saturating_mul(s as u64)) + .saturating_add(T::DbWeight::get().reads(1 as u64)) + .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) // Storage: Preimage PreimageFor (r:0 w:1) fn unnote_preimage() -> Weight { - Weight::from_ref_time(44_380_000 as u64) + Weight::from_ref_time(44_567_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) // Storage: Preimage PreimageFor (r:0 w:1) fn unnote_no_deposit_preimage() -> Weight { - Weight::from_ref_time(30_280_000 as u64) + Weight::from_ref_time(30_065_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) fn request_preimage() -> Weight { - Weight::from_ref_time(42_809_000 as u64) + Weight::from_ref_time(28_470_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) fn request_no_deposit_preimage() -> Weight { - Weight::from_ref_time(28_964_000 as u64) + Weight::from_ref_time(14_601_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) fn request_unnoted_preimage() -> Weight { - Weight::from_ref_time(17_555_000 as u64) + Weight::from_ref_time(20_121_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) fn request_requested_preimage() -> Weight { - Weight::from_ref_time(7_745_000 as u64) + Weight::from_ref_time(9_440_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) // Storage: Preimage PreimageFor (r:0 w:1) fn unrequest_preimage() -> Weight { - Weight::from_ref_time(29_758_000 as u64) + Weight::from_ref_time(29_013_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) fn unrequest_unnoted_preimage() -> Weight { - Weight::from_ref_time(18_360_000 as u64) + Weight::from_ref_time(9_223_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(2 as u64)) + .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) fn unrequest_multi_referenced_preimage() -> Weight { - Weight::from_ref_time(7_439_000 as u64) + Weight::from_ref_time(9_252_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } @@ -150,88 +154,90 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Preimage PreimageFor (r:1 w:1) // Storage: Preimage StatusFor (r:1 w:1) + // Storage: Preimage PreimageFor (r:0 w:1) + /// The range of component `s` is `[0, 4194304]`. fn note_preimage(s: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_ref_time(32_591_000 as u64) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) + .saturating_add(Weight::from_ref_time(1_680 as u64).saturating_mul(s as u64)) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:0) + // Storage: Preimage StatusFor (r:1 w:1) + // Storage: Preimage PreimageFor (r:0 w:1) + /// The range of component `s` is `[0, 4194304]`. fn note_requested_preimage(s: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_ref_time(23_350_000 as u64) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + .saturating_add(Weight::from_ref_time(1_681 as u64).saturating_mul(s as u64)) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) } - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:0) + // Storage: Preimage StatusFor (r:1 w:1) + // Storage: Preimage PreimageFor (r:0 w:1) + /// The range of component `s` is `[0, 4194304]`. fn note_no_deposit_preimage(s: u32, ) -> Weight { - Weight::from_ref_time(0 as u64) + Weight::from_ref_time(21_436_000 as u64) // Standard Error: 0 - .saturating_add(Weight::from_ref_time(2_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + .saturating_add(Weight::from_ref_time(1_680 as u64).saturating_mul(s as u64)) + .saturating_add(RocksDbWeight::get().reads(1 as u64)) + .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) // Storage: Preimage PreimageFor (r:0 w:1) fn unnote_preimage() -> Weight { - Weight::from_ref_time(44_380_000 as u64) + Weight::from_ref_time(44_567_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) // Storage: Preimage PreimageFor (r:0 w:1) fn unnote_no_deposit_preimage() -> Weight { - Weight::from_ref_time(30_280_000 as u64) + Weight::from_ref_time(30_065_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) fn request_preimage() -> Weight { - Weight::from_ref_time(42_809_000 as u64) + Weight::from_ref_time(28_470_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) fn request_no_deposit_preimage() -> Weight { - Weight::from_ref_time(28_964_000 as u64) + Weight::from_ref_time(14_601_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) fn request_unnoted_preimage() -> Weight { - Weight::from_ref_time(17_555_000 as u64) + Weight::from_ref_time(20_121_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) fn request_requested_preimage() -> Weight { - Weight::from_ref_time(7_745_000 as u64) + Weight::from_ref_time(9_440_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) // Storage: Preimage PreimageFor (r:0 w:1) fn unrequest_preimage() -> Weight { - Weight::from_ref_time(29_758_000 as u64) + Weight::from_ref_time(29_013_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Preimage PreimageFor (r:0 w:1) fn unrequest_unnoted_preimage() -> Weight { - Weight::from_ref_time(18_360_000 as u64) + Weight::from_ref_time(9_223_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) + .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Preimage StatusFor (r:1 w:1) fn unrequest_multi_referenced_preimage() -> Weight { - Weight::from_ref_time(7_439_000 as u64) + Weight::from_ref_time(9_252_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } diff --git a/substrate/frame/recovery/src/lib.rs b/substrate/frame/recovery/src/lib.rs index 0b6b7b0f51d9a5035ed96dca8d9fc0171b58a373..18d3d48dc024c72ed4ed826e587947b932dd7c79 100644 --- a/substrate/frame/recovery/src/lib.rs +++ b/substrate/frame/recovery/src/lib.rs @@ -452,7 +452,7 @@ pub mod pallet { ensure!(!friends.is_empty(), Error::<T>::NotEnoughFriends); ensure!(threshold as usize <= friends.len(), Error::<T>::NotEnoughFriends); let bounded_friends: FriendsOf<T> = - friends.try_into().map_err(|()| Error::<T>::MaxFriends)?; + friends.try_into().map_err(|_| Error::<T>::MaxFriends)?; ensure!(Self::is_sorted_and_unique(&bounded_friends), Error::<T>::NotSorted); // Total deposit is base fee + number of friends * factor fee let friend_deposit = T::FriendDepositFactor::get() @@ -554,7 +554,7 @@ pub mod pallet { Err(pos) => active_recovery .friends .try_insert(pos, who.clone()) - .map_err(|()| Error::<T>::MaxFriends)?, + .map_err(|_| Error::<T>::MaxFriends)?, } // Update storage with the latest details <ActiveRecoveries<T>>::insert(&lost, &rescuer, active_recovery); diff --git a/substrate/frame/referenda/src/benchmarking.rs b/substrate/frame/referenda/src/benchmarking.rs index 45ec894e2c2c0312649daaebe6091eca6109b211..bc6fb31bf112786a535e159e151a0f505a00166a 100644 --- a/substrate/frame/referenda/src/benchmarking.rs +++ b/substrate/frame/referenda/src/benchmarking.rs @@ -24,10 +24,10 @@ use frame_benchmarking::{account, benchmarks_instance_pallet, whitelist_account} use frame_support::{ assert_ok, dispatch::UnfilteredDispatchable, - traits::{Currency, EnsureOrigin}, + traits::{Bounded, Currency, EnsureOrigin}, }; use frame_system::RawOrigin; -use sp_runtime::traits::{Bounded, Hash}; +use sp_runtime::traits::Bounded as ArithBounded; const SEED: u32 = 0; @@ -42,6 +42,12 @@ fn funded_account<T: Config<I>, I: 'static>(name: &'static str, index: u32) -> T caller } +fn dummy_call<T: Config<I>, I: 'static>() -> Bounded<<T as Config<I>>::RuntimeCall> { + let inner = frame_system::Call::remark { remark: vec![] }; + let call = <T as Config<I>>::RuntimeCall::from(inner); + T::Preimages::bound(call).unwrap() +} + fn create_referendum<T: Config<I>, I: 'static>() -> (T::RuntimeOrigin, ReferendumIndex) { let origin: T::RuntimeOrigin = T::SubmitOrigin::successful_origin(); if let Ok(caller) = frame_system::ensure_signed(origin.clone()) { @@ -50,9 +56,9 @@ fn create_referendum<T: Config<I>, I: 'static>() -> (T::RuntimeOrigin, Referendu } let proposal_origin = Box::new(RawOrigin::Root.into()); - let proposal_hash = T::Hashing::hash_of(&0); + let proposal = dummy_call::<T, I>(); let enactment_moment = DispatchTime::After(0u32.into()); - let call = Call::<T, I>::submit { proposal_origin, proposal_hash, enactment_moment }; + let call = crate::Call::<T, I>::submit { proposal_origin, proposal, enactment_moment }; assert_ok!(call.dispatch_bypass_filter(origin.clone())); let index = ReferendumCount::<T, I>::get() - 1; (origin, index) @@ -196,7 +202,7 @@ benchmarks_instance_pallet! { }: _<T::RuntimeOrigin>( origin, Box::new(RawOrigin::Root.into()), - T::Hashing::hash_of(&0), + dummy_call::<T, I>(), DispatchTime::After(0u32.into()) ) verify { let index = ReferendumCount::<T, I>::get().checked_sub(1).unwrap(); diff --git a/substrate/frame/referenda/src/lib.rs b/substrate/frame/referenda/src/lib.rs index 739f0dbc30ed4781a65d2503a919e499e25d30b9..1bdc19d49c414cb06fb051856a2d59cd74a4abc5 100644 --- a/substrate/frame/referenda/src/lib.rs +++ b/substrate/frame/referenda/src/lib.rs @@ -69,11 +69,11 @@ use frame_support::{ ensure, traits::{ schedule::{ - v2::{Anon as ScheduleAnon, Named as ScheduleNamed}, - DispatchTime, MaybeHashed, + v3::{Anon as ScheduleAnon, Named as ScheduleNamed}, + DispatchTime, }, - Currency, LockIdentifier, OnUnbalanced, OriginTrait, PollStatus, Polling, - ReservableCurrency, VoteTally, + Currency, LockIdentifier, OnUnbalanced, OriginTrait, PollStatus, Polling, QueryPreimage, + ReservableCurrency, StorePreimage, VoteTally, }, BoundedVec, }; @@ -92,10 +92,10 @@ use self::branch::{BeginDecidingBranch, OneFewerDecidingBranch, ServiceBranch}; pub use self::{ pallet::*, types::{ - BalanceOf, CallOf, Curve, DecidingStatus, DecidingStatusOf, Deposit, InsertSorted, - NegativeImbalanceOf, PalletsOriginOf, ReferendumIndex, ReferendumInfo, ReferendumInfoOf, - ReferendumStatus, ReferendumStatusOf, ScheduleAddressOf, TallyOf, TrackIdOf, TrackInfo, - TrackInfoOf, TracksInfo, VotesOf, + BalanceOf, BoundedCallOf, CallOf, Curve, DecidingStatus, DecidingStatusOf, Deposit, + InsertSorted, NegativeImbalanceOf, PalletsOriginOf, ReferendumIndex, ReferendumInfo, + ReferendumInfoOf, ReferendumStatus, ReferendumStatusOf, ScheduleAddressOf, TallyOf, + TrackIdOf, TrackInfo, TrackInfoOf, TracksInfo, VotesOf, }, weights::WeightInfo, }; @@ -149,23 +149,16 @@ pub mod pallet { // System level stuff. type RuntimeCall: Parameter + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin> - + From<Call<Self, I>>; + + From<Call<Self, I>> + + IsType<<Self as frame_system::Config>::RuntimeCall> + + From<frame_system::Call<Self>>; type RuntimeEvent: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::RuntimeEvent>; /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; /// The Scheduler. - type Scheduler: ScheduleAnon< - Self::BlockNumber, - CallOf<Self, I>, - PalletsOriginOf<Self>, - Hash = Self::Hash, - > + ScheduleNamed< - Self::BlockNumber, - CallOf<Self, I>, - PalletsOriginOf<Self>, - Hash = Self::Hash, - >; + type Scheduler: ScheduleAnon<Self::BlockNumber, CallOf<Self, I>, PalletsOriginOf<Self>> + + ScheduleNamed<Self::BlockNumber, CallOf<Self, I>, PalletsOriginOf<Self>>; /// Currency type for this pallet. type Currency: ReservableCurrency<Self::AccountId>; // Origins and unbalances. @@ -221,6 +214,9 @@ pub mod pallet { Self::BlockNumber, RuntimeOrigin = <Self::RuntimeOrigin as OriginTrait>::PalletsOrigin, >; + + /// The preimage provider. + type Preimages: QueryPreimage + StorePreimage; } /// The next free referendum index, aka the number of referenda started so far. @@ -259,8 +255,8 @@ pub mod pallet { index: ReferendumIndex, /// The track (and by extension proposal dispatch origin) of this referendum. track: TrackIdOf<T, I>, - /// The hash of the proposal up for referendum. - proposal_hash: T::Hash, + /// The proposal for the referendum. + proposal: BoundedCallOf<T, I>, }, /// The decision deposit has been placed. DecisionDepositPlaced { @@ -293,8 +289,8 @@ pub mod pallet { index: ReferendumIndex, /// The track (and by extension proposal dispatch origin) of this referendum. track: TrackIdOf<T, I>, - /// The hash of the proposal up for referendum. - proposal_hash: T::Hash, + /// The proposal for the referendum. + proposal: BoundedCallOf<T, I>, /// The current tally of votes in this referendum. tally: T::Tally, }, @@ -381,7 +377,7 @@ pub mod pallet { /// - `origin`: must be `SubmitOrigin` and the account must have `SubmissionDeposit` funds /// available. /// - `proposal_origin`: The origin from which the proposal should be executed. - /// - `proposal_hash`: The hash of the proposal preimage. + /// - `proposal`: The proposal. /// - `enactment_moment`: The moment that the proposal should be enacted. /// /// Emits `Submitted`. @@ -389,7 +385,7 @@ pub mod pallet { pub fn submit( origin: OriginFor<T>, proposal_origin: Box<PalletsOriginOf<T>>, - proposal_hash: T::Hash, + proposal: BoundedCallOf<T, I>, enactment_moment: DispatchTime<T::BlockNumber>, ) -> DispatchResult { let who = T::SubmitOrigin::ensure_origin(origin)?; @@ -403,11 +399,12 @@ pub mod pallet { r }); let now = frame_system::Pallet::<T>::block_number(); - let nudge_call = Call::nudge_referendum { index }; + let nudge_call = + T::Preimages::bound(CallOf::<T, I>::from(Call::nudge_referendum { index }))?; let status = ReferendumStatus { track, origin: *proposal_origin, - proposal_hash, + proposal: proposal.clone(), enactment: enactment_moment, submitted: now, submission_deposit, @@ -419,7 +416,7 @@ pub mod pallet { }; ReferendumInfoFor::<T, I>::insert(index, ReferendumInfo::Ongoing(status)); - Self::deposit_event(Event::<T, I>::Submitted { index, track, proposal_hash }); + Self::deposit_event(Event::<T, I>::Submitted { index, track, proposal }); Ok(()) } @@ -651,7 +648,8 @@ impl<T: Config<I>, I: 'static> Polling<T::Tally> for Pallet<T, I> { let mut status = ReferendumStatusOf::<T, I> { track: class, origin: frame_support::dispatch::RawOrigin::Root.into(), - proposal_hash: <T::Hashing as sp_runtime::traits::Hash>::hash_of(&index), + proposal: T::Preimages::bound(CallOf::<T, I>::from(Call::nudge_referendum { index })) + .map_err(|_| ())?, enactment: DispatchTime::After(Zero::zero()), submitted: now, submission_deposit: Deposit { who: dummy_account_id, amount: Zero::zero() }, @@ -709,18 +707,18 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { track: &TrackInfoOf<T, I>, desired: DispatchTime<T::BlockNumber>, origin: PalletsOriginOf<T>, - call_hash: T::Hash, + call: BoundedCallOf<T, I>, ) { let now = frame_system::Pallet::<T>::block_number(); let earliest_allowed = now.saturating_add(track.min_enactment_period); let desired = desired.evaluate(now); let ok = T::Scheduler::schedule_named( - (ASSEMBLY_ID, "enactment", index).encode(), + (ASSEMBLY_ID, "enactment", index).using_encoded(sp_io::hashing::blake2_256), DispatchTime::At(desired.max(earliest_allowed)), None, 63, origin, - MaybeHashed::Hash(call_hash), + call, ) .is_ok(); debug_assert!(ok, "LOGIC ERROR: bake_referendum/schedule_named failed"); @@ -728,7 +726,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { /// Set an alarm to dispatch `call` at block number `when`. fn set_alarm( - call: impl Into<CallOf<T, I>>, + call: BoundedCallOf<T, I>, when: T::BlockNumber, ) -> Option<(T::BlockNumber, ScheduleAddressOf<T, I>)> { let alarm_interval = T::AlarmInterval::get().max(One::one()); @@ -739,7 +737,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { None, 128u8, frame_system::RawOrigin::Root.into(), - MaybeHashed::Value(call.into()), + call, ) .ok() .map(|x| (when, x)); @@ -776,7 +774,7 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { Self::deposit_event(Event::<T, I>::DecisionStarted { index, tally: status.tally.clone(), - proposal_hash: status.proposal_hash, + proposal: status.proposal.clone(), track: status.track, }); let confirming = if is_passing { @@ -843,12 +841,21 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { let alarm_interval = T::AlarmInterval::get().max(One::one()); let when = (next_block + alarm_interval - One::one()) / alarm_interval * alarm_interval; + let call = match T::Preimages::bound(CallOf::<T, I>::from(Call::one_fewer_deciding { + track, + })) { + Ok(c) => c, + Err(_) => { + debug_assert!(false, "Unable to create a bounded call from `one_fewer_deciding`??",); + return + }, + }; let maybe_result = T::Scheduler::schedule( DispatchTime::At(when), None, 128u8, frame_system::RawOrigin::Root.into(), - MaybeHashed::Value(Call::one_fewer_deciding { track }.into()), + call, ); debug_assert!( maybe_result.is_ok(), @@ -871,7 +878,18 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { if status.alarm.as_ref().map_or(true, |&(when, _)| when != alarm) { // Either no alarm or one that was different Self::ensure_no_alarm(status); - status.alarm = Self::set_alarm(Call::nudge_referendum { index }, alarm); + let call = + match T::Preimages::bound(CallOf::<T, I>::from(Call::nudge_referendum { index })) { + Ok(c) => c, + Err(_) => { + debug_assert!( + false, + "Unable to create a bounded call from `nudge_referendum`??", + ); + return false + }, + }; + status.alarm = Self::set_alarm(call, alarm); true } else { false @@ -987,14 +1005,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> { // Passed! Self::ensure_no_alarm(&mut status); Self::note_one_fewer_deciding(status.track); - let (desired, call_hash) = (status.enactment, status.proposal_hash); - Self::schedule_enactment( - index, - track, - desired, - status.origin, - call_hash, - ); + let (desired, call) = (status.enactment, status.proposal); + Self::schedule_enactment(index, track, desired, status.origin, call); Self::deposit_event(Event::<T, I>::Confirmed { index, tally: status.tally, diff --git a/substrate/frame/referenda/src/mock.rs b/substrate/frame/referenda/src/mock.rs index 920e529bf05ca93e1ad5ae1d9c7e667830b3d368..c98fbf9a676b1e2dd65d1c90fdbd7e5c875bfbfe 100644 --- a/substrate/frame/referenda/src/mock.rs +++ b/substrate/frame/referenda/src/mock.rs @@ -24,7 +24,7 @@ use frame_support::{ assert_ok, ord_parameter_types, parameter_types, traits::{ ConstU32, ConstU64, Contains, EqualPrivilegeOnly, OnInitialize, OriginTrait, Polling, - PreimageRecipient, SortedMembers, + SortedMembers, }, weights::Weight, }; @@ -32,7 +32,7 @@ use frame_system::{EnsureRoot, EnsureSignedBy}; use sp_core::H256; use sp_runtime::{ testing::Header, - traits::{BlakeTwo256, Hash, IdentityLookup}, + traits::{BlakeTwo256, IdentityLookup}, DispatchResult, Perbill, }; @@ -97,7 +97,6 @@ impl pallet_preimage::Config for Test { type WeightInfo = (); type Currency = Balances; type ManagerOrigin = EnsureRoot<u64>; - type MaxSize = ConstU32<4096>; type BaseDeposit = (); type ByteDeposit = (); } @@ -111,8 +110,7 @@ impl pallet_scheduler::Config for Test { type MaxScheduledPerBlock = ConstU32<100>; type WeightInfo = (); type OriginPrivilegeCmp = EqualPrivilegeOnly; - type PreimageProvider = Preimage; - type NoPreimagePostponement = ConstU64<10>; + type Preimages = Preimage; } impl pallet_balances::Config for Test { type MaxReserves = (); @@ -229,6 +227,7 @@ impl Config for Test { type UndecidingTimeout = ConstU64<20>; type AlarmInterval = AlarmInterval; type Tracks = TestTracksInfo; + type Preimages = Preimage; } pub fn new_test_ext() -> sp_io::TestExternalities { @@ -306,14 +305,13 @@ pub fn set_balance_proposal(value: u64) -> Vec<u8> { .encode() } -pub fn set_balance_proposal_hash(value: u64) -> H256 { +pub fn set_balance_proposal_bounded(value: u64) -> BoundedCallOf<Test, ()> { let c = RuntimeCall::Balances(pallet_balances::Call::set_balance { who: 42, new_free: value, new_reserved: 0, }); - <Preimage as PreimageRecipient<_>>::note_preimage(c.encode().try_into().unwrap()); - BlakeTwo256::hash_of(&c) + <Preimage as StorePreimage>::bound(c).unwrap() } #[allow(dead_code)] @@ -321,7 +319,7 @@ pub fn propose_set_balance(who: u64, value: u64, delay: u64) -> DispatchResult { Referenda::submit( RuntimeOrigin::signed(who), Box::new(frame_system::RawOrigin::Root.into()), - set_balance_proposal_hash(value), + set_balance_proposal_bounded(value), DispatchTime::After(delay), ) } @@ -449,7 +447,7 @@ impl RefState { assert_ok!(Referenda::submit( RuntimeOrigin::signed(1), Box::new(frame_support::dispatch::RawOrigin::Root.into()), - set_balance_proposal_hash(1), + set_balance_proposal_bounded(1), DispatchTime::At(10), )); assert_ok!(Referenda::place_decision_deposit(RuntimeOrigin::signed(2), 0)); diff --git a/substrate/frame/referenda/src/tests.rs b/substrate/frame/referenda/src/tests.rs index 778d00e5166936064d69e42efadc26ded1adecc5..355ce3021b87f8e278ed161afac84a80e1b9ba68 100644 --- a/substrate/frame/referenda/src/tests.rs +++ b/substrate/frame/referenda/src/tests.rs @@ -44,7 +44,7 @@ fn basic_happy_path_works() { assert_ok!(Referenda::submit( RuntimeOrigin::signed(1), Box::new(RawOrigin::Root.into()), - set_balance_proposal_hash(1), + set_balance_proposal_bounded(1), DispatchTime::At(10), )); assert_eq!(Balances::reserved_balance(&1), 2); @@ -175,7 +175,7 @@ fn queueing_works() { assert_ok!(Referenda::submit( RuntimeOrigin::signed(5), Box::new(RawOrigin::Root.into()), - set_balance_proposal_hash(0), + set_balance_proposal_bounded(0), DispatchTime::After(0), )); assert_ok!(Referenda::place_decision_deposit(RuntimeOrigin::signed(5), 0)); @@ -187,7 +187,7 @@ fn queueing_works() { assert_ok!(Referenda::submit( RuntimeOrigin::signed(i), Box::new(RawOrigin::Root.into()), - set_balance_proposal_hash(i), + set_balance_proposal_bounded(i), DispatchTime::After(0), )); assert_ok!(Referenda::place_decision_deposit(RuntimeOrigin::signed(i), i as u32)); @@ -272,7 +272,7 @@ fn auto_timeout_should_happen_with_nothing_but_submit() { assert_ok!(Referenda::submit( RuntimeOrigin::signed(1), Box::new(RawOrigin::Root.into()), - set_balance_proposal_hash(1), + set_balance_proposal_bounded(1), DispatchTime::At(20), )); run_to(20); @@ -292,13 +292,13 @@ fn tracks_are_distinguished() { assert_ok!(Referenda::submit( RuntimeOrigin::signed(1), Box::new(RawOrigin::Root.into()), - set_balance_proposal_hash(1), + set_balance_proposal_bounded(1), DispatchTime::At(10), )); assert_ok!(Referenda::submit( RuntimeOrigin::signed(2), Box::new(RawOrigin::None.into()), - set_balance_proposal_hash(2), + set_balance_proposal_bounded(2), DispatchTime::At(20), )); @@ -315,7 +315,7 @@ fn tracks_are_distinguished() { ReferendumInfo::Ongoing(ReferendumStatus { track: 0, origin: OriginCaller::system(RawOrigin::Root), - proposal_hash: set_balance_proposal_hash(1), + proposal: set_balance_proposal_bounded(1), enactment: DispatchTime::At(10), submitted: 1, submission_deposit: Deposit { who: 1, amount: 2 }, @@ -331,7 +331,7 @@ fn tracks_are_distinguished() { ReferendumInfo::Ongoing(ReferendumStatus { track: 1, origin: OriginCaller::system(RawOrigin::None), - proposal_hash: set_balance_proposal_hash(2), + proposal: set_balance_proposal_bounded(2), enactment: DispatchTime::At(20), submitted: 1, submission_deposit: Deposit { who: 2, amount: 2 }, @@ -350,13 +350,13 @@ fn tracks_are_distinguished() { #[test] fn submit_errors_work() { new_test_ext().execute_with(|| { - let h = set_balance_proposal_hash(1); + let h = set_balance_proposal_bounded(1); // No track for Signed origins. assert_noop!( Referenda::submit( RuntimeOrigin::signed(1), Box::new(RawOrigin::Signed(2).into()), - h, + h.clone(), DispatchTime::At(10), ), Error::<Test>::NoTrack @@ -381,7 +381,7 @@ fn decision_deposit_errors_work() { let e = Error::<Test>::NotOngoing; assert_noop!(Referenda::place_decision_deposit(RuntimeOrigin::signed(2), 0), e); - let h = set_balance_proposal_hash(1); + let h = set_balance_proposal_bounded(1); assert_ok!(Referenda::submit( RuntimeOrigin::signed(1), Box::new(RawOrigin::Root.into()), @@ -403,7 +403,7 @@ fn refund_deposit_works() { let e = Error::<Test>::BadReferendum; assert_noop!(Referenda::refund_decision_deposit(RuntimeOrigin::signed(1), 0), e); - let h = set_balance_proposal_hash(1); + let h = set_balance_proposal_bounded(1); assert_ok!(Referenda::submit( RuntimeOrigin::signed(1), Box::new(RawOrigin::Root.into()), @@ -425,7 +425,7 @@ fn refund_deposit_works() { #[test] fn cancel_works() { new_test_ext().execute_with(|| { - let h = set_balance_proposal_hash(1); + let h = set_balance_proposal_bounded(1); assert_ok!(Referenda::submit( RuntimeOrigin::signed(1), Box::new(RawOrigin::Root.into()), @@ -444,7 +444,7 @@ fn cancel_works() { #[test] fn cancel_errors_works() { new_test_ext().execute_with(|| { - let h = set_balance_proposal_hash(1); + let h = set_balance_proposal_bounded(1); assert_ok!(Referenda::submit( RuntimeOrigin::signed(1), Box::new(RawOrigin::Root.into()), @@ -462,7 +462,7 @@ fn cancel_errors_works() { #[test] fn kill_works() { new_test_ext().execute_with(|| { - let h = set_balance_proposal_hash(1); + let h = set_balance_proposal_bounded(1); assert_ok!(Referenda::submit( RuntimeOrigin::signed(1), Box::new(RawOrigin::Root.into()), @@ -482,7 +482,7 @@ fn kill_works() { #[test] fn kill_errors_works() { new_test_ext().execute_with(|| { - let h = set_balance_proposal_hash(1); + let h = set_balance_proposal_bounded(1); assert_ok!(Referenda::submit( RuntimeOrigin::signed(1), Box::new(RawOrigin::Root.into()), diff --git a/substrate/frame/referenda/src/types.rs b/substrate/frame/referenda/src/types.rs index a6311e5f925be6333a120fe360a25c948bbc0162..2ce93cb6adc3ca725d73f21957ae68f74f33eee0 100644 --- a/substrate/frame/referenda/src/types.rs +++ b/substrate/frame/referenda/src/types.rs @@ -19,7 +19,10 @@ use super::*; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; -use frame_support::{traits::schedule::Anon, Parameter}; +use frame_support::{ + traits::{schedule::v3::Anon, Bounded}, + Parameter, +}; use scale_info::TypeInfo; use sp_arithmetic::{Rounding::*, SignedRounding::*}; use sp_runtime::{FixedI64, PerThing, RuntimeDebug}; @@ -31,6 +34,7 @@ pub type NegativeImbalanceOf<T, I> = <<T as Config<I>>::Currency as Currency< <T as frame_system::Config>::AccountId, >>::NegativeImbalance; pub type CallOf<T, I> = <T as Config<I>>::RuntimeCall; +pub type BoundedCallOf<T, I> = Bounded<<T as Config<I>>::RuntimeCall>; pub type VotesOf<T, I> = <T as Config<I>>::Votes; pub type TallyOf<T, I> = <T as Config<I>>::Tally; pub type PalletsOriginOf<T> = @@ -39,7 +43,7 @@ pub type ReferendumInfoOf<T, I> = ReferendumInfo< TrackIdOf<T, I>, PalletsOriginOf<T>, <T as frame_system::Config>::BlockNumber, - <T as frame_system::Config>::Hash, + BoundedCallOf<T, I>, BalanceOf<T, I>, TallyOf<T, I>, <T as frame_system::Config>::AccountId, @@ -49,7 +53,7 @@ pub type ReferendumStatusOf<T, I> = ReferendumStatus< TrackIdOf<T, I>, PalletsOriginOf<T>, <T as frame_system::Config>::BlockNumber, - <T as frame_system::Config>::Hash, + BoundedCallOf<T, I>, BalanceOf<T, I>, TallyOf<T, I>, <T as frame_system::Config>::AccountId, @@ -160,7 +164,7 @@ pub struct ReferendumStatus< TrackId: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, RuntimeOrigin: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, Moment: Parameter + Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone + EncodeLike, - Hash: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, + Call: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, Balance: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, Tally: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, AccountId: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, @@ -171,7 +175,7 @@ pub struct ReferendumStatus< /// The origin for this referendum. pub(crate) origin: RuntimeOrigin, /// The hash of the proposal up for referendum. - pub(crate) proposal_hash: Hash, + pub(crate) proposal: Call, /// The time the proposal should be scheduled for enactment. pub(crate) enactment: DispatchTime<Moment>, /// The time of submission. Once `UndecidingTimeout` passes, it may be closed by anyone if it @@ -197,7 +201,7 @@ pub enum ReferendumInfo< TrackId: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, RuntimeOrigin: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, Moment: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone + EncodeLike, - Hash: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, + Call: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, Balance: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, Tally: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, AccountId: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, @@ -209,7 +213,7 @@ pub enum ReferendumInfo< TrackId, RuntimeOrigin, Moment, - Hash, + Call, Balance, Tally, AccountId, @@ -232,12 +236,12 @@ impl< TrackId: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, RuntimeOrigin: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, Moment: Parameter + Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone + EncodeLike, - Hash: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, + Call: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, Balance: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, Tally: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, AccountId: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, ScheduleAddress: Eq + PartialEq + Debug + Encode + Decode + TypeInfo + Clone, - > ReferendumInfo<TrackId, RuntimeOrigin, Moment, Hash, Balance, Tally, AccountId, ScheduleAddress> + > ReferendumInfo<TrackId, RuntimeOrigin, Moment, Call, Balance, Tally, AccountId, ScheduleAddress> { /// Take the Decision Deposit from `self`, if there is one. Returns an `Err` if `self` is not /// in a valid state for the Decision Deposit to be refunded. diff --git a/substrate/frame/scheduler/src/benchmarking.rs b/substrate/frame/scheduler/src/benchmarking.rs index 9397c66170425367f62352531ff9c19ac5134b90..aaa30fd88ffda53406ef49732d448cb7c7b824a9 100644 --- a/substrate/frame/scheduler/src/benchmarking.rs +++ b/substrate/frame/scheduler/src/benchmarking.rs @@ -18,201 +18,219 @@ //! Scheduler pallet benchmarking. use super::*; -use frame_benchmarking::benchmarks; +use frame_benchmarking::{account, benchmarks}; use frame_support::{ ensure, - traits::{OnInitialize, PreimageProvider, PreimageRecipient}, + traits::{schedule::Priority, BoundedInline}, }; -use sp_runtime::traits::Hash; +use frame_system::RawOrigin; use sp_std::{prelude::*, vec}; use crate::Pallet as Scheduler; -use frame_system::Pallet as System; +use frame_system::Call as SystemCall; + +const SEED: u32 = 0; const BLOCK_NUMBER: u32 = 2; type SystemOrigin<T> = <T as frame_system::Config>::RuntimeOrigin; -/// Add `n` named items to the schedule. +/// Add `n` items to the schedule. /// /// For `resolved`: +/// - ` /// - `None`: aborted (hash without preimage) /// - `Some(true)`: hash resolves into call if possible, plain call otherwise /// - `Some(false)`: plain call -fn fill_schedule<T: Config>( - when: T::BlockNumber, - n: u32, +fn fill_schedule<T: Config>(when: T::BlockNumber, n: u32) -> Result<(), &'static str> { + let t = DispatchTime::At(when); + let origin: <T as Config>::PalletsOrigin = frame_system::RawOrigin::Root.into(); + for i in 0..n { + let call = make_call::<T>(None); + let period = Some(((i + 100).into(), 100)); + let name = u32_to_name(i); + Scheduler::<T>::do_schedule_named(name, t, period, 0, origin.clone(), call)?; + } + ensure!(Agenda::<T>::get(when).len() == n as usize, "didn't fill schedule"); + Ok(()) +} + +fn u32_to_name(i: u32) -> TaskName { + i.using_encoded(blake2_256) +} + +fn make_task<T: Config>( periodic: bool, named: bool, - resolved: Option<bool>, -) -> Result<(), &'static str> { - for i in 0..n { - // Named schedule is strictly heavier than anonymous - let (call, hash) = call_and_hash::<T>(i); - let call_or_hash = match resolved { - Some(true) => { - T::PreimageProvider::note_preimage(call.encode().try_into().unwrap()); - if T::PreimageProvider::have_preimage(&hash) { - CallOrHashOf::<T>::Hash(hash) - } else { - call.into() - } + signed: bool, + maybe_lookup_len: Option<u32>, + priority: Priority, +) -> ScheduledOf<T> { + let call = make_call::<T>(maybe_lookup_len); + let maybe_periodic = match periodic { + true => Some((100u32.into(), 100)), + false => None, + }; + let maybe_id = match named { + true => Some(u32_to_name(0)), + false => None, + }; + let origin = make_origin::<T>(signed); + Scheduled { maybe_id, priority, call, maybe_periodic, origin, _phantom: PhantomData } +} + +fn bounded<T: Config>(len: u32) -> Option<Bounded<<T as Config>::RuntimeCall>> { + let call = + <<T as Config>::RuntimeCall>::from(SystemCall::remark { remark: vec![0; len as usize] }); + T::Preimages::bound(call).ok() +} + +fn make_call<T: Config>(maybe_lookup_len: Option<u32>) -> Bounded<<T as Config>::RuntimeCall> { + let bound = BoundedInline::bound() as u32; + let mut len = match maybe_lookup_len { + Some(len) => len.min(T::Preimages::MAX_LENGTH as u32 - 2).max(bound) - 3, + None => bound.saturating_sub(4), + }; + + loop { + let c = match bounded::<T>(len) { + Some(x) => x, + None => { + len -= 1; + continue }, - Some(false) => call.into(), - None => CallOrHashOf::<T>::Hash(hash), }; - let period = match periodic { - true => Some(((i + 100).into(), 100)), - false => None, - }; - let t = DispatchTime::At(when); - let origin = frame_system::RawOrigin::Root.into(); - if named { - Scheduler::<T>::do_schedule_named(i.encode(), t, period, 0, origin, call_or_hash)?; + if c.lookup_needed() == maybe_lookup_len.is_some() { + break c + } + if maybe_lookup_len.is_some() { + len += 1; } else { - Scheduler::<T>::do_schedule(t, period, 0, origin, call_or_hash)?; + if len > 0 { + len -= 1; + } else { + break c + } } } - ensure!(Agenda::<T>::get(when).len() == n as usize, "didn't fill schedule"); - Ok(()) } -fn call_and_hash<T: Config>(i: u32) -> (<T as Config>::RuntimeCall, T::Hash) { - // Essentially a no-op call. - let call: <T as Config>::RuntimeCall = frame_system::Call::remark { remark: i.encode() }.into(); - let hash = T::Hashing::hash_of(&call); - (call, hash) +fn make_origin<T: Config>(signed: bool) -> <T as Config>::PalletsOrigin { + match signed { + true => frame_system::RawOrigin::Signed(account("origin", 0, SEED)).into(), + false => frame_system::RawOrigin::Root.into(), + } +} + +fn dummy_counter() -> WeightCounter { + WeightCounter { used: Weight::zero(), limit: Weight::MAX } } benchmarks! { - on_initialize_periodic_named_resolved { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, true, true, Some(true))?; - }: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); } - verify { - assert_eq!(System::<T>::event_count(), s * 2); - for i in 0..s { - assert_eq!(Agenda::<T>::get(when + (i + 100).into()).len(), 1 as usize); - } + // `service_agendas` when no work is done. + service_agendas_base { + let now = T::BlockNumber::from(BLOCK_NUMBER); + IncompleteSince::<T>::put(now - One::one()); + }: { + Scheduler::<T>::service_agendas(&mut dummy_counter(), now, 0); + } verify { + assert_eq!(IncompleteSince::<T>::get(), Some(now - One::one())); } - on_initialize_named_resolved { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, false, true, Some(true))?; - }: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); } - verify { - assert_eq!(System::<T>::event_count(), s * 2); - assert!(Agenda::<T>::iter().count() == 0); + // `service_agenda` when no work is done. + service_agenda_base { + let now = BLOCK_NUMBER.into(); + let s in 0 .. T::MaxScheduledPerBlock::get(); + fill_schedule::<T>(now, s)?; + let mut executed = 0; + }: { + Scheduler::<T>::service_agenda(&mut dummy_counter(), &mut executed, now, now, 0); + } verify { + assert_eq!(executed, 0); } - on_initialize_periodic_resolved { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, true, false, Some(true))?; - }: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); } - verify { - assert_eq!(System::<T>::event_count(), s * 2); - for i in 0..s { - assert_eq!(Agenda::<T>::get(when + (i + 100).into()).len(), 1 as usize); - } + // `service_task` when the task is a non-periodic, non-named, non-fetched call which is not + // dispatched (e.g. due to being overweight). + service_task_base { + let now = BLOCK_NUMBER.into(); + let task = make_task::<T>(false, false, false, None, 0); + // prevent any tasks from actually being executed as we only want the surrounding weight. + let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::zero() }; + }: { + let result = Scheduler::<T>::service_task(&mut counter, now, now, 0, true, task); + } verify { + //assert_eq!(result, Ok(())); } - on_initialize_resolved { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, false, false, Some(true))?; - }: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); } - verify { - assert_eq!(System::<T>::event_count(), s * 2); - assert!(Agenda::<T>::iter().count() == 0); + // `service_task` when the task is a non-periodic, non-named, fetched call (with a known + // preimage length) and which is not dispatched (e.g. due to being overweight). + service_task_fetched { + let s in (BoundedInline::bound() as u32) .. (T::Preimages::MAX_LENGTH as u32); + let now = BLOCK_NUMBER.into(); + let task = make_task::<T>(false, false, false, Some(s), 0); + // prevent any tasks from actually being executed as we only want the surrounding weight. + let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::zero() }; + }: { + let result = Scheduler::<T>::service_task(&mut counter, now, now, 0, true, task); + } verify { } - on_initialize_named_aborted { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, false, true, None)?; - }: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); } - verify { - assert_eq!(System::<T>::event_count(), 0); - if let Some(delay) = T::NoPreimagePostponement::get() { - assert_eq!(Agenda::<T>::get(when + delay).len(), s as usize); - } else { - assert!(Agenda::<T>::iter().count() == 0); - } + // `service_task` when the task is a non-periodic, named, non-fetched call which is not + // dispatched (e.g. due to being overweight). + service_task_named { + let now = BLOCK_NUMBER.into(); + let task = make_task::<T>(false, true, false, None, 0); + // prevent any tasks from actually being executed as we only want the surrounding weight. + let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::zero() }; + }: { + let result = Scheduler::<T>::service_task(&mut counter, now, now, 0, true, task); + } verify { } - on_initialize_aborted { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, false, false, None)?; - }: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); } - verify { - assert_eq!(System::<T>::event_count(), 0); - if let Some(delay) = T::NoPreimagePostponement::get() { - assert_eq!(Agenda::<T>::get(when + delay).len(), s as usize); - } else { - assert!(Agenda::<T>::iter().count() == 0); - } + // `service_task` when the task is a periodic, non-named, non-fetched call which is not + // dispatched (e.g. due to being overweight). + service_task_periodic { + let now = BLOCK_NUMBER.into(); + let task = make_task::<T>(true, false, false, None, 0); + // prevent any tasks from actually being executed as we only want the surrounding weight. + let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::zero() }; + }: { + let result = Scheduler::<T>::service_task(&mut counter, now, now, 0, true, task); + } verify { } - on_initialize_periodic_named { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, true, true, Some(false))?; - }: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); } - verify { - assert_eq!(System::<T>::event_count(), s); - for i in 0..s { - assert_eq!(Agenda::<T>::get(when + (i + 100).into()).len(), 1 as usize); - } + // `execute_dispatch` when the origin is `Signed`, not counting the dispatable's weight. + execute_dispatch_signed { + let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::MAX }; + let origin = make_origin::<T>(true); + let call = T::Preimages::realize(&make_call::<T>(None)).unwrap().0; + }: { + assert!(Scheduler::<T>::execute_dispatch(&mut counter, origin, call).is_ok()); } - - on_initialize_periodic { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, true, false, Some(false))?; - }: { Scheduler::<T>::on_initialize(when); } verify { - assert_eq!(System::<T>::event_count(), s); - for i in 0..s { - assert_eq!(Agenda::<T>::get(when + (i + 100).into()).len(), 1 as usize); - } } - on_initialize_named { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, false, true, Some(false))?; - }: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); } - verify { - assert_eq!(System::<T>::event_count(), s); - assert!(Agenda::<T>::iter().count() == 0); + // `execute_dispatch` when the origin is not `Signed`, not counting the dispatable's weight. + execute_dispatch_unsigned { + let mut counter = WeightCounter { used: Weight::zero(), limit: Weight::MAX }; + let origin = make_origin::<T>(false); + let call = T::Preimages::realize(&make_call::<T>(None)).unwrap().0; + }: { + assert!(Scheduler::<T>::execute_dispatch(&mut counter, origin, call).is_ok()); } - - on_initialize { - let s in 1 .. T::MaxScheduledPerBlock::get(); - let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, false, false, Some(false))?; - }: { Scheduler::<T>::on_initialize(BLOCK_NUMBER.into()); } verify { - assert_eq!(System::<T>::event_count(), s); - assert!(Agenda::<T>::iter().count() == 0); } schedule { - let s in 0 .. T::MaxScheduledPerBlock::get(); + let s in 0 .. (T::MaxScheduledPerBlock::get() - 1); let when = BLOCK_NUMBER.into(); let periodic = Some((T::BlockNumber::one(), 100)); let priority = 0; // Essentially a no-op call. - let inner_call = frame_system::Call::set_storage { items: vec![] }.into(); - let call = Box::new(CallOrHashOf::<T>::Value(inner_call)); + let call = Box::new(SystemCall::set_storage { items: vec![] }.into()); - fill_schedule::<T>(when, s, true, true, Some(false))?; - let schedule_origin = T::ScheduleOrigin::successful_origin(); - }: _<SystemOrigin<T>>(schedule_origin, when, periodic, priority, call) + fill_schedule::<T>(when, s)?; + }: _(RawOrigin::Root, when, periodic, priority, call) verify { ensure!( Agenda::<T>::get(when).len() == (s + 1) as usize, @@ -224,13 +242,13 @@ benchmarks! { let s in 1 .. T::MaxScheduledPerBlock::get(); let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, true, true, Some(false))?; + fill_schedule::<T>(when, s)?; assert_eq!(Agenda::<T>::get(when).len(), s as usize); let schedule_origin = T::ScheduleOrigin::successful_origin(); }: _<SystemOrigin<T>>(schedule_origin, when, 0) verify { ensure!( - Lookup::<T>::get(0.encode()).is_none(), + Lookup::<T>::get(u32_to_name(0)).is_none(), "didn't remove from lookup" ); // Removed schedule is NONE @@ -241,18 +259,16 @@ benchmarks! { } schedule_named { - let s in 0 .. T::MaxScheduledPerBlock::get(); - let id = s.encode(); + let s in 0 .. (T::MaxScheduledPerBlock::get() - 1); + let id = u32_to_name(s); let when = BLOCK_NUMBER.into(); let periodic = Some((T::BlockNumber::one(), 100)); let priority = 0; // Essentially a no-op call. - let inner_call = frame_system::Call::set_storage { items: vec![] }.into(); - let call = Box::new(CallOrHashOf::<T>::Value(inner_call)); + let call = Box::new(SystemCall::set_storage { items: vec![] }.into()); - fill_schedule::<T>(when, s, true, true, Some(false))?; - let schedule_origin = T::ScheduleOrigin::successful_origin(); - }: _<SystemOrigin<T>>(schedule_origin, id, when, periodic, priority, call) + fill_schedule::<T>(when, s)?; + }: _(RawOrigin::Root, id, when, periodic, priority, call) verify { ensure!( Agenda::<T>::get(when).len() == (s + 1) as usize, @@ -264,12 +280,11 @@ benchmarks! { let s in 1 .. T::MaxScheduledPerBlock::get(); let when = BLOCK_NUMBER.into(); - fill_schedule::<T>(when, s, true, true, Some(false))?; - let schedule_origin = T::ScheduleOrigin::successful_origin(); - }: _<SystemOrigin<T>>(schedule_origin, 0.encode()) + fill_schedule::<T>(when, s)?; + }: _(RawOrigin::Root, u32_to_name(0)) verify { ensure!( - Lookup::<T>::get(0.encode()).is_none(), + Lookup::<T>::get(u32_to_name(0)).is_none(), "didn't remove from lookup" ); // Removed schedule is NONE diff --git a/substrate/frame/scheduler/src/lib.rs b/substrate/frame/scheduler/src/lib.rs index 143fa37a9261dcd945c2f4fa04cd6c4869cb9b30..b5ea0deeba9a3ff91bfc81fae62bbce5b252d417 100644 --- a/substrate/frame/scheduler/src/lib.rs +++ b/substrate/frame/scheduler/src/lib.rs @@ -52,27 +52,33 @@ #[cfg(feature = "runtime-benchmarks")] mod benchmarking; +pub mod migration; #[cfg(test)] mod mock; #[cfg(test)] mod tests; pub mod weights; -use codec::{Codec, Decode, Encode}; +use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ - dispatch::{DispatchError, DispatchResult, Dispatchable, GetDispatchInfo, Parameter}, + dispatch::{ + DispatchError, DispatchResult, Dispatchable, GetDispatchInfo, Parameter, RawOrigin, + }, + ensure, traits::{ schedule::{self, DispatchTime, MaybeHashed}, - EnsureOrigin, Get, IsType, OriginTrait, PalletInfoAccess, PrivilegeCmp, StorageVersion, + Bounded, CallerTrait, EnsureOrigin, Get, Hash as PreimageHash, IsType, OriginTrait, + PalletInfoAccess, PrivilegeCmp, QueryPreimage, StorageVersion, StorePreimage, }, weights::Weight, }; -use frame_system::{self as system, ensure_signed}; +use frame_system::{self as system}; pub use pallet::*; use scale_info::TypeInfo; +use sp_io::hashing::blake2_256; use sp_runtime::{ traits::{BadOrigin, One, Saturating, Zero}, - RuntimeDebug, + BoundedVec, RuntimeDebug, }; use sp_std::{borrow::Borrow, cmp::Ordering, marker::PhantomData, prelude::*}; pub use weights::WeightInfo; @@ -96,24 +102,25 @@ struct ScheduledV1<Call, BlockNumber> { /// Information regarding an item to be executed in the future. #[cfg_attr(any(feature = "std", test), derive(PartialEq, Eq))] -#[derive(Clone, RuntimeDebug, Encode, Decode, TypeInfo)] -pub struct ScheduledV3<Call, BlockNumber, PalletsOrigin, AccountId> { +#[derive(Clone, RuntimeDebug, Encode, Decode, MaxEncodedLen, TypeInfo)] +pub struct Scheduled<Name, Call, BlockNumber, PalletsOrigin, AccountId> { /// The unique identity for this task, if there is one. - maybe_id: Option<Vec<u8>>, + maybe_id: Option<Name>, /// This task's priority. priority: schedule::Priority, /// The call to be dispatched. call: Call, /// If the call is periodic, then this points to the information concerning that. maybe_periodic: Option<schedule::Period<BlockNumber>>, - /// The origin to dispatch the call. + /// The origin with which to dispatch the call. origin: PalletsOrigin, _phantom: PhantomData<AccountId>, } -use crate::ScheduledV3 as ScheduledV2; +use crate::{Scheduled as ScheduledV3, Scheduled as ScheduledV2}; -pub type ScheduledV2Of<T> = ScheduledV3< +pub type ScheduledV2Of<T> = ScheduledV2< + Vec<u8>, <T as Config>::RuntimeCall, <T as frame_system::Config>::BlockNumber, <T as Config>::PalletsOrigin, @@ -121,57 +128,54 @@ pub type ScheduledV2Of<T> = ScheduledV3< >; pub type ScheduledV3Of<T> = ScheduledV3< + Vec<u8>, CallOrHashOf<T>, <T as frame_system::Config>::BlockNumber, <T as Config>::PalletsOrigin, <T as frame_system::Config>::AccountId, >; -pub type ScheduledOf<T> = ScheduledV3Of<T>; - -/// The current version of Scheduled struct. -pub type Scheduled<Call, BlockNumber, PalletsOrigin, AccountId> = - ScheduledV2<Call, BlockNumber, PalletsOrigin, AccountId>; +pub type ScheduledOf<T> = Scheduled< + TaskName, + Bounded<<T as Config>::RuntimeCall>, + <T as frame_system::Config>::BlockNumber, + <T as Config>::PalletsOrigin, + <T as frame_system::Config>::AccountId, +>; -#[cfg(feature = "runtime-benchmarks")] -mod preimage_provider { - use frame_support::traits::PreimageRecipient; - pub trait PreimageProviderAndMaybeRecipient<H>: PreimageRecipient<H> {} - impl<H, T: PreimageRecipient<H>> PreimageProviderAndMaybeRecipient<H> for T {} +struct WeightCounter { + used: Weight, + limit: Weight, } - -#[cfg(not(feature = "runtime-benchmarks"))] -mod preimage_provider { - use frame_support::traits::PreimageProvider; - pub trait PreimageProviderAndMaybeRecipient<H>: PreimageProvider<H> {} - impl<H, T: PreimageProvider<H>> PreimageProviderAndMaybeRecipient<H> for T {} +impl WeightCounter { + fn check_accrue(&mut self, w: Weight) -> bool { + let test = self.used.saturating_add(w); + if test.any_gt(self.limit) { + false + } else { + self.used = test; + true + } + } + fn can_accrue(&mut self, w: Weight) -> bool { + self.used.saturating_add(w).all_lte(self.limit) + } } -pub use preimage_provider::PreimageProviderAndMaybeRecipient; - pub(crate) trait MarginalWeightInfo: WeightInfo { - fn item(periodic: bool, named: bool, resolved: Option<bool>) -> Weight { - match (periodic, named, resolved) { - (_, false, None) => Self::on_initialize_aborted(2) - Self::on_initialize_aborted(1), - (_, true, None) => - Self::on_initialize_named_aborted(2) - Self::on_initialize_named_aborted(1), - (false, false, Some(false)) => Self::on_initialize(2) - Self::on_initialize(1), - (false, true, Some(false)) => - Self::on_initialize_named(2) - Self::on_initialize_named(1), - (true, false, Some(false)) => - Self::on_initialize_periodic(2) - Self::on_initialize_periodic(1), - (true, true, Some(false)) => - Self::on_initialize_periodic_named(2) - Self::on_initialize_periodic_named(1), - (false, false, Some(true)) => - Self::on_initialize_resolved(2) - Self::on_initialize_resolved(1), - (false, true, Some(true)) => - Self::on_initialize_named_resolved(2) - Self::on_initialize_named_resolved(1), - (true, false, Some(true)) => - Self::on_initialize_periodic_resolved(2) - Self::on_initialize_periodic_resolved(1), - (true, true, Some(true)) => - Self::on_initialize_periodic_named_resolved(2) - - Self::on_initialize_periodic_named_resolved(1), + fn service_task(maybe_lookup_len: Option<usize>, named: bool, periodic: bool) -> Weight { + let base = Self::service_task_base(); + let mut total = match maybe_lookup_len { + None => base, + Some(l) => Self::service_task_fetched(l as u32), + }; + if named { + total.saturating_accrue(Self::service_task_named().saturating_sub(base)); } + if periodic { + total.saturating_accrue(Self::service_task_periodic().saturating_sub(base)); + } + total } } impl<T: WeightInfo> MarginalWeightInfo for T {} @@ -179,11 +183,7 @@ impl<T: WeightInfo> MarginalWeightInfo for T {} #[frame_support::pallet] pub mod pallet { use super::*; - use frame_support::{ - dispatch::PostDispatchInfo, - pallet_prelude::*, - traits::{schedule::LookupError, PreimageProvider}, - }; + use frame_support::{dispatch::PostDispatchInfo, pallet_prelude::*}; use frame_system::pallet_prelude::*; /// The current storage version. @@ -192,7 +192,6 @@ pub mod pallet { #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] #[pallet::storage_version(STORAGE_VERSION)] - #[pallet::without_storage_info] pub struct Pallet<T>(_); /// `system::Config` should always be included in our implied traits. @@ -207,7 +206,9 @@ pub mod pallet { + IsType<<Self as system::Config>::RuntimeOrigin>; /// The caller origin, overarching type of all pallets origins. - type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + Codec + Clone + Eq + TypeInfo; + type PalletsOrigin: From<system::RawOrigin<Self::AccountId>> + + CallerTrait<Self::AccountId> + + MaxEncodedLen; /// The aggregated call type. type RuntimeCall: Parameter @@ -217,8 +218,7 @@ pub mod pallet { > + GetDispatchInfo + From<system::Call<Self>>; - /// The maximum weight that may be scheduled per block for any dispatchables of less - /// priority than `schedule::HARD_DEADLINE`. + /// The maximum weight that may be scheduled per block for any dispatchables. #[pallet::constant] type MaximumWeight: Get<Weight>; @@ -235,7 +235,6 @@ pub mod pallet { type OriginPrivilegeCmp: PrivilegeCmp<Self::PalletsOrigin>; /// The maximum number of scheduled calls in the queue for a single block. - /// Not strictly enforced, but used for weight estimation. #[pallet::constant] type MaxScheduledPerBlock: Get<u32>; @@ -243,21 +242,29 @@ pub mod pallet { type WeightInfo: WeightInfo; /// The preimage provider with which we look up call hashes to get the call. - type PreimageProvider: PreimageProviderAndMaybeRecipient<Self::Hash>; - - /// If `Some` then the number of blocks to postpone execution for when the item is delayed. - type NoPreimagePostponement: Get<Option<Self::BlockNumber>>; + type Preimages: QueryPreimage + StorePreimage; } - /// Items to be executed, indexed by the block number that they should be executed on. #[pallet::storage] - pub type Agenda<T: Config> = - StorageMap<_, Twox64Concat, T::BlockNumber, Vec<Option<ScheduledV3Of<T>>>, ValueQuery>; + pub type IncompleteSince<T: Config> = StorageValue<_, T::BlockNumber>; - /// Lookup from identity to the block number and index of the task. + /// Items to be executed, indexed by the block number that they should be executed on. + #[pallet::storage] + pub type Agenda<T: Config> = StorageMap< + _, + Twox64Concat, + T::BlockNumber, + BoundedVec<Option<ScheduledOf<T>>, T::MaxScheduledPerBlock>, + ValueQuery, + >; + + /// Lookup from a name to the block number and index of the task. + /// + /// For v3 -> v4 the previously unbounded identities are Blake2-256 hashed to form the v4 + /// identities. #[pallet::storage] pub(crate) type Lookup<T: Config> = - StorageMap<_, Twox64Concat, Vec<u8>, TaskAddress<T::BlockNumber>>; + StorageMap<_, Twox64Concat, TaskName, TaskAddress<T::BlockNumber>>; /// Events type. #[pallet::event] @@ -270,15 +277,15 @@ pub mod pallet { /// Dispatched some task. Dispatched { task: TaskAddress<T::BlockNumber>, - id: Option<Vec<u8>>, + id: Option<[u8; 32]>, result: DispatchResult, }, /// The call for the provided hash was not found so the task has been aborted. - CallLookupFailed { - task: TaskAddress<T::BlockNumber>, - id: Option<Vec<u8>>, - error: LookupError, - }, + CallUnavailable { task: TaskAddress<T::BlockNumber>, id: Option<[u8; 32]> }, + /// The given task was unable to be renewed since the agenda is full at that block. + PeriodicFailed { task: TaskAddress<T::BlockNumber>, id: Option<[u8; 32]> }, + /// The given task can never be executed since it is overweight. + PermanentlyOverweight { task: TaskAddress<T::BlockNumber>, id: Option<[u8; 32]> }, } #[pallet::error] @@ -291,134 +298,18 @@ pub mod pallet { TargetBlockNumberInPast, /// Reschedule failed because it does not change scheduled time. RescheduleNoChange, + /// Attempt to use a non-named function on a named task. + Named, } #[pallet::hooks] impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> { /// Execute the scheduled calls fn on_initialize(now: T::BlockNumber) -> Weight { - let limit = T::MaximumWeight::get(); - - let mut queued = Agenda::<T>::take(now) - .into_iter() - .enumerate() - .filter_map(|(index, s)| Some((index as u32, s?))) - .collect::<Vec<_>>(); - - if queued.len() as u32 > T::MaxScheduledPerBlock::get() { - log::warn!( - target: "runtime::scheduler", - "Warning: This block has more items queued in Scheduler than \ - expected from the runtime configuration. An update might be needed." - ); - } - - queued.sort_by_key(|(_, s)| s.priority); - - let next = now + One::one(); - - let mut total_weight: Weight = T::WeightInfo::on_initialize(0); - for (order, (index, mut s)) in queued.into_iter().enumerate() { - let named = if let Some(ref id) = s.maybe_id { - Lookup::<T>::remove(id); - true - } else { - false - }; - - let (call, maybe_completed) = s.call.resolved::<T::PreimageProvider>(); - s.call = call; - - let resolved = if let Some(completed) = maybe_completed { - T::PreimageProvider::unrequest_preimage(&completed); - true - } else { - false - }; - - let call = match s.call.as_value().cloned() { - Some(c) => c, - None => { - // Preimage not available - postpone until some block. - total_weight.saturating_accrue(T::WeightInfo::item(false, named, None)); - if let Some(delay) = T::NoPreimagePostponement::get() { - let until = now.saturating_add(delay); - if let Some(ref id) = s.maybe_id { - let index = Agenda::<T>::decode_len(until).unwrap_or(0); - Lookup::<T>::insert(id, (until, index as u32)); - } - Agenda::<T>::append(until, Some(s)); - } - continue - }, - }; - - let periodic = s.maybe_periodic.is_some(); - let call_weight = call.get_dispatch_info().weight; - let mut item_weight = T::WeightInfo::item(periodic, named, Some(resolved)); - let origin = <<T as Config>::RuntimeOrigin as From<T::PalletsOrigin>>::from( - s.origin.clone(), - ) - .into(); - if ensure_signed(origin).is_ok() { - // Weights of Signed dispatches expect their signing account to be whitelisted. - item_weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); - } - - // We allow a scheduled call if any is true: - // - It's priority is `HARD_DEADLINE` - // - It does not push the weight past the limit. - // - It is the first item in the schedule - let hard_deadline = s.priority <= schedule::HARD_DEADLINE; - let test_weight = - total_weight.saturating_add(call_weight).saturating_add(item_weight); - if !hard_deadline && order > 0 && test_weight.any_gt(limit) { - // Cannot be scheduled this block - postpone until next. - total_weight.saturating_accrue(T::WeightInfo::item(false, named, None)); - if let Some(ref id) = s.maybe_id { - // NOTE: We could reasonably not do this (in which case there would be one - // block where the named and delayed item could not be referenced by name), - // but we will do it anyway since it should be mostly free in terms of - // weight and it is slightly cleaner. - let index = Agenda::<T>::decode_len(next).unwrap_or(0); - Lookup::<T>::insert(id, (next, index as u32)); - } - Agenda::<T>::append(next, Some(s)); - continue - } - - let dispatch_origin = s.origin.clone().into(); - let (maybe_actual_call_weight, result) = match call.dispatch(dispatch_origin) { - Ok(post_info) => (post_info.actual_weight, Ok(())), - Err(error_and_info) => - (error_and_info.post_info.actual_weight, Err(error_and_info.error)), - }; - let actual_call_weight = maybe_actual_call_weight.unwrap_or(call_weight); - total_weight.saturating_accrue(item_weight); - total_weight.saturating_accrue(actual_call_weight); - - Self::deposit_event(Event::Dispatched { - task: (now, index), - id: s.maybe_id.clone(), - result, - }); - - if let &Some((period, count)) = &s.maybe_periodic { - if count > 1 { - s.maybe_periodic = Some((period, count - 1)); - } else { - s.maybe_periodic = None; - } - let wake = now + period; - // If scheduled is named, place its information in `Lookup` - if let Some(ref id) = s.maybe_id { - let wake_index = Agenda::<T>::decode_len(wake).unwrap_or(0); - Lookup::<T>::insert(id, (wake, wake_index as u32)); - } - Agenda::<T>::append(wake, Some(s)); - } - } - total_weight + let mut weight_counter = + WeightCounter { used: Weight::zero(), limit: T::MaximumWeight::get() }; + Self::service_agendas(&mut weight_counter, now, u32::max_value()); + weight_counter.used } } @@ -431,7 +322,7 @@ pub mod pallet { when: T::BlockNumber, maybe_periodic: Option<schedule::Period<T::BlockNumber>>, priority: schedule::Priority, - call: Box<CallOrHashOf<T>>, + call: Box<<T as Config>::RuntimeCall>, ) -> DispatchResult { T::ScheduleOrigin::ensure_origin(origin.clone())?; let origin = <T as Config>::RuntimeOrigin::from(origin); @@ -440,7 +331,7 @@ pub mod pallet { maybe_periodic, priority, origin.caller().clone(), - *call, + T::Preimages::bound(*call)?, )?; Ok(()) } @@ -458,11 +349,11 @@ pub mod pallet { #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))] pub fn schedule_named( origin: OriginFor<T>, - id: Vec<u8>, + id: TaskName, when: T::BlockNumber, maybe_periodic: Option<schedule::Period<T::BlockNumber>>, priority: schedule::Priority, - call: Box<CallOrHashOf<T>>, + call: Box<<T as Config>::RuntimeCall>, ) -> DispatchResult { T::ScheduleOrigin::ensure_origin(origin.clone())?; let origin = <T as Config>::RuntimeOrigin::from(origin); @@ -472,14 +363,14 @@ pub mod pallet { maybe_periodic, priority, origin.caller().clone(), - *call, + T::Preimages::bound(*call)?, )?; Ok(()) } /// Cancel a named scheduled task. #[pallet::weight(<T as Config>::WeightInfo::cancel_named(T::MaxScheduledPerBlock::get()))] - pub fn cancel_named(origin: OriginFor<T>, id: Vec<u8>) -> DispatchResult { + pub fn cancel_named(origin: OriginFor<T>, id: TaskName) -> DispatchResult { T::ScheduleOrigin::ensure_origin(origin.clone())?; let origin = <T as Config>::RuntimeOrigin::from(origin); Self::do_cancel_named(Some(origin.caller().clone()), id)?; @@ -497,7 +388,7 @@ pub mod pallet { after: T::BlockNumber, maybe_periodic: Option<schedule::Period<T::BlockNumber>>, priority: schedule::Priority, - call: Box<CallOrHashOf<T>>, + call: Box<<T as Config>::RuntimeCall>, ) -> DispatchResult { T::ScheduleOrigin::ensure_origin(origin.clone())?; let origin = <T as Config>::RuntimeOrigin::from(origin); @@ -506,7 +397,7 @@ pub mod pallet { maybe_periodic, priority, origin.caller().clone(), - *call, + T::Preimages::bound(*call)?, )?; Ok(()) } @@ -519,11 +410,11 @@ pub mod pallet { #[pallet::weight(<T as Config>::WeightInfo::schedule_named(T::MaxScheduledPerBlock::get()))] pub fn schedule_named_after( origin: OriginFor<T>, - id: Vec<u8>, + id: TaskName, after: T::BlockNumber, maybe_periodic: Option<schedule::Period<T::BlockNumber>>, priority: schedule::Priority, - call: Box<CallOrHashOf<T>>, + call: Box<<T as Config>::RuntimeCall>, ) -> DispatchResult { T::ScheduleOrigin::ensure_origin(origin.clone())?; let origin = <T as Config>::RuntimeOrigin::from(origin); @@ -533,41 +424,70 @@ pub mod pallet { maybe_periodic, priority, origin.caller().clone(), - *call, + T::Preimages::bound(*call)?, )?; Ok(()) } } } -impl<T: Config> Pallet<T> { - /// Migrate storage format from V1 to V3. +impl<T: Config<Hash = PreimageHash>> Pallet<T> { + /// Migrate storage format from V1 to V4. /// /// Returns the weight consumed by this migration. - pub fn migrate_v1_to_v3() -> Weight { + pub fn migrate_v1_to_v4() -> Weight { + use migration::v1 as old; let mut weight = T::DbWeight::get().reads_writes(1, 1); + // Delete all undecodable values. + // `StorageMap::translate` is not enough since it just skips them and leaves the keys in. + let keys = old::Agenda::<T>::iter_keys().collect::<Vec<_>>(); + for key in keys { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if let Err(_) = old::Agenda::<T>::try_get(&key) { + weight.saturating_accrue(T::DbWeight::get().writes(1)); + old::Agenda::<T>::remove(&key); + log::warn!("Deleted undecodable agenda"); + } + } + Agenda::<T>::translate::< Vec<Option<ScheduledV1<<T as Config>::RuntimeCall, T::BlockNumber>>>, _, >(|_, agenda| { - Some( + Some(BoundedVec::truncate_from( agenda .into_iter() .map(|schedule| { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); - schedule.map(|schedule| ScheduledV3 { - maybe_id: schedule.maybe_id, - priority: schedule.priority, - call: schedule.call.into(), - maybe_periodic: schedule.maybe_periodic, - origin: system::RawOrigin::Root.into(), - _phantom: Default::default(), + schedule.and_then(|schedule| { + if let Some(id) = schedule.maybe_id.as_ref() { + let name = blake2_256(id); + if let Some(item) = old::Lookup::<T>::take(id) { + Lookup::<T>::insert(name, item); + } + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + } + + let call = T::Preimages::bound(schedule.call).ok()?; + + if call.lookup_needed() { + weight.saturating_accrue(T::DbWeight::get().reads_writes(0, 1)); + } + + Some(Scheduled { + maybe_id: schedule.maybe_id.map(|x| blake2_256(&x[..])), + priority: schedule.priority, + call, + maybe_periodic: schedule.maybe_periodic, + origin: system::RawOrigin::Root.into(), + _phantom: Default::default(), + }) }) }) .collect::<Vec<_>>(), - ) + )) }); #[allow(deprecated)] @@ -577,34 +497,62 @@ impl<T: Config> Pallet<T> { &[], ); - StorageVersion::new(3).put::<Self>(); + StorageVersion::new(4).put::<Self>(); weight + T::DbWeight::get().writes(2) } - /// Migrate storage format from V2 to V3. + /// Migrate storage format from V2 to V4. /// /// Returns the weight consumed by this migration. - pub fn migrate_v2_to_v3() -> Weight { + pub fn migrate_v2_to_v4() -> Weight { + use migration::v2 as old; let mut weight = T::DbWeight::get().reads_writes(1, 1); + // Delete all undecodable values. + // `StorageMap::translate` is not enough since it just skips them and leaves the keys in. + let keys = old::Agenda::<T>::iter_keys().collect::<Vec<_>>(); + for key in keys { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if let Err(_) = old::Agenda::<T>::try_get(&key) { + weight.saturating_accrue(T::DbWeight::get().writes(1)); + old::Agenda::<T>::remove(&key); + log::warn!("Deleted undecodable agenda"); + } + } + Agenda::<T>::translate::<Vec<Option<ScheduledV2Of<T>>>, _>(|_, agenda| { - Some( + Some(BoundedVec::truncate_from( agenda .into_iter() .map(|schedule| { weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); - schedule.map(|schedule| ScheduledV3 { - maybe_id: schedule.maybe_id, - priority: schedule.priority, - call: schedule.call.into(), - maybe_periodic: schedule.maybe_periodic, - origin: schedule.origin, - _phantom: Default::default(), + schedule.and_then(|schedule| { + if let Some(id) = schedule.maybe_id.as_ref() { + let name = blake2_256(id); + if let Some(item) = old::Lookup::<T>::take(id) { + Lookup::<T>::insert(name, item); + } + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + } + + let call = T::Preimages::bound(schedule.call).ok()?; + if call.lookup_needed() { + weight.saturating_accrue(T::DbWeight::get().reads_writes(0, 1)); + } + + Some(Scheduled { + maybe_id: schedule.maybe_id.map(|x| blake2_256(&x[..])), + priority: schedule.priority, + call, + maybe_periodic: schedule.maybe_periodic, + origin: schedule.origin, + _phantom: Default::default(), + }) }) }) .collect::<Vec<_>>(), - ) + )) }); #[allow(deprecated)] @@ -614,34 +562,140 @@ impl<T: Config> Pallet<T> { &[], ); - StorageVersion::new(3).put::<Self>(); + StorageVersion::new(4).put::<Self>(); weight + T::DbWeight::get().writes(2) } - #[cfg(feature = "try-runtime")] - pub fn pre_migrate_to_v3() -> Result<(), &'static str> { - Ok(()) - } + /// Migrate storage format from V3 to V4. + /// + /// Returns the weight consumed by this migration. + #[allow(deprecated)] + pub fn migrate_v3_to_v4() -> Weight { + use migration::v3 as old; + let mut weight = T::DbWeight::get().reads_writes(2, 1); + + // Delete all undecodable values. + // `StorageMap::translate` is not enough since it just skips them and leaves the keys in. + let blocks = old::Agenda::<T>::iter_keys().collect::<Vec<_>>(); + for block in blocks { + weight.saturating_accrue(T::DbWeight::get().reads(1)); + if let Err(_) = old::Agenda::<T>::try_get(&block) { + weight.saturating_accrue(T::DbWeight::get().writes(1)); + old::Agenda::<T>::remove(&block); + log::warn!("Deleted undecodable agenda of block: {:?}", block); + } + } - #[cfg(feature = "try-runtime")] - pub fn post_migrate_to_v3() -> Result<(), &'static str> { - use frame_support::dispatch::GetStorageVersion; + Agenda::<T>::translate::<Vec<Option<ScheduledV3Of<T>>>, _>(|block, agenda| { + log::info!("Migrating agenda of block: {:?}", &block); + Some(BoundedVec::truncate_from( + agenda + .into_iter() + .map(|schedule| { + weight.saturating_accrue(T::DbWeight::get().reads_writes(1, 1)); + schedule + .and_then(|schedule| { + if let Some(id) = schedule.maybe_id.as_ref() { + let name = blake2_256(id); + if let Some(item) = old::Lookup::<T>::take(id) { + Lookup::<T>::insert(name, item); + log::info!("Migrated name for id: {:?}", id); + } else { + log::error!("No name in Lookup for id: {:?}", &id); + } + weight.saturating_accrue(T::DbWeight::get().reads_writes(2, 2)); + } else { + log::info!("Schedule is unnamed"); + } + + let call = match schedule.call { + MaybeHashed::Hash(h) => { + let bounded = Bounded::from_legacy_hash(h); + // Check that the call can be decoded in the new runtime. + if let Err(err) = T::Preimages::peek::< + <T as Config>::RuntimeCall, + >(&bounded) + { + log::error!( + "Dropping undecodable call {}: {:?}", + &h, + &err + ); + return None + } + weight.saturating_accrue(T::DbWeight::get().reads(1)); + log::info!("Migrated call by hash, hash: {:?}", h); + bounded + }, + MaybeHashed::Value(v) => { + let call = T::Preimages::bound(v) + .map_err(|e| { + log::error!("Could not bound Call: {:?}", e) + }) + .ok()?; + if call.lookup_needed() { + weight.saturating_accrue( + T::DbWeight::get().reads_writes(0, 1), + ); + } + log::info!( + "Migrated call by value, hash: {:?}", + call.hash() + ); + call + }, + }; + + Some(Scheduled { + maybe_id: schedule.maybe_id.map(|x| blake2_256(&x[..])), + priority: schedule.priority, + call, + maybe_periodic: schedule.maybe_periodic, + origin: schedule.origin, + _phantom: Default::default(), + }) + }) + .or_else(|| { + log::info!("Schedule in agenda for block {:?} is empty - nothing to do here.", &block); + None + }) + }) + .collect::<Vec<_>>(), + )) + }); - assert!(Self::current_storage_version() == 3); - for k in Agenda::<T>::iter_keys() { - let _ = Agenda::<T>::try_get(k).map_err(|()| "Invalid item in Agenda")?; - } - Ok(()) + #[allow(deprecated)] + frame_support::storage::migration::remove_storage_prefix( + Self::name().as_bytes(), + b"StorageVersion", + &[], + ); + + StorageVersion::new(4).put::<Self>(); + + weight + T::DbWeight::get().writes(2) } +} +impl<T: Config> Pallet<T> { /// Helper to migrate scheduler when the pallet origin type has changed. pub fn migrate_origin<OldOrigin: Into<T::PalletsOrigin> + codec::Decode>() { Agenda::<T>::translate::< - Vec<Option<Scheduled<CallOrHashOf<T>, T::BlockNumber, OldOrigin, T::AccountId>>>, + Vec< + Option< + Scheduled< + TaskName, + Bounded<<T as Config>::RuntimeCall>, + T::BlockNumber, + OldOrigin, + T::AccountId, + >, + >, + >, _, >(|_, agenda| { - Some( + Some(BoundedVec::truncate_from( agenda .into_iter() .map(|schedule| { @@ -655,7 +709,7 @@ impl<T: Config> Pallet<T> { }) }) .collect::<Vec<_>>(), - ) + )) }); } @@ -676,34 +730,64 @@ impl<T: Config> Pallet<T> { Ok(when) } + fn place_task( + when: T::BlockNumber, + what: ScheduledOf<T>, + ) -> Result<TaskAddress<T::BlockNumber>, (DispatchError, ScheduledOf<T>)> { + let maybe_name = what.maybe_id; + let index = Self::push_to_agenda(when, what)?; + let address = (when, index); + if let Some(name) = maybe_name { + Lookup::<T>::insert(name, address) + } + Self::deposit_event(Event::Scheduled { when: address.0, index: address.1 }); + Ok(address) + } + + fn push_to_agenda( + when: T::BlockNumber, + what: ScheduledOf<T>, + ) -> Result<u32, (DispatchError, ScheduledOf<T>)> { + let mut agenda = Agenda::<T>::get(when); + let index = if (agenda.len() as u32) < T::MaxScheduledPerBlock::get() { + // will always succeed due to the above check. + let _ = agenda.try_push(Some(what)); + agenda.len() as u32 - 1 + } else { + if let Some(hole_index) = agenda.iter().position(|i| i.is_none()) { + agenda[hole_index] = Some(what); + hole_index as u32 + } else { + return Err((DispatchError::Exhausted, what)) + } + }; + Agenda::<T>::insert(when, agenda); + Ok(index) + } + fn do_schedule( when: DispatchTime<T::BlockNumber>, maybe_periodic: Option<schedule::Period<T::BlockNumber>>, priority: schedule::Priority, origin: T::PalletsOrigin, - call: CallOrHashOf<T>, + call: Bounded<<T as Config>::RuntimeCall>, ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> { let when = Self::resolve_time(when)?; - call.ensure_requested::<T::PreimageProvider>(); // sanitize maybe_periodic let maybe_periodic = maybe_periodic .filter(|p| p.1 > 1 && !p.0.is_zero()) // Remove one from the number of repetitions since we will schedule one now. .map(|(p, c)| (p, c - 1)); - let s = Some(Scheduled { + let task = Scheduled { maybe_id: None, priority, call, maybe_periodic, origin, - _phantom: PhantomData::<T::AccountId>::default(), - }); - Agenda::<T>::append(when, s); - let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1; - Self::deposit_event(Event::Scheduled { when, index }); - - Ok((when, index)) + _phantom: PhantomData, + }; + Self::place_task(when, task).map_err(|x| x.0) } fn do_cancel( @@ -713,7 +797,7 @@ impl<T: Config> Pallet<T> { let scheduled = Agenda::<T>::try_mutate(when, |agenda| { agenda.get_mut(index as usize).map_or( Ok(None), - |s| -> Result<Option<Scheduled<_, _, _, _>>, DispatchError> { + |s| -> Result<Option<Scheduled<_, _, _, _, _>>, DispatchError> { if let (Some(ref o), Some(ref s)) = (origin, s.borrow()) { if matches!( T::OriginPrivilegeCmp::cmp_privilege(o, &s.origin), @@ -727,7 +811,7 @@ impl<T: Config> Pallet<T> { ) })?; if let Some(s) = scheduled { - s.call.ensure_unrequested::<T::PreimageProvider>(); + T::Preimages::drop(&s.call); if let Some(id) = s.maybe_id { Lookup::<T>::remove(id); } @@ -748,27 +832,23 @@ impl<T: Config> Pallet<T> { return Err(Error::<T>::RescheduleNoChange.into()) } - Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult { + let task = Agenda::<T>::try_mutate(when, |agenda| { let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?; - let task = task.take().ok_or(Error::<T>::NotFound)?; - Agenda::<T>::append(new_time, Some(task)); - Ok(()) + ensure!(!matches!(task, Some(Scheduled { maybe_id: Some(_), .. })), Error::<T>::Named); + task.take().ok_or(Error::<T>::NotFound) })?; - - let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1; Self::deposit_event(Event::Canceled { when, index }); - Self::deposit_event(Event::Scheduled { when: new_time, index: new_index }); - Ok((new_time, new_index)) + Self::place_task(new_time, task).map_err(|x| x.0) } fn do_schedule_named( - id: Vec<u8>, + id: TaskName, when: DispatchTime<T::BlockNumber>, maybe_periodic: Option<schedule::Period<T::BlockNumber>>, priority: schedule::Priority, origin: T::PalletsOrigin, - call: CallOrHashOf<T>, + call: Bounded<<T as Config>::RuntimeCall>, ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> { // ensure id it is unique if Lookup::<T>::contains_key(&id) { @@ -777,32 +857,24 @@ impl<T: Config> Pallet<T> { let when = Self::resolve_time(when)?; - call.ensure_requested::<T::PreimageProvider>(); - // sanitize maybe_periodic let maybe_periodic = maybe_periodic .filter(|p| p.1 > 1 && !p.0.is_zero()) // Remove one from the number of repetitions since we will schedule one now. .map(|(p, c)| (p, c - 1)); - let s = Scheduled { - maybe_id: Some(id.clone()), + let task = Scheduled { + maybe_id: Some(id), priority, call, maybe_periodic, origin, _phantom: Default::default(), }; - Agenda::<T>::append(when, Some(s)); - let index = Agenda::<T>::decode_len(when).unwrap_or(1) as u32 - 1; - let address = (when, index); - Lookup::<T>::insert(&id, &address); - Self::deposit_event(Event::Scheduled { when, index }); - - Ok(address) + Self::place_task(when, task).map_err(|x| x.0) } - fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: Vec<u8>) -> DispatchResult { + fn do_cancel_named(origin: Option<T::PalletsOrigin>, id: TaskName) -> DispatchResult { Lookup::<T>::try_mutate_exists(id, |lookup| -> DispatchResult { if let Some((when, index)) = lookup.take() { let i = index as usize; @@ -815,7 +887,7 @@ impl<T: Config> Pallet<T> { ) { return Err(BadOrigin.into()) } - s.call.ensure_unrequested::<T::PreimageProvider>(); + T::Preimages::drop(&s.call); } *s = None; } @@ -830,42 +902,245 @@ impl<T: Config> Pallet<T> { } fn do_reschedule_named( - id: Vec<u8>, + id: TaskName, new_time: DispatchTime<T::BlockNumber>, ) -> Result<TaskAddress<T::BlockNumber>, DispatchError> { let new_time = Self::resolve_time(new_time)?; - Lookup::<T>::try_mutate_exists( - id, - |lookup| -> Result<TaskAddress<T::BlockNumber>, DispatchError> { - let (when, index) = lookup.ok_or(Error::<T>::NotFound)?; + let lookup = Lookup::<T>::get(id); + let (when, index) = lookup.ok_or(Error::<T>::NotFound)?; - if new_time == when { - return Err(Error::<T>::RescheduleNoChange.into()) - } + if new_time == when { + return Err(Error::<T>::RescheduleNoChange.into()) + } - Agenda::<T>::try_mutate(when, |agenda| -> DispatchResult { - let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?; - let task = task.take().ok_or(Error::<T>::NotFound)?; - Agenda::<T>::append(new_time, Some(task)); + let task = Agenda::<T>::try_mutate(when, |agenda| { + let task = agenda.get_mut(index as usize).ok_or(Error::<T>::NotFound)?; + task.take().ok_or(Error::<T>::NotFound) + })?; + Self::deposit_event(Event::Canceled { when, index }); + Self::place_task(new_time, task).map_err(|x| x.0) + } +} - Ok(()) - })?; +enum ServiceTaskError { + /// Could not be executed due to missing preimage. + Unavailable, + /// Could not be executed due to weight limitations. + Overweight, +} +use ServiceTaskError::*; - let new_index = Agenda::<T>::decode_len(new_time).unwrap_or(1) as u32 - 1; - Self::deposit_event(Event::Canceled { when, index }); - Self::deposit_event(Event::Scheduled { when: new_time, index: new_index }); +impl<T: Config> Pallet<T> { + /// Service up to `max` agendas queue starting from earliest incompletely executed agenda. + fn service_agendas(weight: &mut WeightCounter, now: T::BlockNumber, max: u32) { + if !weight.check_accrue(T::WeightInfo::service_agendas_base()) { + return + } + + let mut incomplete_since = now + One::one(); + let mut when = IncompleteSince::<T>::take().unwrap_or(now); + let mut executed = 0; + + let max_items = T::MaxScheduledPerBlock::get(); + let mut count_down = max; + let service_agenda_base_weight = T::WeightInfo::service_agenda_base(max_items); + while count_down > 0 && when <= now && weight.can_accrue(service_agenda_base_weight) { + if !Self::service_agenda(weight, &mut executed, now, when, u32::max_value()) { + incomplete_since = incomplete_since.min(when); + } + when.saturating_inc(); + count_down.saturating_dec(); + } + incomplete_since = incomplete_since.min(when); + if incomplete_since <= now { + IncompleteSince::<T>::put(incomplete_since); + } + } + + /// Returns `true` if the agenda was fully completed, `false` if it should be revisited at a + /// later block. + fn service_agenda( + weight: &mut WeightCounter, + executed: &mut u32, + now: T::BlockNumber, + when: T::BlockNumber, + max: u32, + ) -> bool { + let mut agenda = Agenda::<T>::get(when); + let mut ordered = agenda + .iter() + .enumerate() + .filter_map(|(index, maybe_item)| { + maybe_item.as_ref().map(|item| (index as u32, item.priority)) + }) + .collect::<Vec<_>>(); + ordered.sort_by_key(|k| k.1); + let within_limit = + weight.check_accrue(T::WeightInfo::service_agenda_base(ordered.len() as u32)); + debug_assert!(within_limit, "weight limit should have been checked in advance"); + + // Items which we know can be executed and have postponed for execution in a later block. + let mut postponed = (ordered.len() as u32).saturating_sub(max); + // Items which we don't know can ever be executed. + let mut dropped = 0; + + for (agenda_index, _) in ordered.into_iter().take(max as usize) { + let task = match agenda[agenda_index as usize].take() { + None => continue, + Some(t) => t, + }; + let base_weight = T::WeightInfo::service_task( + task.call.lookup_len().map(|x| x as usize), + task.maybe_id.is_some(), + task.maybe_periodic.is_some(), + ); + if !weight.can_accrue(base_weight) { + postponed += 1; + break + } + let result = Self::service_task(weight, now, when, agenda_index, *executed == 0, task); + agenda[agenda_index as usize] = match result { + Err((Unavailable, slot)) => { + dropped += 1; + slot + }, + Err((Overweight, slot)) => { + postponed += 1; + slot + }, + Ok(()) => { + *executed += 1; + None + }, + }; + } + if postponed > 0 || dropped > 0 { + Agenda::<T>::insert(when, agenda); + } else { + Agenda::<T>::remove(when); + } + postponed == 0 + } - *lookup = Some((new_time, new_index)); + /// Service (i.e. execute) the given task, being careful not to overflow the `weight` counter. + /// + /// This involves: + /// - removing and potentially replacing the `Lookup` entry for the task. + /// - realizing the task's call which can include a preimage lookup. + /// - Rescheduling the task for execution in a later agenda if periodic. + fn service_task( + weight: &mut WeightCounter, + now: T::BlockNumber, + when: T::BlockNumber, + agenda_index: u32, + is_first: bool, + mut task: ScheduledOf<T>, + ) -> Result<(), (ServiceTaskError, Option<ScheduledOf<T>>)> { + if let Some(ref id) = task.maybe_id { + Lookup::<T>::remove(id); + } - Ok((new_time, new_index)) + let (call, lookup_len) = match T::Preimages::peek(&task.call) { + Ok(c) => c, + Err(_) => return Err((Unavailable, Some(task))), + }; + + weight.check_accrue(T::WeightInfo::service_task( + lookup_len.map(|x| x as usize), + task.maybe_id.is_some(), + task.maybe_periodic.is_some(), + )); + + match Self::execute_dispatch(weight, task.origin.clone(), call) { + Err(Unavailable) => { + debug_assert!(false, "Checked to exist with `peek`"); + Self::deposit_event(Event::CallUnavailable { + task: (when, agenda_index), + id: task.maybe_id, + }); + Err((Unavailable, Some(task))) + }, + Err(Overweight) if is_first => { + T::Preimages::drop(&task.call); + Self::deposit_event(Event::PermanentlyOverweight { + task: (when, agenda_index), + id: task.maybe_id, + }); + Err((Unavailable, Some(task))) + }, + Err(Overweight) => Err((Overweight, Some(task))), + Ok(result) => { + Self::deposit_event(Event::Dispatched { + task: (when, agenda_index), + id: task.maybe_id, + result, + }); + if let &Some((period, count)) = &task.maybe_periodic { + if count > 1 { + task.maybe_periodic = Some((period, count - 1)); + } else { + task.maybe_periodic = None; + } + let wake = now.saturating_add(period); + match Self::place_task(wake, task) { + Ok(_) => {}, + Err((_, task)) => { + // TODO: Leave task in storage somewhere for it to be rescheduled + // manually. + T::Preimages::drop(&task.call); + Self::deposit_event(Event::PeriodicFailed { + task: (when, agenda_index), + id: task.maybe_id, + }); + }, + } + } else { + T::Preimages::drop(&task.call); + } + Ok(()) }, - ) + } + } + + /// Make a dispatch to the given `call` from the given `origin`, ensuring that the `weight` + /// counter does not exceed its limit and that it is counted accurately (e.g. accounted using + /// post info if available). + /// + /// NOTE: Only the weight for this function will be counted (origin lookup, dispatch and the + /// call itself). + fn execute_dispatch( + weight: &mut WeightCounter, + origin: T::PalletsOrigin, + call: <T as Config>::RuntimeCall, + ) -> Result<DispatchResult, ServiceTaskError> { + let base_weight = match origin.as_system_ref() { + Some(&RawOrigin::Signed(_)) => T::WeightInfo::execute_dispatch_signed(), + _ => T::WeightInfo::execute_dispatch_unsigned(), + }; + let call_weight = call.get_dispatch_info().weight; + // We only allow a scheduled call if it cannot push the weight past the limit. + let max_weight = base_weight.saturating_add(call_weight); + + if !weight.can_accrue(max_weight) { + return Err(Overweight) + } + + let dispatch_origin = origin.into(); + let (maybe_actual_call_weight, result) = match call.dispatch(dispatch_origin) { + Ok(post_info) => (post_info.actual_weight, Ok(())), + Err(error_and_info) => + (error_and_info.post_info.actual_weight, Err(error_and_info.error)), + }; + let call_weight = maybe_actual_call_weight.unwrap_or(call_weight); + weight.check_accrue(base_weight); + weight.check_accrue(call_weight); + Ok(result) } } -impl<T: Config> schedule::v2::Anon<T::BlockNumber, <T as Config>::RuntimeCall, T::PalletsOrigin> - for Pallet<T> +impl<T: Config<Hash = PreimageHash>> + schedule::v2::Anon<T::BlockNumber, <T as Config>::RuntimeCall, T::PalletsOrigin> for Pallet<T> { type Address = TaskAddress<T::BlockNumber>; type Hash = T::Hash; @@ -877,6 +1152,8 @@ impl<T: Config> schedule::v2::Anon<T::BlockNumber, <T as Config>::RuntimeCall, T origin: T::PalletsOrigin, call: CallOrHashOf<T>, ) -> Result<Self::Address, DispatchError> { + let call = call.as_value().ok_or(DispatchError::CannotLookup)?; + let call = T::Preimages::bound(call)?.transmute(); Self::do_schedule(when, maybe_periodic, priority, origin, call) } @@ -896,8 +1173,8 @@ impl<T: Config> schedule::v2::Anon<T::BlockNumber, <T as Config>::RuntimeCall, T } } -impl<T: Config> schedule::v2::Named<T::BlockNumber, <T as Config>::RuntimeCall, T::PalletsOrigin> - for Pallet<T> +impl<T: Config<Hash = PreimageHash>> + schedule::v2::Named<T::BlockNumber, <T as Config>::RuntimeCall, T::PalletsOrigin> for Pallet<T> { type Address = TaskAddress<T::BlockNumber>; type Hash = T::Hash; @@ -910,23 +1187,108 @@ impl<T: Config> schedule::v2::Named<T::BlockNumber, <T as Config>::RuntimeCall, origin: T::PalletsOrigin, call: CallOrHashOf<T>, ) -> Result<Self::Address, ()> { - Self::do_schedule_named(id, when, maybe_periodic, priority, origin, call).map_err(|_| ()) + let call = call.as_value().ok_or(())?; + let call = T::Preimages::bound(call).map_err(|_| ())?.transmute(); + let name = blake2_256(&id[..]); + Self::do_schedule_named(name, when, maybe_periodic, priority, origin, call).map_err(|_| ()) } fn cancel_named(id: Vec<u8>) -> Result<(), ()> { - Self::do_cancel_named(None, id).map_err(|_| ()) + let name = blake2_256(&id[..]); + Self::do_cancel_named(None, name).map_err(|_| ()) } fn reschedule_named( id: Vec<u8>, when: DispatchTime<T::BlockNumber>, ) -> Result<Self::Address, DispatchError> { - Self::do_reschedule_named(id, when) + let name = blake2_256(&id[..]); + Self::do_reschedule_named(name, when) } fn next_dispatch_time(id: Vec<u8>) -> Result<T::BlockNumber, ()> { - Lookup::<T>::get(id) + let name = blake2_256(&id[..]); + Lookup::<T>::get(name) .and_then(|(when, index)| Agenda::<T>::get(when).get(index as usize).map(|_| when)) .ok_or(()) } } + +impl<T: Config> schedule::v3::Anon<T::BlockNumber, <T as Config>::RuntimeCall, T::PalletsOrigin> + for Pallet<T> +{ + type Address = TaskAddress<T::BlockNumber>; + + fn schedule( + when: DispatchTime<T::BlockNumber>, + maybe_periodic: Option<schedule::Period<T::BlockNumber>>, + priority: schedule::Priority, + origin: T::PalletsOrigin, + call: Bounded<<T as Config>::RuntimeCall>, + ) -> Result<Self::Address, DispatchError> { + Self::do_schedule(when, maybe_periodic, priority, origin, call) + } + + fn cancel((when, index): Self::Address) -> Result<(), DispatchError> { + Self::do_cancel(None, (when, index)).map_err(map_err_to_v3_err::<T>) + } + + fn reschedule( + address: Self::Address, + when: DispatchTime<T::BlockNumber>, + ) -> Result<Self::Address, DispatchError> { + Self::do_reschedule(address, when).map_err(map_err_to_v3_err::<T>) + } + + fn next_dispatch_time((when, index): Self::Address) -> Result<T::BlockNumber, DispatchError> { + Agenda::<T>::get(when) + .get(index as usize) + .ok_or(DispatchError::Unavailable) + .map(|_| when) + } +} + +use schedule::v3::TaskName; + +impl<T: Config> schedule::v3::Named<T::BlockNumber, <T as Config>::RuntimeCall, T::PalletsOrigin> + for Pallet<T> +{ + type Address = TaskAddress<T::BlockNumber>; + + fn schedule_named( + id: TaskName, + when: DispatchTime<T::BlockNumber>, + maybe_periodic: Option<schedule::Period<T::BlockNumber>>, + priority: schedule::Priority, + origin: T::PalletsOrigin, + call: Bounded<<T as Config>::RuntimeCall>, + ) -> Result<Self::Address, DispatchError> { + Self::do_schedule_named(id, when, maybe_periodic, priority, origin, call) + } + + fn cancel_named(id: TaskName) -> Result<(), DispatchError> { + Self::do_cancel_named(None, id).map_err(map_err_to_v3_err::<T>) + } + + fn reschedule_named( + id: TaskName, + when: DispatchTime<T::BlockNumber>, + ) -> Result<Self::Address, DispatchError> { + Self::do_reschedule_named(id, when).map_err(map_err_to_v3_err::<T>) + } + + fn next_dispatch_time(id: TaskName) -> Result<T::BlockNumber, DispatchError> { + Lookup::<T>::get(id) + .and_then(|(when, index)| Agenda::<T>::get(when).get(index as usize).map(|_| when)) + .ok_or(DispatchError::Unavailable) + } +} + +/// Maps a pallet error to an `schedule::v3` error. +fn map_err_to_v3_err<T: Config>(err: DispatchError) -> DispatchError { + if err == DispatchError::from(Error::<T>::NotFound) { + DispatchError::Unavailable + } else { + err + } +} diff --git a/substrate/frame/scheduler/src/migration.rs b/substrate/frame/scheduler/src/migration.rs new file mode 100644 index 0000000000000000000000000000000000000000..6769d20023196d1d8eb9b2d001bb43843b0ca9f7 --- /dev/null +++ b/substrate/frame/scheduler/src/migration.rs @@ -0,0 +1,402 @@ +// This file is part of Substrate. + +// Copyright (C) 2017-2022 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. + +//! Migrations for the scheduler pallet. + +use super::*; +use frame_support::traits::OnRuntimeUpgrade; + +/// The log target. +const TARGET: &'static str = "runtime::scheduler::migration"; + +pub mod v1 { + use super::*; + use frame_support::pallet_prelude::*; + + #[frame_support::storage_alias] + pub(crate) type Agenda<T: Config> = StorageMap< + Pallet<T>, + Twox64Concat, + <T as frame_system::Config>::BlockNumber, + Vec< + Option< + ScheduledV1<<T as Config>::RuntimeCall, <T as frame_system::Config>::BlockNumber>, + >, + >, + ValueQuery, + >; + + #[frame_support::storage_alias] + pub(crate) type Lookup<T: Config> = StorageMap< + Pallet<T>, + Twox64Concat, + Vec<u8>, + TaskAddress<<T as frame_system::Config>::BlockNumber>, + >; +} + +pub mod v2 { + use super::*; + use frame_support::pallet_prelude::*; + + #[frame_support::storage_alias] + pub(crate) type Agenda<T: Config> = StorageMap< + Pallet<T>, + Twox64Concat, + <T as frame_system::Config>::BlockNumber, + Vec<Option<ScheduledV2Of<T>>>, + ValueQuery, + >; + + #[frame_support::storage_alias] + pub(crate) type Lookup<T: Config> = StorageMap< + Pallet<T>, + Twox64Concat, + Vec<u8>, + TaskAddress<<T as frame_system::Config>::BlockNumber>, + >; +} + +pub mod v3 { + use super::*; + use frame_support::pallet_prelude::*; + + #[frame_support::storage_alias] + pub(crate) type Agenda<T: Config> = StorageMap< + Pallet<T>, + Twox64Concat, + <T as frame_system::Config>::BlockNumber, + Vec<Option<ScheduledV3Of<T>>>, + ValueQuery, + >; + + #[frame_support::storage_alias] + pub(crate) type Lookup<T: Config> = StorageMap< + Pallet<T>, + Twox64Concat, + Vec<u8>, + TaskAddress<<T as frame_system::Config>::BlockNumber>, + >; + + /// Migrate the scheduler pallet from V3 to V4. + pub struct MigrateToV4<T>(sp_std::marker::PhantomData<T>); + + impl<T: Config<Hash = PreimageHash>> OnRuntimeUpgrade for MigrateToV4<T> { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result<Vec<u8>, &'static str> { + assert_eq!(StorageVersion::get::<Pallet<T>>(), 3, "Can only upgrade from version 3"); + + let agendas = Agenda::<T>::iter_keys().count() as u32; + let decodable_agendas = Agenda::<T>::iter_values().count() as u32; + if agendas != decodable_agendas { + // This is not necessarily an error, but can happen when there are Calls + // in an Agenda that are not valid anymore with the new runtime. + log::error!( + target: TARGET, + "Can only decode {} of {} agendas - others will be dropped", + decodable_agendas, + agendas + ); + } + log::info!(target: TARGET, "Trying to migrate {} agendas...", decodable_agendas); + + // Check that no agenda overflows `MaxScheduledPerBlock`. + let max_scheduled_per_block = T::MaxScheduledPerBlock::get() as usize; + for (block_number, agenda) in Agenda::<T>::iter() { + if agenda.iter().cloned().filter_map(|s| s).count() > max_scheduled_per_block { + log::error!( + target: TARGET, + "Would truncate agenda of block {:?} from {} items to {} items.", + block_number, + agenda.len(), + max_scheduled_per_block, + ); + return Err("Agenda would overflow `MaxScheduledPerBlock`.") + } + } + // Check that bounding the calls will not overflow `MAX_LENGTH`. + let max_length = T::Preimages::MAX_LENGTH as usize; + for (block_number, agenda) in Agenda::<T>::iter() { + for schedule in agenda.iter().cloned().filter_map(|s| s) { + match schedule.call { + frame_support::traits::schedule::MaybeHashed::Value(call) => { + let l = call.using_encoded(|c| c.len()); + if l > max_length { + log::error!( + target: TARGET, + "Call in agenda of block {:?} is too large: {} byte", + block_number, + l, + ); + return Err("Call is too large.") + } + }, + _ => (), + } + } + } + + Ok((decodable_agendas as u32).encode()) + } + + fn on_runtime_upgrade() -> Weight { + let version = StorageVersion::get::<Pallet<T>>(); + if version != 3 { + log::warn!( + target: TARGET, + "skipping v3 to v4 migration: executed on wrong storage version.\ + Expected version 3, found {:?}", + version, + ); + return T::DbWeight::get().reads(1) + } + + crate::Pallet::<T>::migrate_v3_to_v4() + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(state: Vec<u8>) -> Result<(), &'static str> { + assert_eq!(StorageVersion::get::<Pallet<T>>(), 4, "Must upgrade"); + + // Check that everything decoded fine. + for k in crate::Agenda::<T>::iter_keys() { + assert!(crate::Agenda::<T>::try_get(k).is_ok(), "Cannot decode V4 Agenda"); + } + + let old_agendas: u32 = + Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed"); + let new_agendas = crate::Agenda::<T>::iter_keys().count() as u32; + if old_agendas != new_agendas { + // This is not necessarily an error, but can happen when there are Calls + // in an Agenda that are not valid anymore in the new runtime. + log::error!( + target: TARGET, + "Did not migrate all Agendas. Previous {}, Now {}", + old_agendas, + new_agendas, + ); + } else { + log::info!(target: TARGET, "Migrated {} agendas.", new_agendas); + } + + Ok(()) + } + } +} + +#[cfg(test)] +#[cfg(feature = "try-runtime")] +mod test { + use super::*; + use crate::mock::*; + use frame_support::Hashable; + use sp_std::borrow::Cow; + use substrate_test_utils::assert_eq_uvec; + + #[test] + #[allow(deprecated)] + fn migration_v3_to_v4_works() { + new_test_ext().execute_with(|| { + // Assume that we are at V3. + StorageVersion::new(3).put::<Scheduler>(); + + // Call that will be bounded to a `Lookup`. + let large_call = + RuntimeCall::System(frame_system::Call::remark { remark: vec![0; 1024] }); + // Call that can be inlined. + let small_call = + RuntimeCall::System(frame_system::Call::remark { remark: vec![0; 10] }); + // Call that is already hashed and can will be converted to `Legacy`. + let hashed_call = + RuntimeCall::System(frame_system::Call::remark { remark: vec![0; 2048] }); + let bound_hashed_call = Preimage::bound(hashed_call.clone()).unwrap(); + assert!(bound_hashed_call.lookup_needed()); + // A Call by hash that will fail to decode becomes `None`. + let trash_data = vec![255u8; 1024]; + let undecodable_hash = Preimage::note(Cow::Borrowed(&trash_data)).unwrap(); + + for i in 0..2u64 { + let k = i.twox_64_concat(); + let old = vec![ + Some(ScheduledV3Of::<Test> { + maybe_id: None, + priority: i as u8 + 10, + call: small_call.clone().into(), + maybe_periodic: None, // 1 + origin: root(), + _phantom: PhantomData::<u64>::default(), + }), + None, + Some(ScheduledV3Of::<Test> { + maybe_id: Some(vec![i as u8; 32]), + priority: 123, + call: large_call.clone().into(), + maybe_periodic: Some((4u64, 20)), + origin: signed(i), + _phantom: PhantomData::<u64>::default(), + }), + Some(ScheduledV3Of::<Test> { + maybe_id: Some(vec![255 - i as u8; 320]), + priority: 123, + call: MaybeHashed::Hash(bound_hashed_call.hash()), + maybe_periodic: Some((8u64, 10)), + origin: signed(i), + _phantom: PhantomData::<u64>::default(), + }), + Some(ScheduledV3Of::<Test> { + maybe_id: Some(vec![i as u8; 320]), + priority: 123, + call: MaybeHashed::Hash(undecodable_hash.clone()), + maybe_periodic: Some((4u64, 20)), + origin: root(), + _phantom: PhantomData::<u64>::default(), + }), + ]; + frame_support::migration::put_storage_value(b"Scheduler", b"Agenda", &k, old); + } + + let state = v3::MigrateToV4::<Test>::pre_upgrade().unwrap(); + let _w = v3::MigrateToV4::<Test>::on_runtime_upgrade(); + v3::MigrateToV4::<Test>::post_upgrade(state).unwrap(); + + let mut x = Agenda::<Test>::iter().map(|x| (x.0, x.1.into_inner())).collect::<Vec<_>>(); + x.sort_by_key(|x| x.0); + + let bound_large_call = Preimage::bound(large_call).unwrap(); + assert!(bound_large_call.lookup_needed()); + let bound_small_call = Preimage::bound(small_call).unwrap(); + assert!(!bound_small_call.lookup_needed()); + + let expected = vec![ + ( + 0, + vec![ + Some(ScheduledOf::<Test> { + maybe_id: None, + priority: 10, + call: bound_small_call.clone(), + maybe_periodic: None, + origin: root(), + _phantom: PhantomData::<u64>::default(), + }), + None, + Some(ScheduledOf::<Test> { + maybe_id: Some(blake2_256(&[0u8; 32])), + priority: 123, + call: bound_large_call.clone(), + maybe_periodic: Some((4u64, 20)), + origin: signed(0), + _phantom: PhantomData::<u64>::default(), + }), + Some(ScheduledOf::<Test> { + maybe_id: Some(blake2_256(&[255u8; 320])), + priority: 123, + call: Bounded::from_legacy_hash(bound_hashed_call.hash()), + maybe_periodic: Some((8u64, 10)), + origin: signed(0), + _phantom: PhantomData::<u64>::default(), + }), + None, + ], + ), + ( + 1, + vec![ + Some(ScheduledOf::<Test> { + maybe_id: None, + priority: 11, + call: bound_small_call.clone(), + maybe_periodic: None, + origin: root(), + _phantom: PhantomData::<u64>::default(), + }), + None, + Some(ScheduledOf::<Test> { + maybe_id: Some(blake2_256(&[1u8; 32])), + priority: 123, + call: bound_large_call.clone(), + maybe_periodic: Some((4u64, 20)), + origin: signed(1), + _phantom: PhantomData::<u64>::default(), + }), + Some(ScheduledOf::<Test> { + maybe_id: Some(blake2_256(&[254u8; 320])), + priority: 123, + call: Bounded::from_legacy_hash(bound_hashed_call.hash()), + maybe_periodic: Some((8u64, 10)), + origin: signed(1), + _phantom: PhantomData::<u64>::default(), + }), + None, + ], + ), + ]; + for (outer, (i, j)) in x.iter().zip(expected.iter()).enumerate() { + assert_eq!(i.0, j.0); + for (inner, (x, y)) in i.1.iter().zip(j.1.iter()).enumerate() { + assert_eq!(x, y, "at index: outer {} inner {}", outer, inner); + } + } + assert_eq_uvec!(x, expected); + + assert_eq!(StorageVersion::get::<Scheduler>(), 4); + }); + } + + #[test] + #[allow(deprecated)] + fn migration_v3_to_v4_too_large_calls_are_ignored() { + new_test_ext().execute_with(|| { + // Assume that we are at V3. + StorageVersion::new(3).put::<Scheduler>(); + + let too_large_call = RuntimeCall::System(frame_system::Call::remark { + remark: vec![0; <Test as Config>::Preimages::MAX_LENGTH + 1], + }); + + let i = 0u64; + let k = i.twox_64_concat(); + let old = vec![Some(ScheduledV3Of::<Test> { + maybe_id: None, + priority: 1, + call: too_large_call.clone().into(), + maybe_periodic: None, + origin: root(), + _phantom: PhantomData::<u64>::default(), + })]; + frame_support::migration::put_storage_value(b"Scheduler", b"Agenda", &k, old); + + // The pre_upgrade hook fails: + let err = v3::MigrateToV4::<Test>::pre_upgrade().unwrap_err(); + assert!(err.contains("Call is too large")); + // But the migration itself works: + let _w = v3::MigrateToV4::<Test>::on_runtime_upgrade(); + + let mut x = Agenda::<Test>::iter().map(|x| (x.0, x.1.into_inner())).collect::<Vec<_>>(); + x.sort_by_key(|x| x.0); + // The call becomes `None`. + let expected = vec![(0, vec![None])]; + assert_eq_uvec!(x, expected); + + assert_eq!(StorageVersion::get::<Scheduler>(), 4); + }); + } + + fn signed(i: u64) -> OriginCaller { + system::RawOrigin::Signed(i).into() + } +} diff --git a/substrate/frame/scheduler/src/mock.rs b/substrate/frame/scheduler/src/mock.rs index 6aaad13e4818351636ab2706640029fc458b1898..61efdfb67b73ec7daa112dad64c292edd55be3eb 100644 --- a/substrate/frame/scheduler/src/mock.rs +++ b/substrate/frame/scheduler/src/mock.rs @@ -124,7 +124,7 @@ parameter_types! { } impl system::Config for Test { type BaseCallFilter = BaseFilter; - type BlockWeights = (); + type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = RocksDbWeight; type RuntimeOrigin = RuntimeOrigin; @@ -151,10 +151,6 @@ impl system::Config for Test { impl logger::Config for Test { type RuntimeEvent = RuntimeEvent; } -parameter_types! { - pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * BlockWeights::get().max_block; - pub const NoPreimagePostponement: Option<u64> = Some(2); -} ord_parameter_types! { pub const One: u64 = 1; } @@ -164,11 +160,54 @@ impl pallet_preimage::Config for Test { type WeightInfo = (); type Currency = (); type ManagerOrigin = EnsureRoot<u64>; - type MaxSize = ConstU32<1024>; type BaseDeposit = (); type ByteDeposit = (); } +pub struct TestWeightInfo; +impl WeightInfo for TestWeightInfo { + fn service_agendas_base() -> Weight { + Weight::from_ref_time(0b0000_0001) + } + fn service_agenda_base(i: u32) -> Weight { + Weight::from_ref_time((i << 8) as u64 + 0b0000_0010) + } + fn service_task_base() -> Weight { + Weight::from_ref_time(0b0000_0100) + } + fn service_task_periodic() -> Weight { + Weight::from_ref_time(0b0000_1100) + } + fn service_task_named() -> Weight { + Weight::from_ref_time(0b0001_0100) + } + fn service_task_fetched(s: u32) -> Weight { + Weight::from_ref_time((s << 8) as u64 + 0b0010_0100) + } + fn execute_dispatch_signed() -> Weight { + Weight::from_ref_time(0b0100_0000) + } + fn execute_dispatch_unsigned() -> Weight { + Weight::from_ref_time(0b1000_0000) + } + fn schedule(_s: u32) -> Weight { + Weight::from_ref_time(50) + } + fn cancel(_s: u32) -> Weight { + Weight::from_ref_time(50) + } + fn schedule_named(_s: u32) -> Weight { + Weight::from_ref_time(50) + } + fn cancel_named(_s: u32) -> Weight { + Weight::from_ref_time(50) + } +} +parameter_types! { + pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * + BlockWeights::get().max_block; +} + impl Config for Test { type RuntimeEvent = RuntimeEvent; type RuntimeOrigin = RuntimeOrigin; @@ -177,10 +216,9 @@ impl Config for Test { type MaximumWeight = MaximumSchedulerWeight; type ScheduleOrigin = EitherOfDiverse<EnsureRoot<u64>, EnsureSignedBy<One, u64>>; type MaxScheduledPerBlock = ConstU32<10>; - type WeightInfo = (); + type WeightInfo = TestWeightInfo; type OriginPrivilegeCmp = EqualPrivilegeOnly; - type PreimageProvider = Preimage; - type NoPreimagePostponement = NoPreimagePostponement; + type Preimages = Preimage; } pub type LoggerCall = logger::Call<Test>; diff --git a/substrate/frame/scheduler/src/tests.rs b/substrate/frame/scheduler/src/tests.rs index f6db70ae42d4945a2430320d0c497e43ec78e2b2..033d7879467097329d649503ad250a0bb7e193ba 100644 --- a/substrate/frame/scheduler/src/tests.rs +++ b/substrate/frame/scheduler/src/tests.rs @@ -23,7 +23,7 @@ use crate::mock::{ }; use frame_support::{ assert_err, assert_noop, assert_ok, - traits::{Contains, GetStorageVersion, OnInitialize, PreimageProvider}, + traits::{Contains, GetStorageVersion, OnInitialize, QueryPreimage, StorePreimage}, Hashable, }; use sp_runtime::traits::Hash; @@ -33,9 +33,15 @@ use substrate_test_utils::assert_eq_uvec; fn basic_scheduling_works() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call)); - assert_ok!(Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call.into())); + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap() + )); run_to_block(3); assert!(logger::log().is_empty()); run_to_block(4); @@ -49,51 +55,19 @@ fn basic_scheduling_works() { fn scheduling_with_preimages_works() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); let hash = <Test as frame_system::Config>::Hashing::hash_of(&call); - let hashed = MaybeHashed::Hash(hash); + let len = call.using_encoded(|x| x.len()) as u32; + let hashed = Preimage::pick(hash, len); assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(0), call.encode())); assert_ok!(Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), hashed)); - assert!(Preimage::preimage_requested(&hash)); + assert!(Preimage::is_requested(&hash)); run_to_block(3); assert!(logger::log().is_empty()); run_to_block(4); - assert!(!Preimage::have_preimage(&hash)); - assert!(!Preimage::preimage_requested(&hash)); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - run_to_block(100); - assert_eq!(logger::log(), vec![(root(), 42u32)]); - }); -} - -#[test] -fn scheduling_with_preimage_postpones_correctly() { - new_test_ext().execute_with(|| { - let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }); - let hash = <Test as frame_system::Config>::Hashing::hash_of(&call); - let hashed = MaybeHashed::Hash(hash); - - assert_ok!(Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), hashed)); - assert!(Preimage::preimage_requested(&hash)); - - run_to_block(4); - // #4 empty due to no preimage - assert!(logger::log().is_empty()); - - // Register preimage. - assert_ok!(Preimage::note_preimage(RuntimeOrigin::signed(0), call.encode())); - - run_to_block(5); - // #5 empty since postponement is 2 blocks. - assert!(logger::log().is_empty()); - - run_to_block(6); - // #6 is good. + assert!(!Preimage::len(&hash).is_some()); + assert!(!Preimage::is_requested(&hash)); assert_eq!(logger::log(), vec![(root(), 42u32)]); - assert!(!Preimage::have_preimage(&hash)); - assert!(!Preimage::preimage_requested(&hash)); - run_to_block(100); assert_eq!(logger::log(), vec![(root(), 42u32)]); }); @@ -104,10 +78,16 @@ fn schedule_after_works() { new_test_ext().execute_with(|| { run_to_block(2); let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call)); // This will schedule the call 3 blocks after the next block... so block 3 + 3 = 6 - assert_ok!(Scheduler::do_schedule(DispatchTime::After(3), None, 127, root(), call.into())); + assert_ok!(Scheduler::do_schedule( + DispatchTime::After(3), + None, + 127, + root(), + Preimage::bound(call).unwrap() + )); run_to_block(5); assert!(logger::log().is_empty()); run_to_block(6); @@ -122,9 +102,15 @@ fn schedule_after_zero_works() { new_test_ext().execute_with(|| { run_to_block(2); let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call)); - assert_ok!(Scheduler::do_schedule(DispatchTime::After(0), None, 127, root(), call.into())); + assert_ok!(Scheduler::do_schedule( + DispatchTime::After(0), + None, + 127, + root(), + Preimage::bound(call).unwrap() + )); // Will trigger on the next block. run_to_block(3); assert_eq!(logger::log(), vec![(root(), 42u32)]); @@ -142,8 +128,11 @@ fn periodic_scheduling_works() { Some((3, 3)), 127, root(), - RuntimeCall::Logger(logger::Call::log { i: 42, weight: Weight::from_ref_time(1000) }) - .into() + Preimage::bound(RuntimeCall::Logger(logger::Call::log { + i: 42, + weight: Weight::from_ref_time(10) + })) + .unwrap() )); run_to_block(3); assert!(logger::log().is_empty()); @@ -166,10 +155,17 @@ fn periodic_scheduling_works() { fn reschedule_works() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call)); assert_eq!( - Scheduler::do_schedule(DispatchTime::At(4), None, 127, root(), call.into()).unwrap(), + Scheduler::do_schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap() + ) + .unwrap(), (4, 0) ); @@ -198,16 +194,16 @@ fn reschedule_works() { fn reschedule_named_works() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call)); assert_eq!( Scheduler::do_schedule_named( - 1u32.encode(), + [1u8; 32], DispatchTime::At(4), None, 127, root(), - call.into(), + Preimage::bound(call).unwrap(), ) .unwrap(), (4, 0) @@ -216,13 +212,10 @@ fn reschedule_named_works() { run_to_block(3); assert!(logger::log().is_empty()); - assert_eq!( - Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(), - (6, 0) - ); + assert_eq!(Scheduler::do_reschedule_named([1u8; 32], DispatchTime::At(6)).unwrap(), (6, 0)); assert_noop!( - Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)), + Scheduler::do_reschedule_named([1u8; 32], DispatchTime::At(6)), Error::<Test>::RescheduleNoChange ); @@ -241,16 +234,16 @@ fn reschedule_named_works() { fn reschedule_named_perodic_works() { new_test_ext().execute_with(|| { let call = - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }); + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); assert!(!<Test as frame_system::Config>::BaseCallFilter::contains(&call)); assert_eq!( Scheduler::do_schedule_named( - 1u32.encode(), + [1u8; 32], DispatchTime::At(4), Some((3, 3)), 127, root(), - call.into(), + Preimage::bound(call).unwrap(), ) .unwrap(), (4, 0) @@ -259,14 +252,8 @@ fn reschedule_named_perodic_works() { run_to_block(3); assert!(logger::log().is_empty()); - assert_eq!( - Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(5)).unwrap(), - (5, 0) - ); - assert_eq!( - Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(6)).unwrap(), - (6, 0) - ); + assert_eq!(Scheduler::do_reschedule_named([1u8; 32], DispatchTime::At(5)).unwrap(), (5, 0)); + assert_eq!(Scheduler::do_reschedule_named([1u8; 32], DispatchTime::At(6)).unwrap(), (6, 0)); run_to_block(5); assert!(logger::log().is_empty()); @@ -275,7 +262,7 @@ fn reschedule_named_perodic_works() { assert_eq!(logger::log(), vec![(root(), 42u32)]); assert_eq!( - Scheduler::do_reschedule_named(1u32.encode(), DispatchTime::At(10)).unwrap(), + Scheduler::do_reschedule_named([1u8; 32], DispatchTime::At(10)).unwrap(), (10, 0) ); @@ -298,13 +285,16 @@ fn cancel_named_scheduling_works_with_normal_cancel() { new_test_ext().execute_with(|| { // at #4. Scheduler::do_schedule_named( - 1u32.encode(), + [1u8; 32], DispatchTime::At(4), None, 127, root(), - RuntimeCall::Logger(LoggerCall::log { i: 69, weight: Weight::from_ref_time(1000) }) - .into(), + Preimage::bound(RuntimeCall::Logger(LoggerCall::log { + i: 69, + weight: Weight::from_ref_time(10), + })) + .unwrap(), ) .unwrap(); let i = Scheduler::do_schedule( @@ -312,13 +302,16 @@ fn cancel_named_scheduling_works_with_normal_cancel() { None, 127, root(), - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }) - .into(), + Preimage::bound(RuntimeCall::Logger(LoggerCall::log { + i: 42, + weight: Weight::from_ref_time(10), + })) + .unwrap(), ) .unwrap(); run_to_block(3); assert!(logger::log().is_empty()); - assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode())); + assert_ok!(Scheduler::do_cancel_named(None, [1u8; 32])); assert_ok!(Scheduler::do_cancel(None, i)); run_to_block(100); assert!(logger::log().is_empty()); @@ -330,35 +323,44 @@ fn cancel_named_periodic_scheduling_works() { new_test_ext().execute_with(|| { // at #4, every 3 blocks, 3 times. Scheduler::do_schedule_named( - 1u32.encode(), + [1u8; 32], DispatchTime::At(4), Some((3, 3)), 127, root(), - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }) - .into(), + Preimage::bound(RuntimeCall::Logger(LoggerCall::log { + i: 42, + weight: Weight::from_ref_time(10), + })) + .unwrap(), ) .unwrap(); // same id results in error. assert!(Scheduler::do_schedule_named( - 1u32.encode(), + [1u8; 32], DispatchTime::At(4), None, 127, root(), - RuntimeCall::Logger(LoggerCall::log { i: 69, weight: Weight::from_ref_time(1000) }) - .into(), + Preimage::bound(RuntimeCall::Logger(LoggerCall::log { + i: 69, + weight: Weight::from_ref_time(10) + })) + .unwrap(), ) .is_err()); // different id is ok. Scheduler::do_schedule_named( - 2u32.encode(), + [2u8; 32], DispatchTime::At(8), None, 127, root(), - RuntimeCall::Logger(LoggerCall::log { i: 69, weight: Weight::from_ref_time(1000) }) - .into(), + Preimage::bound(RuntimeCall::Logger(LoggerCall::log { + i: 69, + weight: Weight::from_ref_time(10), + })) + .unwrap(), ) .unwrap(); run_to_block(3); @@ -366,7 +368,7 @@ fn cancel_named_periodic_scheduling_works() { run_to_block(4); assert_eq!(logger::log(), vec![(root(), 42u32)]); run_to_block(6); - assert_ok!(Scheduler::do_cancel_named(None, 1u32.encode())); + assert_ok!(Scheduler::do_cancel_named(None, [1u8; 32])); run_to_block(100); assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]); }); @@ -374,28 +376,23 @@ fn cancel_named_periodic_scheduling_works() { #[test] fn scheduler_respects_weight_limits() { + let max_weight: Weight = <Test as Config>::MaximumWeight::get(); new_test_ext().execute_with(|| { + let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: max_weight / 3 * 2 }); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), None, 127, root(), - RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: MaximumSchedulerWeight::get() / 2 - }) - .into(), + Preimage::bound(call).unwrap(), )); + let call = RuntimeCall::Logger(LoggerCall::log { i: 69, weight: max_weight / 3 * 2 }); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), None, 127, root(), - RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: MaximumSchedulerWeight::get() / 2 - }) - .into(), + Preimage::bound(call).unwrap(), )); // 69 and 42 do not fit together run_to_block(4); @@ -405,62 +402,128 @@ fn scheduler_respects_weight_limits() { }); } +/// Permanently overweight calls are not deleted but also not executed. #[test] -fn scheduler_respects_hard_deadlines_more() { +fn scheduler_does_not_delete_permanently_overweight_call() { + let max_weight: Weight = <Test as Config>::MaximumWeight::get(); new_test_ext().execute_with(|| { + let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: max_weight }); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), None, - 0, + 127, root(), - RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: MaximumSchedulerWeight::get() / 2 - }) - .into(), + Preimage::bound(call).unwrap(), )); + // Never executes. + run_to_block(100); + assert_eq!(logger::log(), vec![]); + + // Assert the `PermanentlyOverweight` event. + assert_eq!( + System::events().last().unwrap().event, + crate::Event::PermanentlyOverweight { task: (4, 0), id: None }.into(), + ); + // The call is still in the agenda. + assert!(Agenda::<Test>::get(4)[0].is_some()); + }); +} + +#[test] +fn scheduler_handles_periodic_failure() { + let max_weight: Weight = <Test as Config>::MaximumWeight::get(); + let max_per_block = <Test as Config>::MaxScheduledPerBlock::get(); + + new_test_ext().execute_with(|| { + let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: (max_weight / 3) * 2 }); + let bound = Preimage::bound(call).unwrap(); + assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), - None, - 0, + Some((4, u32::MAX)), + 127, root(), - RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: MaximumSchedulerWeight::get() / 2 - }) - .into(), + bound.clone(), + )); + // Executes 5 times till block 20. + run_to_block(20); + assert_eq!(logger::log().len(), 5); + + // Block 28 will already be full. + for _ in 0..max_per_block { + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(28), + None, + 120, + root(), + bound.clone(), + )); + } + + // Going to block 24 will emit a `PeriodicFailed` event. + run_to_block(24); + assert_eq!(logger::log().len(), 6); + + assert_eq!( + System::events().last().unwrap().event, + crate::Event::PeriodicFailed { task: (24, 0), id: None }.into(), + ); + }); +} + +#[test] +fn scheduler_handles_periodic_unavailable_preimage() { + let max_weight: Weight = <Test as Config>::MaximumWeight::get(); + + new_test_ext().execute_with(|| { + let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: (max_weight / 3) * 2 }); + let hash = <Test as frame_system::Config>::Hashing::hash_of(&call); + let len = call.using_encoded(|x| x.len()) as u32; + let bound = Preimage::pick(hash, len); + assert_ok!(Preimage::note(call.encode().into())); + + assert_ok!(Scheduler::do_schedule( + DispatchTime::At(4), + Some((4, u32::MAX)), + 127, + root(), + bound.clone(), )); - // With base weights, 69 and 42 should not fit together, but do because of hard - // deadlines + // Executes 1 times till block 4. run_to_block(4); - assert_eq!(logger::log(), vec![(root(), 42u32), (root(), 69u32)]); + assert_eq!(logger::log().len(), 1); + + // Unnote the preimage. + Preimage::unnote(&hash); + + // Does not ever execute again. + run_to_block(100); + assert_eq!(logger::log().len(), 1); + + // The preimage is not requested anymore. + assert!(!Preimage::is_requested(&hash)); }); } #[test] fn scheduler_respects_priority_ordering() { + let max_weight: Weight = <Test as Config>::MaximumWeight::get(); new_test_ext().execute_with(|| { + let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: max_weight / 3 }); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), None, 1, root(), - RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: MaximumSchedulerWeight::get() / 2 - }) - .into(), + Preimage::bound(call).unwrap(), )); + let call = RuntimeCall::Logger(LoggerCall::log { i: 69, weight: max_weight / 3 }); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), None, 0, root(), - RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: MaximumSchedulerWeight::get() / 2 - }) - .into(), + Preimage::bound(call).unwrap(), )); run_to_block(4); assert_eq!(logger::log(), vec![(root(), 69u32), (root(), 42u32)]); @@ -470,35 +533,30 @@ fn scheduler_respects_priority_ordering() { #[test] fn scheduler_respects_priority_ordering_with_soft_deadlines() { new_test_ext().execute_with(|| { - let max_weight = MaximumSchedulerWeight::get() - <() as WeightInfo>::on_initialize(0); - let item_weight = - <() as WeightInfo>::on_initialize(1) - <() as WeightInfo>::on_initialize(0); + let max_weight: Weight = <Test as Config>::MaximumWeight::get(); + let call = RuntimeCall::Logger(LoggerCall::log { i: 42, weight: max_weight / 5 * 2 }); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), None, 255, root(), - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: max_weight / 2 - item_weight }) - .into(), + Preimage::bound(call).unwrap(), )); + let call = RuntimeCall::Logger(LoggerCall::log { i: 69, weight: max_weight / 5 * 2 }); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), None, 127, root(), - RuntimeCall::Logger(LoggerCall::log { i: 69, weight: max_weight / 2 - item_weight }) - .into(), + Preimage::bound(call).unwrap(), )); + let call = RuntimeCall::Logger(LoggerCall::log { i: 2600, weight: max_weight / 5 * 4 }); assert_ok!(Scheduler::do_schedule( DispatchTime::At(4), None, 126, root(), - RuntimeCall::Logger(LoggerCall::log { - i: 2600, - weight: max_weight / 2 - item_weight + Weight::from_ref_time(1) - }) - .into(), + Preimage::bound(call).unwrap(), )); // 2600 does not fit with 69 or 42, but has higher priority, so will go through @@ -513,90 +571,96 @@ fn scheduler_respects_priority_ordering_with_soft_deadlines() { #[test] fn on_initialize_weight_is_correct() { new_test_ext().execute_with(|| { - let base_weight = <() as WeightInfo>::on_initialize(0); - let call_weight = MaximumSchedulerWeight::get() / 4; + let call_weight = Weight::from_ref_time(25); // Named + let call = RuntimeCall::Logger(LoggerCall::log { + i: 3, + weight: call_weight + Weight::from_ref_time(1), + }); assert_ok!(Scheduler::do_schedule_named( - 1u32.encode(), + [1u8; 32], DispatchTime::At(3), None, 255, root(), - RuntimeCall::Logger(LoggerCall::log { - i: 3, - weight: call_weight + Weight::from_ref_time(1) - }) - .into(), + Preimage::bound(call).unwrap(), )); + let call = RuntimeCall::Logger(LoggerCall::log { + i: 42, + weight: call_weight + Weight::from_ref_time(2), + }); // Anon Periodic assert_ok!(Scheduler::do_schedule( DispatchTime::At(2), Some((1000, 3)), 128, root(), - RuntimeCall::Logger(LoggerCall::log { - i: 42, - weight: call_weight + Weight::from_ref_time(2) - }) - .into(), + Preimage::bound(call).unwrap(), )); + let call = RuntimeCall::Logger(LoggerCall::log { + i: 69, + weight: call_weight + Weight::from_ref_time(3), + }); // Anon assert_ok!(Scheduler::do_schedule( DispatchTime::At(2), None, 127, root(), - RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: call_weight + Weight::from_ref_time(3) - }) - .into(), + Preimage::bound(call).unwrap(), )); // Named Periodic + let call = RuntimeCall::Logger(LoggerCall::log { + i: 2600, + weight: call_weight + Weight::from_ref_time(4), + }); assert_ok!(Scheduler::do_schedule_named( - 2u32.encode(), + [2u8; 32], DispatchTime::At(1), Some((1000, 3)), 126, root(), - RuntimeCall::Logger(LoggerCall::log { - i: 2600, - weight: call_weight + Weight::from_ref_time(4) - }) - .into(), + Preimage::bound(call).unwrap(), )); // Will include the named periodic only - let actual_weight = Scheduler::on_initialize(1); assert_eq!( - actual_weight, - base_weight + - call_weight + Weight::from_ref_time(4) + - <() as MarginalWeightInfo>::item(true, true, Some(false)) + Scheduler::on_initialize(1), + TestWeightInfo::service_agendas_base() + + TestWeightInfo::service_agenda_base(1) + + <TestWeightInfo as MarginalWeightInfo>::service_task(None, true, true) + + TestWeightInfo::execute_dispatch_unsigned() + + call_weight + Weight::from_ref_time(4) ); + assert_eq!(IncompleteSince::<Test>::get(), None); assert_eq!(logger::log(), vec![(root(), 2600u32)]); // Will include anon and anon periodic - let actual_weight = Scheduler::on_initialize(2); assert_eq!( - actual_weight, - base_weight + - call_weight + Weight::from_ref_time(2) + - <() as MarginalWeightInfo>::item(false, false, Some(false)) + + Scheduler::on_initialize(2), + TestWeightInfo::service_agendas_base() + + TestWeightInfo::service_agenda_base(2) + + <TestWeightInfo as MarginalWeightInfo>::service_task(None, false, true) + + TestWeightInfo::execute_dispatch_unsigned() + call_weight + Weight::from_ref_time(3) + - <() as MarginalWeightInfo>::item(true, false, Some(false)) + <TestWeightInfo as MarginalWeightInfo>::service_task(None, false, false) + + TestWeightInfo::execute_dispatch_unsigned() + + call_weight + Weight::from_ref_time(2) ); + assert_eq!(IncompleteSince::<Test>::get(), None); assert_eq!(logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32)]); // Will include named only - let actual_weight = Scheduler::on_initialize(3); assert_eq!( - actual_weight, - base_weight + - call_weight + Weight::from_ref_time(1) + - <() as MarginalWeightInfo>::item(false, true, Some(false)) + Scheduler::on_initialize(3), + TestWeightInfo::service_agendas_base() + + TestWeightInfo::service_agenda_base(1) + + <TestWeightInfo as MarginalWeightInfo>::service_task(None, true, false) + + TestWeightInfo::execute_dispatch_unsigned() + + call_weight + Weight::from_ref_time(1) ); + assert_eq!(IncompleteSince::<Test>::get(), None); assert_eq!( logger::log(), vec![(root(), 2600u32), (root(), 69u32), (root(), 42u32), (root(), 3u32)] @@ -604,35 +668,33 @@ fn on_initialize_weight_is_correct() { // Will contain none let actual_weight = Scheduler::on_initialize(4); - assert_eq!(actual_weight, base_weight); + assert_eq!( + actual_weight, + TestWeightInfo::service_agendas_base() + TestWeightInfo::service_agenda_base(0) + ); }); } #[test] fn root_calls_works() { new_test_ext().execute_with(|| { - let call = Box::new( - RuntimeCall::Logger(LoggerCall::log { i: 69, weight: Weight::from_ref_time(1000) }) - .into(), - ); - let call2 = Box::new( - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }) - .into(), + let call = Box::new(RuntimeCall::Logger(LoggerCall::log { + i: 69, + weight: Weight::from_ref_time(10), + })); + let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { + i: 42, + weight: Weight::from_ref_time(10), + })); + assert_ok!( + Scheduler::schedule_named(RuntimeOrigin::root(), [1u8; 32], 4, None, 127, call,) ); - assert_ok!(Scheduler::schedule_named( - RuntimeOrigin::root(), - 1u32.encode(), - 4, - None, - 127, - call, - )); assert_ok!(Scheduler::schedule(RuntimeOrigin::root(), 4, None, 127, call2)); run_to_block(3); // Scheduled calls are in the agenda. assert_eq!(Agenda::<Test>::get(4).len(), 2); assert!(logger::log().is_empty()); - assert_ok!(Scheduler::cancel_named(RuntimeOrigin::root(), 1u32.encode())); + assert_ok!(Scheduler::cancel_named(RuntimeOrigin::root(), [1u8; 32])); assert_ok!(Scheduler::cancel(RuntimeOrigin::root(), 4, 1)); // Scheduled calls are made NONE, so should not effect state run_to_block(100); @@ -645,29 +707,30 @@ fn fails_to_schedule_task_in_the_past() { new_test_ext().execute_with(|| { run_to_block(3); - let call1 = Box::new( - RuntimeCall::Logger(LoggerCall::log { i: 69, weight: Weight::from_ref_time(1000) }) - .into(), - ); - let call2 = Box::new( - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }) - .into(), - ); - let call3 = Box::new( - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }) - .into(), - ); - assert_err!( - Scheduler::schedule_named(RuntimeOrigin::root(), 1u32.encode(), 2, None, 127, call1), + let call1 = Box::new(RuntimeCall::Logger(LoggerCall::log { + i: 69, + weight: Weight::from_ref_time(10), + })); + let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { + i: 42, + weight: Weight::from_ref_time(10), + })); + let call3 = Box::new(RuntimeCall::Logger(LoggerCall::log { + i: 42, + weight: Weight::from_ref_time(10), + })); + + assert_noop!( + Scheduler::schedule_named(RuntimeOrigin::root(), [1u8; 32], 2, None, 127, call1), Error::<Test>::TargetBlockNumberInPast, ); - assert_err!( + assert_noop!( Scheduler::schedule(RuntimeOrigin::root(), 2, None, 127, call2), Error::<Test>::TargetBlockNumberInPast, ); - assert_err!( + assert_noop!( Scheduler::schedule(RuntimeOrigin::root(), 3, None, 127, call3), Error::<Test>::TargetBlockNumberInPast, ); @@ -675,19 +738,19 @@ fn fails_to_schedule_task_in_the_past() { } #[test] -fn should_use_orign() { +fn should_use_origin() { new_test_ext().execute_with(|| { - let call = Box::new( - RuntimeCall::Logger(LoggerCall::log { i: 69, weight: Weight::from_ref_time(1000) }) - .into(), - ); - let call2 = Box::new( - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }) - .into(), - ); + let call = Box::new(RuntimeCall::Logger(LoggerCall::log { + i: 69, + weight: Weight::from_ref_time(10), + })); + let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { + i: 42, + weight: Weight::from_ref_time(10), + })); assert_ok!(Scheduler::schedule_named( system::RawOrigin::Signed(1).into(), - 1u32.encode(), + [1u8; 32], 4, None, 127, @@ -698,7 +761,7 @@ fn should_use_orign() { // Scheduled calls are in the agenda. assert_eq!(Agenda::<Test>::get(4).len(), 2); assert!(logger::log().is_empty()); - assert_ok!(Scheduler::cancel_named(system::RawOrigin::Signed(1).into(), 1u32.encode())); + assert_ok!(Scheduler::cancel_named(system::RawOrigin::Signed(1).into(), [1u8; 32])); assert_ok!(Scheduler::cancel(system::RawOrigin::Signed(1).into(), 4, 1)); // Scheduled calls are made NONE, so should not effect state run_to_block(100); @@ -707,20 +770,20 @@ fn should_use_orign() { } #[test] -fn should_check_orign() { +fn should_check_origin() { new_test_ext().execute_with(|| { - let call = Box::new( - RuntimeCall::Logger(LoggerCall::log { i: 69, weight: Weight::from_ref_time(1000) }) - .into(), - ); - let call2 = Box::new( - RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }) - .into(), - ); + let call = Box::new(RuntimeCall::Logger(LoggerCall::log { + i: 69, + weight: Weight::from_ref_time(10), + })); + let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log { + i: 42, + weight: Weight::from_ref_time(10), + })); assert_noop!( Scheduler::schedule_named( system::RawOrigin::Signed(2).into(), - 1u32.encode(), + [1u8; 32], 4, None, 127, @@ -736,25 +799,19 @@ fn should_check_orign() { } #[test] -fn should_check_orign_for_cancel() { +fn should_check_origin_for_cancel() { new_test_ext().execute_with(|| { - let call = Box::new( - RuntimeCall::Logger(LoggerCall::log_without_filter { - i: 69, - weight: Weight::from_ref_time(1000), - }) - .into(), - ); - let call2 = Box::new( - RuntimeCall::Logger(LoggerCall::log_without_filter { - i: 42, - weight: Weight::from_ref_time(1000), - }) - .into(), - ); + let call = Box::new(RuntimeCall::Logger(LoggerCall::log_without_filter { + i: 69, + weight: Weight::from_ref_time(10), + })); + let call2 = Box::new(RuntimeCall::Logger(LoggerCall::log_without_filter { + i: 42, + weight: Weight::from_ref_time(10), + })); assert_ok!(Scheduler::schedule_named( system::RawOrigin::Signed(1).into(), - 1u32.encode(), + [1u8; 32], 4, None, 127, @@ -766,14 +823,11 @@ fn should_check_orign_for_cancel() { assert_eq!(Agenda::<Test>::get(4).len(), 2); assert!(logger::log().is_empty()); assert_noop!( - Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), 1u32.encode()), + Scheduler::cancel_named(system::RawOrigin::Signed(2).into(), [1u8; 32]), BadOrigin ); assert_noop!(Scheduler::cancel(system::RawOrigin::Signed(2).into(), 4, 1), BadOrigin); - assert_noop!( - Scheduler::cancel_named(system::RawOrigin::Root.into(), 1u32.encode()), - BadOrigin - ); + assert_noop!(Scheduler::cancel_named(system::RawOrigin::Root.into(), [1u8; 32]), BadOrigin); assert_noop!(Scheduler::cancel(system::RawOrigin::Root.into(), 4, 1), BadOrigin); run_to_block(5); assert_eq!( @@ -787,7 +841,7 @@ fn should_check_orign_for_cancel() { } #[test] -fn migration_to_v3_works() { +fn migration_to_v4_works() { new_test_ext().execute_with(|| { for i in 0..3u64 { let k = i.twox_64_concat(); @@ -807,7 +861,7 @@ fn migration_to_v3_works() { priority: 123, call: RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(1000), + weight: Weight::from_ref_time(10), }), maybe_periodic: Some((456u64, 10)), }), @@ -815,103 +869,109 @@ fn migration_to_v3_works() { frame_support::migration::put_storage_value(b"Scheduler", b"Agenda", &k, old); } - Scheduler::migrate_v1_to_v3(); - - assert_eq_uvec!( - Agenda::<Test>::iter().collect::<Vec<_>>(), - vec![ - ( - 0, - vec![ - Some(ScheduledV3Of::<Test> { - maybe_id: None, - priority: 10, - call: RuntimeCall::Logger(LoggerCall::log { - i: 96, - weight: Weight::from_ref_time(100) - }) - .into(), - maybe_periodic: None, - origin: root(), - _phantom: PhantomData::<u64>::default(), - }), - None, - Some(ScheduledV3Of::<Test> { - maybe_id: Some(b"test".to_vec()), - priority: 123, - call: RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: Weight::from_ref_time(1000) - }) - .into(), - maybe_periodic: Some((456u64, 10)), - origin: root(), - _phantom: PhantomData::<u64>::default(), - }), - ] - ), - ( - 1, - vec![ - Some(ScheduledV3Of::<Test> { - maybe_id: None, - priority: 11, - call: RuntimeCall::Logger(LoggerCall::log { - i: 96, - weight: Weight::from_ref_time(100) - }) - .into(), - maybe_periodic: None, - origin: root(), - _phantom: PhantomData::<u64>::default(), - }), - None, - Some(ScheduledV3Of::<Test> { - maybe_id: Some(b"test".to_vec()), - priority: 123, - call: RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: Weight::from_ref_time(1000) - }) - .into(), - maybe_periodic: Some((456u64, 10)), - origin: root(), - _phantom: PhantomData::<u64>::default(), - }), - ] - ), - ( - 2, - vec![ - Some(ScheduledV3Of::<Test> { - maybe_id: None, - priority: 12, - call: RuntimeCall::Logger(LoggerCall::log { - i: 96, - weight: Weight::from_ref_time(100) - }) - .into(), - maybe_periodic: None, - origin: root(), - _phantom: PhantomData::<u64>::default(), - }), - None, - Some(ScheduledV3Of::<Test> { - maybe_id: Some(b"test".to_vec()), - priority: 123, - call: RuntimeCall::Logger(LoggerCall::log { - i: 69, - weight: Weight::from_ref_time(1000) - }) - .into(), - maybe_periodic: Some((456u64, 10)), - origin: root(), - _phantom: PhantomData::<u64>::default(), - }), - ] - ) - ] - ); + Scheduler::migrate_v1_to_v4(); + + let mut x = Agenda::<Test>::iter().map(|x| (x.0, x.1.into_inner())).collect::<Vec<_>>(); + x.sort_by_key(|x| x.0); + let expected = vec![ + ( + 0, + vec![ + Some(ScheduledOf::<Test> { + maybe_id: None, + priority: 10, + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { + i: 96, + weight: Weight::from_ref_time(100), + })) + .unwrap(), + maybe_periodic: None, + origin: root(), + _phantom: PhantomData::<u64>::default(), + }), + None, + Some(ScheduledOf::<Test> { + maybe_id: Some(blake2_256(&b"test"[..])), + priority: 123, + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { + i: 69, + weight: Weight::from_ref_time(10), + })) + .unwrap(), + maybe_periodic: Some((456u64, 10)), + origin: root(), + _phantom: PhantomData::<u64>::default(), + }), + ], + ), + ( + 1, + vec![ + Some(ScheduledOf::<Test> { + maybe_id: None, + priority: 11, + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { + i: 96, + weight: Weight::from_ref_time(100), + })) + .unwrap(), + maybe_periodic: None, + origin: root(), + _phantom: PhantomData::<u64>::default(), + }), + None, + Some(ScheduledOf::<Test> { + maybe_id: Some(blake2_256(&b"test"[..])), + priority: 123, + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { + i: 69, + weight: Weight::from_ref_time(10), + })) + .unwrap(), + maybe_periodic: Some((456u64, 10)), + origin: root(), + _phantom: PhantomData::<u64>::default(), + }), + ], + ), + ( + 2, + vec![ + Some(ScheduledOf::<Test> { + maybe_id: None, + priority: 12, + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { + i: 96, + weight: Weight::from_ref_time(100), + })) + .unwrap(), + maybe_periodic: None, + origin: root(), + _phantom: PhantomData::<u64>::default(), + }), + None, + Some(ScheduledOf::<Test> { + maybe_id: Some(blake2_256(&b"test"[..])), + priority: 123, + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { + i: 69, + weight: Weight::from_ref_time(10), + })) + .unwrap(), + maybe_periodic: Some((456u64, 10)), + origin: root(), + _phantom: PhantomData::<u64>::default(), + }), + ], + ), + ]; + for (i, j) in x.iter().zip(expected.iter()) { + assert_eq!(i.0, j.0); + for (x, y) in i.1.iter().zip(j.1.iter()) { + assert_eq!(x, y); + } + } + assert_eq_uvec!(x, expected); assert_eq!(Scheduler::current_storage_version(), 3); }); @@ -922,29 +982,29 @@ fn test_migrate_origin() { new_test_ext().execute_with(|| { for i in 0..3u64 { let k = i.twox_64_concat(); - let old: Vec<Option<Scheduled<CallOrHashOf<Test>, u64, u32, u64>>> = vec![ + let old: Vec<Option<Scheduled<[u8; 32], Bounded<RuntimeCall>, u64, u32, u64>>> = vec![ Some(Scheduled { maybe_id: None, priority: i as u8 + 10, - call: RuntimeCall::Logger(LoggerCall::log { + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 96, weight: Weight::from_ref_time(100), - }) - .into(), + })) + .unwrap(), origin: 3u32, maybe_periodic: None, _phantom: Default::default(), }), None, Some(Scheduled { - maybe_id: Some(b"test".to_vec()), + maybe_id: Some(blake2_256(&b"test"[..])), priority: 123, origin: 2u32, - call: RuntimeCall::Logger(LoggerCall::log { + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(1000), - }) - .into(), + weight: Weight::from_ref_time(10), + })) + .unwrap(), maybe_periodic: Some((456u64, 10)), _phantom: Default::default(), }), @@ -965,32 +1025,32 @@ fn test_migrate_origin() { Scheduler::migrate_origin::<u32>(); assert_eq_uvec!( - Agenda::<Test>::iter().collect::<Vec<_>>(), + Agenda::<Test>::iter().map(|x| (x.0, x.1.into_inner())).collect::<Vec<_>>(), vec![ ( 0, vec![ - Some(ScheduledV2::<CallOrHashOf<Test>, u64, OriginCaller, u64> { + Some(ScheduledOf::<Test> { maybe_id: None, priority: 10, - call: RuntimeCall::Logger(LoggerCall::log { + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 96, weight: Weight::from_ref_time(100) - }) - .into(), + })) + .unwrap(), maybe_periodic: None, origin: system::RawOrigin::Root.into(), _phantom: PhantomData::<u64>::default(), }), None, - Some(ScheduledV2 { - maybe_id: Some(b"test".to_vec()), + Some(Scheduled { + maybe_id: Some(blake2_256(&b"test"[..])), priority: 123, - call: RuntimeCall::Logger(LoggerCall::log { + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(1000) - }) - .into(), + weight: Weight::from_ref_time(10) + })) + .unwrap(), maybe_periodic: Some((456u64, 10)), origin: system::RawOrigin::None.into(), _phantom: PhantomData::<u64>::default(), @@ -1000,27 +1060,27 @@ fn test_migrate_origin() { ( 1, vec![ - Some(ScheduledV2 { + Some(Scheduled { maybe_id: None, priority: 11, - call: RuntimeCall::Logger(LoggerCall::log { + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 96, weight: Weight::from_ref_time(100) - }) - .into(), + })) + .unwrap(), maybe_periodic: None, origin: system::RawOrigin::Root.into(), _phantom: PhantomData::<u64>::default(), }), None, - Some(ScheduledV2 { - maybe_id: Some(b"test".to_vec()), + Some(Scheduled { + maybe_id: Some(blake2_256(&b"test"[..])), priority: 123, - call: RuntimeCall::Logger(LoggerCall::log { + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(1000) - }) - .into(), + weight: Weight::from_ref_time(10) + })) + .unwrap(), maybe_periodic: Some((456u64, 10)), origin: system::RawOrigin::None.into(), _phantom: PhantomData::<u64>::default(), @@ -1030,27 +1090,27 @@ fn test_migrate_origin() { ( 2, vec![ - Some(ScheduledV2 { + Some(Scheduled { maybe_id: None, priority: 12, - call: RuntimeCall::Logger(LoggerCall::log { + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 96, weight: Weight::from_ref_time(100) - }) - .into(), + })) + .unwrap(), maybe_periodic: None, origin: system::RawOrigin::Root.into(), _phantom: PhantomData::<u64>::default(), }), None, - Some(ScheduledV2 { - maybe_id: Some(b"test".to_vec()), + Some(Scheduled { + maybe_id: Some(blake2_256(&b"test"[..])), priority: 123, - call: RuntimeCall::Logger(LoggerCall::log { + call: Preimage::bound(RuntimeCall::Logger(LoggerCall::log { i: 69, - weight: Weight::from_ref_time(1000) - }) - .into(), + weight: Weight::from_ref_time(10) + })) + .unwrap(), maybe_periodic: Some((456u64, 10)), origin: system::RawOrigin::None.into(), _phantom: PhantomData::<u64>::default(), @@ -1061,3 +1121,649 @@ fn test_migrate_origin() { ); }); } + +#[test] +fn postponed_named_task_cannot_be_rescheduled() { + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(1000) }); + let hash = <Test as frame_system::Config>::Hashing::hash_of(&call); + let len = call.using_encoded(|x| x.len()) as u32; + let hashed = Preimage::pick(hash, len); + let name: [u8; 32] = hash.as_ref().try_into().unwrap(); + + let address = Scheduler::do_schedule_named( + name, + DispatchTime::At(4), + None, + 127, + root(), + hashed.clone(), + ) + .unwrap(); + assert!(Preimage::is_requested(&hash)); + assert!(Lookup::<Test>::contains_key(name)); + + // Run to a very large block. + run_to_block(10); + // It was not executed. + assert!(logger::log().is_empty()); + assert!(Preimage::is_requested(&hash)); + // Postponing removes the lookup. + assert!(!Lookup::<Test>::contains_key(name)); + + // The agenda still contains the call. + let agenda = Agenda::<Test>::iter().collect::<Vec<_>>(); + assert_eq!(agenda.len(), 1); + assert_eq!( + agenda[0].1, + vec![Some(Scheduled { + maybe_id: Some(name), + priority: 127, + call: hashed, + maybe_periodic: None, + origin: root().into(), + _phantom: Default::default(), + })] + ); + + // Finally add the preimage. + assert_ok!(Preimage::note(call.encode().into())); + run_to_block(1000); + // It did not execute. + assert!(logger::log().is_empty()); + assert!(Preimage::is_requested(&hash)); + + // Manually re-schedule the call by name does not work. + assert_err!( + Scheduler::do_reschedule_named(name, DispatchTime::At(1001)), + Error::<Test>::NotFound + ); + // Manually re-scheduling the call by address errors. + assert_err!( + Scheduler::do_reschedule(address, DispatchTime::At(1001)), + Error::<Test>::Named + ); + }); +} + +/// Using the scheduler as `v3::Anon` works. +#[test] +fn scheduler_v3_anon_basic_works() { + use frame_support::traits::schedule::v3::Anon; + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + + // Schedule a call. + let _address = <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap(), + ) + .unwrap(); + + run_to_block(3); + // Did not execute till block 3. + assert!(logger::log().is_empty()); + // Executes in block 4. + run_to_block(4); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // ... but not again. + run_to_block(100); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + }); +} + +#[test] +fn scheduler_v3_anon_cancel_works() { + use frame_support::traits::schedule::v3::Anon; + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + let bound = Preimage::bound(call).unwrap(); + + // Schedule a call. + let address = <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + bound.clone(), + ) + .unwrap(); + // Cancel the call. + assert_ok!(<Scheduler as Anon<_, _, _>>::cancel(address)); + // It did not get executed. + run_to_block(100); + assert!(logger::log().is_empty()); + // Cannot cancel again. + assert_err!(<Scheduler as Anon<_, _, _>>::cancel(address), DispatchError::Unavailable); + }); +} + +#[test] +fn scheduler_v3_anon_reschedule_works() { + use frame_support::traits::schedule::v3::Anon; + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + + // Schedule a call. + let address = <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap(), + ) + .unwrap(); + + run_to_block(3); + // Did not execute till block 3. + assert!(logger::log().is_empty()); + + // Cannot re-schedule into the same block. + assert_noop!( + <Scheduler as Anon<_, _, _>>::reschedule(address, DispatchTime::At(4)), + Error::<Test>::RescheduleNoChange + ); + // Cannot re-schedule into the past. + assert_noop!( + <Scheduler as Anon<_, _, _>>::reschedule(address, DispatchTime::At(3)), + Error::<Test>::TargetBlockNumberInPast + ); + // Re-schedule to block 5. + assert_ok!(<Scheduler as Anon<_, _, _>>::reschedule(address, DispatchTime::At(5))); + // Scheduled for block 5. + run_to_block(4); + assert!(logger::log().is_empty()); + run_to_block(5); + // Does execute in block 5. + assert_eq!(logger::log(), vec![(root(), 42)]); + // Cannot re-schedule executed task. + assert_noop!( + <Scheduler as Anon<_, _, _>>::reschedule(address, DispatchTime::At(10)), + DispatchError::Unavailable + ); + }); +} + +/// Cancelling a call and then scheduling a second call for the same +/// block results in different addresses. +#[test] +fn scheduler_v3_anon_schedule_does_not_resuse_addr() { + use frame_support::traits::schedule::v3::Anon; + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + + // Schedule both calls. + let addr_1 = <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call.clone()).unwrap(), + ) + .unwrap(); + // Cancel the call. + assert_ok!(<Scheduler as Anon<_, _, _>>::cancel(addr_1)); + let addr_2 = <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap(), + ) + .unwrap(); + + // Should not re-use the address. + assert!(addr_1 != addr_2); + }); +} + +#[test] +fn scheduler_v3_anon_next_schedule_time_works() { + use frame_support::traits::schedule::v3::Anon; + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + let bound = Preimage::bound(call).unwrap(); + + // Schedule a call. + let address = <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + bound.clone(), + ) + .unwrap(); + + run_to_block(3); + // Did not execute till block 3. + assert!(logger::log().is_empty()); + + // Scheduled for block 4. + assert_eq!(<Scheduler as Anon<_, _, _>>::next_dispatch_time(address), Ok(4)); + // Block 4 executes it. + run_to_block(4); + assert_eq!(logger::log(), vec![(root(), 42)]); + + // It has no dispatch time anymore. + assert_noop!( + <Scheduler as Anon<_, _, _>>::next_dispatch_time(address), + DispatchError::Unavailable + ); + }); +} + +/// Re-scheduling a task changes its next dispatch time. +#[test] +fn scheduler_v3_anon_reschedule_and_next_schedule_time_work() { + use frame_support::traits::schedule::v3::Anon; + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + let bound = Preimage::bound(call).unwrap(); + + // Schedule a call. + let old_address = <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + bound.clone(), + ) + .unwrap(); + + run_to_block(3); + // Did not execute till block 3. + assert!(logger::log().is_empty()); + + // Scheduled for block 4. + assert_eq!(<Scheduler as Anon<_, _, _>>::next_dispatch_time(old_address), Ok(4)); + // Re-schedule to block 5. + let address = + <Scheduler as Anon<_, _, _>>::reschedule(old_address, DispatchTime::At(5)).unwrap(); + assert!(address != old_address); + // Scheduled for block 5. + assert_eq!(<Scheduler as Anon<_, _, _>>::next_dispatch_time(address), Ok(5)); + + // Block 4 does nothing. + run_to_block(4); + assert!(logger::log().is_empty()); + // Block 5 executes it. + run_to_block(5); + assert_eq!(logger::log(), vec![(root(), 42)]); + }); +} + +#[test] +fn scheduler_v3_anon_schedule_agenda_overflows() { + use frame_support::traits::schedule::v3::Anon; + let max: u32 = <Test as Config>::MaxScheduledPerBlock::get(); + + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + let bound = Preimage::bound(call).unwrap(); + + // Schedule the maximal number allowed per block. + for _ in 0..max { + <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + bound.clone(), + ) + .unwrap(); + } + + // One more time and it errors. + assert_noop!( + <Scheduler as Anon<_, _, _>>::schedule(DispatchTime::At(4), None, 127, root(), bound,), + DispatchError::Exhausted + ); + + run_to_block(4); + // All scheduled calls are executed. + assert_eq!(logger::log().len() as u32, max); + }); +} + +/// Cancelling and scheduling does not overflow the agenda but fills holes. +#[test] +fn scheduler_v3_anon_cancel_and_schedule_fills_holes() { + use frame_support::traits::schedule::v3::Anon; + let max: u32 = <Test as Config>::MaxScheduledPerBlock::get(); + assert!(max > 3, "This test only makes sense for MaxScheduledPerBlock > 3"); + + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + let bound = Preimage::bound(call).unwrap(); + let mut addrs = Vec::<_>::default(); + + // Schedule the maximal number allowed per block. + for _ in 0..max { + addrs.push( + <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + bound.clone(), + ) + .unwrap(), + ); + } + // Cancel three of them. + for addr in addrs.into_iter().take(3) { + <Scheduler as Anon<_, _, _>>::cancel(addr).unwrap(); + } + // Schedule three new ones. + for i in 0..3 { + let (_block, index) = <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + bound.clone(), + ) + .unwrap(); + assert_eq!(i, index); + } + + run_to_block(4); + // Maximum number of calls are executed. + assert_eq!(logger::log().len() as u32, max); + }); +} + +/// Re-scheduling does not overflow the agenda but fills holes. +#[test] +fn scheduler_v3_anon_reschedule_fills_holes() { + use frame_support::traits::schedule::v3::Anon; + let max: u32 = <Test as Config>::MaxScheduledPerBlock::get(); + assert!(max > 3, "pre-condition: This test only makes sense for MaxScheduledPerBlock > 3"); + + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + let bound = Preimage::bound(call).unwrap(); + let mut addrs = Vec::<_>::default(); + + // Schedule the maximal number allowed per block. + for _ in 0..max { + addrs.push( + <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + bound.clone(), + ) + .unwrap(), + ); + } + let mut new_addrs = Vec::<_>::default(); + // Reversed last three elements of block 4. + let last_three = addrs.into_iter().rev().take(3).collect::<Vec<_>>(); + // Re-schedule three of them to block 5. + for addr in last_three.iter().cloned() { + new_addrs + .push(<Scheduler as Anon<_, _, _>>::reschedule(addr, DispatchTime::At(5)).unwrap()); + } + // Re-scheduling them back into block 3 should result in the same addrs. + for (old, want) in new_addrs.into_iter().zip(last_three.into_iter().rev()) { + let new = <Scheduler as Anon<_, _, _>>::reschedule(old, DispatchTime::At(4)).unwrap(); + assert_eq!(new, want); + } + + run_to_block(4); + // Maximum number of calls are executed. + assert_eq!(logger::log().len() as u32, max); + }); +} + +/// Re-scheduling into the same block produces a different address +/// if there is still space in the agenda. +#[test] +fn scheduler_v3_anon_reschedule_does_not_resuse_addr_if_agenda_not_full() { + use frame_support::traits::schedule::v3::Anon; + let max: u32 = <Test as Config>::MaxScheduledPerBlock::get(); + assert!(max > 1, "This test only makes sense for MaxScheduledPerBlock > 1"); + + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + + // Schedule both calls. + let addr_1 = <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call.clone()).unwrap(), + ) + .unwrap(); + // Cancel the call. + assert_ok!(<Scheduler as Anon<_, _, _>>::cancel(addr_1)); + let addr_2 = <Scheduler as Anon<_, _, _>>::schedule( + DispatchTime::At(5), + None, + 127, + root(), + Preimage::bound(call).unwrap(), + ) + .unwrap(); + // Re-schedule `call` to block 4. + let addr_3 = <Scheduler as Anon<_, _, _>>::reschedule(addr_2, DispatchTime::At(4)).unwrap(); + + // Should not re-use the address. + assert!(addr_1 != addr_3); + }); +} + +/// The scheduler can be used as `v3::Named` trait. +#[test] +fn scheduler_v3_named_basic_works() { + use frame_support::traits::schedule::v3::Named; + + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + let name = [1u8; 32]; + + // Schedule a call. + let _address = <Scheduler as Named<_, _, _>>::schedule_named( + name, + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap(), + ) + .unwrap(); + + run_to_block(3); + // Did not execute till block 3. + assert!(logger::log().is_empty()); + // Executes in block 4. + run_to_block(4); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + // ... but not again. + run_to_block(100); + assert_eq!(logger::log(), vec![(root(), 42u32)]); + }); +} + +/// A named task can be cancelled by its name. +#[test] +fn scheduler_v3_named_cancel_named_works() { + use frame_support::traits::schedule::v3::Named; + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + let bound = Preimage::bound(call).unwrap(); + let name = [1u8; 32]; + + // Schedule a call. + <Scheduler as Named<_, _, _>>::schedule_named( + name, + DispatchTime::At(4), + None, + 127, + root(), + bound.clone(), + ) + .unwrap(); + // Cancel the call by name. + assert_ok!(<Scheduler as Named<_, _, _>>::cancel_named(name)); + // It did not get executed. + run_to_block(100); + assert!(logger::log().is_empty()); + // Cannot cancel again. + assert_noop!(<Scheduler as Named<_, _, _>>::cancel_named(name), DispatchError::Unavailable); + }); +} + +/// A named task can also be cancelled by its address. +#[test] +fn scheduler_v3_named_cancel_without_name_works() { + use frame_support::traits::schedule::v3::{Anon, Named}; + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + let bound = Preimage::bound(call).unwrap(); + let name = [1u8; 32]; + + // Schedule a call. + let address = <Scheduler as Named<_, _, _>>::schedule_named( + name, + DispatchTime::At(4), + None, + 127, + root(), + bound.clone(), + ) + .unwrap(); + // Cancel the call by address. + assert_ok!(<Scheduler as Anon<_, _, _>>::cancel(address)); + // It did not get executed. + run_to_block(100); + assert!(logger::log().is_empty()); + // Cannot cancel again. + assert_err!(<Scheduler as Anon<_, _, _>>::cancel(address), DispatchError::Unavailable); + }); +} + +/// A named task can be re-scheduled by its name but not by its address. +#[test] +fn scheduler_v3_named_reschedule_named_works() { + use frame_support::traits::schedule::v3::{Anon, Named}; + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + let name = [1u8; 32]; + + // Schedule a call. + let address = <Scheduler as Named<_, _, _>>::schedule_named( + name, + DispatchTime::At(4), + None, + 127, + root(), + Preimage::bound(call).unwrap(), + ) + .unwrap(); + + run_to_block(3); + // Did not execute till block 3. + assert!(logger::log().is_empty()); + + // Cannot re-schedule by address. + assert_noop!( + <Scheduler as Anon<_, _, _>>::reschedule(address, DispatchTime::At(10)), + Error::<Test>::Named, + ); + // Cannot re-schedule into the same block. + assert_noop!( + <Scheduler as Named<_, _, _>>::reschedule_named(name, DispatchTime::At(4)), + Error::<Test>::RescheduleNoChange + ); + // Cannot re-schedule into the past. + assert_noop!( + <Scheduler as Named<_, _, _>>::reschedule_named(name, DispatchTime::At(3)), + Error::<Test>::TargetBlockNumberInPast + ); + // Re-schedule to block 5. + assert_ok!(<Scheduler as Named<_, _, _>>::reschedule_named(name, DispatchTime::At(5))); + // Scheduled for block 5. + run_to_block(4); + assert!(logger::log().is_empty()); + run_to_block(5); + // Does execute in block 5. + assert_eq!(logger::log(), vec![(root(), 42)]); + // Cannot re-schedule executed task. + assert_noop!( + <Scheduler as Named<_, _, _>>::reschedule_named(name, DispatchTime::At(10)), + DispatchError::Unavailable + ); + // Also not by address. + assert_noop!( + <Scheduler as Anon<_, _, _>>::reschedule(address, DispatchTime::At(10)), + DispatchError::Unavailable + ); + }); +} + +#[test] +fn scheduler_v3_named_next_schedule_time_works() { + use frame_support::traits::schedule::v3::{Anon, Named}; + new_test_ext().execute_with(|| { + let call = + RuntimeCall::Logger(LoggerCall::log { i: 42, weight: Weight::from_ref_time(10) }); + let bound = Preimage::bound(call).unwrap(); + let name = [1u8; 32]; + + // Schedule a call. + let address = <Scheduler as Named<_, _, _>>::schedule_named( + name, + DispatchTime::At(4), + None, + 127, + root(), + bound.clone(), + ) + .unwrap(); + + run_to_block(3); + // Did not execute till block 3. + assert!(logger::log().is_empty()); + + // Scheduled for block 4. + assert_eq!(<Scheduler as Named<_, _, _>>::next_dispatch_time(name), Ok(4)); + // Also works by address. + assert_eq!(<Scheduler as Anon<_, _, _>>::next_dispatch_time(address), Ok(4)); + // Block 4 executes it. + run_to_block(4); + assert_eq!(logger::log(), vec![(root(), 42)]); + + // It has no dispatch time anymore. + assert_noop!( + <Scheduler as Named<_, _, _>>::next_dispatch_time(name), + DispatchError::Unavailable + ); + // Also not by address. + assert_noop!( + <Scheduler as Anon<_, _, _>>::next_dispatch_time(address), + DispatchError::Unavailable + ); + }); +} diff --git a/substrate/frame/scheduler/src/weights.rs b/substrate/frame/scheduler/src/weights.rs index afbcf9373b2deb5bd28d1a5f39ea8dabaf9f4236..cb72fe3e2fdda29db5b261bd99dc7ea030275846 100644 --- a/substrate/frame/scheduler/src/weights.rs +++ b/substrate/frame/scheduler/src/weights.rs @@ -18,22 +18,24 @@ //! Autogenerated weights for pallet_scheduler //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2022-05-23, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2022-10-03, STEPS: `50`, REPEAT: 20, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! HOSTNAME: `bm3`, CPU: `Intel(R) Core(TM) i7-7700K CPU @ 4.20GHz` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("dev"), DB CACHE: 1024 // Executed Command: -// ./target/production/substrate +// /home/benchbot/cargo_target_dir/production/substrate // benchmark // pallet -// --chain=dev // --steps=50 // --repeat=20 -// --pallet=pallet_scheduler // --extrinsic=* // --execution=wasm // --wasm-execution=compiled -// --template=./.maintain/frame-weight-template.hbs +// --heap-pages=4096 +// --pallet=pallet_scheduler +// --chain=dev // --output=./frame/scheduler/src/weights.rs +// --template=./.maintain/frame-weight-template.hbs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -44,16 +46,14 @@ use sp_std::marker::PhantomData; /// Weight functions needed for pallet_scheduler. pub trait WeightInfo { - fn on_initialize_periodic_named_resolved(s: u32, ) -> Weight; - fn on_initialize_named_resolved(s: u32, ) -> Weight; - fn on_initialize_periodic_resolved(s: u32, ) -> Weight; - fn on_initialize_resolved(s: u32, ) -> Weight; - fn on_initialize_named_aborted(s: u32, ) -> Weight; - fn on_initialize_aborted(s: u32, ) -> Weight; - fn on_initialize_periodic_named(s: u32, ) -> Weight; - fn on_initialize_periodic(s: u32, ) -> Weight; - fn on_initialize_named(s: u32, ) -> Weight; - fn on_initialize(s: u32, ) -> Weight; + fn service_agendas_base() -> Weight; + fn service_agenda_base(s: u32, ) -> Weight; + fn service_task_base() -> Weight; + fn service_task_fetched(s: u32, ) -> Weight; + fn service_task_named() -> Weight; + fn service_task_periodic() -> Weight; + fn execute_dispatch_signed() -> Weight; + fn execute_dispatch_unsigned() -> Weight; fn schedule(s: u32, ) -> Weight; fn cancel(s: u32, ) -> Weight; fn schedule_named(s: u32, ) -> Weight; @@ -63,149 +63,84 @@ pub trait WeightInfo { /// Weights for pallet_scheduler using the Substrate node and recommended hardware. pub struct SubstrateWeight<T>(PhantomData<T>); impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { - // Storage: Scheduler Agenda (r:2 w:2) - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Scheduler Lookup (r:0 w:1) - fn on_initialize_periodic_named_resolved(s: u32, ) -> Weight { - Weight::from_ref_time(9_994_000 as u64) - // Standard Error: 20_000 - .saturating_add(Weight::from_ref_time(19_843_000 as u64).saturating_mul(s as u64)) + // Storage: Scheduler IncompleteSince (r:1 w:1) + fn service_agendas_base() -> Weight { + Weight::from_ref_time(4_992_000 as u64) .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().reads((3 as u64).saturating_mul(s as u64))) .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((4 as u64).saturating_mul(s as u64))) } // Storage: Scheduler Agenda (r:1 w:1) - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Scheduler Lookup (r:0 w:1) - fn on_initialize_named_resolved(s: u32, ) -> Weight { - Weight::from_ref_time(10_318_000 as u64) - // Standard Error: 17_000 - .saturating_add(Weight::from_ref_time(15_451_000 as u64).saturating_mul(s as u64)) + /// The range of component `s` is `[0, 512]`. + fn service_agenda_base(s: u32, ) -> Weight { + Weight::from_ref_time(4_320_000 as u64) + // Standard Error: 619 + .saturating_add(Weight::from_ref_time(336_713 as u64).saturating_mul(s as u64)) .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().reads((2 as u64).saturating_mul(s as u64))) .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((3 as u64).saturating_mul(s as u64))) } - // Storage: Scheduler Agenda (r:2 w:2) - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) - fn on_initialize_periodic_resolved(s: u32, ) -> Weight { - Weight::from_ref_time(11_675_000 as u64) - // Standard Error: 17_000 - .saturating_add(Weight::from_ref_time(17_019_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().reads((3 as u64).saturating_mul(s as u64))) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((3 as u64).saturating_mul(s as u64))) + fn service_task_base() -> Weight { + Weight::from_ref_time(10_864_000 as u64) } - // Storage: Scheduler Agenda (r:1 w:1) // Storage: Preimage PreimageFor (r:1 w:1) // Storage: Preimage StatusFor (r:1 w:1) - fn on_initialize_resolved(s: u32, ) -> Weight { - Weight::from_ref_time(11_934_000 as u64) - // Standard Error: 11_000 - .saturating_add(Weight::from_ref_time(14_134_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().reads((2 as u64).saturating_mul(s as u64))) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((2 as u64).saturating_mul(s as u64))) - } - // Storage: Scheduler Agenda (r:2 w:2) - // Storage: Preimage PreimageFor (r:1 w:0) - // Storage: Scheduler Lookup (r:0 w:1) - fn on_initialize_named_aborted(s: u32, ) -> Weight { - Weight::from_ref_time(7_279_000 as u64) - // Standard Error: 5_000 - .saturating_add(Weight::from_ref_time(5_388_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(s as u64))) - .saturating_add(T::DbWeight::get().writes(2 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(s as u64))) - } - // Storage: Scheduler Agenda (r:2 w:2) - // Storage: Preimage PreimageFor (r:1 w:0) - fn on_initialize_aborted(s: u32, ) -> Weight { - Weight::from_ref_time(8_619_000 as u64) - // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(2_969_000 as u64).saturating_mul(s as u64)) + /// The range of component `s` is `[128, 4194304]`. + fn service_task_fetched(s: u32, ) -> Weight { + Weight::from_ref_time(24_586_000 as u64) + // Standard Error: 1 + .saturating_add(Weight::from_ref_time(1_138 as u64).saturating_mul(s as u64)) .saturating_add(T::DbWeight::get().reads(2 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(s as u64))) .saturating_add(T::DbWeight::get().writes(2 as u64)) } - // Storage: Scheduler Agenda (r:2 w:2) // Storage: Scheduler Lookup (r:0 w:1) - fn on_initialize_periodic_named(s: u32, ) -> Weight { - Weight::from_ref_time(16_129_000 as u64) - // Standard Error: 7_000 - .saturating_add(Weight::from_ref_time(9_772_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(s as u64))) + fn service_task_named() -> Weight { + Weight::from_ref_time(13_127_000 as u64) .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((2 as u64).saturating_mul(s as u64))) } - // Storage: Scheduler Agenda (r:2 w:2) - fn on_initialize_periodic(s: u32, ) -> Weight { - Weight::from_ref_time(15_785_000 as u64) - // Standard Error: 5_000 - .saturating_add(Weight::from_ref_time(7_208_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().reads((1 as u64).saturating_mul(s as u64))) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(s as u64))) + fn service_task_periodic() -> Weight { + Weight::from_ref_time(11_053_000 as u64) } - // Storage: Scheduler Agenda (r:1 w:1) - // Storage: Scheduler Lookup (r:0 w:1) - fn on_initialize_named(s: u32, ) -> Weight { - Weight::from_ref_time(15_778_000 as u64) - // Standard Error: 3_000 - .saturating_add(Weight::from_ref_time(5_597_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) - .saturating_add(T::DbWeight::get().writes((1 as u64).saturating_mul(s as u64))) + fn execute_dispatch_signed() -> Weight { + Weight::from_ref_time(4_158_000 as u64) } - // Storage: Scheduler Agenda (r:1 w:1) - fn on_initialize(s: u32, ) -> Weight { - Weight::from_ref_time(15_912_000 as u64) - // Standard Error: 5_000 - .saturating_add(Weight::from_ref_time(4_530_000 as u64).saturating_mul(s as u64)) - .saturating_add(T::DbWeight::get().reads(1 as u64)) - .saturating_add(T::DbWeight::get().writes(1 as u64)) + fn execute_dispatch_unsigned() -> Weight { + Weight::from_ref_time(4_104_000 as u64) } // Storage: Scheduler Agenda (r:1 w:1) + /// The range of component `s` is `[0, 511]`. fn schedule(s: u32, ) -> Weight { - Weight::from_ref_time(18_013_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(87_000 as u64).saturating_mul(s as u64)) + Weight::from_ref_time(20_074_000 as u64) + // Standard Error: 765 + .saturating_add(Weight::from_ref_time(343_285 as u64).saturating_mul(s as u64)) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(1 as u64)) } // Storage: Scheduler Agenda (r:1 w:1) // Storage: Scheduler Lookup (r:0 w:1) + /// The range of component `s` is `[1, 512]`. fn cancel(s: u32, ) -> Weight { - Weight::from_ref_time(18_131_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(595_000 as u64).saturating_mul(s as u64)) + Weight::from_ref_time(21_509_000 as u64) + // Standard Error: 708 + .saturating_add(Weight::from_ref_time(323_013 as u64).saturating_mul(s as u64)) .saturating_add(T::DbWeight::get().reads(1 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Scheduler Lookup (r:1 w:1) // Storage: Scheduler Agenda (r:1 w:1) + /// The range of component `s` is `[0, 511]`. fn schedule_named(s: u32, ) -> Weight { - Weight::from_ref_time(21_230_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(98_000 as u64).saturating_mul(s as u64)) + Weight::from_ref_time(22_427_000 as u64) + // Standard Error: 850 + .saturating_add(Weight::from_ref_time(357_265 as u64).saturating_mul(s as u64)) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } // Storage: Scheduler Lookup (r:1 w:1) // Storage: Scheduler Agenda (r:1 w:1) + /// The range of component `s` is `[1, 512]`. fn cancel_named(s: u32, ) -> Weight { - Weight::from_ref_time(20_139_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(595_000 as u64).saturating_mul(s as u64)) + Weight::from_ref_time(22_875_000 as u64) + // Standard Error: 693 + .saturating_add(Weight::from_ref_time(336_643 as u64).saturating_mul(s as u64)) .saturating_add(T::DbWeight::get().reads(2 as u64)) .saturating_add(T::DbWeight::get().writes(2 as u64)) } @@ -213,149 +148,84 @@ impl<T: frame_system::Config> WeightInfo for SubstrateWeight<T> { // For backwards compatibility and tests impl WeightInfo for () { - // Storage: Scheduler Agenda (r:2 w:2) - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Scheduler Lookup (r:0 w:1) - fn on_initialize_periodic_named_resolved(s: u32, ) -> Weight { - Weight::from_ref_time(9_994_000 as u64) - // Standard Error: 20_000 - .saturating_add(Weight::from_ref_time(19_843_000 as u64).saturating_mul(s as u64)) + // Storage: Scheduler IncompleteSince (r:1 w:1) + fn service_agendas_base() -> Weight { + Weight::from_ref_time(4_992_000 as u64) .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().reads((3 as u64).saturating_mul(s as u64))) .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((4 as u64).saturating_mul(s as u64))) } // Storage: Scheduler Agenda (r:1 w:1) - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) - // Storage: Scheduler Lookup (r:0 w:1) - fn on_initialize_named_resolved(s: u32, ) -> Weight { - Weight::from_ref_time(10_318_000 as u64) - // Standard Error: 17_000 - .saturating_add(Weight::from_ref_time(15_451_000 as u64).saturating_mul(s as u64)) + /// The range of component `s` is `[0, 512]`. + fn service_agenda_base(s: u32, ) -> Weight { + Weight::from_ref_time(4_320_000 as u64) + // Standard Error: 619 + .saturating_add(Weight::from_ref_time(336_713 as u64).saturating_mul(s as u64)) .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().reads((2 as u64).saturating_mul(s as u64))) .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((3 as u64).saturating_mul(s as u64))) } - // Storage: Scheduler Agenda (r:2 w:2) - // Storage: Preimage PreimageFor (r:1 w:1) - // Storage: Preimage StatusFor (r:1 w:1) - fn on_initialize_periodic_resolved(s: u32, ) -> Weight { - Weight::from_ref_time(11_675_000 as u64) - // Standard Error: 17_000 - .saturating_add(Weight::from_ref_time(17_019_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().reads((3 as u64).saturating_mul(s as u64))) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((3 as u64).saturating_mul(s as u64))) + fn service_task_base() -> Weight { + Weight::from_ref_time(10_864_000 as u64) } - // Storage: Scheduler Agenda (r:1 w:1) // Storage: Preimage PreimageFor (r:1 w:1) // Storage: Preimage StatusFor (r:1 w:1) - fn on_initialize_resolved(s: u32, ) -> Weight { - Weight::from_ref_time(11_934_000 as u64) - // Standard Error: 11_000 - .saturating_add(Weight::from_ref_time(14_134_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().reads((2 as u64).saturating_mul(s as u64))) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(s as u64))) - } - // Storage: Scheduler Agenda (r:2 w:2) - // Storage: Preimage PreimageFor (r:1 w:0) - // Storage: Scheduler Lookup (r:0 w:1) - fn on_initialize_named_aborted(s: u32, ) -> Weight { - Weight::from_ref_time(7_279_000 as u64) - // Standard Error: 5_000 - .saturating_add(Weight::from_ref_time(5_388_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(s as u64))) - .saturating_add(RocksDbWeight::get().writes(2 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(s as u64))) - } - // Storage: Scheduler Agenda (r:2 w:2) - // Storage: Preimage PreimageFor (r:1 w:0) - fn on_initialize_aborted(s: u32, ) -> Weight { - Weight::from_ref_time(8_619_000 as u64) - // Standard Error: 4_000 - .saturating_add(Weight::from_ref_time(2_969_000 as u64).saturating_mul(s as u64)) + /// The range of component `s` is `[128, 4194304]`. + fn service_task_fetched(s: u32, ) -> Weight { + Weight::from_ref_time(24_586_000 as u64) + // Standard Error: 1 + .saturating_add(Weight::from_ref_time(1_138 as u64).saturating_mul(s as u64)) .saturating_add(RocksDbWeight::get().reads(2 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(s as u64))) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } - // Storage: Scheduler Agenda (r:2 w:2) // Storage: Scheduler Lookup (r:0 w:1) - fn on_initialize_periodic_named(s: u32, ) -> Weight { - Weight::from_ref_time(16_129_000 as u64) - // Standard Error: 7_000 - .saturating_add(Weight::from_ref_time(9_772_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(s as u64))) + fn service_task_named() -> Weight { + Weight::from_ref_time(13_127_000 as u64) .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((2 as u64).saturating_mul(s as u64))) } - // Storage: Scheduler Agenda (r:2 w:2) - fn on_initialize_periodic(s: u32, ) -> Weight { - Weight::from_ref_time(15_785_000 as u64) - // Standard Error: 5_000 - .saturating_add(Weight::from_ref_time(7_208_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().reads((1 as u64).saturating_mul(s as u64))) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(s as u64))) + fn service_task_periodic() -> Weight { + Weight::from_ref_time(11_053_000 as u64) } - // Storage: Scheduler Agenda (r:1 w:1) - // Storage: Scheduler Lookup (r:0 w:1) - fn on_initialize_named(s: u32, ) -> Weight { - Weight::from_ref_time(15_778_000 as u64) - // Standard Error: 3_000 - .saturating_add(Weight::from_ref_time(5_597_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) - .saturating_add(RocksDbWeight::get().writes((1 as u64).saturating_mul(s as u64))) + fn execute_dispatch_signed() -> Weight { + Weight::from_ref_time(4_158_000 as u64) } - // Storage: Scheduler Agenda (r:1 w:1) - fn on_initialize(s: u32, ) -> Weight { - Weight::from_ref_time(15_912_000 as u64) - // Standard Error: 5_000 - .saturating_add(Weight::from_ref_time(4_530_000 as u64).saturating_mul(s as u64)) - .saturating_add(RocksDbWeight::get().reads(1 as u64)) - .saturating_add(RocksDbWeight::get().writes(1 as u64)) + fn execute_dispatch_unsigned() -> Weight { + Weight::from_ref_time(4_104_000 as u64) } // Storage: Scheduler Agenda (r:1 w:1) + /// The range of component `s` is `[0, 511]`. fn schedule(s: u32, ) -> Weight { - Weight::from_ref_time(18_013_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(87_000 as u64).saturating_mul(s as u64)) + Weight::from_ref_time(20_074_000 as u64) + // Standard Error: 765 + .saturating_add(Weight::from_ref_time(343_285 as u64).saturating_mul(s as u64)) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(1 as u64)) } // Storage: Scheduler Agenda (r:1 w:1) // Storage: Scheduler Lookup (r:0 w:1) + /// The range of component `s` is `[1, 512]`. fn cancel(s: u32, ) -> Weight { - Weight::from_ref_time(18_131_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(595_000 as u64).saturating_mul(s as u64)) + Weight::from_ref_time(21_509_000 as u64) + // Standard Error: 708 + .saturating_add(Weight::from_ref_time(323_013 as u64).saturating_mul(s as u64)) .saturating_add(RocksDbWeight::get().reads(1 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Scheduler Lookup (r:1 w:1) // Storage: Scheduler Agenda (r:1 w:1) + /// The range of component `s` is `[0, 511]`. fn schedule_named(s: u32, ) -> Weight { - Weight::from_ref_time(21_230_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(98_000 as u64).saturating_mul(s as u64)) + Weight::from_ref_time(22_427_000 as u64) + // Standard Error: 850 + .saturating_add(Weight::from_ref_time(357_265 as u64).saturating_mul(s as u64)) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } // Storage: Scheduler Lookup (r:1 w:1) // Storage: Scheduler Agenda (r:1 w:1) + /// The range of component `s` is `[1, 512]`. fn cancel_named(s: u32, ) -> Weight { - Weight::from_ref_time(20_139_000 as u64) - // Standard Error: 1_000 - .saturating_add(Weight::from_ref_time(595_000 as u64).saturating_mul(s as u64)) + Weight::from_ref_time(22_875_000 as u64) + // Standard Error: 693 + .saturating_add(Weight::from_ref_time(336_643 as u64).saturating_mul(s as u64)) .saturating_add(RocksDbWeight::get().reads(2 as u64)) .saturating_add(RocksDbWeight::get().writes(2 as u64)) } diff --git a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs index acbcb65a3e986add41cd080a8785a2b0b99e5f95..1551d85ea4c96fac8fae8d38280610929e85d3a1 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/expand/origin.rs @@ -169,6 +169,10 @@ pub fn expand_outer_origin( &self.caller } + fn into_caller(self) -> Self::PalletsOrigin { + self.caller + } + fn try_with_caller<R>( mut self, f: impl FnOnce(Self::PalletsOrigin) -> Result<R, Self::PalletsOrigin>, @@ -190,13 +194,6 @@ pub fn expand_outer_origin( fn signed(by: Self::AccountId) -> Self { #system_path::RawOrigin::Signed(by).into() } - - fn as_signed(self) -> Option<Self::AccountId> { - match self.caller { - OriginCaller::system(#system_path::RawOrigin::Signed(by)) => Some(by), - _ => None, - } - } } #[derive( @@ -215,7 +212,6 @@ pub fn expand_outer_origin( // For backwards compatibility and ease of accessing these functions. #[allow(dead_code)] impl RuntimeOrigin { - #[doc = #doc_string_none_origin] pub fn none() -> Self { <RuntimeOrigin as #scrate::traits::OriginTrait>::none() @@ -238,6 +234,21 @@ pub fn expand_outer_origin( } } + impl #scrate::traits::CallerTrait<<#runtime as #system_path::Config>::AccountId> for OriginCaller { + fn into_system(self) -> Option<#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { + match self { + OriginCaller::system(x) => Some(x), + _ => None, + } + } + fn as_system_ref(&self) -> Option<&#system_path::RawOrigin<<#runtime as #system_path::Config>::AccountId>> { + match &self { + OriginCaller::system(o) => Some(o), + _ => None, + } + } + } + impl TryFrom<OriginCaller> for #system_path::Origin<#runtime> { type Error = OriginCaller; fn try_from(x: OriginCaller) diff --git a/substrate/frame/support/src/dispatch.rs b/substrate/frame/support/src/dispatch.rs index db2bc90658ee2e4b02994c42b08729e1cce77ff1..d497a672e2970c6fa0e01452d4a5553df46bbdfe 100644 --- a/substrate/frame/support/src/dispatch.rs +++ b/substrate/frame/support/src/dispatch.rs @@ -3181,8 +3181,8 @@ mod tests { dispatch::{DispatchClass, DispatchInfo, Pays}, metadata::*, traits::{ - CrateVersion, Get, GetCallName, IntegrityTest, OnFinalize, OnIdle, OnInitialize, - OnRuntimeUpgrade, PalletInfo, + CallerTrait, CrateVersion, Get, GetCallName, IntegrityTest, OnFinalize, OnIdle, + OnInitialize, OnRuntimeUpgrade, PalletInfo, }, }; use sp_weights::RuntimeDbWeight; @@ -3300,6 +3300,16 @@ mod tests { } } + impl CallerTrait<<TraitImpl as system::Config>::AccountId> for OuterOrigin { + fn into_system(self) -> Option<RawOrigin<<TraitImpl as system::Config>::AccountId>> { + unimplemented!("Not required in tests!") + } + + fn as_system_ref(&self) -> Option<&RawOrigin<<TraitImpl as system::Config>::AccountId>> { + unimplemented!("Not required in tests!") + } + } + impl crate::traits::OriginTrait for OuterOrigin { type Call = <TraitImpl as system::Config>::RuntimeCall; type PalletsOrigin = OuterOrigin; @@ -3325,6 +3335,10 @@ mod tests { unimplemented!("Not required in tests!") } + fn into_caller(self) -> Self::PalletsOrigin { + unimplemented!("Not required in tests!") + } + fn try_with_caller<R>( self, _f: impl FnOnce(Self::PalletsOrigin) -> Result<R, Self::PalletsOrigin>, @@ -3344,6 +3358,9 @@ mod tests { fn as_signed(self) -> Option<Self::AccountId> { unimplemented!("Not required in tests!") } + fn as_system_ref(&self) -> Option<&RawOrigin<Self::AccountId>> { + unimplemented!("Not required in tests!") + } } impl system::Config for TraitImpl { diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index d51c32649a797c52debd1ffa908e658243e4349d..302d3354dae5e1b2278fda6485b095b5a9f4ccc4 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -58,10 +58,11 @@ pub use misc::{ Backing, ConstBool, ConstI128, ConstI16, ConstI32, ConstI64, ConstI8, ConstU128, ConstU16, ConstU32, ConstU64, ConstU8, DefensiveSaturating, EnsureInherentsAreFirst, EqualPrivilegeOnly, EstimateCallFee, ExecuteBlock, ExtrinsicCall, Get, GetBacking, GetDefault, HandleLifetime, - IsSubType, IsType, Len, OffchainWorker, OnKilledAccount, OnNewAccount, PreimageProvider, - PreimageRecipient, PrivilegeCmp, SameOrOther, Time, TryCollect, TryDrop, TypedGet, UnixTime, - WrapperKeepOpaque, WrapperOpaque, + IsSubType, IsType, Len, OffchainWorker, OnKilledAccount, OnNewAccount, PrivilegeCmp, + SameOrOther, Time, TryCollect, TryDrop, TypedGet, UnixTime, WrapperKeepOpaque, WrapperOpaque, }; +#[allow(deprecated)] +pub use misc::{PreimageProvider, PreimageRecipient}; #[doc(hidden)] pub use misc::{DEFENSIVE_OP_INTERNAL_ERROR, DEFENSIVE_OP_PUBLIC_ERROR}; @@ -96,8 +97,9 @@ mod dispatch; #[allow(deprecated)] pub use dispatch::EnsureOneOf; pub use dispatch::{ - AsEnsureOriginWithArg, EitherOf, EitherOfDiverse, EnsureOrigin, EnsureOriginWithArg, - MapSuccess, NeverEnsureOrigin, OriginTrait, TryMapSuccess, UnfilteredDispatchable, + AsEnsureOriginWithArg, CallerTrait, EitherOf, EitherOfDiverse, EnsureOrigin, + EnsureOriginWithArg, MapSuccess, NeverEnsureOrigin, OriginTrait, TryMapSuccess, + UnfilteredDispatchable, }; mod voting; @@ -106,6 +108,9 @@ pub use voting::{ U128CurrencyToVote, VoteTally, }; +mod preimages; +pub use preimages::{Bounded, BoundedInline, FetchResult, Hash, QueryPreimage, StorePreimage}; + #[cfg(feature = "try-runtime")] mod try_runtime; #[cfg(feature = "try-runtime")] diff --git a/substrate/frame/support/src/traits/dispatch.rs b/substrate/frame/support/src/traits/dispatch.rs index c0e7e32a5529e9d825cdb31c0b665ad5f1d7d440..b96cfae4500e23c5c731afc151dc64d07687ceee 100644 --- a/substrate/frame/support/src/traits/dispatch.rs +++ b/substrate/frame/support/src/traits/dispatch.rs @@ -236,17 +236,25 @@ pub trait UnfilteredDispatchable { fn dispatch_bypass_filter(self, origin: Self::RuntimeOrigin) -> DispatchResultWithPostInfo; } +/// The trait implemented by the overarching enumeration of the different pallets' origins. +/// Unlike `OriginTrait` impls, this does not include any kind of dispatch/call filter. Also, this +/// trait is more flexible in terms of how it can be used: it is a `Parameter` and `Member`, so it +/// can be used as dispatchable parameters as well as in storage items. +pub trait CallerTrait<AccountId>: Parameter + Member + From<RawOrigin<AccountId>> { + /// Extract the signer from the message if it is a `Signed` origin. + fn into_system(self) -> Option<RawOrigin<AccountId>>; + + /// Extract a reference to the system-level `RawOrigin` if it is that. + fn as_system_ref(&self) -> Option<&RawOrigin<AccountId>>; +} + /// Methods available on `frame_system::Config::RuntimeOrigin`. pub trait OriginTrait: Sized { /// Runtime call type, as in `frame_system::Config::Call` type Call; /// The caller origin, overarching type of all pallets origins. - type PalletsOrigin: Parameter - + Member - + Into<Self> - + From<RawOrigin<Self::AccountId>> - + MaxEncodedLen; + type PalletsOrigin: Into<Self> + CallerTrait<Self::AccountId> + MaxEncodedLen; /// The AccountId used across the system. type AccountId; @@ -266,9 +274,12 @@ pub trait OriginTrait: Sized { /// For root origin caller, the filters are bypassed and true is returned. fn filter_call(&self, call: &Self::Call) -> bool; - /// Get the caller. + /// Get a reference to the caller (`CallerTrait` impl). fn caller(&self) -> &Self::PalletsOrigin; + /// Consume `self` and return the caller. + fn into_caller(self) -> Self::PalletsOrigin; + /// Do something with the caller, consuming self but returning it if the caller was unused. fn try_with_caller<R>( self, @@ -285,7 +296,20 @@ pub trait OriginTrait: Sized { fn signed(by: Self::AccountId) -> Self; /// Extract the signer from the message if it is a `Signed` origin. - fn as_signed(self) -> Option<Self::AccountId>; + fn as_signed(self) -> Option<Self::AccountId> { + self.into_caller().into_system().and_then(|s| { + if let RawOrigin::Signed(who) = s { + Some(who) + } else { + None + } + }) + } + + /// Extract a reference to the sytsem origin, if that's what the caller is. + fn as_system_ref(&self) -> Option<&RawOrigin<Self::AccountId>> { + self.caller().as_system_ref() + } } #[cfg(test)] diff --git a/substrate/frame/support/src/traits/misc.rs b/substrate/frame/support/src/traits/misc.rs index 7fc4a6fb08a5ac914a8b5c7b69e0a86266c43357..5a976478fa7c4769a62861383091dadf172e6957 100644 --- a/substrate/frame/support/src/traits/misc.rs +++ b/substrate/frame/support/src/traits/misc.rs @@ -932,7 +932,7 @@ pub trait PreimageRecipient<Hash>: PreimageProvider<Hash> { /// Maximum size of a preimage. type MaxSize: Get<u32>; - /// Store the bytes of a preimage on chain. + /// Store the bytes of a preimage on chain infallible due to the bounded type. fn note_preimage(bytes: crate::BoundedVec<u8, Self::MaxSize>); /// Clear a previously noted preimage. This is infallible and should be treated more like a diff --git a/substrate/frame/support/src/traits/preimages.rs b/substrate/frame/support/src/traits/preimages.rs new file mode 100644 index 0000000000000000000000000000000000000000..594532ba969036ffc04b4b6bf8be18bc4b2f3f4e --- /dev/null +++ b/substrate/frame/support/src/traits/preimages.rs @@ -0,0 +1,317 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 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. + +//! Stuff for dealing with 32-byte hashed preimages. + +use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; +use sp_core::{RuntimeDebug, H256}; +use sp_io::hashing::blake2_256; +use sp_runtime::{traits::ConstU32, DispatchError}; +use sp_std::borrow::Cow; + +pub type Hash = H256; +pub type BoundedInline = crate::BoundedVec<u8, ConstU32<128>>; + +#[derive( + Encode, Decode, MaxEncodedLen, Clone, Eq, PartialEq, scale_info::TypeInfo, RuntimeDebug, +)] +#[codec(mel_bound())] +pub enum Bounded<T> { + /// A Blake2 256 hash with no preimage length. We + /// do not support creation of this except for transitioning from legacy state. + /// In the future we will make this a pure `Dummy` item storing only the final `dummy` field. + Legacy { hash: Hash, dummy: sp_std::marker::PhantomData<T> }, + /// A an bounded `Call`. Its encoding must be at most 128 bytes. + Inline(BoundedInline), + /// A Blake2-256 hash of the call together with an upper limit for its size. + Lookup { hash: Hash, len: u32 }, +} + +impl<T> Bounded<T> { + /// Casts the wrapped type into something that encodes alike. + /// + /// # Examples + /// ``` + /// use frame_support::traits::Bounded; + /// + /// // Transmute from `String` to `&str`. + /// let x: Bounded<String> = Bounded::Inline(Default::default()); + /// let _: Bounded<&str> = x.transmute(); + /// ``` + pub fn transmute<S: Encode>(self) -> Bounded<S> + where + T: Encode + EncodeLike<S>, + { + use Bounded::*; + match self { + Legacy { hash, .. } => Legacy { hash, dummy: sp_std::marker::PhantomData }, + Inline(x) => Inline(x), + Lookup { hash, len } => Lookup { hash, len }, + } + } + + /// Returns the hash of the preimage. + /// + /// The hash is re-calculated every time if the preimage is inlined. + pub fn hash(&self) -> H256 { + use Bounded::*; + match self { + Legacy { hash, .. } => *hash, + Inline(x) => blake2_256(x.as_ref()).into(), + Lookup { hash, .. } => *hash, + } + } +} + +// The maximum we expect a single legacy hash lookup to be. +const MAX_LEGACY_LEN: u32 = 1_000_000; + +impl<T> Bounded<T> { + /// Returns the length of the preimage or `None` if the length is unknown. + pub fn len(&self) -> Option<u32> { + match self { + Self::Legacy { .. } => None, + Self::Inline(i) => Some(i.len() as u32), + Self::Lookup { len, .. } => Some(*len), + } + } + + /// Returns whether the image will require a lookup to be peeked. + pub fn lookup_needed(&self) -> bool { + match self { + Self::Inline(..) => false, + Self::Legacy { .. } | Self::Lookup { .. } => true, + } + } + + /// The maximum length of the lookup that is needed to peek `Self`. + pub fn lookup_len(&self) -> Option<u32> { + match self { + Self::Inline(..) => None, + Self::Legacy { .. } => Some(MAX_LEGACY_LEN), + Self::Lookup { len, .. } => Some(*len), + } + } + + /// Constructs a `Lookup` bounded item. + pub fn unrequested(hash: Hash, len: u32) -> Self { + Self::Lookup { hash, len } + } + + /// Constructs a `Legacy` bounded item. + #[deprecated = "This API is only for transitioning to Scheduler v3 API"] + pub fn from_legacy_hash(hash: impl Into<Hash>) -> Self { + Self::Legacy { hash: hash.into(), dummy: sp_std::marker::PhantomData } + } +} + +pub type FetchResult = Result<Cow<'static, [u8]>, DispatchError>; + +/// A interface for looking up preimages from their hash on chain. +pub trait QueryPreimage { + /// Returns whether a preimage exists for a given hash and if so its length. + fn len(hash: &Hash) -> Option<u32>; + + /// Returns the preimage for a given hash. If given, `len` must be the size of the preimage. + fn fetch(hash: &Hash, len: Option<u32>) -> FetchResult; + + /// Returns whether a preimage request exists for a given hash. + fn is_requested(hash: &Hash) -> bool; + + /// Request that someone report a preimage. Providers use this to optimise the economics for + /// preimage reporting. + fn request(hash: &Hash); + + /// Cancel a previous preimage request. + fn unrequest(hash: &Hash); + + /// Request that the data required for decoding the given `bounded` value is made available. + fn hold<T>(bounded: &Bounded<T>) { + use Bounded::*; + match bounded { + Inline(..) => {}, + Legacy { hash, .. } | Lookup { hash, .. } => Self::request(hash), + } + } + + /// No longer request that the data required for decoding the given `bounded` value is made + /// available. + fn drop<T>(bounded: &Bounded<T>) { + use Bounded::*; + match bounded { + Inline(..) => {}, + Legacy { hash, .. } | Lookup { hash, .. } => Self::unrequest(hash), + } + } + + /// Check to see if all data required for the given `bounded` value is available for its + /// decoding. + fn have<T>(bounded: &Bounded<T>) -> bool { + use Bounded::*; + match bounded { + Inline(..) => true, + Legacy { hash, .. } | Lookup { hash, .. } => Self::len(hash).is_some(), + } + } + + /// Create a `Bounded` instance based on the `hash` and `len` of the encoded value. This may not + /// be `peek`-able or `realize`-able. + fn pick<T>(hash: Hash, len: u32) -> Bounded<T> { + Self::request(&hash); + Bounded::Lookup { hash, len } + } + + /// Convert the given `bounded` instance back into its original instance, also returning the + /// exact size of its encoded form if it needed to be looked-up from a stored preimage). + /// + /// NOTE: This does not remove any data needed for realization. If you will no longer use the + /// `bounded`, call `realize` instead or call `drop` afterwards. + fn peek<T: Decode>(bounded: &Bounded<T>) -> Result<(T, Option<u32>), DispatchError> { + use Bounded::*; + match bounded { + Inline(data) => T::decode(&mut &data[..]).ok().map(|x| (x, None)), + Lookup { hash, len } => { + let data = Self::fetch(hash, Some(*len))?; + T::decode(&mut &data[..]).ok().map(|x| (x, Some(data.len() as u32))) + }, + Legacy { hash, .. } => { + let data = Self::fetch(hash, None)?; + T::decode(&mut &data[..]).ok().map(|x| (x, Some(data.len() as u32))) + }, + } + .ok_or(DispatchError::Corruption) + } + + /// Convert the given `bounded` value back into its original instance. If successful, + /// `drop` any data backing it. This will not break the realisability of independently + /// created instances of `Bounded` which happen to have identical data. + fn realize<T: Decode>(bounded: &Bounded<T>) -> Result<(T, Option<u32>), DispatchError> { + let r = Self::peek(bounded)?; + Self::drop(bounded); + Ok(r) + } +} + +/// A interface for managing preimages to hashes on chain. +/// +/// Note that this API does not assume any underlying user is calling, and thus +/// does not handle any preimage ownership or fees. Other system level logic that +/// uses this API should implement that on their own side. +pub trait StorePreimage: QueryPreimage { + /// The maximum length of preimage we can store. + /// + /// This is the maximum length of the *encoded* value that can be passed to `bound`. + const MAX_LENGTH: usize; + + /// Request and attempt to store the bytes of a preimage on chain. + /// + /// May return `DispatchError::Exhausted` if the preimage is just too big. + fn note(bytes: Cow<[u8]>) -> Result<Hash, DispatchError>; + + /// Attempt to clear a previously noted preimage. Exactly the same as `unrequest` but is + /// provided for symmetry. + fn unnote(hash: &Hash) { + Self::unrequest(hash) + } + + /// Convert an otherwise unbounded or large value into a type ready for placing in storage. The + /// result is a type whose `MaxEncodedLen` is 131 bytes. + /// + /// NOTE: Once this API is used, you should use either `drop` or `realize`. + fn bound<T: Encode>(t: T) -> Result<Bounded<T>, DispatchError> { + let data = t.encode(); + let len = data.len() as u32; + Ok(match BoundedInline::try_from(data) { + Ok(bounded) => Bounded::Inline(bounded), + Err(unbounded) => Bounded::Lookup { hash: Self::note(unbounded.into())?, len }, + }) + } +} + +impl QueryPreimage for () { + fn len(_: &Hash) -> Option<u32> { + None + } + fn fetch(_: &Hash, _: Option<u32>) -> FetchResult { + Err(DispatchError::Unavailable) + } + fn is_requested(_: &Hash) -> bool { + false + } + fn request(_: &Hash) {} + fn unrequest(_: &Hash) {} +} + +impl StorePreimage for () { + const MAX_LENGTH: usize = 0; + fn note(_: Cow<[u8]>) -> Result<Hash, DispatchError> { + Err(DispatchError::Exhausted) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{bounded_vec, BoundedVec}; + + #[test] + fn bounded_size_is_correct() { + assert_eq!(<Bounded<Vec<u8>> as MaxEncodedLen>::max_encoded_len(), 131); + } + + #[test] + fn bounded_basic_works() { + let data: BoundedVec<u8, _> = bounded_vec![b'a', b'b', b'c']; + let len = data.len() as u32; + let hash = blake2_256(&data).into(); + + // Inline works + { + let bound: Bounded<Vec<u8>> = Bounded::Inline(data.clone()); + assert_eq!(bound.hash(), hash); + assert_eq!(bound.len(), Some(len)); + assert!(!bound.lookup_needed()); + assert_eq!(bound.lookup_len(), None); + } + // Legacy works + { + let bound: Bounded<Vec<u8>> = Bounded::Legacy { hash, dummy: Default::default() }; + assert_eq!(bound.hash(), hash); + assert_eq!(bound.len(), None); + assert!(bound.lookup_needed()); + assert_eq!(bound.lookup_len(), Some(1_000_000)); + } + // Lookup works + { + let bound: Bounded<Vec<u8>> = Bounded::Lookup { hash, len: data.len() as u32 }; + assert_eq!(bound.hash(), hash); + assert_eq!(bound.len(), Some(len)); + assert!(bound.lookup_needed()); + assert_eq!(bound.lookup_len(), Some(len)); + } + } + + #[test] + fn bounded_transmuting_works() { + let data: BoundedVec<u8, _> = bounded_vec![b'a', b'b', b'c']; + + // Transmute a `String` into a `&str`. + let x: Bounded<String> = Bounded::Inline(data.clone()); + let y: Bounded<&str> = x.transmute(); + assert_eq!(y, Bounded::Inline(data)); + } +} diff --git a/substrate/frame/support/src/traits/schedule.rs b/substrate/frame/support/src/traits/schedule.rs index 0dbbbd9e2a553e67dbbb08025aec2017efaf62ca..b8e6a7f80790436f2b7a4380533d46dbafe38a70 100644 --- a/substrate/frame/support/src/traits/schedule.rs +++ b/substrate/frame/support/src/traits/schedule.rs @@ -17,6 +17,8 @@ //! Traits and associated utilities for scheduling dispatchables in FRAME. +#[allow(deprecated)] +use super::PreimageProvider; use codec::{Codec, Decode, Encode, EncodeLike, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime::{traits::Saturating, DispatchError, RuntimeDebug}; @@ -128,6 +130,7 @@ impl<T: Decode, H> MaybeHashed<T, H> { } } +// TODO: deprecate pub mod v1 { use super::*; @@ -283,6 +286,7 @@ pub mod v1 { } } +// TODO: deprecate pub mod v2 { use super::*; @@ -375,6 +379,97 @@ pub mod v2 { } } -pub use v1::*; +pub mod v3 { + use super::*; + use crate::traits::Bounded; -use super::PreimageProvider; + /// A type that can be used as a scheduler. + pub trait Anon<BlockNumber, Call, Origin> { + /// An address which can be used for removing a scheduled task. + type Address: Codec + MaxEncodedLen + Clone + Eq + EncodeLike + Debug + TypeInfo; + + /// Schedule a dispatch to happen at the beginning of some block in the future. + /// + /// This is not named. + fn schedule( + when: DispatchTime<BlockNumber>, + maybe_periodic: Option<Period<BlockNumber>>, + priority: Priority, + origin: Origin, + call: Bounded<Call>, + ) -> Result<Self::Address, DispatchError>; + + /// Cancel a scheduled task. If periodic, then it will cancel all further instances of that, + /// also. + /// + /// Will return an `Unavailable` error if the `address` is invalid. + /// + /// NOTE: This guaranteed to work only *before* the point that it is due to be executed. + /// If it ends up being delayed beyond the point of execution, then it cannot be cancelled. + /// + /// NOTE2: This will not work to cancel periodic tasks after their initial execution. For + /// that, you must name the task explicitly using the `Named` trait. + fn cancel(address: Self::Address) -> Result<(), DispatchError>; + + /// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed + /// only if it is executed *before* the currently scheduled block. For periodic tasks, + /// this dispatch is guaranteed to succeed only before the *initial* execution; for + /// others, use `reschedule_named`. + /// + /// Will return an `Unavailable` error if the `address` is invalid. + fn reschedule( + address: Self::Address, + when: DispatchTime<BlockNumber>, + ) -> Result<Self::Address, DispatchError>; + + /// Return the next dispatch time for a given task. + /// + /// Will return an `Unavailable` error if the `address` is invalid. + fn next_dispatch_time(address: Self::Address) -> Result<BlockNumber, DispatchError>; + } + + pub type TaskName = [u8; 32]; + + /// A type that can be used as a scheduler. + pub trait Named<BlockNumber, Call, Origin> { + /// An address which can be used for removing a scheduled task. + type Address: Codec + MaxEncodedLen + Clone + Eq + EncodeLike + sp_std::fmt::Debug; + + /// Schedule a dispatch to happen at the beginning of some block in the future. + /// + /// - `id`: The identity of the task. This must be unique and will return an error if not. + fn schedule_named( + id: TaskName, + when: DispatchTime<BlockNumber>, + maybe_periodic: Option<Period<BlockNumber>>, + priority: Priority, + origin: Origin, + call: Bounded<Call>, + ) -> Result<Self::Address, DispatchError>; + + /// Cancel a scheduled, named task. If periodic, then it will cancel all further instances + /// of that, also. + /// + /// Will return an `Unavailable` error if the `id` is invalid. + /// + /// NOTE: This guaranteed to work only *before* the point that it is due to be executed. + /// If it ends up being delayed beyond the point of execution, then it cannot be cancelled. + fn cancel_named(id: TaskName) -> Result<(), DispatchError>; + + /// Reschedule a task. For one-off tasks, this dispatch is guaranteed to succeed + /// only if it is executed *before* the currently scheduled block. + /// + /// Will return an `Unavailable` error if the `id` is invalid. + fn reschedule_named( + id: TaskName, + when: DispatchTime<BlockNumber>, + ) -> Result<Self::Address, DispatchError>; + + /// Return the next dispatch time for a given task. + /// + /// Will return an `Unavailable` error if the `id` is invalid. + fn next_dispatch_time(id: TaskName) -> Result<BlockNumber, DispatchError>; + } +} + +pub use v1::*; diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr index b0716d569409c07fac1756d98ab1cc6fe69eccab..b8a9a1128d669ea132c44278d92f742127aaaa49 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen.stderr @@ -28,7 +28,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied <&[(T,)] as EncodeLike<BinaryHeap<LikeT>>> <&[(T,)] as EncodeLike<LinkedList<LikeT>>> <&[T] as EncodeLike<Vec<U>>> - and 279 others + and 280 others = note: required because of the requirements on the impl of `FullEncode` for `Bar` = note: required because of the requirements on the impl of `FullCodec` for `Bar` = note: required because of the requirements on the impl of `PartialStorageInfoTrait` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` @@ -69,7 +69,7 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied (A, B, C, D) (A, B, C, D, E) (A, B, C, D, E, F) - and 160 others + and 161 others = note: required because of the requirements on the impl of `StaticTypeInfo` for `Bar` = note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` @@ -103,7 +103,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied <&[(T,)] as EncodeLike<BinaryHeap<LikeT>>> <&[(T,)] as EncodeLike<LinkedList<LikeT>>> <&[T] as EncodeLike<Vec<U>>> - and 279 others + and 280 others = note: required because of the requirements on the impl of `FullEncode` for `Bar` = note: required because of the requirements on the impl of `FullCodec` for `Bar` = note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr index 926dc925306594c495fcd802b76f904a9ceb43fc..5032f63bc1b1b20ad93218e0abd76a0a2c19fe57 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_ensure_span_are_ok_on_wrong_gen_unnamed.stderr @@ -28,7 +28,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied <&[(T,)] as EncodeLike<BinaryHeap<LikeT>>> <&[(T,)] as EncodeLike<LinkedList<LikeT>>> <&[T] as EncodeLike<Vec<U>>> - and 279 others + and 280 others = note: required because of the requirements on the impl of `FullEncode` for `Bar` = note: required because of the requirements on the impl of `FullCodec` for `Bar` = note: required because of the requirements on the impl of `PartialStorageInfoTrait` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` @@ -69,7 +69,7 @@ error[E0277]: the trait bound `Bar: TypeInfo` is not satisfied (A, B, C, D) (A, B, C, D, E) (A, B, C, D, E, F) - and 160 others + and 161 others = note: required because of the requirements on the impl of `StaticTypeInfo` for `Bar` = note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` @@ -103,7 +103,7 @@ error[E0277]: the trait bound `Bar: EncodeLike` is not satisfied <&[(T,)] as EncodeLike<BinaryHeap<LikeT>>> <&[(T,)] as EncodeLike<LinkedList<LikeT>>> <&[T] as EncodeLike<Vec<U>>> - and 279 others + and 280 others = note: required because of the requirements on the impl of `FullEncode` for `Bar` = note: required because of the requirements on the impl of `FullCodec` for `Bar` = note: required because of the requirements on the impl of `StorageEntryMetadataBuilder` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied.stderr index 563190a06f76fb395a3b9ecf055deeb5a92d78da..8d3d7a71a313e7a0a1b111449e31a2b117a25ea7 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied.stderr @@ -13,5 +13,5 @@ error[E0277]: the trait bound `Bar: MaxEncodedLen` is not satisfied (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) - and 77 others + and 78 others = note: required because of the requirements on the impl of `StorageInfoTrait` for `frame_support::pallet_prelude::StorageValue<_GeneratedPrefixForStorageFoo<T>, Bar>` diff --git a/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied_nmap.stderr b/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied_nmap.stderr index c10005223b67439d0c13d997f53934b4cd385af2..ebf24a1232e3c61613f20fa73933b741dd2db634 100644 --- a/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied_nmap.stderr +++ b/substrate/frame/support/test/tests/pallet_ui/storage_info_unsatisfied_nmap.stderr @@ -13,6 +13,6 @@ error[E0277]: the trait bound `Bar: MaxEncodedLen` is not satisfied (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5) (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6) (TupleElement0, TupleElement1, TupleElement2, TupleElement3, TupleElement4, TupleElement5, TupleElement6, TupleElement7) - and 77 others + and 78 others = note: required because of the requirements on the impl of `KeyGeneratorMaxEncodedLen` for `Key<frame_support::Twox64Concat, Bar>` = note: required because of the requirements on the impl of `StorageInfoTrait` for `frame_support::pallet_prelude::StorageNMap<_GeneratedPrefixForStorageFoo<T>, Key<frame_support::Twox64Concat, Bar>, u32>` diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index dc74157da79de56e9c746923524dc19f12ef9f5c..7577d0dc6b158f3b2bd71b546c801e61e65fcb1b 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -222,7 +222,10 @@ pub mod pallet { + OriginTrait<Call = Self::RuntimeCall>; /// The aggregated `RuntimeCall` type. - type RuntimeCall: Dispatchable + Debug; + type RuntimeCall: Parameter + + Dispatchable<RuntimeOrigin = Self::RuntimeOrigin> + + Debug + + From<Call<Self>>; /// Account index (aka nonce) type. This stores the number of previous transactions /// associated with a sender account. diff --git a/substrate/frame/whitelist/src/mock.rs b/substrate/frame/whitelist/src/mock.rs index 44aea86be6f1998ea0de5da0e0240d16d645a5a4..d4446cb8031ab81e29a0d339ccfa1feb1504a66f 100644 --- a/substrate/frame/whitelist/src/mock.rs +++ b/substrate/frame/whitelist/src/mock.rs @@ -96,7 +96,6 @@ impl pallet_preimage::Config for Test { type RuntimeEvent = RuntimeEvent; type Currency = Balances; type ManagerOrigin = EnsureRoot<Self::AccountId>; - type MaxSize = ConstU32<{ 4096 * 1024 }>; // PreimageMaxSize Taken from Polkadot as reference. type BaseDeposit = ConstU64<1>; type ByteDeposit = ConstU64<1>; type WeightInfo = (); diff --git a/substrate/primitives/core/src/bounded/bounded_vec.rs b/substrate/primitives/core/src/bounded/bounded_vec.rs index 85f2bed316793c6135a507e14cfd4836a117fd5f..1832e43e8646c75cee43f2fec1c8dac983ab8180 100644 --- a/substrate/primitives/core/src/bounded/bounded_vec.rs +++ b/substrate/primitives/core/src/bounded/bounded_vec.rs @@ -276,6 +276,14 @@ impl<'a, T, S> sp_std::iter::IntoIterator for BoundedSlice<'a, T, S> { } } +impl<'a, T, S: Get<u32>> BoundedSlice<'a, T, S> { + /// Create an instance from the first elements of the given slice (or all of it if it is smaller + /// than the length bound). + pub fn truncate_from(s: &'a [T]) -> Self { + Self(&s[0..(s.len().min(S::get() as usize))], PhantomData) + } +} + impl<T: Decode, S: Get<u32>> Decode for BoundedVec<T, S> { fn decode<I: codec::Input>(input: &mut I) -> Result<Self, codec::Error> { let inner = Vec::<T>::decode(input)?; @@ -620,12 +628,12 @@ impl<T, S: Get<u32>> BoundedVec<T, S> { /// # Panics /// /// Panics if `index > len`. - pub fn try_insert(&mut self, index: usize, element: T) -> Result<(), ()> { + pub fn try_insert(&mut self, index: usize, element: T) -> Result<(), T> { if self.len() < Self::bound() { self.0.insert(index, element); Ok(()) } else { - Err(()) + Err(element) } } @@ -635,12 +643,12 @@ impl<T, S: Get<u32>> BoundedVec<T, S> { /// # Panics /// /// Panics if the new capacity exceeds isize::MAX bytes. - pub fn try_push(&mut self, element: T) -> Result<(), ()> { + pub fn try_push(&mut self, element: T) -> Result<(), T> { if self.len() < Self::bound() { self.0.push(element); Ok(()) } else { - Err(()) + Err(element) } } } @@ -673,13 +681,13 @@ where } impl<T, S: Get<u32>> TryFrom<Vec<T>> for BoundedVec<T, S> { - type Error = (); + type Error = Vec<T>; fn try_from(t: Vec<T>) -> Result<Self, Self::Error> { if t.len() <= Self::bound() { // explicit check just above Ok(Self::unchecked_from(t)) } else { - Err(()) + Err(t) } } } @@ -886,6 +894,16 @@ pub mod test { use super::*; use crate::{bounded_vec, ConstU32}; + #[test] + fn slice_truncate_from_works() { + let bounded = BoundedSlice::<u32, ConstU32<4>>::truncate_from(&[1, 2, 3, 4, 5]); + assert_eq!(bounded.deref(), &[1, 2, 3, 4]); + let bounded = BoundedSlice::<u32, ConstU32<4>>::truncate_from(&[1, 2, 3, 4]); + assert_eq!(bounded.deref(), &[1, 2, 3, 4]); + let bounded = BoundedSlice::<u32, ConstU32<4>>::truncate_from(&[1, 2, 3]); + assert_eq!(bounded.deref(), &[1, 2, 3]); + } + #[test] fn slide_works() { let mut b: BoundedVec<u32, ConstU32<6>> = bounded_vec![0, 1, 2, 3, 4, 5]; diff --git a/substrate/primitives/runtime/src/lib.rs b/substrate/primitives/runtime/src/lib.rs index 8017a6ac529a283a9651372dd53b46dffd801915..96706dd91965004d5f7a2f6dcb5db2a28d2bfb5d 100644 --- a/substrate/primitives/runtime/src/lib.rs +++ b/substrate/primitives/runtime/src/lib.rs @@ -547,6 +547,12 @@ pub enum DispatchError { /// The number of transactional layers has been reached, or we are not in a transactional /// layer. Transactional(TransactionalError), + /// Resources exhausted, e.g. attempt to read/write data which is too large to manipulate. + Exhausted, + /// The state is corrupt; this is generally not going to fix itself. + Corruption, + /// Some resource (e.g. a preimage) is unavailable right now. This might fix itself later. + Unavailable, } /// Result of a `Dispatchable` which contains the `DispatchResult` and additional information about @@ -671,18 +677,21 @@ impl From<&'static str> for DispatchError { impl From<DispatchError> for &'static str { fn from(err: DispatchError) -> &'static str { + use DispatchError::*; match err { - DispatchError::Other(msg) => msg, - DispatchError::CannotLookup => "Cannot lookup", - DispatchError::BadOrigin => "Bad origin", - DispatchError::Module(ModuleError { message, .. }) => - message.unwrap_or("Unknown module error"), - DispatchError::ConsumerRemaining => "Consumer remaining", - DispatchError::NoProviders => "No providers", - DispatchError::TooManyConsumers => "Too many consumers", - DispatchError::Token(e) => e.into(), - DispatchError::Arithmetic(e) => e.into(), - DispatchError::Transactional(e) => e.into(), + Other(msg) => msg, + CannotLookup => "Cannot lookup", + BadOrigin => "Bad origin", + Module(ModuleError { message, .. }) => message.unwrap_or("Unknown module error"), + ConsumerRemaining => "Consumer remaining", + NoProviders => "No providers", + TooManyConsumers => "Too many consumers", + Token(e) => e.into(), + Arithmetic(e) => e.into(), + Transactional(e) => e.into(), + Exhausted => "Resources exhausted", + Corruption => "State corrupt", + Unavailable => "Resource unavailable", } } } @@ -698,33 +707,37 @@ where impl traits::Printable for DispatchError { fn print(&self) { + use DispatchError::*; "DispatchError".print(); match self { - Self::Other(err) => err.print(), - Self::CannotLookup => "Cannot lookup".print(), - Self::BadOrigin => "Bad origin".print(), - Self::Module(ModuleError { index, error, message }) => { + Other(err) => err.print(), + CannotLookup => "Cannot lookup".print(), + BadOrigin => "Bad origin".print(), + Module(ModuleError { index, error, message }) => { index.print(); error.print(); if let Some(msg) = message { msg.print(); } }, - Self::ConsumerRemaining => "Consumer remaining".print(), - Self::NoProviders => "No providers".print(), - Self::TooManyConsumers => "Too many consumers".print(), - Self::Token(e) => { + ConsumerRemaining => "Consumer remaining".print(), + NoProviders => "No providers".print(), + TooManyConsumers => "Too many consumers".print(), + Token(e) => { "Token error: ".print(); <&'static str>::from(*e).print(); }, - Self::Arithmetic(e) => { + Arithmetic(e) => { "Arithmetic error: ".print(); <&'static str>::from(*e).print(); }, - Self::Transactional(e) => { + Transactional(e) => { "Transactional error: ".print(); <&'static str>::from(*e).print(); }, + Exhausted => "Resources exhausted".print(), + Corruption => "State corrupt".print(), + Unavailable => "Resource unavailable".print(), } } } diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index a64e3f25ef0417c9ee52b9d9ce94e7eb910c009c..3db0e5510057bd80ecb0707b920a25c5ba0544a1 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -37,8 +37,9 @@ use trie_db::{Trie, TrieMut}; use cfg_if::cfg_if; use frame_support::{ + dispatch::RawOrigin, parameter_types, - traits::{ConstU32, ConstU64, CrateVersion, KeyOwnerProofSystem}, + traits::{CallerTrait, ConstU32, ConstU64, CrateVersion, KeyOwnerProofSystem}, weights::{RuntimeDbWeight, Weight}, }; use frame_system::limits::{BlockLength, BlockWeights}; @@ -119,7 +120,7 @@ pub fn native_version() -> NativeVersion { } /// Calls in transactions. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub struct Transfer { pub from: AccountId, pub to: AccountId, @@ -150,7 +151,7 @@ impl Transfer { } /// Extrinsic for test-runtime. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] +#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug, TypeInfo)] pub enum Extrinsic { AuthoritiesChange(Vec<AuthorityId>), Transfer { @@ -446,11 +447,22 @@ impl GetRuntimeBlockType for Runtime { #[derive(Clone, RuntimeDebug, Encode, Decode, PartialEq, Eq, TypeInfo, MaxEncodedLen)] pub struct RuntimeOrigin; -impl From<frame_system::Origin<Runtime>> for RuntimeOrigin { - fn from(_o: frame_system::Origin<Runtime>) -> Self { +impl From<RawOrigin<<Runtime as frame_system::Config>::AccountId>> for RuntimeOrigin { + fn from(_: RawOrigin<<Runtime as frame_system::Config>::AccountId>) -> Self { unimplemented!("Not required in tests!") } } + +impl CallerTrait<<Runtime as frame_system::Config>::AccountId> for RuntimeOrigin { + fn into_system(self) -> Option<RawOrigin<<Runtime as frame_system::Config>::AccountId>> { + unimplemented!("Not required in tests!") + } + + fn as_system_ref(&self) -> Option<&RawOrigin<<Runtime as frame_system::Config>::AccountId>> { + unimplemented!("Not required in tests!") + } +} + impl From<RuntimeOrigin> for Result<frame_system::Origin<Runtime>, RuntimeOrigin> { fn from(_origin: RuntimeOrigin) -> Result<frame_system::Origin<Runtime>, RuntimeOrigin> { unimplemented!("Not required in tests!") @@ -482,6 +494,10 @@ impl frame_support::traits::OriginTrait for RuntimeOrigin { unimplemented!("Not required in tests!") } + fn into_caller(self) -> Self::PalletsOrigin { + unimplemented!("Not required in tests!") + } + fn try_with_caller<R>( self, _f: impl FnOnce(Self::PalletsOrigin) -> Result<R, Self::PalletsOrigin>, @@ -501,6 +517,9 @@ impl frame_support::traits::OriginTrait for RuntimeOrigin { fn as_signed(self) -> Option<Self::AccountId> { unimplemented!("Not required in tests!") } + fn as_system_ref(&self) -> Option<&RawOrigin<Self::AccountId>> { + unimplemented!("Not required in tests!") + } } #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug, TypeInfo)] @@ -583,6 +602,12 @@ parameter_types! { BlockWeights::with_sensible_defaults(Weight::from_ref_time(4 * 1024 * 1024), Perbill::from_percent(75)); } +impl From<frame_system::Call<Runtime>> for Extrinsic { + fn from(_: frame_system::Call<Runtime>) -> Self { + unimplemented!("Not required in tests!") + } +} + impl frame_system::Config for Runtime { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = RuntimeBlockWeights;