|
| 1 | +//! Wrapper around a proposer which only authors blocks when certain conditions are met. |
| 2 | +//! |
| 3 | +//! These conditions are: |
| 4 | +//! 1. There is at least one transaction ready to post. This is used to determine that there were |
| 5 | +//! non-inherent extrinsics and avoid authoring empty blocks. |
| 6 | +//! 2. There is an incoming downward message from the relay chain. |
| 7 | +//! 3. There is a go-ahead signal for a parachain code upgrade. |
| 8 | +//! |
| 9 | +//! If any of these conditions are met, then the block is authored. |
| 10 | +
|
| 11 | +use anyhow::anyhow; |
| 12 | + |
| 13 | +use sc_transaction_pool_api::TransactionPool; |
| 14 | +use sp_api::StorageProof; |
| 15 | +use sp_consensus::Proposal; |
| 16 | +use sp_inherents::InherentData; |
| 17 | +use sp_runtime::generic::Digest; |
| 18 | + |
| 19 | +use cumulus_client_consensus_proposer::{Error as ProposerError, ProposerInterface}; |
| 20 | +use cumulus_pallet_parachain_system::relay_state_snapshot::RelayChainStateProof; |
| 21 | +use cumulus_primitives_core::ParaId; |
| 22 | +use cumulus_primitives_parachain_inherent::ParachainInherentData; |
| 23 | + |
| 24 | +use sugondat_primitives::opaque::{Block, Header}; |
| 25 | + |
| 26 | +use std::time::Duration; |
| 27 | +use std::sync::Arc; |
| 28 | + |
| 29 | +use crate::service::ParachainClient; |
| 30 | + |
| 31 | +/// Proposes blocks, but only under certain conditions. See module docs. |
| 32 | +pub struct BlockLimitingProposer<P>{ |
| 33 | + inner: P, |
| 34 | + para_id: ParaId, |
| 35 | + transaction_pool: Arc<sc_transaction_pool::FullPool<Block, ParachainClient>>, |
| 36 | +} |
| 37 | + |
| 38 | +#[async_trait::async_trait] |
| 39 | +impl<P: ProposerInterface<Block> + Send> ProposerInterface<Block> for BlockLimitingProposer<P> { |
| 40 | + async fn propose( |
| 41 | + &mut self, |
| 42 | + parent_header: &Header, |
| 43 | + paras_inherent_data: &ParachainInherentData, |
| 44 | + other_inherent_data: InherentData, |
| 45 | + inherent_digests: Digest, |
| 46 | + max_duration: Duration, |
| 47 | + block_size_limit: Option<usize>, |
| 48 | + ) -> Result<Proposal<Block, StorageProof>, ProposerError> { |
| 49 | + let has_downward_message = !paras_inherent_data.downward_messages.is_empty(); |
| 50 | + let has_transactions = self.transaction_pool.status().ready > 0; |
| 51 | + let has_go_ahead = { |
| 52 | + let maybe_go_ahead = RelayChainStateProof::new( |
| 53 | + self.para_id, |
| 54 | + paras_inherent_data.validation_data.relay_parent_storage_root, |
| 55 | + paras_inherent_data.relay_chain_state.clone(), |
| 56 | + ).and_then(|p| p.read_upgrade_go_ahead_signal()); |
| 57 | + |
| 58 | + // when we encounter errors reading the go ahead signal, |
| 59 | + // we pessimistically opt to create a block as such errors indicate |
| 60 | + // changes in the relay chain protocol and would likely persist |
| 61 | + // until something is fixed here. |
| 62 | + match maybe_go_ahead { |
| 63 | + Err(_) => true, |
| 64 | + Ok(Some(_)) => true, |
| 65 | + Ok(None) => false, |
| 66 | + } |
| 67 | + }; |
| 68 | + |
| 69 | + if has_downward_message || has_go_ahead || has_transactions { |
| 70 | + self.inner.propose( |
| 71 | + parent_header, |
| 72 | + paras_inherent_data, |
| 73 | + other_inherent_data, |
| 74 | + inherent_digests, |
| 75 | + max_duration, |
| 76 | + block_size_limit, |
| 77 | + ).await |
| 78 | + } else { |
| 79 | + Err(ProposerError::proposing(anyhow!("no need to create a block"))) |
| 80 | + } |
| 81 | + } |
| 82 | +} |
0 commit comments