Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit e296263

Browse files
shunsukewbkchr
authored andcommitted
Manual seal delayed finalize (#13999)
* up * up * added test * remove unncessary dep * cargo fmt * cargo fmt * up * Update client/consensus/manual-seal/src/lib.rs Co-authored-by: Bastian Köcher <git@kchr.de> * fix test * cargo fmt * added docs * updated doc --------- Co-authored-by: Bastian Köcher <git@kchr.de> Co-authored-by: parity-processbot <>
1 parent 6ebcadd commit e296263

File tree

4 files changed

+160
-7
lines changed

4 files changed

+160
-7
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

client/consensus/manual-seal/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ assert_matches = "1.3.0"
1818
async-trait = "0.1.57"
1919
codec = { package = "parity-scale-codec", version = "3.2.2" }
2020
futures = "0.3.21"
21+
futures-timer = "3.0.1"
2122
log = "0.4.17"
2223
serde = { version = "1.0", features = ["derive"] }
2324
thiserror = "1.0"

client/consensus/manual-seal/src/lib.rs

Lines changed: 154 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,22 @@
2020
//! This is suitable for a testing environment.
2121
2222
use futures::prelude::*;
23+
use futures_timer::Delay;
2324
use prometheus_endpoint::Registry;
24-
use sc_client_api::backend::{Backend as ClientBackend, Finalizer};
25+
use sc_client_api::{
26+
backend::{Backend as ClientBackend, Finalizer},
27+
client::BlockchainEvents,
28+
};
2529
use sc_consensus::{
2630
block_import::{BlockImport, BlockImportParams, ForkChoiceStrategy},
2731
import_queue::{BasicQueue, BoxBlockImport, Verifier},
2832
};
2933
use sp_blockchain::HeaderBackend;
3034
use sp_consensus::{Environment, Proposer, SelectChain};
35+
use sp_core::traits::SpawnNamed;
3136
use sp_inherents::CreateInherentDataProviders;
3237
use sp_runtime::{traits::Block as BlockT, ConsensusEngineId};
33-
use std::{marker::PhantomData, sync::Arc};
38+
use std::{marker::PhantomData, sync::Arc, time::Duration};
3439

3540
mod error;
3641
mod finalize_block;
@@ -84,7 +89,7 @@ where
8489

8590
/// Params required to start the instant sealing authorship task.
8691
pub struct ManualSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, TP, SC, CS, CIDP, P> {
87-
/// Block import instance for well. importing blocks.
92+
/// Block import instance.
8893
pub block_import: BI,
8994

9095
/// The environment we are producing blocks for.
@@ -136,7 +141,19 @@ pub struct InstantSealParams<B: BlockT, BI, E, C: ProvideRuntimeApi<B>, TP, SC,
136141
pub create_inherent_data_providers: CIDP,
137142
}
138143

139-
/// Creates the background authorship task for the manual seal engine.
144+
/// Params required to start the delayed finalization task.
145+
pub struct DelayedFinalizeParams<C, S> {
146+
/// Block import instance.
147+
pub client: Arc<C>,
148+
149+
/// Handle for spawning delayed finalization tasks.
150+
pub spawn_handle: S,
151+
152+
/// The delay in seconds before a block is finalized.
153+
pub delay_sec: u64,
154+
}
155+
156+
/// Creates the background authorship task for the manually seal engine.
140157
pub async fn run_manual_seal<B, BI, CB, E, C, TP, SC, CS, CIDP, P>(
141158
ManualSealParams {
142159
mut block_import,
@@ -303,6 +320,44 @@ pub async fn run_instant_seal_and_finalize<B, BI, CB, E, C, TP, SC, CIDP, P>(
303320
.await
304321
}
305322

323+
/// Creates a future for delayed finalization of manual sealed blocks.
324+
///
325+
/// The future needs to be spawned in the background alongside the
326+
/// [`run_manual_seal`]/[`run_instant_seal`] future. It is required that
327+
/// [`EngineCommand::SealNewBlock`] is send with `finalize = false` to not finalize blocks directly
328+
/// after building them. This also means that delayed finality can not be used with
329+
/// [`run_instant_seal_and_finalize`].
330+
pub async fn run_delayed_finalize<B, CB, C, S>(
331+
DelayedFinalizeParams { client, spawn_handle, delay_sec }: DelayedFinalizeParams<C, S>,
332+
) where
333+
B: BlockT + 'static,
334+
CB: ClientBackend<B> + 'static,
335+
C: HeaderBackend<B> + Finalizer<B, CB> + ProvideRuntimeApi<B> + BlockchainEvents<B> + 'static,
336+
S: SpawnNamed,
337+
{
338+
let mut block_import_stream = client.import_notification_stream();
339+
340+
while let Some(notification) = block_import_stream.next().await {
341+
let delay = Delay::new(Duration::from_secs(delay_sec));
342+
let cloned_client = client.clone();
343+
spawn_handle.spawn(
344+
"delayed-finalize",
345+
None,
346+
Box::pin(async move {
347+
delay.await;
348+
finalize_block(FinalizeBlockParams {
349+
hash: notification.hash,
350+
sender: None,
351+
justification: None,
352+
finalizer: cloned_client,
353+
_phantom: PhantomData,
354+
})
355+
.await
356+
}),
357+
);
358+
}
359+
}
360+
306361
#[cfg(test)]
307362
mod tests {
308363
use super::*;
@@ -428,6 +483,101 @@ mod tests {
428483
assert_eq!(client.header(created_block.hash).unwrap().unwrap().number, 1)
429484
}
430485

486+
#[tokio::test]
487+
async fn instant_seal_delayed_finalize() {
488+
let builder = TestClientBuilder::new();
489+
let (client, select_chain) = builder.build_with_longest_chain();
490+
let client = Arc::new(client);
491+
let spawner = sp_core::testing::TaskExecutor::new();
492+
let genesis_hash = client.info().genesis_hash;
493+
let pool = Arc::new(BasicPool::with_revalidation_type(
494+
Options::default(),
495+
true.into(),
496+
api(),
497+
None,
498+
RevalidationType::Full,
499+
spawner.clone(),
500+
0,
501+
genesis_hash,
502+
genesis_hash,
503+
));
504+
let env = ProposerFactory::new(spawner.clone(), client.clone(), pool.clone(), None, None);
505+
// this test checks that blocks are created as soon as transactions are imported into the
506+
// pool.
507+
let (sender, receiver) = futures::channel::oneshot::channel();
508+
let mut sender = Arc::new(Some(sender));
509+
let commands_stream =
510+
pool.pool().validated_pool().import_notification_stream().map(move |_| {
511+
// we're only going to submit one tx so this fn will only be called once.
512+
let mut_sender = Arc::get_mut(&mut sender).unwrap();
513+
let sender = std::mem::take(mut_sender);
514+
EngineCommand::SealNewBlock {
515+
create_empty: false,
516+
// set to `false`, expecting to be finalized by delayed finalize
517+
finalize: false,
518+
parent_hash: None,
519+
sender,
520+
}
521+
});
522+
523+
let future_instant_seal = run_manual_seal(ManualSealParams {
524+
block_import: client.clone(),
525+
commands_stream,
526+
env,
527+
client: client.clone(),
528+
pool: pool.clone(),
529+
select_chain,
530+
create_inherent_data_providers: |_, _| async { Ok(()) },
531+
consensus_data_provider: None,
532+
});
533+
std::thread::spawn(|| {
534+
let rt = tokio::runtime::Runtime::new().unwrap();
535+
// spawn the background authorship task
536+
rt.block_on(future_instant_seal);
537+
});
538+
539+
let delay_sec = 5;
540+
let future_delayed_finalize = run_delayed_finalize(DelayedFinalizeParams {
541+
client: client.clone(),
542+
delay_sec,
543+
spawn_handle: spawner,
544+
});
545+
std::thread::spawn(|| {
546+
let rt = tokio::runtime::Runtime::new().unwrap();
547+
// spawn the background authorship task
548+
rt.block_on(future_delayed_finalize);
549+
});
550+
551+
let mut finality_stream = client.finality_notification_stream();
552+
// submit a transaction to pool.
553+
let result = pool.submit_one(&BlockId::Number(0), SOURCE, uxt(Alice, 0)).await;
554+
// assert that it was successfully imported
555+
assert!(result.is_ok());
556+
// assert that the background task returns ok
557+
let created_block = receiver.await.unwrap().unwrap();
558+
assert_eq!(
559+
created_block,
560+
CreatedBlock {
561+
hash: created_block.hash,
562+
aux: ImportedAux {
563+
header_only: false,
564+
clear_justification_requests: false,
565+
needs_justification: false,
566+
bad_justification: false,
567+
is_new_best: true,
568+
}
569+
}
570+
);
571+
// assert that there's a new block in the db.
572+
assert!(client.header(created_block.hash).unwrap().is_some());
573+
assert_eq!(client.header(created_block.hash).unwrap().unwrap().number, 1);
574+
575+
assert_eq!(client.info().finalized_hash, client.info().genesis_hash);
576+
577+
let finalized = finality_stream.select_next_some().await;
578+
assert_eq!(finalized.hash, created_block.hash);
579+
}
580+
431581
#[tokio::test]
432582
async fn manual_seal_and_finalization() {
433583
let builder = TestClientBuilder::new();

client/consensus/manual-seal/src/rpc.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,11 @@ pub fn send_result<T: std::fmt::Debug>(
160160
}
161161
}
162162
} else {
163-
// instant seal doesn't report errors over rpc, simply log them.
163+
// Sealing/Finalization with no RPC sender such as instant seal or delayed finalize doesn't
164+
// report errors over rpc, simply log them.
164165
match result {
165-
Ok(r) => log::info!("Instant Seal success: {:?}", r),
166-
Err(e) => log::error!("Instant Seal encountered an error: {}", e),
166+
Ok(r) => log::info!("Consensus with no RPC sender success: {:?}", r),
167+
Err(e) => log::error!("Consensus with no RPC sender encountered an error: {}", e),
167168
}
168169
}
169170
}

0 commit comments

Comments
 (0)