diff --git a/pallets/aura-ext/Cargo.toml b/pallets/aura-ext/Cargo.toml index 1db43697511..df145aad522 100644 --- a/pallets/aura-ext/Cargo.toml +++ b/pallets/aura-ext/Cargo.toml @@ -18,6 +18,9 @@ sp-consensus-aura = { git = "https://github.com/paritytech/substrate", default-f sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "master" } +# Cumulus +cumulus-pallet-parachain-system = { path = "../parachain-system", default-features = false } + [dev-dependencies] # Cumulus @@ -35,5 +38,6 @@ std = [ "sp-consensus-aura/std", "sp-runtime/std", "sp-std/std", + "cumulus-pallet-parachain-system/std", ] try-runtime = ["frame-support/try-runtime"] diff --git a/pallets/aura-ext/src/consensus_hook.rs b/pallets/aura-ext/src/consensus_hook.rs new file mode 100644 index 00000000000..c8806b1f4cb --- /dev/null +++ b/pallets/aura-ext/src/consensus_hook.rs @@ -0,0 +1,56 @@ +// Copyright 2023 Parity Technologies (UK) Ltd. +// This file is part of Cumulus. + +// Cumulus is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Cumulus is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Cumulus. If not, see . + +//! The definition of a [`FixedVelocityConsensusHook`] for consensus logic to manage +//! block velocity. +//! +//! The velocity `V` refers to the rate of block processing by the relay chain. + +use super::pallet; +use cumulus_pallet_parachain_system::{ + consensus_hook::{ConsensusHook, UnincludedSegmentCapacity}, + relay_state_snapshot::RelayChainStateProof, +}; +use frame_support::pallet_prelude::*; +use sp_std::{marker::PhantomData, num::NonZeroU32}; + +/// A consensus hook for a fixed block processing velocity and unincluded segment capacity. +pub struct FixedVelocityConsensusHook(PhantomData); + +impl ConsensusHook + for FixedVelocityConsensusHook +{ + // Validates the number of authored blocks within the slot with respect to the `V + 1` limit. + fn on_state_proof(_state_proof: &RelayChainStateProof) -> (Weight, UnincludedSegmentCapacity) { + // Ensure velocity is non-zero. + let velocity = V.max(1); + + let authored = pallet::Pallet::::slot_info() + .map(|(_slot, authored)| authored) + .expect("slot info is inserted on block initialization"); + if authored > velocity + 1 { + panic!("authored blocks limit is reached for the slot") + } + let weight = T::DbWeight::get().reads(1); + + ( + weight, + NonZeroU32::new(sp_std::cmp::max(C, 1)) + .expect("1 is the minimum value and non-zero; qed") + .into(), + ) + } +} diff --git a/pallets/aura-ext/src/lib.rs b/pallets/aura-ext/src/lib.rs index 15e82edeefe..5605e2f2ac5 100644 --- a/pallets/aura-ext/src/lib.rs +++ b/pallets/aura-ext/src/lib.rs @@ -37,9 +37,12 @@ use frame_support::traits::{ExecuteBlock, FindAuthor}; use sp_application_crypto::RuntimeAppPublic; -use sp_consensus_aura::digests::CompatibleDigestItem; +use sp_consensus_aura::{digests::CompatibleDigestItem, Slot}; use sp_runtime::traits::{Block as BlockT, Header as HeaderT}; +pub mod consensus_hook; +pub use consensus_hook::FixedVelocityConsensusHook; + type Aura = pallet_aura::Pallet; pub use pallet::*; @@ -68,6 +71,19 @@ pub mod pallet { // Fetch the authorities once to get them into the storage proof of the PoV. Authorities::::get(); + let new_slot = Aura::::current_slot(); + + let (new_slot, authored) = match SlotInfo::::get() { + Some((slot, authored)) if slot == new_slot => (slot, authored + 1), + Some((slot, _)) if slot < new_slot => (new_slot, 1), + Some(..) => { + panic!("slot moved backwards") + }, + None => (new_slot, 1), + }; + + SlotInfo::::put((new_slot, authored)); + T::DbWeight::get().reads_writes(2, 1) } } @@ -84,6 +100,13 @@ pub mod pallet { ValueQuery, >; + /// Current slot paired with a number of authored blocks. + /// + /// Updated on each block initialization. + #[pallet::storage] + #[pallet::getter(fn slot_info)] + pub(crate) type SlotInfo = StorageValue<_, (Slot, u32), OptionQuery>; + #[pallet::genesis_config] #[derive(Default)] pub struct GenesisConfig; diff --git a/pallets/parachain-system/src/consensus_hook.rs b/pallets/parachain-system/src/consensus_hook.rs index 8d3606d14b8..bdf590a0fd5 100644 --- a/pallets/parachain-system/src/consensus_hook.rs +++ b/pallets/parachain-system/src/consensus_hook.rs @@ -18,6 +18,7 @@ //! of parachain blocks ready to submit to the relay chain, as well as some basic implementations. use super::relay_state_snapshot::RelayChainStateProof; +use frame_support::weights::Weight; use sp_std::num::NonZeroU32; /// The possible capacity of the unincluded segment. @@ -61,8 +62,8 @@ pub trait ConsensusHook { /// This hook is called partway through the `set_validation_data` inherent in parachain-system. /// /// The hook is allowed to panic if customized consensus rules aren't met and is required - /// to return a maximum capacity for the unincluded segment. - fn on_state_proof(state_proof: &RelayChainStateProof) -> UnincludedSegmentCapacity; + /// to return a maximum capacity for the unincluded segment with weight consumed. + fn on_state_proof(state_proof: &RelayChainStateProof) -> (Weight, UnincludedSegmentCapacity); } /// A special consensus hook for handling the migration to asynchronous backing gracefully, @@ -75,8 +76,11 @@ pub trait ConsensusHook { pub struct ExpectParentIncluded; impl ConsensusHook for ExpectParentIncluded { - fn on_state_proof(_state_proof: &RelayChainStateProof) -> UnincludedSegmentCapacity { - UnincludedSegmentCapacity(UnincludedSegmentCapacityInner::ExpectParentIncluded) + fn on_state_proof(_state_proof: &RelayChainStateProof) -> (Weight, UnincludedSegmentCapacity) { + ( + Weight::zero(), + UnincludedSegmentCapacity(UnincludedSegmentCapacityInner::ExpectParentIncluded), + ) } } @@ -88,10 +92,13 @@ impl ConsensusHook for ExpectParentIncluded { pub struct FixedCapacityUnincludedSegment; impl ConsensusHook for FixedCapacityUnincludedSegment { - fn on_state_proof(_state_proof: &RelayChainStateProof) -> UnincludedSegmentCapacity { - NonZeroU32::new(sp_std::cmp::max(N, 1)) - .expect("1 is the minimum value and non-zero; qed") - .into() + fn on_state_proof(_state_proof: &RelayChainStateProof) -> (Weight, UnincludedSegmentCapacity) { + ( + Weight::zero(), + NonZeroU32::new(sp_std::cmp::max(N, 1)) + .expect("1 is the minimum value and non-zero; qed") + .into(), + ) } } diff --git a/pallets/parachain-system/src/lib.rs b/pallets/parachain-system/src/lib.rs index 50df5e8ba59..5601bcdd17d 100644 --- a/pallets/parachain-system/src/lib.rs +++ b/pallets/parachain-system/src/lib.rs @@ -58,12 +58,12 @@ use sp_std::{cmp, collections::btree_map::BTreeMap, prelude::*}; use xcm::latest::XcmHash; mod migration; -mod relay_state_snapshot; #[cfg(test)] mod tests; mod unincluded_segment; pub mod consensus_hook; +pub mod relay_state_snapshot; #[macro_use] pub mod validate_block; @@ -484,7 +484,8 @@ pub mod pallet { .expect("Invalid relay chain state proof"); // Update the desired maximum capacity according to the consensus hook. - let capacity = T::ConsensusHook::on_state_proof(&relay_state_proof); + let (consensus_hook_weight, capacity) = + T::ConsensusHook::on_state_proof(&relay_state_proof); // initialization logic: we know that this runs exactly once every block, // which means we can put the initialization logic here to remove the @@ -543,7 +544,7 @@ pub mod pallet { // ancestor was included, the MQC heads wouldn't match and the block would be invalid. // // - let mut total_weight = Weight::zero(); + let mut total_weight = consensus_hook_weight; total_weight += Self::process_inbound_downward_messages( relevant_messaging_state.dmq_mqc_head, downward_messages, diff --git a/pallets/parachain-system/src/relay_state_snapshot.rs b/pallets/parachain-system/src/relay_state_snapshot.rs index 9da5a03ce83..ead077f527d 100644 --- a/pallets/parachain-system/src/relay_state_snapshot.rs +++ b/pallets/parachain-system/src/relay_state_snapshot.rs @@ -14,6 +14,8 @@ // You should have received a copy of the GNU General Public License // along with Cumulus. If not, see . +//! Relay chain state proof provides means for accessing part of relay chain storage for reads. + use codec::{Decode, Encode}; use cumulus_primitives_core::{ relay_chain, AbridgedHostConfiguration, AbridgedHrmpChannel, ParaId, diff --git a/pallets/parachain-system/src/tests.rs b/pallets/parachain-system/src/tests.rs index e1010f89be0..574ab43078d 100755 --- a/pallets/parachain-system/src/tests.rs +++ b/pallets/parachain-system/src/tests.rs @@ -121,14 +121,14 @@ std::thread_local! { static HANDLED_DMP_MESSAGES: RefCell)>> = RefCell::new(Vec::new()); static HANDLED_XCMP_MESSAGES: RefCell)>> = RefCell::new(Vec::new()); static SENT_MESSAGES: RefCell)>> = RefCell::new(Vec::new()); - static CONSENSUS_HOOK: RefCell UnincludedSegmentCapacity>> - = RefCell::new(Box::new(|_| NonZeroU32::new(1).unwrap().into())); + static CONSENSUS_HOOK: RefCell (Weight, UnincludedSegmentCapacity)>> + = RefCell::new(Box::new(|_| (Weight::zero(), NonZeroU32::new(1).unwrap().into()))); } pub struct TestConsensusHook; impl ConsensusHook for TestConsensusHook { - fn on_state_proof(s: &RelayChainStateProof) -> UnincludedSegmentCapacity { + fn on_state_proof(s: &RelayChainStateProof) -> (Weight, UnincludedSegmentCapacity) { CONSENSUS_HOOK.with(|f| f.borrow_mut()(s)) } } @@ -440,7 +440,9 @@ fn block_tests_run_on_drop() { #[test] fn unincluded_segment_works() { - CONSENSUS_HOOK.with(|c| *c.borrow_mut() = Box::new(|_| NonZeroU32::new(10).unwrap().into())); + CONSENSUS_HOOK.with(|c| { + *c.borrow_mut() = Box::new(|_| (Weight::zero(), NonZeroU32::new(10).unwrap().into())) + }); BlockTests::new() .with_inclusion_delay(1) @@ -475,7 +477,9 @@ fn unincluded_segment_works() { #[test] #[should_panic = "no space left for the block in the unincluded segment"] fn unincluded_segment_is_limited() { - CONSENSUS_HOOK.with(|c| *c.borrow_mut() = Box::new(|_| NonZeroU32::new(1).unwrap().into())); + CONSENSUS_HOOK.with(|c| { + *c.borrow_mut() = Box::new(|_| (Weight::zero(), NonZeroU32::new(1).unwrap().into())) + }); BlockTests::new() .with_inclusion_delay(2)