diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index cdb05d1597..5722603fe6 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -210,6 +210,7 @@ parameter_types! { }) .avg_block_initialization(AVERAGE_ON_INITIALIZE_RATIO) .build_or_panic(); + pub MaxCollectivesProposalWeight: Weight = Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block; } const_assert!(NORMAL_DISPATCH_RATIO.deconstruct() >= AVERAGE_ON_INITIALIZE_RATIO.deconstruct()); @@ -1012,6 +1013,7 @@ impl pallet_collective::Config for Runtime { type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxCollectivesProposalWeight; } parameter_types! { @@ -1072,6 +1074,7 @@ impl pallet_collective::Config for Runtime { type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxCollectivesProposalWeight; } type EnsureRootOrHalfCouncil = EitherOfDiverse< @@ -1700,6 +1703,7 @@ impl pallet_collective::Config for Runtime { type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = pallet_collective::weights::SubstrateWeight; type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxCollectivesProposalWeight; } parameter_types! { diff --git a/substrate/frame/alliance/src/mock.rs b/substrate/frame/alliance/src/mock.rs index 848f6b2a00..c334a3943b 100644 --- a/substrate/frame/alliance/src/mock.rs +++ b/substrate/frame/alliance/src/mock.rs @@ -43,10 +43,12 @@ type AccountId = u64; parameter_types! { pub const BlockHashCount: BlockNumber = 250; + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::MAX); } impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); + type BlockWeights = BlockWeights; type BlockLength = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; @@ -97,6 +99,7 @@ parameter_types! { pub const MotionDuration: BlockNumber = MOTION_DURATION_IN_BLOCKS; pub const MaxProposals: u32 = 100; pub const MaxMembers: u32 = 100; + pub MaxProposalWeight: Weight = sp_runtime::Perbill::from_percent(50) * BlockWeights::get().max_block; } type AllianceCollective = pallet_collective::Instance1; impl pallet_collective::Config for Test { @@ -109,6 +112,7 @@ impl pallet_collective::Config for Test { type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = (); type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxProposalWeight; } parameter_types! { diff --git a/substrate/frame/collective/src/lib.rs b/substrate/frame/collective/src/lib.rs index 0cbf7e9726..0ecf008fee 100644 --- a/substrate/frame/collective/src/lib.rs +++ b/substrate/frame/collective/src/lib.rs @@ -217,6 +217,10 @@ pub mod pallet { /// Origin allowed to set collective members type SetMembersOrigin: EnsureOrigin<::RuntimeOrigin>; + + /// The maximum weight of a dispatch call that can be proposed and executed. + #[pallet::constant] + type MaxProposalWeight: Get; } #[pallet::genesis_config] @@ -667,6 +671,11 @@ impl, I: 'static> Pallet { ) -> Result<(u32, DispatchResultWithPostInfo), DispatchError> { let proposal_len = proposal.encoded_size(); ensure!(proposal_len <= length_bound as usize, Error::::WrongProposalLength); + let proposal_weight = proposal.get_dispatch_info().weight; + ensure!( + proposal_weight.all_lte(T::MaxProposalWeight::get()), + Error::::WrongProposalWeight + ); let proposal_hash = T::Hashing::hash_of(&proposal); ensure!(!>::contains_key(proposal_hash), Error::::DuplicateProposal); @@ -689,6 +698,11 @@ impl, I: 'static> Pallet { ) -> Result<(u32, u32), DispatchError> { let proposal_len = proposal.encoded_size(); ensure!(proposal_len <= length_bound as usize, Error::::WrongProposalLength); + let proposal_weight = proposal.get_dispatch_info().weight; + ensure!( + proposal_weight.all_lte(T::MaxProposalWeight::get()), + Error::::WrongProposalWeight + ); let proposal_hash = T::Hashing::hash_of(&proposal); ensure!(!>::contains_key(proposal_hash), Error::::DuplicateProposal); diff --git a/substrate/frame/collective/src/tests.rs b/substrate/frame/collective/src/tests.rs index 79dc3becff..4775133ffa 100644 --- a/substrate/frame/collective/src/tests.rs +++ b/substrate/frame/collective/src/tests.rs @@ -90,10 +90,13 @@ pub type MaxMembers = ConstU32<100>; parameter_types! { pub const MotionDuration: u64 = 3; pub const MaxProposals: u32 = 257; + pub BlockWeights: frame_system::limits::BlockWeights = + frame_system::limits::BlockWeights::simple_max(Weight::MAX); + pub static MaxProposalWeight: Weight = default_max_proposal_weight(); } impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; - type BlockWeights = (); + type BlockWeights = BlockWeights; type BlockLength = (); type DbWeight = (); type RuntimeOrigin = RuntimeOrigin; @@ -127,6 +130,7 @@ impl Config for Test { type DefaultVote = PrimeDefaultVote; type WeightInfo = (); type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxProposalWeight; } impl Config for Test { type RuntimeOrigin = RuntimeOrigin; @@ -138,6 +142,7 @@ impl Config for Test { type DefaultVote = MoreThanMajorityThenPrimeDefaultVote; type WeightInfo = (); type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxProposalWeight; } impl mock_democracy::Config for Test { type RuntimeEvent = RuntimeEvent; @@ -153,6 +158,7 @@ impl Config for Test { type DefaultVote = PrimeDefaultVote; type WeightInfo = (); type SetMembersOrigin = EnsureRoot; + type MaxProposalWeight = MaxProposalWeight; } pub struct ExtBuilder {} @@ -201,6 +207,10 @@ fn record(event: RuntimeEvent) -> EventRecord { EventRecord { phase: Phase::Initialization, event, topics: vec![] } } +fn default_max_proposal_weight() -> Weight { + sp_runtime::Perbill::from_percent(80) * BlockWeights::get().max_block +} + #[test] fn motions_basic_environment_works() { ExtBuilder::default().build_and_execute(|| { @@ -209,6 +219,36 @@ fn motions_basic_environment_works() { }); } +#[test] +fn proposal_weight_limit_works() { + ExtBuilder::default().build_and_execute(|| { + let proposal = make_proposal(42); + let proposal_len: u32 = proposal.using_encoded(|p| p.len() as u32); + + assert_ok!(Collective::propose( + RuntimeOrigin::signed(1), + 2, + Box::new(proposal.clone()), + proposal_len + )); + + // set a small limit for max proposal weight. + MaxProposalWeight::set(Weight::from_parts(1, 1)); + assert_noop!( + Collective::propose( + RuntimeOrigin::signed(1), + 2, + Box::new(proposal.clone()), + proposal_len + ), + Error::::WrongProposalWeight + ); + + // reset the max weight to default. + MaxProposalWeight::set(default_max_proposal_weight()); + }); +} + #[test] fn close_works() { ExtBuilder::default().build_and_execute(|| { diff --git a/substrate/frame/utility/src/tests.rs b/substrate/frame/utility/src/tests.rs index d23c57e69b..6f156e318c 100644 --- a/substrate/frame/utility/src/tests.rs +++ b/substrate/frame/utility/src/tests.rs @@ -209,6 +209,7 @@ parameter_types! { pub const MotionDuration: BlockNumber = MOTION_DURATION_IN_BLOCKS; pub const MaxProposals: u32 = 100; pub const MaxMembers: u32 = 100; + pub MaxProposalWeight: Weight = sp_runtime::Perbill::from_percent(50) * BlockWeights::get().max_block; } type CouncilCollective = pallet_collective::Instance1; @@ -222,6 +223,7 @@ impl pallet_collective::Config for Test { type DefaultVote = pallet_collective::PrimeDefaultVote; type WeightInfo = (); type SetMembersOrigin = frame_system::EnsureRoot; + type MaxProposalWeight = MaxProposalWeight; } impl example::Config for Test {}