diff --git a/substrate/tendermint/client/src/gossip.rs b/substrate/tendermint/client/src/authority/gossip.rs similarity index 100% rename from substrate/tendermint/client/src/gossip.rs rename to substrate/tendermint/client/src/authority/gossip.rs diff --git a/substrate/tendermint/client/src/import_queue.rs b/substrate/tendermint/client/src/authority/import_future.rs similarity index 54% rename from substrate/tendermint/client/src/import_queue.rs rename to substrate/tendermint/client/src/authority/import_future.rs index 5c6c58ba..ba372c4b 100644 --- a/substrate/tendermint/client/src/import_queue.rs +++ b/substrate/tendermint/client/src/authority/import_future.rs @@ -1,22 +1,17 @@ use std::{ pin::Pin, - sync::{Arc, RwLock}, + sync::RwLock, task::{Poll, Context}, future::Future, }; use sp_runtime::traits::{Header, Block}; -use sp_consensus::Error; -use sc_consensus::{BlockImportStatus, BlockImportError, BlockImport, Link, BasicQueue}; +use sc_consensus::{BlockImportStatus, BlockImportError, Link}; use sc_service::ImportQueue; -use substrate_prometheus_endpoint::Registry; - -use crate::{types::TendermintValidator, TendermintImport}; - -pub type TendermintImportQueue = BasicQueue; +use crate::TendermintImportQueue; // Custom helpers for ImportQueue in order to obtain the result of a block's importing struct ValidateLink(Option<(B::Hash, bool)>); @@ -63,23 +58,3 @@ impl<'a, B: Block, T: Send> Future for ImportFuture<'a, B, T> { } } } - -pub fn import_queue( - spawner: &impl sp_core::traits::SpawnEssentialNamed, - client: Arc, - registry: Option<&Registry>, -) -> (TendermintImport, TendermintImportQueue) -where - Arc: BlockImport, - as BlockImport>::Error: Into, -{ - let import = TendermintImport::::new(client); - - let boxed = Box::new(import.clone()); - // Use None for the justification importer since justifications always come with blocks - // Therefore, they're never imported after the fact, which is what mandates an importer - let queue = || BasicQueue::new(import.clone(), boxed.clone(), None, spawner, registry); - - *futures::executor::block_on(import.queue.write()) = Some(queue()); - (import.clone(), queue()) -} diff --git a/substrate/tendermint/client/src/authority.rs b/substrate/tendermint/client/src/authority/mod.rs similarity index 98% rename from substrate/tendermint/client/src/authority.rs rename to substrate/tendermint/client/src/authority/mod.rs index 4b37c9bd..c4907802 100644 --- a/substrate/tendermint/client/src/authority.rs +++ b/substrate/tendermint/client/src/authority/mod.rs @@ -34,10 +34,15 @@ use tendermint_machine::{ }; use crate::{ - CONSENSUS_ID, types::TendermintValidator, validators::TendermintValidators, - import_queue::ImportFuture, tendermint::TendermintImport, gossip::TendermintGossip, + CONSENSUS_ID, TendermintValidator, validators::TendermintValidators, tendermint::TendermintImport, }; +mod gossip; +use gossip::TendermintGossip; + +mod import_future; +use import_future::ImportFuture; + // Data for an active validator // This is distinct as even when we aren't an authority, we still create stubbed Authority objects // as it's only Authority which implements tendermint_machine::ext::Network. Network has diff --git a/substrate/tendermint/client/src/block_import.rs b/substrate/tendermint/client/src/block_import.rs index 379cb1e5..21ea4484 100644 --- a/substrate/tendermint/client/src/block_import.rs +++ b/substrate/tendermint/client/src/block_import.rs @@ -1,11 +1,17 @@ -use std::{sync::Arc, collections::HashMap}; +use std::{marker::PhantomData, sync::Arc, collections::HashMap}; use async_trait::async_trait; -use sp_consensus::{Error, CacheKeyId}; +use sp_api::BlockId; +use sp_runtime::traits::Block; +use sp_blockchain::{HeaderBackend, Backend as BlockchainBackend}; +use sp_consensus::{Error, CacheKeyId, SelectChain}; + use sc_consensus::{BlockCheckParams, BlockImportParams, ImportResult, BlockImport, Verifier}; -use crate::{types::TendermintValidator, tendermint::TendermintImport}; +use sc_client_api::Backend; + +use crate::{TendermintValidator, tendermint::TendermintImport}; #[async_trait] impl BlockImport for TendermintImport @@ -60,3 +66,52 @@ where Ok((block, None)) } } + +// SelectChain, while provided by Substrate and part of PartialComponents, isn't used by Substrate +// It's common between various block-production/finality crates, yet Substrate as a system doesn't +// rely on it, which is good, because its definition is explicitly incompatible with Tendermint +// +// leaves is supposed to return all leaves of the blockchain. While Tendermint maintains that view, +// an honest node will only build on the most recently finalized block, so it is a 'leaf' despite +// having descendants +// +// best_chain will always be this finalized block, yet Substrate explicitly defines it as one of +// the above leaves, which this finalized block is explicitly not included in. Accordingly, we +// can never provide a compatible decision +// +// Since PartialComponents expects it, an implementation which does its best is provided. It panics +// if leaves is called, yet returns the finalized chain tip for best_chain, as that's intended to +// be the header to build upon +pub struct TendermintSelectChain>(Arc, PhantomData); + +impl> Clone for TendermintSelectChain { + fn clone(&self) -> Self { + TendermintSelectChain(self.0.clone(), PhantomData) + } +} + +impl> TendermintSelectChain { + pub fn new(backend: Arc) -> TendermintSelectChain { + TendermintSelectChain(backend, PhantomData) + } +} + +#[async_trait] +impl> SelectChain for TendermintSelectChain { + async fn leaves(&self) -> Result, Error> { + panic!("Substrate definition of leaves is incompatible with Tendermint") + } + + async fn best_chain(&self) -> Result { + Ok( + self + .0 + .blockchain() + // There should always be a finalized block + .header(BlockId::Hash(self.0.blockchain().last_finalized().unwrap())) + // There should not be an error in retrieving it and since it's finalized, it should exist + .unwrap() + .unwrap(), + ) + } +} diff --git a/substrate/tendermint/client/src/lib.rs b/substrate/tendermint/client/src/lib.rs index 0888db56..bb370e49 100644 --- a/substrate/tendermint/client/src/lib.rs +++ b/substrate/tendermint/client/src/lib.rs @@ -1,5 +1,19 @@ -mod types; -pub use types::{TendermintClientMinimal, TendermintValidator}; +use std::sync::Arc; + +use sp_inherents::CreateInherentDataProviders; +use sp_runtime::traits::{Header, Block}; +use sp_blockchain::HeaderBackend; +use sp_api::{StateBackend, StateBackendFor, TransactionFor, ApiExt, ProvideRuntimeApi}; +use sp_consensus::{Error, Environment}; + +use sc_client_api::{BlockBackend, Backend, Finalizer}; +use sc_consensus::{BlockImport, BasicQueue}; +use sc_network::NetworkBlock; +use sc_network_gossip::Network; + +use sp_tendermint::TendermintApi; + +use substrate_prometheus_endpoint::Registry; mod validators; @@ -7,14 +21,103 @@ pub(crate) mod tendermint; pub use tendermint::TendermintImport; mod block_import; -mod import_queue; -pub use import_queue::{TendermintImportQueue, import_queue}; +pub use block_import::TendermintSelectChain; -pub(crate) mod gossip; pub(crate) mod authority; pub use authority::TendermintAuthority; -mod select_chain; -pub use select_chain::TendermintSelectChain; - const CONSENSUS_ID: [u8; 4] = *b"tend"; + +/// Trait consolidating all generics required by sc_tendermint for processing. +pub trait TendermintClient: Send + Sync + 'static { + const BLOCK_TIME_IN_SECONDS: u32; + + type Block: Block; + type Backend: Backend + 'static; + + /// TransactionFor + type BackendTransaction: Send + Sync + 'static; + /// StateBackendFor + type StateBackend: StateBackend< + <::Header as Header>::Hashing, + Transaction = Self::BackendTransaction, + >; + // Client::Api + type Api: ApiExt + TendermintApi; + type Client: Send + + Sync + + HeaderBackend + + BlockBackend + + BlockImport + + Finalizer + + ProvideRuntimeApi + + 'static; +} + +/// Trait implementable on firm types to automatically provide a full TendermintClient impl. +pub trait TendermintClientMinimal: Send + Sync + 'static { + const BLOCK_TIME_IN_SECONDS: u32; + + type Block: Block; + type Backend: Backend + 'static; + type Api: ApiExt + TendermintApi; + type Client: Send + + Sync + + HeaderBackend + + BlockBackend + + BlockImport> + + Finalizer + + ProvideRuntimeApi + + 'static; +} + +impl TendermintClient for T +where + >::Api: TendermintApi, + TransactionFor: Send + Sync + 'static, +{ + const BLOCK_TIME_IN_SECONDS: u32 = T::BLOCK_TIME_IN_SECONDS; + + type Block = T::Block; + type Backend = T::Backend; + + type BackendTransaction = TransactionFor; + type StateBackend = StateBackendFor; + type Api = >::Api; + type Client = T::Client; +} + +/// Trait consolidating additional generics required by sc_tendermint for authoring. +pub trait TendermintValidator: TendermintClient { + type CIDP: CreateInherentDataProviders + 'static; + type Environment: Send + Sync + Environment + 'static; + + type Network: Clone + + Send + + Sync + + Network + + NetworkBlock<::Hash, <::Header as Header>::Number> + + 'static; +} + +pub type TendermintImportQueue = BasicQueue; + +pub fn import_queue( + spawner: &impl sp_core::traits::SpawnEssentialNamed, + client: Arc, + registry: Option<&Registry>, +) -> (TendermintImport, TendermintImportQueue) +where + Arc: BlockImport, + as BlockImport>::Error: Into, +{ + let import = TendermintImport::::new(client); + + let boxed = Box::new(import.clone()); + // Use None for the justification importer since justifications always come with blocks + // Therefore, they're never imported after the fact, which is what mandates an importer + let queue = || BasicQueue::new(import.clone(), boxed.clone(), None, spawner, registry); + + *futures::executor::block_on(import.queue.write()) = Some(queue()); + (import.clone(), queue()) +} diff --git a/substrate/tendermint/client/src/select_chain.rs b/substrate/tendermint/client/src/select_chain.rs deleted file mode 100644 index 32a409ea..00000000 --- a/substrate/tendermint/client/src/select_chain.rs +++ /dev/null @@ -1,59 +0,0 @@ -// SelectChain, while provided by Substrate and part of PartialComponents, isn't used by Substrate -// It's common between various block-production/finality crates, yet Substrate as a system doesn't -// rely on it, which is good, because its definition is explicitly incompatible with Tendermint -// -// leaves is supposed to return all leaves of the blockchain. While Tendermint maintains that view, -// an honest node will only build on the most recently finalized block, so it is a 'leaf' despite -// having descendants -// -// best_chain will always be this finalized block, yet Substrate explicitly defines it as one of -// the above leaves, which this finalized block is explicitly not included in. Accordingly, we -// can never provide a compatible decision -// -// Since PartialComponents expects it, an implementation which does its best is provided. It panics -// if leaves is called, yet returns the finalized chain tip for best_chain, as that's intended to -// be the header to build upon - -use std::{marker::PhantomData, sync::Arc}; - -use async_trait::async_trait; - -use sp_api::BlockId; -use sp_runtime::traits::Block; -use sp_blockchain::{HeaderBackend, Backend as BlockchainBackend}; -use sc_client_api::Backend; -use sp_consensus::{Error, SelectChain}; - -pub struct TendermintSelectChain>(Arc, PhantomData); - -impl> Clone for TendermintSelectChain { - fn clone(&self) -> Self { - TendermintSelectChain(self.0.clone(), PhantomData) - } -} - -impl> TendermintSelectChain { - pub fn new(backend: Arc) -> TendermintSelectChain { - TendermintSelectChain(backend, PhantomData) - } -} - -#[async_trait] -impl> SelectChain for TendermintSelectChain { - async fn leaves(&self) -> Result, Error> { - panic!("Substrate definition of leaves is incompatible with Tendermint") - } - - async fn best_chain(&self) -> Result { - Ok( - self - .0 - .blockchain() - // There should always be a finalized block - .header(BlockId::Hash(self.0.blockchain().last_finalized().unwrap())) - // There should not be an error in retrieving it and since it's finalized, it should exist - .unwrap() - .unwrap(), - ) - } -} diff --git a/substrate/tendermint/client/src/tendermint.rs b/substrate/tendermint/client/src/tendermint.rs index 99e0f4ca..5fddd5bb 100644 --- a/substrate/tendermint/client/src/tendermint.rs +++ b/substrate/tendermint/client/src/tendermint.rs @@ -18,8 +18,8 @@ use sc_consensus::{ForkChoiceStrategy, BlockImportParams}; use tendermint_machine::ext::{Commit, Network}; use crate::{ - CONSENSUS_ID, types::TendermintValidator, validators::TendermintValidators, - import_queue::TendermintImportQueue, authority::TendermintAuthority, + CONSENSUS_ID, TendermintValidator, validators::TendermintValidators, TendermintImportQueue, + authority::TendermintAuthority, }; pub struct TendermintImport { diff --git a/substrate/tendermint/client/src/types.rs b/substrate/tendermint/client/src/types.rs deleted file mode 100644 index a1474ff5..00000000 --- a/substrate/tendermint/client/src/types.rs +++ /dev/null @@ -1,86 +0,0 @@ -use sp_inherents::CreateInherentDataProviders; -use sp_runtime::traits::{Header, Block}; - -use sp_blockchain::HeaderBackend; -use sp_api::{StateBackend, StateBackendFor, TransactionFor, ApiExt, ProvideRuntimeApi}; - -use sp_consensus::Environment; -use sc_consensus::BlockImport; - -use sc_client_api::{BlockBackend, Backend, Finalizer}; -use sc_network::NetworkBlock; -use sc_network_gossip::Network; - -use sp_tendermint::TendermintApi; - -/// Trait consolidating all generics required by sc_tendermint for processing. -pub trait TendermintClient: Send + Sync + 'static { - const BLOCK_TIME_IN_SECONDS: u32; - - type Block: Block; - type Backend: Backend + 'static; - - /// TransactionFor - type BackendTransaction: Send + Sync + 'static; - /// StateBackendFor - type StateBackend: StateBackend< - <::Header as Header>::Hashing, - Transaction = Self::BackendTransaction, - >; - // Client::Api - type Api: ApiExt + TendermintApi; - type Client: Send - + Sync - + HeaderBackend - + BlockBackend - + BlockImport - + Finalizer - + ProvideRuntimeApi - + 'static; -} - -/// Trait implementable on firm types to automatically provide a full TendermintClient impl. -pub trait TendermintClientMinimal: Send + Sync + 'static { - const BLOCK_TIME_IN_SECONDS: u32; - - type Block: Block; - type Backend: Backend + 'static; - type Api: ApiExt + TendermintApi; - type Client: Send - + Sync - + HeaderBackend - + BlockBackend - + BlockImport> - + Finalizer - + ProvideRuntimeApi - + 'static; -} - -impl TendermintClient for T -where - >::Api: TendermintApi, - TransactionFor: Send + Sync + 'static, -{ - const BLOCK_TIME_IN_SECONDS: u32 = T::BLOCK_TIME_IN_SECONDS; - - type Block = T::Block; - type Backend = T::Backend; - - type BackendTransaction = TransactionFor; - type StateBackend = StateBackendFor; - type Api = >::Api; - type Client = T::Client; -} - -/// Trait consolidating additional generics required by sc_tendermint for authoring. -pub trait TendermintValidator: TendermintClient { - type CIDP: CreateInherentDataProviders + 'static; - type Environment: Send + Sync + Environment + 'static; - - type Network: Clone - + Send - + Sync - + Network - + NetworkBlock<::Hash, <::Header as Header>::Number> - + 'static; -} diff --git a/substrate/tendermint/client/src/validators.rs b/substrate/tendermint/client/src/validators.rs index 5398e127..046a2738 100644 --- a/substrate/tendermint/client/src/validators.rs +++ b/substrate/tendermint/client/src/validators.rs @@ -15,7 +15,7 @@ use tendermint_machine::ext::{BlockNumber, Round, Weights, SignatureScheme}; use sp_tendermint::TendermintApi; -use crate::types::TendermintClient; +use crate::TendermintClient; struct TendermintValidatorsStruct { session: SessionIndex,