Implement block proposal logic

This commit is contained in:
Luke Parker 2022-10-21 23:36:24 -04:00
parent adfc9a5d1d
commit bf5bdb89c2
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
9 changed files with 91 additions and 23 deletions

1
Cargo.lock generated
View file

@ -7387,6 +7387,7 @@ name = "serai-consensus"
version = "0.1.0"
dependencies = [
"async-trait",
"log",
"sc-basic-authorship",
"sc-client-api",
"sc-consensus",

View file

@ -15,6 +15,8 @@ rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
async-trait = "0.1"
log = "0.4"
tokio = "1"
sp-core = { git = "https://github.com/serai-dex/substrate" }

View file

@ -14,24 +14,27 @@
use std::{
marker::PhantomData,
sync::{Arc, RwLock},
time::Duration,
collections::HashMap,
};
use async_trait::async_trait;
use log::warn;
use tokio::sync::RwLock as AsyncRwLock;
use sp_core::{Encode, Decode};
use sp_application_crypto::sr25519::Signature;
use sp_inherents::CreateInherentDataProviders;
use sp_inherents::{InherentData, InherentDataProvider, CreateInherentDataProviders};
use sp_runtime::{
traits::{Header, Block},
Justification,
Digest, Justification,
};
use sp_blockchain::HeaderBackend;
use sp_api::{BlockId, TransactionFor, ProvideRuntimeApi};
use sp_consensus::{Error, CacheKeyId};
use sp_consensus::{Error, CacheKeyId, Proposer, Environment};
#[rustfmt::skip] // rustfmt doesn't know how to handle this line
use sc_consensus::{
ForkChoiceStrategy,
@ -63,6 +66,7 @@ struct TendermintImport<
C: Send + Sync + HeaderBackend<B> + Finalizer<B, Be> + ProvideRuntimeApi<B> + 'static,
I: Send + Sync + BlockImport<B, Transaction = TransactionFor<C, B>> + 'static,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
E: Send + Sync + Environment<B> + 'static,
> {
_block: PhantomData<B>,
_backend: PhantomData<Be>,
@ -72,6 +76,8 @@ struct TendermintImport<
client: Arc<C>,
inner: Arc<AsyncRwLock<I>>,
providers: Arc<CIDP>,
env: Arc<AsyncRwLock<E>>,
}
impl<
@ -80,7 +86,8 @@ impl<
C: Send + Sync + HeaderBackend<B> + Finalizer<B, Be> + ProvideRuntimeApi<B> + 'static,
I: Send + Sync + BlockImport<B, Transaction = TransactionFor<C, B>> + 'static,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
> Clone for TendermintImport<B, Be, C, I, CIDP>
E: Send + Sync + Environment<B> + 'static,
> Clone for TendermintImport<B, Be, C, I, CIDP, E>
{
fn clone(&self) -> Self {
TendermintImport {
@ -92,6 +99,8 @@ impl<
client: self.client.clone(),
inner: self.inner.clone(),
providers: self.providers.clone(),
env: self.env.clone(),
}
}
}
@ -102,7 +111,8 @@ impl<
C: Send + Sync + HeaderBackend<B> + Finalizer<B, Be> + ProvideRuntimeApi<B> + 'static,
I: Send + Sync + BlockImport<B, Transaction = TransactionFor<C, B>> + 'static,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
> TendermintImport<B, Be, C, I, CIDP>
E: Send + Sync + Environment<B> + 'static,
> TendermintImport<B, Be, C, I, CIDP, E>
{
async fn check_inherents(
&self,
@ -240,7 +250,8 @@ impl<
C: Send + Sync + HeaderBackend<B> + Finalizer<B, Be> + ProvideRuntimeApi<B> + 'static,
I: Send + Sync + BlockImport<B, Transaction = TransactionFor<C, B>> + 'static,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
> BlockImport<B> for TendermintImport<B, Be, C, I, CIDP>
E: Send + Sync + Environment<B> + 'static,
> BlockImport<B> for TendermintImport<B, Be, C, I, CIDP, E>
where
I::Error: Into<Error>,
{
@ -281,7 +292,8 @@ impl<
C: Send + Sync + HeaderBackend<B> + Finalizer<B, Be> + ProvideRuntimeApi<B> + 'static,
I: Send + Sync + BlockImport<B, Transaction = TransactionFor<C, B>> + 'static,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
> JustificationImport<B> for TendermintImport<B, Be, C, I, CIDP>
E: Send + Sync + Environment<B> + 'static,
> JustificationImport<B> for TendermintImport<B, Be, C, I, CIDP, E>
{
type Error = Error;
@ -306,7 +318,8 @@ impl<
C: Send + Sync + HeaderBackend<B> + Finalizer<B, Be> + ProvideRuntimeApi<B> + 'static,
I: Send + Sync + BlockImport<B, Transaction = TransactionFor<C, B>> + 'static,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
> Verifier<B> for TendermintImport<B, Be, C, I, CIDP>
E: Send + Sync + Environment<B> + 'static,
> Verifier<B> for TendermintImport<B, Be, C, I, CIDP, E>
{
async fn verify(
&mut self,
@ -324,7 +337,8 @@ impl<
C: Send + Sync + HeaderBackend<B> + Finalizer<B, Be> + ProvideRuntimeApi<B> + 'static,
I: Send + Sync + BlockImport<B, Transaction = TransactionFor<C, B>> + 'static,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
> Network for TendermintImport<B, Be, C, I, CIDP>
E: Send + Sync + Environment<B> + 'static,
> Network for TendermintImport<B, Be, C, I, CIDP, E>
{
type ValidatorId = u16;
type SignatureScheme = TendermintSigner;
@ -356,10 +370,38 @@ impl<
// Ok(())
}
fn add_block(&mut self, block: B, commit: Commit<TendermintSigner>) -> B {
self.import_justification_actual(block.hash(), (CONSENSUS_ID, commit.encode())).unwrap();
// Next block proposal
todo!()
async fn add_block(&mut self, block: B, commit: Commit<TendermintSigner>) -> B {
let hash = block.hash();
self.import_justification_actual(hash, (CONSENSUS_ID, commit.encode())).unwrap();
let inherent_data = match self.providers.create_inherent_data_providers(hash, ()).await {
Ok(providers) => match providers.create_inherent_data() {
Ok(data) => Some(data),
Err(err) => {
warn!(target: "tendermint", "Failed to create inherent data: {}", err);
None
}
},
Err(err) => {
warn!(target: "tendermint", "Failed to create inherent data providers: {}", err);
None
}
}
.unwrap_or_else(InherentData::new);
let proposer = self
.env
.write()
.await
.init(block.header())
.await
.expect("Failed to create a proposer for the new block");
// TODO: Production time, size limit
let proposal = proposer
.propose(inherent_data, Digest::default(), Duration::from_secs(1), None)
.await
.expect("Failed to crate a new block proposal");
proposal.block
}
}
@ -371,10 +413,12 @@ pub fn import_queue<
C: Send + Sync + HeaderBackend<B> + Finalizer<B, Be> + ProvideRuntimeApi<B> + 'static,
I: Send + Sync + BlockImport<B, Transaction = TransactionFor<C, B>> + 'static,
CIDP: CreateInherentDataProviders<B, ()> + 'static,
E: Send + Sync + Environment<B> + 'static,
>(
client: Arc<C>,
inner: I,
providers: Arc<CIDP>,
env: E,
spawner: &impl sp_core::traits::SpawnEssentialNamed,
registry: Option<&Registry>,
) -> TendermintImportQueue<B, TransactionFor<C, B>>
@ -390,6 +434,8 @@ where
client,
inner: Arc::new(AsyncRwLock::new(inner)),
providers,
env: Arc::new(AsyncRwLock::new(env)),
};
let boxed = Box::new(import.clone());

View file

@ -4,6 +4,7 @@ use sp_api::TransactionFor;
use sp_consensus::Error;
use sc_executor::{NativeVersion, NativeExecutionDispatch, NativeElseWasmExecutor};
use sc_transaction_pool::FullPool;
use sc_service::{TaskManager, TFullClient};
use substrate_prometheus_endpoint::Registry;
@ -40,12 +41,20 @@ pub type FullClient = TFullClient<Block, RuntimeApi, NativeElseWasmExecutor<Exec
pub fn import_queue(
task_manager: &TaskManager,
client: Arc<FullClient>,
pool: Arc<FullPool<Block, FullClient>>,
registry: Option<&Registry>,
) -> Result<TendermintImportQueue<Block, TransactionFor<FullClient, Block>>, Error> {
Ok(import_queue::import_queue(
client.clone(),
client,
client.clone(),
Arc::new(|_, _| async { Ok(sp_timestamp::InherentDataProvider::from_system_time()) }),
sc_basic_authorship::ProposerFactory::new(
task_manager.spawn_handle(),
client,
pool,
registry,
None,
),
&task_manager.spawn_essential_handle(),
registry,
))
@ -56,7 +65,7 @@ pub fn authority(
task_manager: &TaskManager,
client: Arc<FullClient>,
network: Arc<sc_network::NetworkService<Block, <Block as sp_runtime::traits::Block>::Hash>>,
pool: Arc<sc_transaction_pool::FullPool<Block, FullClient>>,
pool: Arc<FullPool<Block, FullClient>>,
registry: Option<&Registry>,
) {
todo!()

View file

@ -1,9 +1,8 @@
use sc_service::ChainType;
use sp_runtime::traits::Verify;
use sp_core::{Pair as PairTrait, sr25519::Pair};
use serai_runtime::{WASM_BINARY, AccountId, Signature, GenesisConfig, SystemConfig, BalancesConfig};
use serai_runtime::{WASM_BINARY, AccountId, GenesisConfig, SystemConfig, BalancesConfig};
pub type ChainSpec = sc_service::GenericChainSpec<GenesisConfig>;

View file

@ -63,8 +63,12 @@ pub fn new_partial(config: &Configuration) -> Result<PartialComponents, ServiceE
client.clone(),
);
let import_queue =
serai_consensus::import_queue(&task_manager, client.clone(), config.prometheus_registry())?;
let import_queue = serai_consensus::import_queue(
&task_manager,
client.clone(),
transaction_pool.clone(),
config.prometheus_registry(),
)?;
let select_chain = serai_consensus::TendermintSelectChain::new(backend.clone());

View file

@ -186,6 +186,9 @@ pub trait Network: Send + Sync {
/// consider it valid and created a commit for it. This deviates from the paper which will have a
/// local node refuse to decide on a block it considers invalid. This library acknowledges the
/// network did decide on it, leaving handling of it to the network, and outside of this scope.
fn add_block(&mut self, block: Self::Block, commit: Commit<Self::SignatureScheme>)
-> Self::Block;
async fn add_block(
&mut self,
block: Self::Block,
commit: Commit<Self::SignatureScheme>,
) -> Self::Block;
}

View file

@ -318,7 +318,7 @@ impl<N: Network + 'static> TendermintMachine<N> {
};
debug_assert!(machine.network.read().await.verify_commit(block.id(), &commit));
let proposal = machine.network.write().await.add_block(block, commit);
let proposal = machine.network.write().await.add_block(block, commit).await;
machine.reset(proposal).await;
}
Err(TendermintError::Malicious(validator)) => {

View file

@ -113,7 +113,11 @@ impl Network for TestNetwork {
block.valid
}
fn add_block(&mut self, block: TestBlock, commit: Commit<TestSignatureScheme>) -> TestBlock {
async fn add_block(
&mut self,
block: TestBlock,
commit: Commit<TestSignatureScheme>,
) -> TestBlock {
dbg!("Adding ", &block);
assert!(block.valid.is_ok());
assert!(self.verify_commit(block.id(), &commit));