Skip to content

Commit

Permalink
Move proof generation to the type system level (paritytech#8185)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
2 people authored and KalitaAlexey committed Jul 9, 2021
1 parent 3bc5c2f commit 4218cd4
Show file tree
Hide file tree
Showing 17 changed files with 230 additions and 121 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions bin/node/bench/src/construct.rs
Expand Up @@ -48,7 +48,7 @@ use sp_transaction_pool::{
TransactionStatusStreamFor,
TxHash,
};
use sp_consensus::{Environment, Proposer, RecordProof};
use sp_consensus::{Environment, Proposer};

use crate::{
common::SizeType,
Expand Down Expand Up @@ -170,7 +170,6 @@ impl core::Benchmark for ConstructionBenchmark {
inherent_data_providers.create_inherent_data().expect("Create inherent data failed"),
Default::default(),
std::time::Duration::from_secs(20),
RecordProof::Yes,
),
).map(|r| r.block).expect("Proposing failed");

Expand Down
2 changes: 0 additions & 2 deletions bin/node/cli/src/service.rs
Expand Up @@ -470,7 +470,6 @@ mod tests {
use sc_consensus_epochs::descendent_query;
use sp_consensus::{
Environment, Proposer, BlockImportParams, BlockOrigin, ForkChoiceStrategy, BlockImport,
RecordProof,
};
use node_primitives::{Block, DigestItem, Signature};
use node_runtime::{BalancesCall, Call, UncheckedExtrinsic, Address};
Expand Down Expand Up @@ -603,7 +602,6 @@ mod tests {
inherent_data,
digest,
std::time::Duration::from_secs(1),
RecordProof::Yes,
).await
}).expect("Error making test block").block;

Expand Down
3 changes: 1 addition & 2 deletions client/basic-authorship/README.md
Expand Up @@ -20,7 +20,6 @@ let future = proposer.propose(
Default::default(),
Default::default(),
Duration::from_secs(2),
RecordProof::Yes,
);

// We wait until the proposition is performed.
Expand All @@ -29,4 +28,4 @@ println!("Generated block: {:?}", block.block);
```


License: GPL-3.0-or-later WITH Classpath-exception-2.0
License: GPL-3.0-or-later WITH Classpath-exception-2.0
84 changes: 57 additions & 27 deletions client/basic-authorship/src/basic_authorship.rs
Expand Up @@ -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};
Expand Down Expand Up @@ -52,20 +52,23 @@ 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>,
/// The transaction pool.
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>,
Expand All @@ -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:
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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(
Expand All @@ -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,
Expand All @@ -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,
Expand All @@ -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();
Expand All @@ -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!");
Expand All @@ -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,
Expand All @@ -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.
Expand All @@ -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)? {
Expand Down Expand Up @@ -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 })
}
}
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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();
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
3 changes: 1 addition & 2 deletions client/basic-authorship/src/lib.rs
Expand Up @@ -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::{
Expand Down Expand Up @@ -61,7 +61,6 @@
//! Default::default(),
//! Default::default(),
//! Duration::from_secs(2),
//! RecordProof::Yes,
//! );
//!
//! // We wait until the proposition is performed.
Expand Down
1 change: 0 additions & 1 deletion client/block-builder/Cargo.toml
Expand Up @@ -17,7 +17,6 @@ targets = ["x86_64-unknown-linux-gnu"]
sp-state-machine = { version = "0.9.0", path = "../../primitives/state-machine" }
sp-runtime = { version = "3.0.0", path = "../../primitives/runtime" }
sp-api = { version = "3.0.0", path = "../../primitives/api" }
sp-consensus = { version = "0.9.0", path = "../../primitives/consensus/common" }
sp-blockchain = { version = "3.0.0", path = "../../primitives/blockchain" }
sp-core = { version = "3.0.0", path = "../../primitives/core" }
sp-block-builder = { version = "3.0.0", path = "../../primitives/block-builder" }
Expand Down
38 changes: 37 additions & 1 deletion client/block-builder/src/lib.rs
Expand Up @@ -37,12 +37,48 @@ use sp_core::ExecutionContext;
use sp_api::{
Core, ApiExt, ApiRef, ProvideRuntimeApi, StorageChanges, StorageProof, TransactionOutcome,
};
use sp_consensus::RecordProof;

pub use sp_block_builder::BlockBuilder as BlockBuilderApi;

use sc_client_api::backend;

/// Used as parameter to [`BlockBuilderProvider`] to express if proof recording should be enabled.
///
/// When `RecordProof::Yes` is given, all accessed trie nodes should be saved. These recorded
/// trie nodes can be used by a third party to proof this proposal without having access to the
/// full storage.
#[derive(Copy, Clone, PartialEq)]
pub enum RecordProof {
/// `Yes`, record a proof.
Yes,
/// `No`, don't record any proof.
No,
}

impl RecordProof {
/// Returns if `Self` == `Yes`.
pub fn yes(&self) -> bool {
matches!(self, Self::Yes)
}
}

/// Will return [`RecordProof::No`] as default value.
impl Default for RecordProof {
fn default() -> Self {
Self::No
}
}

impl From<bool> for RecordProof {
fn from(val: bool) -> Self {
if val {
Self::Yes
} else {
Self::No
}
}
}

/// A block that was build by [`BlockBuilder`] plus some additional data.
///
/// This additional data includes the `storage_changes`, these changes can be applied to the
Expand Down

0 comments on commit 4218cd4

Please sign in to comment.