// 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 Cumulus [`Proposer`] is a wrapper around a Substrate [`sp_consensus::Environment`]
//! for creating new parachain blocks.
//!
//! This utility is designed to be composed within any collator consensus algorithm.
use async_trait::async_trait;
use cumulus_primitives_parachain_inherent::ParachainInherentData;
use sp_consensus::{EnableProofRecording, Environment, Proposal, Proposer as SubstrateProposer};
use sp_inherents::InherentData;
use sp_runtime::{traits::Block as BlockT, Digest};
use sp_state_machine::StorageProof;
use std::{fmt::Debug, time::Duration};
/// Errors that can occur when proposing a parachain block.
#[derive(thiserror::Error, Debug)]
#[error(transparent)]
pub struct Error {
inner: anyhow::Error,
}
impl Error {
/// Create an error tied to the creation of a proposer.
pub fn proposer_creation(err: impl Into) -> Self {
Error { inner: err.into().context("Proposer Creation") }
}
/// Create an error tied to the proposing logic itself.
pub fn proposing(err: impl Into) -> Self {
Error { inner: err.into().context("Proposing") }
}
}
/// A type alias for easily referring to the type of a proposal produced by a specific
/// [`Proposer`].
pub type ProposalOf = Proposal>::Transaction, StorageProof>;
/// An interface for proposers.
#[async_trait]
pub trait ProposerInterface {
/// The underlying DB transaction type produced with the block proposal.
type Transaction: Default + Send + 'static;
/// Propose a collation using the supplied `InherentData` and the provided
/// `ParachainInherentData`.
///
/// Also specify any required inherent digests, the maximum proposal duration,
/// and the block size limit in bytes. See the documentation on [`sp_consensus::Proposer::propose`]
/// for more details on how to interpret these parameters.
///
/// The `InherentData` and `Digest` are left deliberately general in order to accommodate
/// all possible collator selection algorithms or inherent creation mechanisms,
/// while the `ParachainInherentData` is made explicit so it will be constructed appropriately.
///
/// If the `InherentData` passed into this function already has a `ParachainInherentData`,
/// this should throw an error.
async fn propose(
&mut self,
parent_header: &Block::Header,
paras_inherent_data: &ParachainInherentData,
other_inherent_data: InherentData,
inherent_digests: Digest,
max_duration: Duration,
block_size_limit: Option,
) -> Result, Error>;
}
/// A simple wrapper around a Substrate proposer for creating collations.
pub struct Proposer {
inner: T,
_marker: std::marker::PhantomData,
}
impl Proposer {
/// Create a new Cumulus [`Proposer`].
pub fn new(inner: T) -> Self {
Proposer { inner, _marker: std::marker::PhantomData }
}
}
#[async_trait]
impl ProposerInterface for Proposer
where
B: sp_runtime::traits::Block,
T: Environment + Send,
T::Error: Send + Sync + 'static,
T::Proposer: SubstrateProposer,
>::Error: Send + Sync + 'static,
{
type Transaction = <>::Proposer as SubstrateProposer>::Transaction;
async fn propose(
&mut self,
parent_header: &B::Header,
paras_inherent_data: &ParachainInherentData,
other_inherent_data: InherentData,
inherent_digests: Digest,
max_duration: Duration,
block_size_limit: Option,
) -> Result, Error> {
let proposer = self
.inner
.init(parent_header)
.await
.map_err(|e| Error::proposer_creation(anyhow::Error::new(e)))?;
let mut inherent_data = other_inherent_data;
inherent_data
.put_data(
cumulus_primitives_parachain_inherent::INHERENT_IDENTIFIER,
¶s_inherent_data,
)
.map_err(|e| Error::proposing(anyhow::Error::new(e)))?;
proposer
.propose(inherent_data, inherent_digests, max_duration, block_size_limit)
.await
.map_err(|e| Error::proposing(anyhow::Error::new(e)).into())
}
}