Skip to content
This repository has been archived by the owner on Nov 15, 2023. It is now read-only.

aura-ext: limit the number of authored blocks per slot #2551

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions pallets/aura-ext/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"]
56 changes: 56 additions & 0 deletions pallets/aura-ext/src/consensus_hook.rs
Original file line number Diff line number Diff line change
@@ -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 <http://www.gnu.org/licenses/>.

//! 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<T, const V: u32, const C: u32>(PhantomData<T>);

impl<T: pallet::Config, const V: u32, const C: u32> ConsensusHook
for FixedVelocityConsensusHook<T, V, C>
{
// 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::<T>::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(),
)
}
}
25 changes: 24 additions & 1 deletion pallets/aura-ext/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<T> = pallet_aura::Pallet<T>;

pub use pallet::*;
Expand Down Expand Up @@ -68,6 +71,19 @@ pub mod pallet {
// Fetch the authorities once to get them into the storage proof of the PoV.
Authorities::<T>::get();

let new_slot = Aura::<T>::current_slot();

let (new_slot, authored) = match SlotInfo::<T>::get() {
slumber marked this conversation as resolved.
Show resolved Hide resolved
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::<T>::put((new_slot, authored));

T::DbWeight::get().reads_writes(2, 1)
}
}
Expand All @@ -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<T: Config> = StorageValue<_, (Slot, u32), OptionQuery>;

#[pallet::genesis_config]
#[derive(Default)]
pub struct GenesisConfig;
Expand Down
23 changes: 15 additions & 8 deletions pallets/parachain-system/src/consensus_hook.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand All @@ -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),
)
}
}

Expand All @@ -88,10 +92,13 @@ impl ConsensusHook for ExpectParentIncluded {
pub struct FixedCapacityUnincludedSegment<const N: u32>;

impl<const N: u32> ConsensusHook for FixedCapacityUnincludedSegment<N> {
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(),
)
}
}

Expand Down
7 changes: 4 additions & 3 deletions pallets/parachain-system/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -543,7 +544,7 @@ pub mod pallet {
// ancestor was included, the MQC heads wouldn't match and the block would be invalid.
//
// <https://github.com/paritytech/cumulus/issues/2472>
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,
Expand Down
2 changes: 2 additions & 0 deletions pallets/parachain-system/src/relay_state_snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
// You should have received a copy of the GNU General Public License
// along with Cumulus. If not, see <http://www.gnu.org/licenses/>.

//! 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,
Expand Down
14 changes: 9 additions & 5 deletions pallets/parachain-system/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,14 @@ std::thread_local! {
static HANDLED_DMP_MESSAGES: RefCell<Vec<(relay_chain::BlockNumber, Vec<u8>)>> = RefCell::new(Vec::new());
static HANDLED_XCMP_MESSAGES: RefCell<Vec<(ParaId, relay_chain::BlockNumber, Vec<u8>)>> = RefCell::new(Vec::new());
static SENT_MESSAGES: RefCell<Vec<(ParaId, Vec<u8>)>> = RefCell::new(Vec::new());
static CONSENSUS_HOOK: RefCell<Box<dyn Fn(&RelayChainStateProof) -> UnincludedSegmentCapacity>>
= RefCell::new(Box::new(|_| NonZeroU32::new(1).unwrap().into()));
static CONSENSUS_HOOK: RefCell<Box<dyn Fn(&RelayChainStateProof) -> (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))
}
}
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down