瀏覽代碼

Merge pull request #1731 from conectado/proposals_update_engine_benchmark

Rebase with olympia and benchmark proposals engine and add weights for proposals
shamil-gadelshin 4 年之前
父節點
當前提交
b0f22d42d1

+ 6 - 2
Cargo.lock

@@ -4036,7 +4036,7 @@ dependencies = [
 
 [[package]]
 name = "pallet-proposals-discussion"
-version = "4.0.0"
+version = "4.0.1"
 dependencies = [
  "frame-benchmarking",
  "frame-support",
@@ -4055,14 +4055,18 @@ dependencies = [
 
 [[package]]
 name = "pallet-proposals-engine"
-version = "4.0.0"
+version = "4.0.1"
 dependencies = [
+ "frame-benchmarking",
  "frame-support",
  "frame-system",
  "pallet-balances",
  "pallet-common",
+ "pallet-governance",
  "pallet-membership",
+ "pallet-recurring-reward",
  "pallet-timestamp",
+ "pallet-token-mint",
  "parity-scale-codec",
  "serde",
  "sp-arithmetic",

+ 55 - 1
runtime-modules/proposals/codex/src/tests/mock/mod.rs

@@ -1,7 +1,7 @@
 #![cfg(test)]
 
 use frame_support::traits::LockIdentifier;
-use frame_support::{impl_outer_dispatch, impl_outer_origin, parameter_types};
+use frame_support::{impl_outer_dispatch, impl_outer_origin, parameter_types, weights::Weight};
 pub use frame_system;
 use sp_core::H256;
 use sp_runtime::curve::PiecewiseLinear;
@@ -86,6 +86,8 @@ parameter_types! {
     pub const LockId: LockIdentifier = [2; 8];
 }
 
+pub struct MockProposalsEngineWeight;
+
 impl proposals_engine::Trait for Test {
     type Event = ();
     type ProposerOriginValidator = ();
@@ -100,6 +102,41 @@ impl proposals_engine::Trait for Test {
     type MaxActiveProposalLimit = MaxActiveProposalLimit;
     type DispatchableCallCode = crate::Call<Test>;
     type ProposalObserver = crate::Module<Test>;
+    type WeightInfo = MockProposalsEngineWeight;
+}
+
+impl proposals_engine::WeightInfo for MockProposalsEngineWeight {
+    fn vote(_: u32) -> Weight {
+        0
+    }
+
+    fn cancel_proposal(_: u32) -> Weight {
+        0
+    }
+
+    fn veto_proposal() -> Weight {
+        0
+    }
+
+    fn on_initialize_immediate_execution_decode_fails(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_pending_execution_decode_fails(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_approved_pending_constitutionality(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_rejected(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_slashed(_: u32) -> Weight {
+        0
+    }
 }
 
 impl Default for crate::Call<Test> {
@@ -132,6 +169,8 @@ parameter_types! {
     pub const MaxWhiteListSize: u32 = 20;
 }
 
+pub struct MockProposalsDiscussionWeight;
+
 impl proposals_discussion::Trait for Test {
     type Event = ();
     type AuthorOriginValidator = ();
@@ -139,6 +178,21 @@ impl proposals_discussion::Trait for Test {
     type ThreadId = u64;
     type PostId = u64;
     type MaxWhiteListSize = MaxWhiteListSize;
+    type WeightInfo = MockProposalsDiscussionWeight;
+}
+
+impl proposals_discussion::WeightInfo for MockProposalsDiscussionWeight {
+    fn add_post(_: u32) -> Weight {
+        0
+    }
+
+    fn update_post() -> Weight {
+        0
+    }
+
+    fn change_thread_mode(_: u32) -> Weight {
+        0
+    }
 }
 
 pub struct MockVotersParameters;

+ 2 - 2
runtime-modules/proposals/discussion/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = 'pallet-proposals-discussion'
-version = '4.0.0'
+version = '4.0.1'
 authors = ['Joystream contributors']
 edition = '2018'
 
@@ -32,4 +32,4 @@ std = [
 	'frame-system/std',
     'membership/std',
     'common/std',
-]
+]

+ 4 - 7
runtime-modules/proposals/discussion/src/benchmarking.rs

@@ -1,15 +1,14 @@
 #![cfg(feature = "runtime-benchmarks")]
 use super::*;
 use crate::Module as ProposalsDiscussion;
-use core::convert::TryInto;
 use frame_benchmarking::{account, benchmarks};
+use frame_system::EventRecord;
+use frame_system::Module as System;
+use frame_system::RawOrigin;
 use membership::Module as Membership;
 use sp_std::cmp::min;
+use sp_std::convert::TryInto;
 use sp_std::prelude::*;
-use system as frame_system;
-use system::EventRecord;
-use system::Module as System;
-use system::RawOrigin;
 
 const SEED: u32 = 0;
 
@@ -115,8 +114,6 @@ benchmarks! {
     }
 
     update_post {
-        // TODO: this parameter doesn't affect the running time
-        // maybe we should bound it here with the UI limit?
         let j in 0 .. MAX_BYTES;
 
         // We do this to ignore the id 0 because the `Test` runtime

+ 54 - 4
runtime-modules/proposals/discussion/src/lib.rs

@@ -54,7 +54,9 @@ mod types;
 use frame_support::dispatch::{DispatchError, DispatchResult};
 use frame_support::sp_runtime::SaturatedConversion;
 use frame_support::traits::Get;
-use frame_support::{decl_error, decl_event, decl_module, decl_storage, ensure, Parameter};
+use frame_support::{
+    decl_error, decl_event, decl_module, decl_storage, ensure, weights::Weight, Parameter,
+};
 use sp_std::clone::Clone;
 use sp_std::vec::Vec;
 
@@ -65,6 +67,16 @@ pub use types::ThreadMode;
 
 type MemberId<T> = <T as membership::Trait>::MemberId;
 
+/// Proposals discussion WeightInfo.
+/// Note: This was auto generated through the benchmark CLI using the `--weight-trait` flag
+pub trait WeightInfo {
+    fn add_post(i: u32) -> Weight; // Note: since parameter doesn't affect weight it's discarded
+    fn update_post() -> Weight; // Note: since parameter doesn't affect weight it's discarded
+    fn change_thread_mode(i: u32) -> Weight;
+}
+
+type WeightInfoDiscussion<T> = <T as Trait>::WeightInfo;
+
 decl_event!(
     /// Proposals engine events
     pub enum Event<T>
@@ -112,6 +124,9 @@ pub trait Trait: frame_system::Trait + membership::Trait {
 
     /// Defines author list size limit for the Closed discussion.
     type MaxWhiteListSize: Get<u32>;
+
+    /// Weight information for extrinsics in this pallet.
+    type WeightInfo: WeightInfo;
 }
 
 decl_error! {
@@ -170,7 +185,18 @@ decl_module! {
         fn deposit_event() = default;
 
         /// Adds a post with author origin check.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (W)` where:
+        /// - `W` is the number of whitelisted members for `thread_id`
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or parameters
+        /// # </weight>
+        #[weight = WeightInfoDiscussion::<T>::add_post(
+            T::MaxWhiteListSize::get(),
+        )]
         pub fn add_post(
             origin,
             post_author_id: MemberId<T>,
@@ -202,7 +228,15 @@ decl_module! {
        }
 
         /// Updates a post with author origin check. Update attempts number is limited.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (1)` doesn't depend on the state or parameters
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or parameters
+        /// # </weight>
+        #[weight = WeightInfoDiscussion::<T>::update_post()]
         pub fn update_post(
             origin,
             post_author_id: MemberId<T>,
@@ -228,7 +262,22 @@ decl_module! {
        }
 
         /// Changes thread permission mode.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (W)` if ThreadMode is close or O(1) otherwise where:
+        /// - `W` is the number of whitelisted members in `mode`
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or parameters
+        /// # </weight>
+        #[weight = WeightInfoDiscussion::<T>::change_thread_mode(
+            if let ThreadMode::Closed(ref list) = mode {
+                list.len().saturated_into()
+            } else {
+                0
+            }
+        )]
         pub fn change_thread_mode(
             origin,
             member_id: MemberId<T>,
@@ -260,6 +309,7 @@ decl_module! {
             <ThreadById<T>>::mutate(thread_id, |thread| {
                 thread.mode = mode.clone();
             });
+
             Self::deposit_event(RawEvent::ThreadModeChanged(thread_id, mode));
        }
     }

+ 17 - 1
runtime-modules/proposals/discussion/src/tests/mock.rs

@@ -3,7 +3,7 @@
 pub use frame_system;
 
 use frame_support::traits::{OnFinalize, OnInitialize};
-use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
+use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight};
 use sp_core::H256;
 use sp_runtime::{
     testing::Header,
@@ -12,6 +12,7 @@ use sp_runtime::{
 };
 
 use crate::ActorOriginValidator;
+use crate::WeightInfo;
 
 impl_outer_origin! {
     pub enum Origin for Test {}
@@ -87,6 +88,21 @@ impl crate::Trait for Test {
     type ThreadId = u64;
     type PostId = u64;
     type MaxWhiteListSize = MaxWhiteListSize;
+    type WeightInfo = ();
+}
+
+impl WeightInfo for () {
+    fn add_post(_: u32) -> Weight {
+        0
+    }
+
+    fn update_post() -> Weight {
+        0
+    }
+
+    fn change_thread_mode(_: u32) -> Weight {
+        0
+    }
 }
 
 impl ActorOriginValidator<Origin, u64, u64> for () {

+ 17 - 2
runtime-modules/proposals/engine/Cargo.toml

@@ -1,6 +1,6 @@
 [package]
 name = 'pallet-proposals-engine'
-version = '4.0.0'
+version = '4.0.1'
 authors = ['Joystream contributors']
 edition = '2018'
 
@@ -17,12 +17,27 @@ balances = { package = 'pallet-balances', default-features = false, git = 'https
 membership = { package = 'pallet-membership', default-features = false, path = '../../membership'}
 common = { package = 'pallet-common', default-features = false, path = '../../common'}
 
+# Benchmark dependencies.
+frame-benchmarking = { package = 'frame-benchmarking', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca', optional = true}
+governance = { package = 'pallet-governance', default-features = false, path = '../../governance', optional = true}
+recurringrewards = { package = 'pallet-recurring-reward', default-features = false, path = '../../recurring-reward', optional = true}
+minting = { package = 'pallet-token-mint', default-features = false, path = '../../token-minting', optional = true}
+
 [dev-dependencies]
 sp-io = { package = 'sp-io', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
 sp-core = { package = 'sp-core', default-features = false, git = 'https://github.com/paritytech/substrate.git', rev = 'a200cdb93c6af5763b9c7bf313fa708764ac88ca'}
+governance = { package = 'pallet-governance', default-features = false, path = '../../governance'}
+recurringrewards = { package = 'pallet-recurring-reward', default-features = false, path = '../../recurring-reward'}
+minting = { package = 'pallet-token-mint', default-features = false, path = '../../token-minting'}
 
 [features]
 default = ['std']
+runtime-benchmarks = [
+    'frame-benchmarking',
+    'governance',
+    'recurringrewards',
+    'minting',
+]
 std = [
 	'serde',
 	'codec/std',
@@ -35,4 +50,4 @@ std = [
 	'balances/std',
     'membership/std',
     'common/std',
-]
+]

+ 631 - 0
runtime-modules/proposals/engine/src/benchmarking.rs

@@ -0,0 +1,631 @@
+#![cfg(feature = "runtime-benchmarks")]
+use super::*;
+use crate::Module as ProposalsEngine;
+use balances::Module as Balances;
+use core::convert::TryInto;
+use frame_benchmarking::{account, benchmarks};
+use frame_support::traits::{Currency, OnFinalize, OnInitialize};
+use frame_system::EventRecord;
+use frame_system::Module as System;
+use frame_system::RawOrigin;
+use governance::council::Module as Council;
+use membership::Module as Membership;
+use sp_runtime::traits::{Bounded, One};
+use sp_std::cmp::{max, min};
+use sp_std::prelude::*;
+
+const SEED: u32 = 0;
+
+fn get_byte(num: u32, byte_number: u8) -> u8 {
+    ((num & (0xff << (8 * byte_number))) >> 8 * byte_number) as u8
+}
+
+// Method to generate a distintic valid handle
+// for a membership. For each index.
+fn handle_from_id<T: membership::Trait>(id: u32) -> Vec<u8> {
+    let min_handle_length = Membership::<T>::min_handle_length();
+
+    let mut handle = vec![];
+
+    for i in 0..min(Membership::<T>::max_handle_length().try_into().unwrap(), 4) {
+        handle.push(get_byte(id, i));
+    }
+
+    while handle.len() < (min_handle_length as usize) {
+        handle.push(0u8);
+    }
+
+    handle
+}
+
+fn assert_last_event<T: Trait>(generic_event: <T as Trait>::Event) {
+    let events = System::<T>::events();
+    let system_event: <T as frame_system::Trait>::Event = generic_event.into();
+    assert!(
+        events.len() > 0,
+        "If you are checking for last event there must be at least 1 event"
+    );
+    let EventRecord { event, .. } = &events[events.len() - 1];
+    assert_eq!(event, &system_event);
+}
+
+fn assert_in_events<T: Trait>(generic_event: <T as Trait>::Event) {
+    let events = System::<T>::events();
+    let system_event: <T as frame_system::Trait>::Event = generic_event.into();
+
+    assert!(
+        events.len() > 0,
+        "If you are checking for last event there must be at least 1 event"
+    );
+
+    assert!(events.iter().any(|event| {
+        let EventRecord { event, .. } = event;
+        event == &system_event
+    }));
+}
+
+fn member_funded_account<T: Trait>(name: &'static str, id: u32) -> (T::AccountId, T::MemberId) {
+    let account_id = account::<T::AccountId>(name, id, SEED);
+    let handle = handle_from_id::<T>(id);
+
+    let authority_account = account::<T::AccountId>(name, 0, SEED);
+
+    Membership::<T>::set_screening_authority(RawOrigin::Root.into(), authority_account.clone())
+        .unwrap();
+
+    Membership::<T>::add_screened_member(
+        RawOrigin::Signed(authority_account.clone()).into(),
+        account_id.clone(),
+        Some(handle),
+        None,
+        None,
+    )
+    .unwrap();
+
+    let _ = Balances::<T>::make_free_balance_be(&account_id, T::Balance::max_value());
+
+    (account_id, T::MemberId::from(id.try_into().unwrap()))
+}
+
+fn create_proposal<T: Trait>(
+    id: u32,
+    proposal_number: u32,
+    constitutionality: u32,
+    grace_period: u32,
+) -> (T::AccountId, T::MemberId, T::ProposalId) {
+    let (account_id, member_id) = member_funded_account::<T>("member", id);
+
+    let proposal_parameters = ProposalParameters {
+        voting_period: T::BlockNumber::from(1),
+        grace_period: T::BlockNumber::from(grace_period),
+        approval_quorum_percentage: 1,
+        approval_threshold_percentage: 1,
+        slashing_quorum_percentage: 0,
+        slashing_threshold_percentage: 1,
+        required_stake: Some(T::Balance::max_value()),
+        constitutionality,
+    };
+
+    let call_code = vec![];
+
+    let proposal_creation_parameters = ProposalCreationParameters {
+        account_id: account_id.clone(),
+        proposer_id: member_id.clone(),
+        proposal_parameters,
+        title: vec![0u8],
+        description: vec![0u8],
+        staking_account_id: Some(account_id.clone()),
+        encoded_dispatchable_call_code: call_code.clone(),
+        exact_execution_block: None,
+    };
+
+    let proposal_id = ProposalsEngine::<T>::create_proposal(proposal_creation_parameters).unwrap();
+
+    assert!(
+        Proposals::<T>::contains_key(proposal_id),
+        "Proposal not created"
+    );
+    assert!(
+        DispatchableCallCode::<T>::contains_key(proposal_id),
+        "Dispatchable code not added"
+    );
+
+    assert_eq!(
+        ProposalsEngine::<T>::proposal_codes(proposal_id),
+        call_code,
+        "Dispatchable code does not match"
+    );
+
+    assert_eq!(
+        ProposalsEngine::<T>::proposal_count(),
+        proposal_number,
+        "Not correct number of proposals stored"
+    );
+
+    // We assume here that active proposals == number of proposals
+    assert_eq!(
+        ProposalsEngine::<T>::active_proposal_count(),
+        proposal_number,
+        "Created proposal not active"
+    );
+
+    assert_eq!(
+        T::StakingHandler::current_stake(&account_id),
+        T::Balance::max_value()
+    );
+
+    (account_id, member_id, proposal_id)
+}
+
+fn create_multiple_finalized_proposals<T: Trait + governance::council::Trait>(
+    number_of_proposals: u32,
+    constitutionality: u32,
+    vote_kind: VoteKind,
+    total_voters: u32,
+    grace_period: u32,
+) -> (Vec<T::AccountId>, Vec<T::ProposalId>) {
+    let mut voters = Vec::new();
+    for i in 0..total_voters {
+        voters.push(member_funded_account::<T>("voter", i));
+    }
+
+    Council::<T>::set_council(
+        RawOrigin::Root.into(),
+        voters
+            .iter()
+            .map(|(account_id, _)| account_id.clone())
+            .collect(),
+    )
+    .unwrap();
+
+    let mut proposers = Vec::new();
+    let mut proposals = Vec::new();
+    for id in total_voters..number_of_proposals + total_voters {
+        let (proposer_account_id, _, proposal_id) =
+            create_proposal::<T>(id, id - total_voters + 1, constitutionality, grace_period);
+        proposers.push(proposer_account_id);
+        proposals.push(proposal_id);
+
+        for (voter_id, member_id) in voters.clone() {
+            ProposalsEngine::<T>::vote(
+                RawOrigin::Signed(voter_id.clone()).into(),
+                member_id,
+                proposal_id,
+                vote_kind.clone(),
+                vec![0u8],
+            )
+            .unwrap()
+        }
+    }
+
+    (proposers, proposals)
+}
+
+const MAX_BYTES: u32 = 16384;
+
+benchmarks! {
+    // Note: this is the syntax for this macro can't use "+"
+    where_clause {
+        where T: governance::council::Trait
+    }
+
+    _ { }
+
+    vote {
+        let i in 0 .. MAX_BYTES;
+
+        let (_, _, proposal_id) = create_proposal::<T>(0, 1, 0, 0);
+
+        let (account_voter_id, member_voter_id) = member_funded_account::<T>("voter", 1);
+
+        Council::<T>::set_council(RawOrigin::Root.into(), vec![account_voter_id.clone()]).unwrap();
+    }: _ (
+            RawOrigin::Signed(account_voter_id),
+            member_voter_id,
+            proposal_id,
+            VoteKind::Approve,
+            vec![0u8; i.try_into().unwrap()]
+        )
+    verify {
+        assert!(Proposals::<T>::contains_key(proposal_id), "Proposal should still exist");
+
+        let voting_results = ProposalsEngine::<T>::proposals(proposal_id).voting_results;
+
+        assert_eq!(
+          voting_results,
+          VotingResults{ approvals: 1, abstentions: 0, rejections: 0, slashes: 0 },
+          "There should only be 1 approval"
+        );
+
+        assert!(
+          VoteExistsByProposalByVoter::<T>::contains_key(proposal_id, member_voter_id),
+          "Voter not added to existing voters"
+        );
+
+        assert_eq!(
+          ProposalsEngine::<T>::vote_by_proposal_by_voter(proposal_id, member_voter_id),
+          VoteKind::Approve,
+          "Stored vote doesn't match"
+        );
+
+        assert_last_event::<T>(
+            RawEvent::Voted(member_voter_id, proposal_id, VoteKind::Approve).into()
+        );
+    }
+
+    cancel_proposal {
+        let i in 1 .. T::MaxLocks::get();
+
+        let (account_id, member_id, proposal_id) = create_proposal::<T>(0, 1, 0, 0);
+
+        for lock_number in 1 .. i {
+            let (locked_account_id, _) = member_funded_account::<T>("locked_member", lock_number);
+            T::StakingHandler::set_stake(&locked_account_id, One::one()).unwrap();
+        }
+
+    }: _ (RawOrigin::Signed(account_id.clone()), member_id, proposal_id)
+    verify {
+        assert!(!Proposals::<T>::contains_key(proposal_id), "Proposal still in storage");
+
+        assert!(
+            !DispatchableCallCode::<T>::contains_key(proposal_id),
+            "Proposal code still in storage"
+        );
+
+        assert_eq!(ProposalsEngine::<T>::active_proposal_count(), 0, "Proposal still active");
+
+        assert_eq!(
+            Balances::<T>::usable_balance(account_id),
+            T::Balance::max_value() - T::CancellationFee::get(),
+            "Balance not slashed"
+        );
+
+        assert_last_event::<T>(
+            RawEvent::ProposalDecisionMade(proposal_id, ProposalDecision::Canceled).into()
+        );
+    }
+
+    veto_proposal {
+        let (account_id, _, proposal_id) = create_proposal::<T>(0, 1, 0, 0);
+    }: _ (RawOrigin::Root, proposal_id)
+    verify {
+        assert!(!Proposals::<T>::contains_key(proposal_id), "Proposal still in storage");
+
+        assert!(
+            !DispatchableCallCode::<T>::contains_key(proposal_id),
+            "Proposal code still in storage"
+        );
+
+        assert_eq!(ProposalsEngine::<T>::active_proposal_count(), 0, "Proposal still active");
+
+        assert_eq!(
+            Balances::<T>::usable_balance(account_id),
+            T::Balance::max_value(),
+            "Vetoed proposals shouldn't be slashed"
+        );
+
+        assert_last_event::<T>(
+            RawEvent::ProposalDecisionMade(proposal_id, ProposalDecision::Vetoed).into()
+        );
+    }
+
+    // We use that branches for decode failing, failing and passing are very similar
+    // without any different DB access in each. To use the failing/passing branch
+    // we need to include the EncodeProposal trait from codex which depends on engine
+    // therefore we should move it to a common crate
+    on_initialize_immediate_execution_decode_fails {
+        let i in 1 .. T::MaxActiveProposalLimit::get();
+
+        let (proposers, proposals) = create_multiple_finalized_proposals::<T>(
+            i,
+            0,
+            VoteKind::Approve,
+            1,
+            0,
+        );
+
+    }: { ProposalsEngine::<T>::on_initialize(System::<T>::block_number().into()) }
+    verify {
+        for proposer_account_id in proposers {
+            assert_eq!(
+                T::StakingHandler::current_stake(&proposer_account_id),
+                Zero::zero(),
+                "Should've unlocked all stake"
+            );
+        }
+
+        assert_eq!(
+            ProposalsEngine::<T>::active_proposal_count(),
+            0,
+            "Proposals should no longer be active"
+        );
+
+        for proposal_id in proposals.iter() {
+            assert!(
+                !Proposals::<T>::contains_key(proposal_id),
+                "Proposals should've been removed"
+            );
+
+            assert!(
+                !DispatchableCallCode::<T>::contains_key(proposal_id),
+                "Dispatchable code should've been removed"
+            );
+        }
+
+        if cfg!(test) {
+            for proposal_id in proposals.iter() {
+                assert_in_events::<T>(
+                    RawEvent::ProposalExecuted(
+                        proposal_id.clone(),
+                        ExecutionStatus::failed_execution("Not enough data to fill buffer")).into()
+                );
+            }
+        }
+    }
+
+    on_initialize_pending_execution_decode_fails {
+        let i in 1 .. T::MaxActiveProposalLimit::get();
+
+        let (proposers, proposals) = create_multiple_finalized_proposals::<T>(
+            i,
+            0,
+            VoteKind::Approve,
+            1,
+            1,
+        );
+
+        let mut current_block_number = System::<T>::block_number();
+
+        System::<T>::on_finalize(current_block_number);
+        System::<T>::on_finalize(current_block_number);
+
+        current_block_number += One::one();
+
+        System::<T>::on_initialize(current_block_number);
+        ProposalsEngine::<T>::on_initialize(current_block_number);
+
+        assert_eq!(
+            ProposalsEngine::<T>::active_proposal_count(),
+            i,
+            "Proposals should still be active"
+        );
+
+        for proposal_id in proposals.iter() {
+            assert!(
+                Proposals::<T>::contains_key(proposal_id),
+                "All proposals should still be stored"
+            );
+
+            assert!(
+                DispatchableCallCode::<T>::contains_key(proposal_id),
+                "All dispatchable call code should still be stored"
+            );
+        }
+
+    }: { ProposalsEngine::<T>::on_initialize(current_block_number) }
+    verify {
+        for proposer_account_id in proposers {
+            assert_eq!(
+                T::StakingHandler::current_stake(&proposer_account_id),
+                Zero::zero(),
+                "Should've unlocked all stake"
+            );
+        }
+
+        assert_eq!(ProposalsEngine::<T>::active_proposal_count(), 0, "Proposals should no longer be active");
+        for proposal_id in proposals.iter() {
+            assert!(!Proposals::<T>::contains_key(proposal_id), "Proposals should've been removed");
+            assert!(!DispatchableCallCode::<T>::contains_key(proposal_id), "Dispatchable code should've been removed");
+        }
+
+        if cfg!(test) {
+            for proposal_id in proposals.iter() {
+                assert_in_events::<T>(
+                    RawEvent::ProposalExecuted(
+                        proposal_id.clone(),
+                        ExecutionStatus::failed_execution("Not enough data to fill buffer")).into()
+                );
+            }
+        }
+    }
+
+    on_initialize_approved_pending_constitutionality {
+        let i in 1 .. T::MaxActiveProposalLimit::get();
+
+        let (proposers, proposals) = create_multiple_finalized_proposals::<T>(
+            i,
+            2,
+            VoteKind::Approve,
+            1,
+            0,
+        );
+
+    }: { ProposalsEngine::<T>::on_initialize(System::<T>::block_number().into()) }
+    verify {
+        for proposer_account_id in proposers {
+            assert_ne!(
+                T::StakingHandler::current_stake(&proposer_account_id),
+                Zero::zero(),
+                "Should've still stake locked"
+            );
+        }
+
+        for proposal_id in proposals.iter() {
+            assert!(
+                Proposals::<T>::contains_key(proposal_id),
+                "Proposal should still be in the store"
+            );
+            let proposal = ProposalsEngine::<T>::proposals(proposal_id);
+            let status = ProposalStatus::approved(
+                ApprovedProposalDecision::PendingConstitutionality,
+                System::<T>::block_number()
+            );
+
+            assert_eq!(proposal.status, status);
+            assert_eq!(proposal.current_constitutionality_level, 1);
+            assert_in_events::<T>(
+                RawEvent::ProposalStatusUpdated(proposal_id.clone(), status).into()
+            );
+        }
+    }
+
+    on_initialize_rejected {
+        let i in 1 .. T::MaxActiveProposalLimit::get();
+
+        let (proposers, proposals) = create_multiple_finalized_proposals::<T>(
+            i,
+            0,
+            VoteKind::Reject,
+            max(T::TotalVotersCounter::total_voters_count(), 1),
+            0,
+        );
+    }: { ProposalsEngine::<T>::on_initialize(System::<T>::block_number().into()) }
+    verify {
+        for proposer_account_id in proposers {
+            assert_eq!(
+                T::StakingHandler::current_stake(&proposer_account_id),
+                Zero::zero(),
+                "Shouldn't have any stake locked"
+            );
+        }
+
+        for proposal_id in proposals.iter() {
+
+            assert!(
+                !Proposals::<T>::contains_key(proposal_id),
+                "Proposal should not be in store"
+            );
+
+            assert!(
+                !DispatchableCallCode::<T>::contains_key(proposal_id),
+                "Dispatchable should not be in store"
+            );
+
+            assert_in_events::<T>(
+                RawEvent::ProposalDecisionMade(proposal_id.clone(), ProposalDecision::Rejected)
+                    .into()
+            );
+        }
+
+        assert_eq!(
+            ProposalsEngine::<T>::active_proposal_count(),
+            0,
+            "There should not be any proposal left active"
+        );
+    }
+
+    on_initialize_slashed {
+        let i in 1 .. T::MaxActiveProposalLimit::get();
+
+        let (proposers, proposals) = create_multiple_finalized_proposals::<T>(
+            i,
+            0,
+            VoteKind::Slash,
+            max(T::TotalVotersCounter::total_voters_count(), 1),
+            0,
+        );
+    }: { ProposalsEngine::<T>::on_initialize(System::<T>::block_number().into()) }
+    verify {
+        for proposer_account_id in proposers {
+            assert_eq!(
+                T::StakingHandler::current_stake(&proposer_account_id),
+                Zero::zero(),
+                "Shouldn't have any stake locked"
+            );
+
+            assert_eq!(
+                Balances::<T>::free_balance(&proposer_account_id),
+                Zero::zero(),
+                "Should've all balance slashed"
+            );
+        }
+
+        for proposal_id in proposals.iter() {
+
+            assert!(
+                !Proposals::<T>::contains_key(proposal_id),
+                "Proposal should not be in store"
+            );
+            assert!(
+                !DispatchableCallCode::<T>::contains_key(proposal_id),
+                "Dispatchable should not be in store"
+            );
+
+            assert_in_events::<T>(
+                RawEvent::ProposalDecisionMade(
+                    proposal_id.clone(),
+                    ProposalDecision::Slashed
+                ).into()
+            );
+        }
+
+        assert_eq!(
+            ProposalsEngine::<T>::active_proposal_count(),
+            0,
+            "There should not be any proposal left active"
+        );
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+    use crate::tests::mock::{initial_test_ext, Test};
+    use frame_support::assert_ok;
+
+    #[test]
+    fn test_vote() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_vote::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_cancel_proposal() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_cancel_proposal::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_veto_proposal() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_veto_proposal::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_initialize_immediate_execution_decode_fails() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_immediate_execution_decode_fails::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_initialize_approved_pending_constitutionality() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_approved_pending_constitutionality::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_initialize_pending_execution_decode_fails() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_pending_execution_decode_fails::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_initialize_rejected() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_rejected::<Test>());
+        });
+    }
+
+    #[test]
+    fn test_on_initialize_slashed() {
+        initial_test_ext().execute_with(|| {
+            assert_ok!(test_benchmark_on_initialize_slashed::<Test>());
+        });
+    }
+}

+ 151 - 16
runtime-modules/proposals/engine/src/lib.rs

@@ -119,6 +119,8 @@ pub use types::{
 
 pub(crate) mod types;
 
+mod benchmarking;
+
 #[cfg(test)]
 mod tests;
 
@@ -126,16 +128,32 @@ use codec::Decode;
 use frame_support::dispatch::{DispatchError, DispatchResult, UnfilteredDispatchable};
 use frame_support::storage::IterableStorageMap;
 use frame_support::traits::Get;
+use frame_support::weights::{GetDispatchInfo, Weight};
 use frame_support::{
     decl_error, decl_event, decl_module, decl_storage, ensure, Parameter, StorageDoubleMap,
 };
 use frame_system::{ensure_root, RawOrigin};
-use sp_arithmetic::traits::Zero;
+use sp_arithmetic::traits::{SaturatedConversion, Saturating, Zero};
 use sp_std::vec::Vec;
 
 use common::origin::ActorOriginValidator;
 use membership::staking_handler::StakingHandler;
 
+/// Proposals engine WeightInfo.
+/// Note: This was auto generated through the benchmark CLI using the `--weight-trait` flag
+pub trait WeightInfo {
+    fn vote(i: u32) -> Weight;
+    fn cancel_proposal(i: u32) -> Weight;
+    fn veto_proposal() -> Weight;
+    fn on_initialize_immediate_execution_decode_fails(i: u32) -> Weight;
+    fn on_initialize_pending_execution_decode_fails(i: u32) -> Weight;
+    fn on_initialize_approved_pending_constitutionality(i: u32) -> Weight;
+    fn on_initialize_rejected(i: u32) -> Weight;
+    fn on_initialize_slashed(i: u32) -> Weight;
+}
+
+type WeightInfoEngine<T> = <T as Trait>::WeightInfo;
+
 /// Proposals engine trait.
 pub trait Trait:
     frame_system::Trait + pallet_timestamp::Trait + membership::Trait + balances::Trait
@@ -178,10 +196,16 @@ pub trait Trait:
     type MaxActiveProposalLimit: Get<u32>;
 
     /// Proposals executable code. Can be instantiated by external module Call enum members.
-    type DispatchableCallCode: Parameter + UnfilteredDispatchable<Origin = Self::Origin> + Default;
+    type DispatchableCallCode: Parameter
+        + UnfilteredDispatchable<Origin = Self::Origin>
+        + GetDispatchInfo
+        + Default;
 
     /// Proposal state change observer.
     type ProposalObserver: ProposalObserver<Self>;
+
+    /// Weight information for extrinsics in this pallet.
+    type WeightInfo: WeightInfo;
 }
 
 /// Proposal state change observer.
@@ -348,8 +372,47 @@ decl_module! {
         /// Exports const -  max simultaneous active proposals number.
         const MaxActiveProposalLimit: u32 = T::MaxActiveProposalLimit::get();
 
+        /// Block Initialization. Perform voting period check, vote result tally, approved proposals
+        /// grace period checks, and proposal execution.
+        /// # <weight>
+        ///
+        /// ## Weight
+        /// `O (P + I)` where:
+        /// - `P` is the weight of all executed proposals
+        /// - `I` is the weight of the worst branch for anything else in `on_initialize`
+        /// - DB:
+        ///    - O(1) doesn't depend on the state
+        /// # </weight>
+        fn on_initialize() -> Weight {
+            // `process_proposal` returns the weight of the executed proposals. The weight of the
+            // executed proposals doesn't include any access to the store or calculation that
+            // `on_initialize` does. Therefore, to get the total weight of `on_initialize` we need
+            // to add the weight of the execution of `on_intialize` to the weight returned by
+            // `process_proposal`.
+            // To be safe, we use the worst possible case for `on_initialize`, meaning that there
+            // are as many proposals active as possible and they all take the worst possible branch.
+
+            // Maximum Weight of all possible worst case scenarios
+            let maximum_branch_weight = Self::weight_of_worst_on_initialize_branch();
+
+            // Weight of the executed proposals
+            let executed_proposals_weight = Self::process_proposals();
+
+            // total_weight = executed_proposals_weight + maximum_branch_weight
+            executed_proposals_weight.saturating_add(maximum_branch_weight)
+        }
+
         /// Vote extrinsic. Conditions:  origin must allow votes.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (R)` where:
+        /// - `R` is the length of `rationale`
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or paraemters
+        /// # </weight>
+        #[weight = WeightInfoEngine::<T>::vote(_rationale.len().saturated_into())]
         pub fn vote(
             origin,
             voter_id: MemberId<T>,
@@ -381,7 +444,16 @@ decl_module! {
         }
 
         /// Cancel a proposal by its original proposer.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (L)` where:
+        /// - `L` is the total number of locks in `Balances`
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or parameters
+        /// # </weight>
+        #[weight = WeightInfoEngine::<T>::cancel_proposal(T::MaxLocks::get())]
         pub fn cancel_proposal(origin, proposer_id: MemberId<T>, proposal_id: T::ProposalId) {
             T::ProposerOriginValidator::ensure_actor_origin(origin, proposer_id)?;
 
@@ -398,7 +470,15 @@ decl_module! {
         }
 
         /// Veto a proposal. Must be root.
-        #[weight = 10_000_000] // TODO: adjust weight
+        ///
+        /// <weight>
+        ///
+        /// ## Weight
+        /// `O (1)` doesn't depend on the state or parameters
+        /// - DB:
+        ///    - O(1) doesn't depend on the state or parameters
+        /// # </weight>
+        #[weight = WeightInfoEngine::<T>::veto_proposal()]
         pub fn veto_proposal(origin, proposal_id: T::ProposalId) {
             ensure_root(origin)?;
 
@@ -415,11 +495,6 @@ decl_module! {
             Self::finalize_proposal(proposal_id, proposal, ProposalDecision::Vetoed);
         }
 
-        /// Block finalization. Perform voting period check, vote result tally, approved proposals
-        /// grace period checks, and proposal execution.
-        fn on_finalize(_n: T::BlockNumber) {
-            Self::process_proposals();
-        }
     }
 }
 
@@ -609,19 +684,62 @@ impl<T: Trait> Module<T> {
 }
 
 impl<T: Trait> Module<T> {
+    // Helper to calculate the weight of the worst `on_initialize` branch
+    fn weight_of_worst_on_initialize_branch() -> Weight {
+        let max_active_proposals = T::MaxActiveProposalLimit::get();
+
+        // Weight when all the proposals are immediatly approved and executed
+        let immediate_execution_branch_weight =
+            WeightInfoEngine::<T>::on_initialize_immediate_execution_decode_fails(
+                max_active_proposals,
+            );
+
+        let pending_execution_branch_weight =
+            WeightInfoEngine::<T>::on_initialize_pending_execution_decode_fails(
+                max_active_proposals,
+            );
+
+        // Weight when all the proposals are approved and pending constitutionality
+        let approved_pending_constitutionality_branch_weight =
+            WeightInfoEngine::<T>::on_initialize_approved_pending_constitutionality(
+                max_active_proposals,
+            );
+
+        // Weight when all proposals are rejected
+        let rejected_branch_weight =
+            WeightInfoEngine::<T>::on_initialize_rejected(max_active_proposals);
+
+        // Weight when all proposals are slashed
+        let slashed_branch_weight =
+            WeightInfoEngine::<T>::on_initialize_slashed(max_active_proposals);
+
+        // Maximum Weight of all possible worst case scenarios
+        immediate_execution_branch_weight
+            .max(pending_execution_branch_weight)
+            .max(approved_pending_constitutionality_branch_weight)
+            .max(rejected_branch_weight)
+            .max(slashed_branch_weight)
+    }
+
     // Wrapper-function over System::block_number()
     fn current_block() -> T::BlockNumber {
         <frame_system::Module<T>>::block_number()
     }
 
     // Executes proposal code.
-    fn execute_proposal(proposal_id: T::ProposalId) {
+    // Returns the weight of the proposal(wether execution failed or not) or 0 if the proposal
+    // couldn't be decoded.
+    fn execute_proposal(proposal_id: T::ProposalId) -> Weight {
         let proposal_code = Self::proposal_codes(proposal_id);
 
         let proposal_code_result = T::DispatchableCallCode::decode(&mut &proposal_code[..]);
 
+        let mut execution_code_weight = 0;
+
         let execution_status = match proposal_code_result {
             Ok(proposal_code) => {
+                execution_code_weight = proposal_code.get_dispatch_info().weight;
+
                 if let Err(dispatch_error) =
                     proposal_code.dispatch_bypass_filter(T::Origin::from(RawOrigin::Root))
                 {
@@ -638,6 +756,8 @@ impl<T: Trait> Module<T> {
         Self::deposit_event(RawEvent::ProposalExecuted(proposal_id, execution_status));
 
         Self::remove_proposal_data(&proposal_id);
+
+        execution_code_weight
     }
 
     // Computes a finalized proposal:
@@ -648,17 +768,20 @@ impl<T: Trait> Module<T> {
     // - fire an event,
     // - update or delete proposal state.
     // Executes the proposal if it ready.
+    // If proposal was executed returns its weight otherwise it returns 0.
     fn finalize_proposal(
         proposal_id: T::ProposalId,
         proposal: ProposalOf<T>,
         proposal_decision: ProposalDecision,
-    ) {
+    ) -> Weight {
         // fire the proposal decision event
         Self::deposit_event(RawEvent::ProposalDecisionMade(
             proposal_id,
             proposal_decision.clone(),
         ));
 
+        let mut executed_weight = 0;
+
         // deal with stakes if necessary
         if proposal_decision
             != ProposalDecision::Approved(ApprovedProposalDecision::PendingConstitutionality)
@@ -685,13 +808,15 @@ impl<T: Trait> Module<T> {
 
             // immediately execute proposal if it ready for execution or save it for the future otherwise.
             if finalized_proposal.is_ready_for_execution(now) {
-                Self::execute_proposal(proposal_id);
+                executed_weight = Self::execute_proposal(proposal_id);
             } else {
                 <Proposals<T>>::insert(proposal_id, finalized_proposal);
             }
         } else {
             Self::remove_proposal_data(&proposal_id);
         }
+
+        executed_weight
     }
 
     // Slashes the stake and perform unstake only in case of existing stake.
@@ -766,11 +891,14 @@ impl<T: Trait> Module<T> {
 
     /// Perform voting period check, vote result tally, approved proposals
     /// grace period checks, and proposal execution.
-    fn process_proposals() {
+    /// Returns the total weight of all the executed proposals or 0 if none was executed.
+    fn process_proposals() -> Weight {
         // Collect all proposals.
         let proposals = <Proposals<T>>::iter().collect::<Vec<_>>();
         let now = Self::current_block();
 
+        let mut executed_weight = 0;
+
         for (proposal_id, proposal) in proposals {
             match proposal.status {
                 // Try to determine a decision for an active proposal.
@@ -780,18 +908,25 @@ impl<T: Trait> Module<T> {
 
                     // If decision is calculated for a proposal - finalize it.
                     if let Some(decision_status) = decision_status {
-                        Self::finalize_proposal(proposal_id, proposal, decision_status);
+                        executed_weight.saturating_add(Self::finalize_proposal(
+                            proposal_id,
+                            proposal,
+                            decision_status,
+                        ));
                     }
                 }
                 // Execute the proposal code if the proposal is ready for execution.
                 ProposalStatus::PendingExecution(_) => {
                     if proposal.is_ready_for_execution(now) {
-                        Self::execute_proposal(proposal_id);
+                        executed_weight =
+                            executed_weight.saturating_add(Self::execute_proposal(proposal_id));
                     }
                 }
                 // Skip the proposal until it gets reactivated.
                 ProposalStatus::PendingConstitutionality => {}
             }
         }
+
+        executed_weight
     }
 }

+ 57 - 1
runtime-modules/proposals/engine/src/tests/mock/mod.rs

@@ -7,7 +7,7 @@
 #![cfg(test)]
 
 use frame_support::traits::LockIdentifier;
-use frame_support::{impl_outer_event, impl_outer_origin, parameter_types};
+use frame_support::{impl_outer_event, impl_outer_origin, parameter_types, weights::Weight};
 pub use frame_system;
 use sp_core::H256;
 use sp_runtime::{
@@ -38,12 +38,17 @@ mod membership_mod {
     pub use membership::Event;
 }
 
+mod council {
+    pub use governance::council::Event;
+}
+
 impl_outer_event! {
     pub enum TestEvent for Test {
         balances<T>,
         engine<T>,
         membership_mod<T>,
         frame_system<T>,
+        council<T>,
     }
 }
 
@@ -98,6 +103,41 @@ impl crate::Trait for Test {
     type MaxActiveProposalLimit = MaxActiveProposalLimit;
     type DispatchableCallCode = proposals::Call<Test>;
     type ProposalObserver = ();
+    type WeightInfo = ();
+}
+
+impl crate::WeightInfo for () {
+    fn vote(_: u32) -> Weight {
+        0
+    }
+
+    fn cancel_proposal(_: u32) -> Weight {
+        0
+    }
+
+    fn veto_proposal() -> Weight {
+        0
+    }
+
+    fn on_initialize_immediate_execution_decode_fails(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_pending_execution_decode_fails(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_approved_pending_constitutionality(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_rejected(_: u32) -> Weight {
+        0
+    }
+
+    fn on_initialize_slashed(_: u32) -> Weight {
+        0
+    }
 }
 
 impl ProposalObserver<Test> for () {
@@ -170,6 +210,22 @@ impl pallet_timestamp::Trait for Test {
     type WeightInfo = ();
 }
 
+impl governance::council::Trait for Test {
+    type Event = TestEvent;
+    type CouncilTermEnded = ();
+}
+
+impl recurringrewards::Trait for Test {
+    type PayoutStatusHandler = ();
+    type RecipientId = u64;
+    type RewardRelationshipId = u64;
+}
+
+impl minting::Trait for Test {
+    type Currency = Balances;
+    type MintId = u64;
+}
+
 pub fn initial_test_ext() -> sp_io::TestExternalities {
     let t = frame_system::GenesisConfig::default()
         .build_storage::<Test>()

+ 29 - 31
runtime-modules/proposals/engine/src/tests/mod.rs

@@ -375,7 +375,7 @@ fn vote_fails_with_insufficient_rights() {
 fn proposal_execution_succeeds() {
     initial_test_ext().execute_with(|| {
         let starting_block = 1;
-        run_to_block_and_finalize(starting_block);
+        run_to_block(starting_block);
 
         let parameters_fixture = ProposalParametersFixture::default();
         let dummy_proposal =
@@ -391,7 +391,7 @@ fn proposal_execution_succeeds() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(2);
+        run_to_block(2);
 
         EventFixture::assert_events(vec![
             RawEvent::ProposalCreated(1, proposal_id),
@@ -405,7 +405,7 @@ fn proposal_execution_succeeds() {
             ),
             RawEvent::ProposalStatusUpdated(
                 proposal_id,
-                ProposalStatus::PendingExecution(starting_block),
+                ProposalStatus::PendingExecution(starting_block + 1),
             ),
             RawEvent::ProposalExecuted(proposal_id, ExecutionStatus::Executed),
         ]);
@@ -420,7 +420,7 @@ fn proposal_execution_succeeds() {
 fn proposal_execution_failed() {
     initial_test_ext().execute_with(|| {
         let starting_block = 1;
-        run_to_block_and_finalize(starting_block);
+        run_to_block(starting_block);
 
         let parameters_fixture = ProposalParametersFixture::default();
 
@@ -441,7 +441,7 @@ fn proposal_execution_failed() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(2);
+        run_to_block(2);
 
         assert!(!<crate::Proposals<Test>>::contains_key(proposal_id));
 
@@ -457,7 +457,7 @@ fn proposal_execution_failed() {
             ),
             RawEvent::ProposalStatusUpdated(
                 proposal_id,
-                ProposalStatus::PendingExecution(starting_block),
+                ProposalStatus::PendingExecution(starting_block + 1),
             ),
             RawEvent::ProposalExecuted(
                 proposal_id,
@@ -472,7 +472,7 @@ fn voting_results_calculation_succeeds() {
     initial_test_ext().execute_with(|| {
         // to enable events
         let starting_block = 1;
-        run_to_block_and_finalize(starting_block);
+        run_to_block(starting_block);
 
         let parameters = ProposalParameters {
             voting_period: 3,
@@ -494,7 +494,7 @@ fn voting_results_calculation_succeeds() {
         vote_generator.vote_and_assert_ok(VoteKind::Abstain);
 
         let block_number = 3;
-        run_to_block_and_finalize(block_number);
+        run_to_block(block_number);
 
         EventFixture::assert_events(vec![
             RawEvent::ProposalCreated(1, proposal_id),
@@ -508,7 +508,7 @@ fn voting_results_calculation_succeeds() {
             ),
             RawEvent::ProposalStatusUpdated(
                 proposal_id,
-                ProposalStatus::PendingExecution(starting_block),
+                ProposalStatus::PendingExecution(starting_block + 1),
             ),
             RawEvent::ProposalExecuted(proposal_id, ExecutionStatus::Executed),
         ]);
@@ -877,8 +877,7 @@ fn proposal_execution_postponed_because_of_grace_period() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(1);
-        run_to_block_and_finalize(2);
+        run_to_block(3);
 
         let proposal = <crate::Proposals<Test>>::get(proposal_id);
 
@@ -888,7 +887,7 @@ fn proposal_execution_postponed_because_of_grace_period() {
                 parameters: parameters_fixture.params(),
                 proposer_id: 1,
                 activated_at: 0,
-                status: ProposalStatus::approved(ApprovedProposalDecision::PendingExecution, 0),
+                status: ProposalStatus::approved(ApprovedProposalDecision::PendingExecution, 1),
                 voting_results: VotingResults {
                     abstentions: 0,
                     approvals: 4,
@@ -907,7 +906,7 @@ fn proposal_execution_postponed_because_of_grace_period() {
 fn proposal_execution_vetoed_successfully_during_the_grace_period() {
     initial_test_ext().execute_with(|| {
         let starting_block = 1;
-        run_to_block_and_finalize(starting_block);
+        run_to_block(starting_block);
 
         let parameters_fixture = ProposalParametersFixture::default().with_grace_period(3);
         let dummy_proposal =
@@ -921,8 +920,7 @@ fn proposal_execution_vetoed_successfully_during_the_grace_period() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(1);
-        run_to_block_and_finalize(2);
+        run_to_block(3);
 
         let pre_veto_proposal = <crate::Proposals<Test>>::get(proposal_id);
 
@@ -934,7 +932,7 @@ fn proposal_execution_vetoed_successfully_during_the_grace_period() {
                 activated_at: starting_block,
                 status: ProposalStatus::approved(
                     ApprovedProposalDecision::PendingExecution,
-                    starting_block
+                    starting_block + 1
                 ),
                 voting_results: VotingResults {
                     abstentions: 0,
@@ -962,7 +960,7 @@ fn proposal_execution_vetoed_successfully_during_the_grace_period() {
 fn proposal_execution_succeeds_after_the_grace_period() {
     initial_test_ext().execute_with(|| {
         let starting_block = 1;
-        run_to_block_and_finalize(starting_block);
+        run_to_block(starting_block);
 
         let parameters_fixture = ProposalParametersFixture::default().with_grace_period(2);
         let dummy_proposal =
@@ -975,7 +973,7 @@ fn proposal_execution_succeeds_after_the_grace_period() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(1);
+        run_to_block(2);
 
         let proposal = <crate::Proposals<Test>>::get(proposal_id);
 
@@ -985,7 +983,7 @@ fn proposal_execution_succeeds_after_the_grace_period() {
             activated_at: starting_block,
             status: ProposalStatus::approved(
                 ApprovedProposalDecision::PendingExecution,
-                starting_block,
+                starting_block + 1,
             ),
             voting_results: VotingResults {
                 abstentions: 0,
@@ -1000,8 +998,8 @@ fn proposal_execution_succeeds_after_the_grace_period() {
 
         assert_eq!(proposal, expected_proposal);
 
-        let finalization_block = 3;
-        run_to_block_and_finalize(finalization_block);
+        let finalization_block = 4;
+        run_to_block(finalization_block);
 
         EventFixture::assert_last_crate_event(RawEvent::ProposalExecuted(
             proposal_id,
@@ -1535,7 +1533,7 @@ fn proposal_execution_with_exact_execution_works() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
         // Proposal exists after the grace period
-        run_to_block_and_finalize(5);
+        run_to_block(5);
 
         let proposal = <crate::Proposals<Test>>::get(proposal_id);
 
@@ -1545,7 +1543,7 @@ fn proposal_execution_with_exact_execution_works() {
                 parameters: parameters_fixture.params(),
                 proposer_id: 1,
                 activated_at: 0,
-                status: ProposalStatus::approved(ApprovedProposalDecision::PendingExecution, 0),
+                status: ProposalStatus::approved(ApprovedProposalDecision::PendingExecution, 1),
                 voting_results: VotingResults {
                     abstentions: 0,
                     approvals: 4,
@@ -1559,7 +1557,7 @@ fn proposal_execution_with_exact_execution_works() {
         );
 
         // Exact execution block time.
-        run_to_block_and_finalize(exact_block);
+        run_to_block(exact_block);
 
         EventFixture::assert_last_crate_event(RawEvent::ProposalExecuted(
             proposal_id,
@@ -1719,7 +1717,7 @@ fn proposal_with_pending_constitutionality_reactivation_succeeds() {
 fn proposal_with_pending_constitutionality_execution_succeeds() {
     initial_test_ext().execute_with(|| {
         let starting_block = 1;
-        run_to_block_and_finalize(1);
+        run_to_block(starting_block);
 
         let account_id = 1;
         let total_balance = 1000;
@@ -1749,7 +1747,7 @@ fn proposal_with_pending_constitutionality_execution_succeeds() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        run_to_block_and_finalize(2);
+        run_to_block(2);
 
         // first chain of event from the creation to the approval
         EventFixture::assert_global_events(vec![
@@ -1780,7 +1778,7 @@ fn proposal_with_pending_constitutionality_execution_succeeds() {
                 activated_at: starting_block,
                 status: ProposalStatus::approved(
                     ApprovedProposalDecision::PendingConstitutionality,
-                    starting_block
+                    starting_block + 1
                 ),
                 voting_results: VotingResults {
                     abstentions: 0,
@@ -1800,7 +1798,7 @@ fn proposal_with_pending_constitutionality_execution_succeeds() {
         );
 
         let reactivation_block = 5;
-        run_to_block_and_finalize(reactivation_block);
+        run_to_block(reactivation_block);
 
         ProposalsEngine::reactivate_pending_constitutionality_proposals();
 
@@ -1822,8 +1820,8 @@ fn proposal_with_pending_constitutionality_execution_succeeds() {
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
         vote_generator.vote_and_assert_ok(VoteKind::Approve);
 
-        let next_block_after_approval = 6;
-        run_to_block_and_finalize(next_block_after_approval);
+        let next_block_after_approval = 7;
+        run_to_block(next_block_after_approval);
 
         // internal active proposal counter check
         assert_eq!(<ActiveProposalCount>::get(), 0);
@@ -1863,7 +1861,7 @@ fn proposal_with_pending_constitutionality_execution_succeeds() {
             )),
             TestEvent::engine(RawEvent::ProposalStatusUpdated(
                 proposal_id,
-                ProposalStatus::PendingExecution(reactivation_block),
+                ProposalStatus::PendingExecution(reactivation_block + 1),
             )),
             // execution
             TestEvent::engine(RawEvent::ProposalExecuted(

+ 1 - 0
runtime/Cargo.toml

@@ -169,6 +169,7 @@ runtime-benchmarks = [
 	"pallet-session-benchmarking",
     "pallet-utility/runtime-benchmarks",
     "proposals-discussion/runtime-benchmarks",
+    "proposals-engine/runtime-benchmarks",
     "hex-literal",
 ]
 

+ 2 - 0
runtime/src/lib.rs

@@ -659,6 +659,7 @@ impl proposals_engine::Trait for Runtime {
     type MaxActiveProposalLimit = ProposalMaxActiveProposalLimit;
     type DispatchableCallCode = Call;
     type ProposalObserver = ProposalsCodex;
+    type WeightInfo = weights::proposals_engine::WeightInfo;
 }
 
 impl Default for Call {
@@ -678,6 +679,7 @@ impl proposals_discussion::Trait for Runtime {
     type ThreadId = ThreadId;
     type PostId = PostId;
     type MaxWhiteListSize = MaxWhiteListSize;
+    type WeightInfo = weights::proposals_discussion::WeightInfo;
 }
 
 parameter_types! {

+ 16 - 22
runtime/src/runtime_api.rs

@@ -255,48 +255,42 @@ impl_runtime_apis! {
         }
     }
 
-     #[cfg(feature = "runtime-benchmarks")]
+    #[cfg(feature = "runtime-benchmarks")]
     impl frame_benchmarking::Benchmark<Block> for Runtime {
         fn dispatch_benchmark(
-            pallet: Vec<u8>,
-            benchmark: Vec<u8>,
-            lowest_range_values: Vec<u32>,
-            highest_range_values: Vec<u32>,
-            steps: Vec<u32>,
-            repeat: u32,
+            config: frame_benchmarking::BenchmarkConfig
         ) -> Result<Vec<frame_benchmarking::BenchmarkBatch>, sp_runtime::RuntimeString> {
-            /*
-             * TODO: remember to benchhmark every pallet
-             */
             use sp_std::vec;
-            use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark};
+            use frame_benchmarking::{Benchmarking, BenchmarkBatch, add_benchmark, TrackedStorageKey};
             use frame_system_benchmarking::Module as SystemBench;
             impl frame_system_benchmarking::Trait for Runtime {}
 
             use crate::ProposalsDiscussion;
+            use crate::ProposalsEngine;
 
-            let whitelist: Vec<Vec<u8>> = vec![
+            let whitelist: Vec<TrackedStorageKey> = vec![
                 // Block Number
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef702a5c1b19ab7a04f536c519aca4983ac").to_vec().into(),
                 // Total Issuance
-                hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec(),
+                hex_literal::hex!("c2261276cc9d1f8598ea4b6a74b15c2f57c875e4cff74148e4628f264b974c80").to_vec().into(),
                 // Execution Phase
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7ff553b5a9862a516939d82b3d3d8661a").to_vec().into(),
                 // Event Count
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef70a98fdbe9ce6c55837576c60c7af3850").to_vec().into(),
                 // System Events
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef780d41e5e16056765bc8461851072c9d7").to_vec().into(),
                 // Caller 0 Account
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da946c154ffd9992e395af90b5b13cc6f295c77033fce8a9045824a6690bbf99c6db269502f0a8d1d2a008542d5690a0749").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da946c154ffd9992e395af90b5b13cc6f295c77033fce8a9045824a6690bbf99c6db269502f0a8d1d2a008542d5690a0749").to_vec().into(),
                 // Treasury Account
-                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95ecffd7b6c0f78751baa9d281e0bfa3a6d6f646c70792f74727372790000000000000000000000000000000000000000").to_vec(),
+                hex_literal::hex!("26aa394eea5630e07c48ae0c9558cef7b99d880ec681799c0cf30e8886371da95ecffd7b6c0f78751baa9d281e0bfa3a6d6f646c70792f74727372790000000000000000000000000000000000000000").to_vec().into(),
             ];
 
             let mut batches = Vec::<BenchmarkBatch>::new();
-            let params = (&pallet, &benchmark, &lowest_range_values, &highest_range_values, &steps, repeat, &whitelist);
+            let params = (&config, &whitelist);
 
-            add_benchmark!(params, batches, b"system", SystemBench::<Runtime>);
-            add_benchmark!(params, batches, b"proposals-discussion", ProposalsDiscussion);
+            add_benchmark!(params, batches, system, SystemBench::<Runtime>);
+            add_benchmark!(params, batches, proposals_discussion, ProposalsDiscussion);
+            add_benchmark!(params, batches, proposals_engine, ProposalsEngine);
 
             if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) }
             Ok(batches)

+ 3 - 3
runtime/src/tests/proposals_integration/working_group_proposals.rs

@@ -451,7 +451,7 @@ fn run_create_begin_review_working_group_leader_applications_proposal_execution_
             hiring_opening.stage,
             hiring::OpeningStage::Active {
                 stage: hiring::ActiveOpeningStage::AcceptingApplications {
-                    started_accepting_applicants_at_block: 0
+                    started_accepting_applicants_at_block: 1
                 },
                 applications_added: BTreeSet::new(),
                 active_application_count: 0,
@@ -469,8 +469,8 @@ fn run_create_begin_review_working_group_leader_applications_proposal_execution_
             hiring_opening.stage,
             hiring::OpeningStage::Active {
                 stage: hiring::ActiveOpeningStage::ReviewPeriod {
-                    started_accepting_applicants_at_block: 0,
-                    started_review_period_at_block: grace_period + 2,
+                    started_accepting_applicants_at_block: 1,
+                    started_review_period_at_block: grace_period + 3,
                 },
                 applications_added: BTreeSet::new(),
                 active_application_count: 0,

+ 3 - 0
runtime/src/weights/mod.rs

@@ -22,3 +22,6 @@ pub mod pallet_session;
 pub mod pallet_staking;
 pub mod pallet_timestamp;
 pub mod pallet_utility;
+
+pub mod proposals_discussion;
+pub mod proposals_engine;

+ 27 - 0
runtime/src/weights/proposals_discussion.rs

@@ -0,0 +1,27 @@
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
+
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::weights::{constants::RocksDbWeight as DbWeight, Weight};
+
+pub struct WeightInfo;
+impl proposals_discussion::WeightInfo for WeightInfo {
+    // WARNING! Some components were not used: ["j"]
+    fn add_post(i: u32) -> Weight {
+        (361_189_000 as Weight)
+            .saturating_add((508_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(4 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }
+    // WARNING! Some components were not used: ["j"]
+    fn update_post() -> Weight {
+        (231_487_000 as Weight).saturating_add(DbWeight::get().reads(3 as Weight))
+    }
+    fn change_thread_mode(i: u32) -> Weight {
+        (379_400_000 as Weight)
+            .saturating_add((1_244_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(3 as Weight))
+            .saturating_add(DbWeight::get().writes(1 as Weight))
+    }
+}

+ 66 - 0
runtime/src/weights/proposals_engine.rs

@@ -0,0 +1,66 @@
+//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 2.0.0
+
+#![allow(unused_parens)]
+#![allow(unused_imports)]
+
+use frame_support::weights::{constants::RocksDbWeight as DbWeight, Weight};
+
+pub struct WeightInfo;
+impl proposals_engine::WeightInfo for WeightInfo {
+    fn vote(i: u32) -> Weight {
+        (375_240_000 as Weight)
+            .saturating_add((35_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(4 as Weight))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+    }
+    fn cancel_proposal(i: u32) -> Weight {
+        (874_300_000 as Weight)
+            .saturating_add((1_713_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(5 as Weight))
+            .saturating_add(DbWeight::get().writes(8 as Weight))
+    }
+    fn veto_proposal() -> Weight {
+        (404_254_000 as Weight)
+            .saturating_add(DbWeight::get().reads(4 as Weight))
+            .saturating_add(DbWeight::get().writes(8 as Weight))
+    }
+    fn on_initialize_immediate_execution_decode_fails(i: u32) -> Weight {
+        (22_531_000 as Weight)
+            .saturating_add((578_486_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(3 as Weight))
+            .saturating_add(DbWeight::get().reads((4 as Weight).saturating_mul(i as Weight)))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+            .saturating_add(DbWeight::get().writes((7 as Weight).saturating_mul(i as Weight)))
+    }
+    fn on_initialize_pending_execution_decode_fails(i: u32) -> Weight {
+        (31_944_000 as Weight)
+            .saturating_add((274_852_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(2 as Weight))
+            .saturating_add(DbWeight::get().reads((2 as Weight).saturating_mul(i as Weight)))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+            .saturating_add(DbWeight::get().writes((5 as Weight).saturating_mul(i as Weight)))
+    }
+    fn on_initialize_approved_pending_constitutionality(i: u32) -> Weight {
+        (50_422_000 as Weight)
+            .saturating_add((250_210_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(2 as Weight))
+            .saturating_add(DbWeight::get().reads((1 as Weight).saturating_mul(i as Weight)))
+            .saturating_add(DbWeight::get().writes((1 as Weight).saturating_mul(i as Weight)))
+    }
+    fn on_initialize_rejected(i: u32) -> Weight {
+        (0 as Weight)
+            .saturating_add((884_947_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(3 as Weight))
+            .saturating_add(DbWeight::get().reads((3 as Weight).saturating_mul(i as Weight)))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+            .saturating_add(DbWeight::get().writes((7 as Weight).saturating_mul(i as Weight)))
+    }
+    fn on_initialize_slashed(i: u32) -> Weight {
+        (24_867_000 as Weight)
+            .saturating_add((628_899_000 as Weight).saturating_mul(i as Weight))
+            .saturating_add(DbWeight::get().reads(3 as Weight))
+            .saturating_add(DbWeight::get().reads((3 as Weight).saturating_mul(i as Weight)))
+            .saturating_add(DbWeight::get().writes(2 as Weight))
+            .saturating_add(DbWeight::get().writes((7 as Weight).saturating_mul(i as Weight)))
+    }
+}

+ 14 - 0
scripts/generate-weights.sh

@@ -0,0 +1,14 @@
+#!/usr/bin/env bash
+
+# Executes and replaces all benchmarks with the new weights
+
+echo "Benchmarking proposals_discussion..."
+./target/release/joystream-node benchmark --pallet=proposals_discussion --extrinsic=* --chain=dev --steps=50 --repeat=20 --execution=wasm --output=. > /dev/null
+mv proposals_discussion.rs runtime/src/weights/
+echo "proposals_discussion benchmarked"
+
+
+echo "Benchmarking proposals_engine..."
+./target/release/joystream-node benchmark --pallet=proposals_engine --extrinsic=* --chain=dev --steps=50 --repeat=20 --execution=wasm --output=. > /dev/null
+mv proposals_engine.rs runtime/src/weights/
+echo "proposals_engine benchmarked"