Clean generics in Tendermint with a monolith with associated types

This commit is contained in:
Luke Parker 2022-10-30 03:26:31 -04:00
parent 8d3efd6259
commit 6838d5c922
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
9 changed files with 218 additions and 269 deletions

1
Cargo.lock generated
View file

@ -7536,6 +7536,7 @@ dependencies = [
"pallet-session", "pallet-session",
"sc-basic-authorship", "sc-basic-authorship",
"sc-client-api", "sc-client-api",
"sc-client-db",
"sc-consensus", "sc-consensus",
"sc-executor", "sc-executor",
"sc-network", "sc-network",

View file

@ -38,6 +38,7 @@ sc-executor = { git = "https://github.com/serai-dex/substrate" }
sc-network = { git = "https://github.com/serai-dex/substrate" } sc-network = { git = "https://github.com/serai-dex/substrate" }
sc-network-gossip = { git = "https://github.com/serai-dex/substrate" } sc-network-gossip = { git = "https://github.com/serai-dex/substrate" }
sc-service = { git = "https://github.com/serai-dex/substrate" } sc-service = { git = "https://github.com/serai-dex/substrate" }
sc-client-db = { git = "https://github.com/serai-dex/substrate" }
sc-client-api = { git = "https://github.com/serai-dex/substrate" } sc-client-api = { git = "https://github.com/serai-dex/substrate" }
sc-consensus = { git = "https://github.com/serai-dex/substrate" } sc-consensus = { git = "https://github.com/serai-dex/substrate" }

View file

@ -2,45 +2,25 @@ use std::{sync::Arc, collections::HashMap};
use async_trait::async_trait; use async_trait::async_trait;
use sp_inherents::CreateInherentDataProviders; use sp_consensus::{Error, CacheKeyId};
use sp_runtime::traits::Block;
use sp_api::TransactionFor;
use sp_consensus::{Error, CacheKeyId, Environment};
use sc_consensus::{BlockCheckParams, BlockImportParams, ImportResult, BlockImport}; use sc_consensus::{BlockCheckParams, BlockImportParams, ImportResult, BlockImport};
use sc_client_api::Backend; use crate::{types::TendermintAuthor, tendermint::TendermintImport};
use sp_tendermint::TendermintApi;
use crate::{
tendermint::{TendermintClient, TendermintImport},
Announce,
};
#[async_trait] #[async_trait]
impl< impl<T: TendermintAuthor> BlockImport<T::Block> for TendermintImport<T>
B: Block,
Be: Backend<B> + 'static,
C: TendermintClient<B, Be>,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
E: Send + Sync + Environment<B> + 'static,
A: Announce<B>,
> BlockImport<B> for TendermintImport<B, Be, C, CIDP, E, A>
where where
TransactionFor<C, B>: Send + Sync + 'static, Arc<T::Client>: BlockImport<T::Block, Transaction = T::BackendTransaction>,
Arc<C>: BlockImport<B, Transaction = TransactionFor<C, B>>, <Arc<T::Client> as BlockImport<T::Block>>::Error: Into<Error>,
<Arc<C> as BlockImport<B>>::Error: Into<Error>,
C::Api: TendermintApi<B>,
{ {
type Error = Error; type Error = Error;
type Transaction = TransactionFor<C, B>; type Transaction = T::BackendTransaction;
// TODO: Is there a DoS where you send a block without justifications, causing it to error, // TODO: Is there a DoS where you send a block without justifications, causing it to error,
// yet adding it to the blacklist in the process preventing further syncing? // yet adding it to the blacklist in the process preventing further syncing?
async fn check_block( async fn check_block(
&mut self, &mut self,
mut block: BlockCheckParams<B>, mut block: BlockCheckParams<T::Block>,
) -> Result<ImportResult, Self::Error> { ) -> Result<ImportResult, Self::Error> {
self.verify_order(block.parent_hash, block.number)?; self.verify_order(block.parent_hash, block.number)?;
@ -55,7 +35,7 @@ where
async fn import_block( async fn import_block(
&mut self, &mut self,
mut block: BlockImportParams<B, TransactionFor<C, B>>, mut block: BlockImportParams<T::Block, Self::Transaction>,
new_cache: HashMap<CacheKeyId, Vec<u8>>, new_cache: HashMap<CacheKeyId, Vec<u8>>,
) -> Result<ImportResult, Self::Error> { ) -> Result<ImportResult, Self::Error> {
self.check(&mut block).await?; self.check(&mut block).await?;

View file

@ -8,15 +8,14 @@ use std::{
}; };
use sp_core::Decode; use sp_core::Decode;
use sp_inherents::CreateInherentDataProviders;
use sp_runtime::traits::{Header, Block}; use sp_runtime::traits::{Header, Block};
use sp_api::{BlockId, TransactionFor}; use sp_api::BlockId;
use sp_consensus::{Error, Environment}; use sp_consensus::Error;
use sc_consensus::{BlockImportStatus, BlockImportError, BlockImport, Link, BasicQueue}; use sc_consensus::{BlockImportStatus, BlockImportError, BlockImport, Link, BasicQueue};
use sc_service::ImportQueue; use sc_service::ImportQueue;
use sc_client_api::Backend; use sc_client_api::{HeaderBackend, BlockBackend};
use substrate_prometheus_endpoint::Registry; use substrate_prometheus_endpoint::Registry;
@ -25,13 +24,9 @@ use tendermint_machine::{
TendermintMachine, TendermintMachine,
}; };
use sp_tendermint::TendermintApi;
use crate::{ use crate::{
CONSENSUS_ID, CONSENSUS_ID, types::TendermintAuthor, validators::TendermintValidators,
validators::TendermintValidators, tendermint::TendermintImport,
tendermint::{TendermintClient, TendermintImport},
Announce,
}; };
pub type TendermintImportQueue<Block, Transaction> = BasicQueue<Block, Transaction>; pub type TendermintImportQueue<Block, Transaction> = BasicQueue<Block, Transaction>;
@ -82,28 +77,19 @@ impl<'a, B: Block, T: Send> Future for ImportFuture<'a, B, T> {
} }
} }
pub fn import_queue< pub fn import_queue<T: TendermintAuthor>(
B: Block, client: Arc<T::Client>,
Be: Backend<B> + 'static, announce: T::Announce,
C: TendermintClient<B, Be>, providers: Arc<T::CIDP>,
CIDP: CreateInherentDataProviders<B, ()> + 'static, env: T::Environment,
E: Send + Sync + Environment<B> + 'static,
A: Announce<B>,
>(
client: Arc<C>,
announce: A,
providers: Arc<CIDP>,
env: E,
spawner: &impl sp_core::traits::SpawnEssentialNamed, spawner: &impl sp_core::traits::SpawnEssentialNamed,
registry: Option<&Registry>, registry: Option<&Registry>,
) -> (impl Future<Output = ()>, TendermintImportQueue<B, TransactionFor<C, B>>) ) -> (impl Future<Output = ()>, TendermintImportQueue<T::Block, T::BackendTransaction>)
where where
TransactionFor<C, B>: Send + Sync + 'static, Arc<T::Client>: BlockImport<T::Block, Transaction = T::BackendTransaction>,
Arc<C>: BlockImport<B, Transaction = TransactionFor<C, B>>, <Arc<T::Client> as BlockImport<T::Block>>::Error: Into<Error>,
<Arc<C> as BlockImport<B>>::Error: Into<Error>,
C::Api: TendermintApi<B>,
{ {
let import = TendermintImport::new(client, announce, providers, env); let import = TendermintImport::<T>::new(client, announce, providers, env);
let authority = { let authority = {
let machine_clone = import.machine.clone(); let machine_clone = import.machine.clone();
@ -120,7 +106,7 @@ where
Ok(best) => BlockNumber(best + 1), Ok(best) => BlockNumber(best + 1),
Err(_) => panic!("BlockNumber exceeded u64"), Err(_) => panic!("BlockNumber exceeded u64"),
}, },
Commit::<TendermintValidators<B, Be, C>>::decode( Commit::<TendermintValidators<T>>::decode(
&mut import_clone &mut import_clone
.client .client
.justifications(&BlockId::Number(best)) .justifications(&BlockId::Number(best))

View file

@ -1,7 +1,9 @@
use std::{sync::Arc, future::Future}; use std::{marker::PhantomData, boxed::Box, sync::Arc, future::Future, error::Error};
use sp_runtime::traits::Block as BlockTrait; use sp_runtime::traits::Block as BlockTrait;
use sp_api::TransactionFor; use sp_inherents::CreateInherentDataProviders;
use sp_consensus::DisableProofRecording;
use sp_api::{TransactionFor, ProvideRuntimeApi};
use sc_executor::{NativeVersion, NativeExecutionDispatch, NativeElseWasmExecutor}; use sc_executor::{NativeVersion, NativeExecutionDispatch, NativeElseWasmExecutor};
use sc_transaction_pool::FullPool; use sc_transaction_pool::FullPool;
@ -11,6 +13,9 @@ use substrate_prometheus_endpoint::Registry;
use serai_runtime::{self, opaque::Block, RuntimeApi}; use serai_runtime::{self, opaque::Block, RuntimeApi};
mod types;
use types::{TendermintClientMinimal, TendermintAuthor};
mod validators; mod validators;
mod tendermint; mod tendermint;
@ -49,6 +54,39 @@ pub trait Announce<B: BlockTrait>: Send + Sync + Clone + 'static {
fn announce(&self, hash: B::Hash); fn announce(&self, hash: B::Hash);
} }
struct Cidp;
#[async_trait::async_trait]
impl CreateInherentDataProviders<Block, ()> for Cidp {
type InherentDataProviders = (sp_timestamp::InherentDataProvider,);
async fn create_inherent_data_providers(
&self,
_: <Block as BlockTrait>::Hash,
_: (),
) -> Result<Self::InherentDataProviders, Box<dyn Send + Sync + Error>> {
Ok((sp_timestamp::InherentDataProvider::from_system_time(),))
}
}
struct TendermintAuthorFirm<A: Announce<Block>>(PhantomData<A>);
impl<A: Announce<Block>> TendermintClientMinimal for TendermintAuthorFirm<A> {
type Block = Block;
type Backend = sc_client_db::Backend<Block>;
type Api = <FullClient as ProvideRuntimeApi<Block>>::Api;
type Client = FullClient;
}
impl<A: Announce<Block>> TendermintAuthor for TendermintAuthorFirm<A> {
type CIDP = Cidp;
type Environment = sc_basic_authorship::ProposerFactory<
FullPool<Block, FullClient>,
Self::Backend,
Self::Client,
DisableProofRecording,
>;
type Announce = A;
}
pub fn import_queue<A: Announce<Block>>( pub fn import_queue<A: Announce<Block>>(
task_manager: &TaskManager, task_manager: &TaskManager,
client: Arc<FullClient>, client: Arc<FullClient>,
@ -56,10 +94,10 @@ pub fn import_queue<A: Announce<Block>>(
pool: Arc<FullPool<Block, FullClient>>, pool: Arc<FullPool<Block, FullClient>>,
registry: Option<&Registry>, registry: Option<&Registry>,
) -> (impl Future<Output = ()>, TendermintImportQueue<Block, TransactionFor<FullClient, Block>>) { ) -> (impl Future<Output = ()>, TendermintImportQueue<Block, TransactionFor<FullClient, Block>>) {
import_queue::import_queue( import_queue::import_queue::<TendermintAuthorFirm<A>>(
client.clone(), client.clone(),
announce, announce,
Arc::new(|_, _| async { Ok(sp_timestamp::InherentDataProvider::from_system_time()) }), Arc::new(Cidp),
sc_basic_authorship::ProposerFactory::new( sc_basic_authorship::ProposerFactory::new(
task_manager.spawn_handle(), task_manager.spawn_handle(),
client, client,

View file

@ -17,103 +17,48 @@ use sp_runtime::{
Digest, Justification, Digest, Justification,
}; };
use sp_blockchain::HeaderBackend; use sp_blockchain::HeaderBackend;
use sp_api::{BlockId, TransactionFor, ProvideRuntimeApi}; use sp_api::BlockId;
use sp_consensus::{Error, BlockOrigin, Proposer, Environment}; use sp_consensus::{Error, BlockOrigin, Proposer, Environment};
use sc_consensus::{ForkChoiceStrategy, BlockImportParams, BlockImport, import_queue::IncomingBlock}; use sc_consensus::{ForkChoiceStrategy, BlockImportParams, import_queue::IncomingBlock};
use sc_service::ImportQueue; use sc_service::ImportQueue;
use sc_client_api::{BlockBackend, Backend, Finalizer}; use sc_client_api::Finalizer;
use tendermint_machine::{ use tendermint_machine::{
ext::{BlockError, Commit, Network}, ext::{BlockError, Commit, Network},
SignedMessage, TendermintHandle, SignedMessage, TendermintHandle,
}; };
use sp_tendermint::TendermintApi;
use crate::{ use crate::{
CONSENSUS_ID, CONSENSUS_ID,
types::TendermintAuthor,
validators::TendermintValidators, validators::TendermintValidators,
import_queue::{ImportFuture, TendermintImportQueue}, import_queue::{ImportFuture, TendermintImportQueue},
Announce, Announce,
}; };
pub trait TendermintClient<B: Block, Be: Backend<B> + 'static>: pub(crate) struct TendermintImport<T: TendermintAuthor> {
Send _ta: PhantomData<T>,
+ Sync
+ HeaderBackend<B>
+ BlockBackend<B>
+ BlockImport<B, Transaction = TransactionFor<Self, B>>
+ Finalizer<B, Be>
+ ProvideRuntimeApi<B>
+ 'static
where
TransactionFor<Self, B>: Send + Sync + 'static,
Self::Api: TendermintApi<B>,
{
}
impl<
B: Send + Sync + Block + 'static,
Be: Send + Sync + Backend<B> + 'static,
C: Send
+ Sync
+ HeaderBackend<B>
+ BlockBackend<B>
+ BlockImport<B, Transaction = TransactionFor<C, B>>
+ Finalizer<B, Be>
+ ProvideRuntimeApi<B>
+ 'static,
> TendermintClient<B, Be> for C
where
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>,
{
}
pub(crate) struct TendermintImport< validators: Arc<TendermintValidators<T>>,
B: Block,
Be: Backend<B> + 'static,
C: TendermintClient<B, Be>,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
E: Send + Sync + Environment<B> + 'static,
A: Announce<B>,
> where
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>,
{
_block: PhantomData<B>,
_backend: PhantomData<Be>,
validators: Arc<TendermintValidators<B, Be, C>>, importing_block: Arc<RwLock<Option<<T::Block as Block>::Hash>>>,
importing_block: Arc<RwLock<Option<B::Hash>>>,
pub(crate) machine: Arc<RwLock<Option<TendermintHandle<Self>>>>, pub(crate) machine: Arc<RwLock<Option<TendermintHandle<Self>>>>,
pub(crate) client: Arc<C>, pub(crate) client: Arc<T::Client>,
announce: A, announce: T::Announce,
providers: Arc<CIDP>, providers: Arc<T::CIDP>,
env: Arc<AsyncRwLock<E>>, env: Arc<AsyncRwLock<T::Environment>>,
pub(crate) queue: Arc<AsyncRwLock<Option<TendermintImportQueue<B, TransactionFor<C, B>>>>>, pub(crate) queue:
Arc<AsyncRwLock<Option<TendermintImportQueue<T::Block, T::BackendTransaction>>>>,
} }
impl< impl<T: TendermintAuthor> Clone for TendermintImport<T> {
B: Block,
Be: Backend<B> + 'static,
C: TendermintClient<B, Be>,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
E: Send + Sync + Environment<B> + 'static,
A: Announce<B>,
> Clone for TendermintImport<B, Be, C, CIDP, E, A>
where
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>,
{
fn clone(&self) -> Self { fn clone(&self) -> Self {
TendermintImport { TendermintImport {
_block: PhantomData, _ta: PhantomData,
_backend: PhantomData,
validators: self.validators.clone(), validators: self.validators.clone(),
@ -130,27 +75,15 @@ where
} }
} }
impl< impl<T: TendermintAuthor> TendermintImport<T> {
B: Block,
Be: Backend<B> + 'static,
C: TendermintClient<B, Be>,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
E: Send + Sync + Environment<B> + 'static,
A: Announce<B>,
> TendermintImport<B, Be, C, CIDP, E, A>
where
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>,
{
pub(crate) fn new( pub(crate) fn new(
client: Arc<C>, client: Arc<T::Client>,
announce: A, announce: T::Announce,
providers: Arc<CIDP>, providers: Arc<T::CIDP>,
env: E, env: T::Environment,
) -> TendermintImport<B, Be, C, CIDP, E, A> { ) -> TendermintImport<T> {
TendermintImport { TendermintImport {
_block: PhantomData, _ta: PhantomData,
_backend: PhantomData,
validators: Arc::new(TendermintValidators::new(client.clone())), validators: Arc::new(TendermintValidators::new(client.clone())),
@ -168,8 +101,8 @@ where
async fn check_inherents( async fn check_inherents(
&self, &self,
block: B, block: T::Block,
providers: CIDP::InherentDataProviders, providers: <T::CIDP as CreateInherentDataProviders<T::Block, ()>>::InherentDataProviders,
) -> Result<(), Error> { ) -> Result<(), Error> {
// TODO // TODO
Ok(()) Ok(())
@ -178,8 +111,8 @@ where
// Ensure this is part of a sequential import // Ensure this is part of a sequential import
pub(crate) fn verify_order( pub(crate) fn verify_order(
&self, &self,
parent: B::Hash, parent: <T::Block as Block>::Hash,
number: <B::Header as Header>::Number, number: <<T::Block as Block>::Header as Header>::Number,
) -> Result<(), Error> { ) -> Result<(), Error> {
let info = self.client.info(); let info = self.client.info();
if (info.best_hash != parent) || ((info.best_number + 1u16.into()) != number) { if (info.best_hash != parent) || ((info.best_number + 1u16.into()) != number) {
@ -193,7 +126,7 @@ where
// Tendermint's propose message could be rewritten as a seal OR Tendermint could produce blocks // Tendermint's propose message could be rewritten as a seal OR Tendermint could produce blocks
// which this checks the proposer slot for, and then tells the Tendermint machine // which this checks the proposer slot for, and then tells the Tendermint machine
// While those would be more seamless with Substrate, there's no actual benefit to doing so // While those would be more seamless with Substrate, there's no actual benefit to doing so
fn verify_origin(&self, hash: B::Hash) -> Result<(), Error> { fn verify_origin(&self, hash: <T::Block as Block>::Hash) -> Result<(), Error> {
if let Some(tm_hash) = *self.importing_block.read().unwrap() { if let Some(tm_hash) = *self.importing_block.read().unwrap() {
if hash == tm_hash { if hash == tm_hash {
return Ok(()); return Ok(());
@ -205,14 +138,14 @@ where
// Errors if the justification isn't valid // Errors if the justification isn't valid
pub(crate) fn verify_justification( pub(crate) fn verify_justification(
&self, &self,
hash: B::Hash, hash: <T::Block as Block>::Hash,
justification: &Justification, justification: &Justification,
) -> Result<(), Error> { ) -> Result<(), Error> {
if justification.0 != CONSENSUS_ID { if justification.0 != CONSENSUS_ID {
Err(Error::InvalidJustification)?; Err(Error::InvalidJustification)?;
} }
let commit: Commit<TendermintValidators<B, Be, C>> = let commit: Commit<TendermintValidators<T>> =
Commit::decode(&mut justification.1.as_ref()).map_err(|_| Error::InvalidJustification)?; Commit::decode(&mut justification.1.as_ref()).map_err(|_| Error::InvalidJustification)?;
if !self.verify_commit(hash, &commit) { if !self.verify_commit(hash, &commit) {
Err(Error::InvalidJustification)?; Err(Error::InvalidJustification)?;
@ -223,7 +156,10 @@ where
// Verifies the justifications aren't malformed, not that the block is justified // Verifies the justifications aren't malformed, not that the block is justified
// Errors if justifications is neither empty nor a sinlge Tendermint justification // Errors if justifications is neither empty nor a sinlge Tendermint justification
// If the block does have a justification, finalized will be set to true // If the block does have a justification, finalized will be set to true
fn verify_justifications<T>(&self, block: &mut BlockImportParams<B, T>) -> Result<(), Error> { fn verify_justifications<BT>(
&self,
block: &mut BlockImportParams<T::Block, BT>,
) -> Result<(), Error> {
if !block.finalized { if !block.finalized {
if let Some(justifications) = &block.justifications { if let Some(justifications) = &block.justifications {
let mut iter = justifications.iter(); let mut iter = justifications.iter();
@ -238,7 +174,10 @@ where
Ok(()) Ok(())
} }
pub(crate) async fn check<T>(&self, block: &mut BlockImportParams<B, T>) -> Result<(), Error> { pub(crate) async fn check<BT>(
&self,
block: &mut BlockImportParams<T::Block, BT>,
) -> Result<(), Error> {
if block.finalized { if block.finalized {
if block.fork_choice.is_none() { if block.fork_choice.is_none() {
// Since we alw1ays set the fork choice, this means something else marked the block as // Since we alw1ays set the fork choice, this means something else marked the block as
@ -261,7 +200,7 @@ where
if let Some(body) = block.body.clone() { if let Some(body) = block.body.clone() {
self self
.check_inherents( .check_inherents(
B::new(block.header.clone(), body), T::Block::new(block.header.clone(), body),
self.providers.create_inherent_data_providers(*block.header.parent_hash(), ()).await?, self.providers.create_inherent_data_providers(*block.header.parent_hash(), ()).await?,
) )
.await?; .await?;
@ -281,7 +220,7 @@ where
Ok(()) Ok(())
} }
pub(crate) async fn get_proposal(&mut self, header: &B::Header) -> B { pub(crate) async fn get_proposal(&mut self, header: &<T::Block as Block>::Header) -> T::Block {
let inherent_data = let inherent_data =
match self.providers.create_inherent_data_providers(header.hash(), ()).await { match self.providers.create_inherent_data_providers(header.hash(), ()).await {
Ok(providers) => match providers.create_inherent_data() { Ok(providers) => match providers.create_inherent_data() {
@ -315,30 +254,19 @@ where
} }
#[async_trait] #[async_trait]
impl< impl<T: TendermintAuthor> Network for TendermintImport<T> {
B: Block,
Be: Backend<B> + 'static,
C: TendermintClient<B, Be>,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
E: Send + Sync + Environment<B> + 'static,
A: Announce<B>,
> Network for TendermintImport<B, Be, C, CIDP, E, A>
where
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>,
{
type ValidatorId = u16; type ValidatorId = u16;
type SignatureScheme = TendermintValidators<B, Be, C>; type SignatureScheme = TendermintValidators<T>;
type Weights = TendermintValidators<B, Be, C>; type Weights = TendermintValidators<T>;
type Block = B; type Block = T::Block;
const BLOCK_TIME: u32 = { (serai_runtime::MILLISECS_PER_BLOCK / 1000) as u32 }; const BLOCK_TIME: u32 = { (serai_runtime::MILLISECS_PER_BLOCK / 1000) as u32 };
fn signature_scheme(&self) -> Arc<TendermintValidators<B, Be, C>> { fn signature_scheme(&self) -> Arc<TendermintValidators<T>> {
self.validators.clone() self.validators.clone()
} }
fn weights(&self) -> Arc<TendermintValidators<B, Be, C>> { fn weights(&self) -> Arc<TendermintValidators<T>> {
self.validators.clone() self.validators.clone()
} }
@ -362,7 +290,7 @@ where
// the node. // the node.
// //
// When Tendermint completes, the block is finalized, setting it as the tip regardless of work. // When Tendermint completes, the block is finalized, setting it as the tip regardless of work.
async fn validate(&mut self, block: &B) -> Result<(), BlockError> { async fn validate(&mut self, block: &T::Block) -> Result<(), BlockError> {
let hash = block.hash(); let hash = block.hash();
let (header, body) = block.clone().deconstruct(); let (header, body) = block.clone().deconstruct();
let parent = *header.parent_hash(); let parent = *header.parent_hash();
@ -407,7 +335,11 @@ where
Ok(()) Ok(())
} }
async fn add_block(&mut self, block: B, commit: Commit<TendermintValidators<B, Be, C>>) -> B { async fn add_block(
&mut self,
block: T::Block,
commit: Commit<TendermintValidators<T>>,
) -> T::Block {
let hash = block.hash(); let hash = block.hash();
let justification = (CONSENSUS_ID, commit.encode()); let justification = (CONSENSUS_ID, commit.encode());
debug_assert!(self.verify_justification(hash, &justification).is_ok()); debug_assert!(self.verify_justification(hash, &justification).is_ok());

View file

@ -0,0 +1,75 @@
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 sp_tendermint::TendermintApi;
use crate::Announce;
/// Trait consolidating all generics required by sc_tendermint for processing.
pub trait TendermintClient: Send + Sync + 'static {
type Block: Block;
type Backend: Backend<Self::Block> + 'static;
/// TransactionFor<Client, Block>
type BackendTransaction: Send + Sync + 'static;
/// StateBackendFor<Client, Block>
type StateBackend: StateBackend<
<<Self::Block as Block>::Header as Header>::Hashing,
Transaction = Self::BackendTransaction,
>;
// Client::Api
type Api: ApiExt<Self::Block, StateBackend = Self::StateBackend> + TendermintApi<Self::Block>;
type Client: Send
+ Sync
+ HeaderBackend<Self::Block>
+ BlockBackend<Self::Block>
+ BlockImport<Self::Block, Transaction = Self::BackendTransaction>
+ Finalizer<Self::Block, Self::Backend>
+ ProvideRuntimeApi<Self::Block, Api = Self::Api>
+ 'static;
}
/// Trait implementable on firm types to automatically provide a full TendermintClient impl.
pub trait TendermintClientMinimal: Send + Sync + 'static {
type Block: Block;
type Backend: Backend<Self::Block> + 'static;
type Api: ApiExt<Self::Block> + TendermintApi<Self::Block>;
type Client: Send
+ Sync
+ HeaderBackend<Self::Block>
+ BlockBackend<Self::Block>
+ BlockImport<Self::Block, Transaction = TransactionFor<Self::Client, Self::Block>>
+ Finalizer<Self::Block, Self::Backend>
+ ProvideRuntimeApi<Self::Block, Api = Self::Api>
+ 'static;
}
impl<T: TendermintClientMinimal> TendermintClient for T
where
<T::Client as ProvideRuntimeApi<T::Block>>::Api: TendermintApi<T::Block>,
TransactionFor<T::Client, T::Block>: Send + Sync + 'static,
{
type Block = T::Block;
type Backend = T::Backend;
type BackendTransaction = TransactionFor<T::Client, T::Block>;
type StateBackend = StateBackendFor<T::Client, T::Block>;
type Api = <T::Client as ProvideRuntimeApi<T::Block>>::Api;
type Client = T::Client;
}
/// Trait consolidating additional generics required by sc_tendermint for authoring.
pub trait TendermintAuthor: TendermintClient {
type CIDP: CreateInherentDataProviders<Self::Block, ()> + 'static;
type Environment: Send + Sync + Environment<Self::Block> + 'static;
type Announce: Announce<Self::Block>;
}

View file

@ -6,17 +6,16 @@ use sp_application_crypto::{
sr25519::{Public, Pair, Signature}, sr25519::{Public, Pair, Signature},
}; };
use sp_runtime::traits::Block;
use sp_staking::SessionIndex; use sp_staking::SessionIndex;
use sp_api::{BlockId, TransactionFor}; use sp_api::{BlockId, ProvideRuntimeApi};
use sc_client_api::Backend; use sc_client_api::HeaderBackend;
use tendermint_machine::ext::{BlockNumber, Round, Weights, SignatureScheme}; use tendermint_machine::ext::{BlockNumber, Round, Weights, SignatureScheme};
use sp_tendermint::TendermintApi; use sp_tendermint::TendermintApi;
use crate::tendermint::TendermintClient; use crate::types::TendermintClient;
struct TendermintValidatorsStruct { struct TendermintValidatorsStruct {
session: SessionIndex, session: SessionIndex,
@ -29,13 +28,7 @@ struct TendermintValidatorsStruct {
} }
impl TendermintValidatorsStruct { impl TendermintValidatorsStruct {
fn from_module<B: Block, Be: Backend<B> + 'static, C: TendermintClient<B, Be>>( fn from_module<T: TendermintClient>(client: &Arc<T::Client>) -> TendermintValidatorsStruct {
client: &Arc<C>,
) -> TendermintValidatorsStruct
where
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>,
{
let last = client.info().best_hash; let last = client.info().best_hash;
let api = client.runtime_api(); let api = client.runtime_api();
let session = api.current_session(&BlockId::Hash(last)).unwrap(); let session = api.current_session(&BlockId::Hash(last)).unwrap();
@ -56,23 +49,13 @@ impl TendermintValidatorsStruct {
} }
// Wrap every access of the validators struct in something which forces calling refresh // Wrap every access of the validators struct in something which forces calling refresh
struct Refresh<B: Block, Be: Backend<B> + 'static, C: TendermintClient<B, Be>> struct Refresh<T: TendermintClient> {
where _tc: PhantomData<T>,
TransactionFor<C, B>: Send + Sync + 'static, client: Arc<T::Client>,
C::Api: TendermintApi<B>,
{
_block: PhantomData<B>,
_backend: PhantomData<Be>,
client: Arc<C>,
_refresh: Arc<RwLock<TendermintValidatorsStruct>>, _refresh: Arc<RwLock<TendermintValidatorsStruct>>,
} }
impl<B: Block, Be: Backend<B> + 'static, C: TendermintClient<B, Be>> Refresh<B, Be, C> impl<T: TendermintClient> Refresh<T> {
where
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>,
{
// If the session has changed, re-create the struct with the data on it // If the session has changed, re-create the struct with the data on it
fn refresh(&self) { fn refresh(&self) {
let session = self._refresh.read().unwrap().session; let session = self._refresh.read().unwrap().session;
@ -83,16 +66,12 @@ where
.current_session(&BlockId::Hash(self.client.info().best_hash)) .current_session(&BlockId::Hash(self.client.info().best_hash))
.unwrap() .unwrap()
{ {
*self._refresh.write().unwrap() = TendermintValidatorsStruct::from_module(&self.client); *self._refresh.write().unwrap() = TendermintValidatorsStruct::from_module::<T>(&self.client);
} }
} }
} }
impl<B: Block, Be: Backend<B> + 'static, C: TendermintClient<B, Be>> Deref for Refresh<B, Be, C> impl<T: TendermintClient> Deref for Refresh<T> {
where
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>,
{
type Target = RwLock<TendermintValidatorsStruct>; type Target = RwLock<TendermintValidatorsStruct>;
fn deref(&self) -> &RwLock<TendermintValidatorsStruct> { fn deref(&self) -> &RwLock<TendermintValidatorsStruct> {
self.refresh(); self.refresh();
@ -100,37 +79,19 @@ where
} }
} }
pub(crate) struct TendermintValidators< pub(crate) struct TendermintValidators<T: TendermintClient>(Refresh<T>);
B: Block,
Be: Backend<B> + 'static,
C: TendermintClient<B, Be>,
>(Refresh<B, Be, C>)
where
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>;
impl<B: Block, Be: Backend<B> + 'static, C: TendermintClient<B, Be>> TendermintValidators<B, Be, C> impl<T: TendermintClient> TendermintValidators<T> {
where pub(crate) fn new(client: Arc<T::Client>) -> TendermintValidators<T> {
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>,
{
pub(crate) fn new(client: Arc<C>) -> TendermintValidators<B, Be, C> {
TendermintValidators(Refresh { TendermintValidators(Refresh {
_block: PhantomData, _tc: PhantomData,
_backend: PhantomData, _refresh: Arc::new(RwLock::new(TendermintValidatorsStruct::from_module::<T>(&client))),
_refresh: Arc::new(RwLock::new(TendermintValidatorsStruct::from_module(&client))),
client, client,
}) })
} }
} }
impl<B: Block, Be: Backend<B> + 'static, C: TendermintClient<B, Be>> SignatureScheme impl<T: TendermintClient> SignatureScheme for TendermintValidators<T> {
for TendermintValidators<B, Be, C>
where
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>,
{
type ValidatorId = u16; type ValidatorId = u16;
type Signature = Signature; type Signature = Signature;
type AggregateSignature = Vec<Signature>; type AggregateSignature = Vec<Signature>;
@ -160,12 +121,7 @@ where
} }
} }
impl<B: Block, Be: Backend<B> + 'static, C: TendermintClient<B, Be>> Weights impl<T: TendermintClient> Weights for TendermintValidators<T> {
for TendermintValidators<B, Be, C>
where
TransactionFor<C, B>: Send + Sync + 'static,
C::Api: TendermintApi<B>,
{
type ValidatorId = u16; type ValidatorId = u16;
fn total_weight(&self) -> u64 { fn total_weight(&self) -> u64 {

View file

@ -2,41 +2,21 @@ use std::sync::Arc;
use async_trait::async_trait; use async_trait::async_trait;
use sp_inherents::CreateInherentDataProviders; use sp_consensus::{Error, CacheKeyId};
use sp_runtime::traits::Block;
use sp_api::TransactionFor;
use sp_consensus::{Error, CacheKeyId, Environment};
use sc_consensus::{BlockImportParams, BlockImport, Verifier}; use sc_consensus::{BlockImportParams, BlockImport, Verifier};
use sc_client_api::Backend; use crate::{types::TendermintAuthor, tendermint::TendermintImport};
use sp_tendermint::TendermintApi;
use crate::{
tendermint::{TendermintClient, TendermintImport},
Announce,
};
#[async_trait] #[async_trait]
impl< impl<T: TendermintAuthor> Verifier<T::Block> for TendermintImport<T>
B: Block,
Be: Backend<B> + 'static,
C: TendermintClient<B, Be>,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
E: Send + Sync + Environment<B> + 'static,
A: Announce<B>,
> Verifier<B> for TendermintImport<B, Be, C, CIDP, E, A>
where where
TransactionFor<C, B>: Send + Sync + 'static, Arc<T::Client>: BlockImport<T::Block, Transaction = T::BackendTransaction>,
Arc<C>: BlockImport<B, Transaction = TransactionFor<C, B>>, <Arc<T::Client> as BlockImport<T::Block>>::Error: Into<Error>,
<Arc<C> as BlockImport<B>>::Error: Into<Error>,
C::Api: TendermintApi<B>,
{ {
async fn verify( async fn verify(
&mut self, &mut self,
mut block: BlockImportParams<B, ()>, mut block: BlockImportParams<T::Block, ()>,
) -> Result<(BlockImportParams<B, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> { ) -> Result<(BlockImportParams<T::Block, ()>, Option<Vec<(CacheKeyId, Vec<u8>)>>), String> {
self.check(&mut block).await.map_err(|e| format!("{}", e))?; self.check(&mut block).await.map_err(|e| format!("{}", e))?;
Ok((block, None)) Ok((block, None))
} }