Initial work on an import queue

This commit is contained in:
Luke Parker 2022-10-20 03:50:06 -04:00
parent 975c9d7456
commit eb59dd5a55
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
4 changed files with 155 additions and 5 deletions

5
Cargo.lock generated
View file

@ -7426,6 +7426,7 @@ checksum = "930c0acf610d3fdb5e2ab6213019aaa04e227ebe9547b0649ba599b16d788bd7"
name = "serai-consensus"
version = "0.1.0"
dependencies = [
"async-trait",
"sc-basic-authorship",
"sc-client-api",
"sc-consensus",
@ -7436,13 +7437,17 @@ dependencies = [
"sc-transaction-pool",
"serai-runtime",
"sp-api",
"sp-application-crypto",
"sp-blockchain",
"sp-consensus",
"sp-consensus-pow",
"sp-core",
"sp-inherents",
"sp-runtime",
"sp-timestamp",
"sp-trie",
"substrate-prometheus-endpoint",
"tendermint-machine",
"tokio",
]

View file

@ -13,11 +13,24 @@ all-features = true
rustdoc-args = ["--cfg", "docsrs"]
[dependencies]
async-trait = "0.1"
sp-core = { git = "https://github.com/serai-dex/substrate" }
sp-application-crypto = { git = "https://github.com/serai-dex/substrate" }
sp-inherents = { git = "https://github.com/serai-dex/substrate" }
sp-runtime = { git = "https://github.com/serai-dex/substrate" }
sp-blockchain = { git = "https://github.com/serai-dex/substrate" }
sp-api = { git = "https://github.com/serai-dex/substrate" }
sp-consensus = { git = "https://github.com/serai-dex/substrate" }
sc-consensus = { git = "https://github.com/serai-dex/substrate" }
tendermint-machine = { path = "../tendermint" }
# --
sp-trie = { git = "https://github.com/serai-dex/substrate" }
sp-timestamp = { git = "https://github.com/serai-dex/substrate" }
sc-consensus = { git = "https://github.com/serai-dex/substrate" }
sp-consensus = { git = "https://github.com/serai-dex/substrate" }
sc-transaction-pool = { git = "https://github.com/serai-dex/substrate" }
sc-basic-authorship = { git = "https://github.com/serai-dex/substrate" }
sc-consensus-pow = { git = "https://github.com/serai-dex/substrate" }
@ -26,12 +39,10 @@ sp-consensus-pow = { git = "https://github.com/serai-dex/substrate" }
sc-network = { git = "https://github.com/serai-dex/substrate" }
sc-service = { git = "https://github.com/serai-dex/substrate", features = ["wasmtime"] }
sc-executor = { git = "https://github.com/serai-dex/substrate", features = ["wasmtime"] }
sp-runtime = { git = "https://github.com/serai-dex/substrate" }
substrate-prometheus-endpoint = { git = "https://github.com/serai-dex/substrate" }
sc-client-api = { git = "https://github.com/serai-dex/substrate" }
sp-api = { git = "https://github.com/serai-dex/substrate" }
serai-runtime = { path = "../runtime" }

View file

@ -0,0 +1,131 @@
use std::{marker::PhantomData, sync::Arc, collections::HashMap};
use sp_core::Decode;
use sp_inherents::CreateInherentDataProviders;
use sp_runtime::traits::{Header, Block};
use sp_blockchain::HeaderBackend;
use sp_api::{ProvideRuntimeApi, TransactionFor};
use sp_consensus::{Error, CacheKeyId};
#[rustfmt::skip]
use sc_consensus::{
ForkChoiceStrategy,
BlockCheckParams,
BlockImportParams,
ImportResult,
BlockImport,
};
use tendermint_machine::ext::*;
use crate::signature_scheme::TendermintSigner;
struct TendermintBlockImport<
B: Block,
C: Send + Sync + HeaderBackend<B> + ProvideRuntimeApi<B> + 'static,
I: Send + Sync + BlockImport<B, Transaction = TransactionFor<C, B>>,
CIDP: CreateInherentDataProviders<B, ()>,
> {
_block: PhantomData<B>,
client: Arc<C>,
inner: I,
providers: Arc<CIDP>,
}
impl<
B: Block,
C: Send + Sync + HeaderBackend<B> + ProvideRuntimeApi<B>,
I: Send + Sync + BlockImport<B, Transaction = TransactionFor<C, B>>,
CIDP: CreateInherentDataProviders<B, ()>,
> TendermintBlockImport<B, C, I, CIDP>
{
async fn check_inherents(
&self,
block: B,
providers: CIDP::InherentDataProviders,
) -> Result<(), Error> {
todo!()
}
}
// The Tendermint machine will call add_block for any block which is committed to, regardless of
// validity. To determine validity, it expects a validate function, which Substrate doesn't
// directly offer, and an add function. In order to comply with Serai's modified view of inherent
// transactions, validate MUST check inherents, yet add_block must not.
//
// In order to acquire a validate function, any block proposed by a legitimate proposer is
// imported. This performs full validation and makes the block available as a tip. While this would
// be incredibly unsafe thanks to the unchecked inherents, it's defined as a tip with less work,
// despite being a child of some parent. This means it won't be moved to nor operated on by the
// node.
//
// When Tendermint completes, the block is finalized, setting it as the tip regardless of work.
const CONSENSUS_ID: [u8; 4] = *b"tend";
#[async_trait::async_trait]
impl<
B: Block,
C: Send + Sync + HeaderBackend<B> + ProvideRuntimeApi<B>,
I: Send + Sync + BlockImport<B, Transaction = TransactionFor<C, B>>,
CIDP: CreateInherentDataProviders<B, ()>,
> BlockImport<B> for TendermintBlockImport<B, C, I, CIDP>
where
I::Error: Into<Error>,
{
type Error = Error;
type Transaction = TransactionFor<C, B>;
async fn check_block(&mut self, block: BlockCheckParams<B>) -> Result<ImportResult, Self::Error> {
self.inner.check_block(block).await.map_err(Into::into)
}
async fn import_block(
&mut self,
mut block: BlockImportParams<B, Self::Transaction>,
new_cache: HashMap<CacheKeyId, Vec<u8>>,
) -> Result<ImportResult, Self::Error> {
let parent_hash = *block.header.parent_hash();
if self.client.info().best_hash != parent_hash {
Err(Error::Other("non-sequential import".into()))?;
}
if let Some(body) = block.body.clone() {
if let Some(justifications) = block.justifications {
let mut iter = justifications.iter();
let next = iter.next();
if next.is_none() || iter.next().is_some() {
Err(Error::InvalidJustification)?;
}
let justification = next.unwrap();
let commit: Commit<TendermintSigner> =
Commit::decode(&mut justification.1.as_ref()).map_err(|_| Error::InvalidJustification)?;
if justification.0 != CONSENSUS_ID {
Err(Error::InvalidJustification)?;
}
// verify_commit
todo!()
} else {
self
.check_inherents(
B::new(block.header.clone(), body),
self.providers.create_inherent_data_providers(parent_hash, ()).await?,
)
.await?;
}
}
if !block.post_digests.is_empty() {
Err(Error::Other("post-digests included".into()))?;
}
if !block.auxiliary.is_empty() {
Err(Error::Other("auxiliary included".into()))?;
}
block.fork_choice = Some(ForkChoiceStrategy::Custom(false));
self.inner.import_block(block, new_cache).await.map_err(Into::into)
}
}

View file

@ -9,7 +9,10 @@ use sc_service::TaskManager;
use serai_runtime::{self, opaque::Block, RuntimeApi};
mod algorithm;
mod tendermint;
mod signature_scheme;
mod import;
//mod tendermint;
pub struct ExecutorDispatch;
impl sc_executor::NativeExecutionDispatch for ExecutorDispatch {