Move proof generation to the type system level (#8185)

* Start

* Finish!!!!

* Update client/basic-authorship/src/basic_authorship.rs

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>

* Review comments

Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com>
This commit is contained in:
Bastian Köcher
2021-02-24 21:43:50 +01:00
committed by GitHub
parent 3309c4366b
commit 8a0e8ea9a6
17 changed files with 230 additions and 121 deletions
@@ -23,7 +23,7 @@
use std::{pin::Pin, time, sync::Arc};
use sc_client_api::backend;
use codec::Decode;
use sp_consensus::{evaluation, Proposal, RecordProof};
use sp_consensus::{evaluation, Proposal, ProofRecording, DisableProofRecording, EnableProofRecording};
use sp_core::traits::SpawnNamed;
use sp_inherents::InherentData;
use log::{error, info, debug, trace, warn};
@@ -52,7 +52,7 @@ use sc_proposer_metrics::MetricsLink as PrometheusMetrics;
pub const DEFAULT_MAX_BLOCK_SIZE: usize = 4 * 1024 * 1024 + 512;
/// Proposer factory.
pub struct ProposerFactory<A, B, C> {
pub struct ProposerFactory<A, B, C, PR> {
spawn_handle: Box<dyn SpawnNamed>,
/// The client instance.
client: Arc<C>,
@@ -60,12 +60,15 @@ pub struct ProposerFactory<A, B, C> {
transaction_pool: Arc<A>,
/// Prometheus Link,
metrics: PrometheusMetrics,
/// phantom member to pin the `Backend` type.
_phantom: PhantomData<B>,
/// phantom member to pin the `Backend`/`ProofRecording` type.
_phantom: PhantomData<(B, PR)>,
max_block_size: usize,
}
impl<A, B, C> ProposerFactory<A, B, C> {
impl<A, B, C> ProposerFactory<A, B, C, DisableProofRecording> {
/// Create a new proposer factory.
///
/// Proof recording will be disabled when using proposers built by this instance to build blocks.
pub fn new(
spawn_handle: impl SpawnNamed + 'static,
client: Arc<C>,
@@ -81,7 +84,30 @@ impl<A, B, C> ProposerFactory<A, B, C> {
max_block_size: DEFAULT_MAX_BLOCK_SIZE,
}
}
}
impl<A, B, C> ProposerFactory<A, B, C, EnableProofRecording> {
/// Create a new proposer factory with proof recording enabled.
///
/// Each proposer created by this instance will record a proof while building a block.
pub fn with_proof_recording(
spawn_handle: impl SpawnNamed + 'static,
client: Arc<C>,
transaction_pool: Arc<A>,
prometheus: Option<&PrometheusRegistry>,
) -> Self {
ProposerFactory {
spawn_handle: Box::new(spawn_handle),
client,
transaction_pool,
metrics: PrometheusMetrics::new(prometheus),
_phantom: PhantomData,
max_block_size: DEFAULT_MAX_BLOCK_SIZE,
}
}
}
impl<A, B, C, PR> ProposerFactory<A, B, C, PR> {
/// Set the maximum block size in bytes.
///
/// The default value for the maximum block size is:
@@ -91,7 +117,7 @@ impl<A, B, C> ProposerFactory<A, B, C> {
}
}
impl<B, Block, C, A> ProposerFactory<A, B, C>
impl<B, Block, C, A, PR> ProposerFactory<A, B, C, PR>
where
A: TransactionPool<Block = Block> + 'static,
B: backend::Backend<Block> + Send + Sync + 'static,
@@ -101,18 +127,18 @@ impl<B, Block, C, A> ProposerFactory<A, B, C>
C::Api: ApiExt<Block, StateBackend = backend::StateBackendFor<B, Block>>
+ BlockBuilderApi<Block>,
{
pub fn init_with_now(
fn init_with_now(
&mut self,
parent_header: &<Block as BlockT>::Header,
now: Box<dyn Fn() -> time::Instant + Send + Sync>,
) -> Proposer<B, Block, C, A> {
) -> Proposer<B, Block, C, A, PR> {
let parent_hash = parent_header.hash();
let id = BlockId::hash(parent_hash);
info!("🙌 Starting consensus session on top of parent {:?}", parent_hash);
let proposer = Proposer {
let proposer = Proposer::<_, _, _, _, PR> {
spawn_handle: self.spawn_handle.clone(),
client: self.client.clone(),
parent_hash,
@@ -129,8 +155,8 @@ impl<B, Block, C, A> ProposerFactory<A, B, C>
}
}
impl<A, B, Block, C> sp_consensus::Environment<Block> for
ProposerFactory<A, B, C>
impl<A, B, Block, C, PR> sp_consensus::Environment<Block> for
ProposerFactory<A, B, C, PR>
where
A: TransactionPool<Block = Block> + 'static,
B: backend::Backend<Block> + Send + Sync + 'static,
@@ -139,9 +165,10 @@ impl<A, B, Block, C> sp_consensus::Environment<Block> for
+ Send + Sync + 'static,
C::Api: ApiExt<Block, StateBackend = backend::StateBackendFor<B, Block>>
+ BlockBuilderApi<Block>,
PR: ProofRecording,
{
type CreateProposer = future::Ready<Result<Self::Proposer, Self::Error>>;
type Proposer = Proposer<B, Block, C, A>;
type Proposer = Proposer<B, Block, C, A, PR>;
type Error = sp_blockchain::Error;
fn init(
@@ -153,7 +180,7 @@ impl<A, B, Block, C> sp_consensus::Environment<Block> for
}
/// The proposer logic.
pub struct Proposer<B, Block: BlockT, C, A: TransactionPool> {
pub struct Proposer<B, Block: BlockT, C, A: TransactionPool, PR> {
spawn_handle: Box<dyn SpawnNamed>,
client: Arc<C>,
parent_hash: <Block as BlockT>::Hash,
@@ -162,12 +189,12 @@ pub struct Proposer<B, Block: BlockT, C, A: TransactionPool> {
transaction_pool: Arc<A>,
now: Box<dyn Fn() -> time::Instant + Send + Sync>,
metrics: PrometheusMetrics,
_phantom: PhantomData<B>,
_phantom: PhantomData<(B, PR)>,
max_block_size: usize,
}
impl<A, B, Block, C> sp_consensus::Proposer<Block> for
Proposer<B, Block, C, A>
impl<A, B, Block, C, PR> sp_consensus::Proposer<Block> for
Proposer<B, Block, C, A, PR>
where
A: TransactionPool<Block = Block> + 'static,
B: backend::Backend<Block> + Send + Sync + 'static,
@@ -176,19 +203,21 @@ impl<A, B, Block, C> sp_consensus::Proposer<Block> for
+ Send + Sync + 'static,
C::Api: ApiExt<Block, StateBackend = backend::StateBackendFor<B, Block>>
+ BlockBuilderApi<Block>,
PR: ProofRecording,
{
type Transaction = backend::TransactionFor<B, Block>;
type Proposal = Pin<Box<dyn Future<
Output = Result<Proposal<Block, Self::Transaction>, Self::Error>
Output = Result<Proposal<Block, Self::Transaction, PR::Proof>, Self::Error>
> + Send>>;
type Error = sp_blockchain::Error;
type ProofRecording = PR;
type Proof = PR::Proof;
fn propose(
self,
inherent_data: InherentData,
inherent_digests: DigestFor<Block>,
max_duration: time::Duration,
record_proof: RecordProof,
) -> Self::Proposal {
let (tx, rx) = oneshot::channel();
let spawn_handle = self.spawn_handle.clone();
@@ -200,7 +229,6 @@ impl<A, B, Block, C> sp_consensus::Proposer<Block> for
inherent_data,
inherent_digests,
deadline,
record_proof,
).await;
if tx.send(res).is_err() {
trace!("Could not send block production result to proposer!");
@@ -213,7 +241,7 @@ impl<A, B, Block, C> sp_consensus::Proposer<Block> for
}
}
impl<A, B, Block, C> Proposer<B, Block, C, A>
impl<A, B, Block, C, PR> Proposer<B, Block, C, A, PR>
where
A: TransactionPool<Block = Block>,
B: backend::Backend<Block> + Send + Sync + 'static,
@@ -222,14 +250,14 @@ impl<A, B, Block, C> Proposer<B, Block, C, A>
+ Send + Sync + 'static,
C::Api: ApiExt<Block, StateBackend = backend::StateBackendFor<B, Block>>
+ BlockBuilderApi<Block>,
PR: ProofRecording,
{
async fn propose_with(
self,
inherent_data: InherentData,
inherent_digests: DigestFor<Block>,
deadline: time::Instant,
record_proof: RecordProof,
) -> Result<Proposal<Block, backend::TransactionFor<B, Block>>, sp_blockchain::Error> {
) -> Result<Proposal<Block, backend::TransactionFor<B, Block>, PR::Proof>, sp_blockchain::Error> {
/// If the block is full we will attempt to push at most
/// this number of transactions before quitting for real.
/// It allows us to increase block utilization.
@@ -238,7 +266,7 @@ impl<A, B, Block, C> Proposer<B, Block, C, A>
let mut block_builder = self.client.new_block_at(
&self.parent_id,
inherent_digests,
record_proof,
PR::ENABLED,
)?;
for inherent in block_builder.create_inherents(inherent_data)? {
@@ -361,6 +389,8 @@ impl<A, B, Block, C> Proposer<B, Block, C, A>
error!("Failed to evaluate authored block: {:?}", err);
}
let proof = PR::into_proof(proof)
.map_err(|e| sp_blockchain::Error::Application(Box::new(e)))?;
Ok(Proposal { block, proof, storage_changes })
}
}
@@ -452,7 +482,7 @@ mod tests {
// when
let deadline = time::Duration::from_secs(3);
let block = futures::executor::block_on(
proposer.propose(Default::default(), Default::default(), deadline, RecordProof::No)
proposer.propose(Default::default(), Default::default(), deadline)
).map(|r| r.block).unwrap();
// then
@@ -497,7 +527,7 @@ mod tests {
let deadline = time::Duration::from_secs(1);
futures::executor::block_on(
proposer.propose(Default::default(), Default::default(), deadline, RecordProof::No)
proposer.propose(Default::default(), Default::default(), deadline)
).map(|r| r.block).unwrap();
}
@@ -543,7 +573,7 @@ mod tests {
let deadline = time::Duration::from_secs(9);
let proposal = futures::executor::block_on(
proposer.propose(Default::default(), Default::default(), deadline, RecordProof::No),
proposer.propose(Default::default(), Default::default(), deadline),
).unwrap();
assert_eq!(proposal.block.extrinsics().len(), 1);
@@ -624,7 +654,7 @@ mod tests {
// when
let deadline = time::Duration::from_secs(9);
let block = futures::executor::block_on(
proposer.propose(Default::default(), Default::default(), deadline, RecordProof::No)
proposer.propose(Default::default(), Default::default(), deadline)
).map(|r| r.block).unwrap();
// then
+1 -2
View File
@@ -22,7 +22,7 @@
//!
//! ```
//! # use sc_basic_authorship::ProposerFactory;
//! # use sp_consensus::{Environment, Proposer, RecordProof};
//! # use sp_consensus::{Environment, Proposer};
//! # use sp_runtime::generic::BlockId;
//! # use std::{sync::Arc, time::Duration};
//! # use substrate_test_runtime_client::{
@@ -61,7 +61,6 @@
//! Default::default(),
//! Default::default(),
//! Duration::from_secs(2),
//! RecordProof::Yes,
//! );
//!
//! // We wait until the proposition is performed.