Skip to content

Commit

Permalink
Support instant sealing (#170)
Browse files Browse the repository at this point in the history
* support both manual and instant sealing

* fix name

* remove sealing.unwrap()

* make MockTimestamp to start from 0

* update docs

* use thread_local to avoid unsafe

* remove expect
  • Loading branch information
ermalkaleci authored Oct 21, 2020
1 parent 6173a3c commit 7b836a2
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 43 deletions.
18 changes: 15 additions & 3 deletions template/node/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.

use structopt::StructOpt;
use structopt::{StructOpt, clap::arg_enum};

arg_enum! {
/// Available Sealing methods.
#[allow(missing_docs)]
#[derive(Debug, Copy, Clone, StructOpt)]
pub enum Sealing {
// Seal using rpc method.
Manual,
// Seal when transaction is executed.
Instant,
}
}

#[allow(missing_docs)]
#[derive(Debug, StructOpt)]
Expand All @@ -25,8 +37,8 @@ pub struct RunCmd {
pub base: sc_cli::RunCmd,

/// Force using Kusama native runtime.
#[structopt(long = "manual-seal")]
pub manual_seal: bool,
#[structopt(long = "sealing")]
pub sealing: Option<Sealing>,
}

#[derive(Debug, StructOpt)]
Expand Down
12 changes: 6 additions & 6 deletions template/node/src/command.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,31 +75,31 @@ pub fn run() -> sc_cli::Result<()> {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let PartialComponents { client, task_manager, import_queue, ..}
= new_partial(&config, cli.run.manual_seal)?;
= new_partial(&config, cli.run.sealing)?;
Ok((cmd.run(client, import_queue), task_manager))
})
},
Some(Subcommand::ExportBlocks(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let PartialComponents { client, task_manager, ..}
= new_partial(&config, cli.run.manual_seal)?;
= new_partial(&config, cli.run.sealing)?;
Ok((cmd.run(client, config.database), task_manager))
})
},
Some(Subcommand::ExportState(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let PartialComponents { client, task_manager, ..}
= new_partial(&config, cli.run.manual_seal)?;
= new_partial(&config, cli.run.sealing)?;
Ok((cmd.run(client, config.chain_spec), task_manager))
})
},
Some(Subcommand::ImportBlocks(cmd)) => {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let PartialComponents { client, task_manager, import_queue, ..}
= new_partial(&config, cli.run.manual_seal)?;
= new_partial(&config, cli.run.sealing)?;
Ok((cmd.run(client, import_queue), task_manager))
})
},
Expand All @@ -111,15 +111,15 @@ pub fn run() -> sc_cli::Result<()> {
let runner = cli.create_runner(cmd)?;
runner.async_run(|config| {
let PartialComponents { client, task_manager, backend, ..}
= new_partial(&config, cli.run.manual_seal)?;
= new_partial(&config, cli.run.sealing)?;
Ok((cmd.run(client, backend), task_manager))
})
},
None => {
let runner = cli.create_runner(&cli.run.base)?;
runner.run_node_until_exit(|config| match config.role {
Role::Light => service::new_light(config),
_ => service::new_full(config, cli.run.manual_seal),
_ => service::new_full(config, cli.run.sealing),
})
}
}
Expand Down
100 changes: 74 additions & 26 deletions template/node/src/service.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,20 @@
//! Service and ServiceFactory implementation. Specialized wrapper over substrate service.

use std::sync::Arc;
use std::time::Duration;
use std::{sync::Arc, cell::RefCell, time::Duration};
use sc_client_api::{ExecutorProvider, RemoteBackend};
use sc_consensus_manual_seal::{self as manual_seal};
use frontier_consensus::FrontierBlockImport;
use frontier_template_runtime::{self, opaque::Block, RuntimeApi};
use frontier_template_runtime::{self, opaque::Block, RuntimeApi, SLOT_DURATION};
use sc_service::{error::Error as ServiceError, Configuration, TaskManager};
use sp_inherents::InherentDataProviders;
use sp_inherents::{InherentDataProviders, ProvideInherentData, InherentIdentifier, InherentData};
use sc_executor::native_executor_instance;
pub use sc_executor::NativeExecutor;
use sp_consensus_aura::sr25519::{AuthorityPair as AuraPair};
use sc_finality_grandpa::{
FinalityProofProvider as GrandpaFinalityProofProvider, SharedVoterState,
};
use sp_timestamp::InherentError;
use crate::cli::Sealing;

// Our native executor instance.
native_executor_instance!(
Expand All @@ -40,10 +41,38 @@ pub enum ConsensusResult {
>,
sc_finality_grandpa::LinkHalf<Block, FullClient, FullSelectChain>
),
ManualSeal(FrontierBlockImport<Block, Arc<FullClient>, FullClient>)
ManualSeal(FrontierBlockImport<Block, Arc<FullClient>, FullClient>, Sealing)
}

pub fn new_partial(config: &Configuration, manual_seal: bool) -> Result<
/// Provide a mock duration starting at 0 in millisecond for timestamp inherent.
/// Each call will increment timestamp by slot_duration making Aura think time has passed.
pub struct MockTimestampInherentDataProvider;

pub const INHERENT_IDENTIFIER: InherentIdentifier = *b"timstap0";

thread_local!(static TIMESTAMP: RefCell<u64> = RefCell::new(0));

impl ProvideInherentData for MockTimestampInherentDataProvider {
fn inherent_identifier(&self) -> &'static InherentIdentifier {
&INHERENT_IDENTIFIER
}

fn provide_inherent_data(
&self,
inherent_data: &mut InherentData,
) -> Result<(), sp_inherents::Error> {
TIMESTAMP.with(|x| {
*x.borrow_mut() += SLOT_DURATION;
inherent_data.put_data(INHERENT_IDENTIFIER, &*x.borrow())
})
}

fn error_to_string(&self, error: &[u8]) -> Option<String> {
InherentError::try_from(&INHERENT_IDENTIFIER, error).map(|e| format!("{:?}", e))
}
}

pub fn new_partial(config: &Configuration, sealing: Option<Sealing>) -> Result<
sc_service::PartialComponents<
FullClient, FullBackend, FullSelectChain,
sp_consensus::import_queue::BasicQueue<Block, sp_api::TransactionFor<FullClient, Block>>,
Expand All @@ -65,9 +94,9 @@ pub fn new_partial(config: &Configuration, manual_seal: bool) -> Result<
client.clone(),
);

if manual_seal {
if let Some(sealing) = sealing {
inherent_data_providers
.register_provider(sp_timestamp::InherentDataProvider)
.register_provider(MockTimestampInherentDataProvider)
.map_err(Into::into)
.map_err(sp_consensus::error::Error::InherentData)?;

Expand All @@ -86,7 +115,7 @@ pub fn new_partial(config: &Configuration, manual_seal: bool) -> Result<
return Ok(sc_service::PartialComponents {
client, backend, task_manager, import_queue, keystore, select_chain, transaction_pool,
inherent_data_providers,
other: ConsensusResult::ManualSeal(frontier_block_import)
other: ConsensusResult::ManualSeal(frontier_block_import, sealing)
})
}

Expand Down Expand Up @@ -124,15 +153,15 @@ pub fn new_partial(config: &Configuration, manual_seal: bool) -> Result<
}

/// Builds a new service for a full client.
pub fn new_full(config: Configuration, manual_seal: bool) -> Result<TaskManager, ServiceError> {
pub fn new_full(config: Configuration, sealing: Option<Sealing>) -> Result<TaskManager, ServiceError> {
let sc_service::PartialComponents {
client, backend, mut task_manager, import_queue, keystore, select_chain, transaction_pool,
inherent_data_providers,
other: consensus_result
} = new_partial(&config, manual_seal)?;
} = new_partial(&config, sealing)?;

let (network, network_status_sinks, system_rpc_tx, network_starter) = match consensus_result {
ConsensusResult::ManualSeal(_) => {
ConsensusResult::ManualSeal(_, _) => {
sc_service::build_network(sc_service::BuildNetworkParams {
config: &config,
client: client.clone(),
Expand Down Expand Up @@ -213,7 +242,7 @@ pub fn new_full(config: Configuration, manual_seal: bool) -> Result<TaskManager,
})?;

match consensus_result {
ConsensusResult::ManualSeal(block_import) => {
ConsensusResult::ManualSeal(block_import, sealing) => {
if role.is_authority() {
let env = sc_basic_authorship::ProposerFactory::new(
client.clone(),
Expand All @@ -222,21 +251,40 @@ pub fn new_full(config: Configuration, manual_seal: bool) -> Result<TaskManager,
);

// Background authorship future
let authorship_future = manual_seal::run_manual_seal(
manual_seal::ManualSealParams {
block_import,
env,
client: client.clone(),
pool: transaction_pool.pool().clone(),
commands_stream,
select_chain,
consensus_data_provider: None,
inherent_data_providers,
match sealing {
Sealing::Manual => {
let authorship_future = manual_seal::run_manual_seal(
manual_seal::ManualSealParams {
block_import,
env,
client,
pool: transaction_pool.pool().clone(),
commands_stream,
select_chain,
consensus_data_provider: None,
inherent_data_providers,
}
);
// we spawn the future on a background thread managed by service.
task_manager.spawn_essential_handle().spawn_blocking("manual-seal", authorship_future);
},
Sealing::Instant => {
let authorship_future = manual_seal::run_instant_seal(
manual_seal::InstantSealParams {
block_import,
env,
client: client.clone(),
pool: transaction_pool.pool().clone(),
select_chain,
consensus_data_provider: None,
inherent_data_providers,
}
);
// we spawn the future on a background thread managed by service.
task_manager.spawn_essential_handle().spawn_blocking("instant-seal", authorship_future);
}
);
};

// we spawn the future on a background thread managed by service.
task_manager.spawn_essential_handle().spawn_blocking("manual-seal", authorship_future);
}
log::info!("Manual Seal Ready");
},
Expand Down
11 changes: 4 additions & 7 deletions ts-tests/tests/test-block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ describeWithFrontier("Frontier RPC (Block)", `simple-specs.json`, (context) => {
receiptsRoot: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 533,
stateRoot: "0x0000000000000000000000000000000000000000000000000000000000000000",
//timestamp: 1595012243836,
timestamp: 0,
totalDifficulty: null,
//transactions: [],
transactionsRoot: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
Expand Down Expand Up @@ -60,10 +60,7 @@ describeWithFrontier("Frontier RPC (Block)", `simple-specs.json`, (context) => {

step("should have valid timestamp after block production", async function () {
const block = await context.web3.eth.getBlock("latest");
const last5Minutes = (Date.now() / 1000) - 300;
const next5Minutes = (Date.now() / 1000) + 300;
expect(block.timestamp).to.be.least(last5Minutes);
expect(block.timestamp).to.be.below(next5Minutes);
expect(block.timestamp).to.be.eq(6);
});

step("retrieve block information", async function () {
Expand All @@ -83,9 +80,9 @@ describeWithFrontier("Frontier RPC (Block)", `simple-specs.json`, (context) => {
number: 1,
//parentHash: "0x04540257811b46d103d9896e7807040e7de5080e285841c5430d1a81588a0ce4",
receiptsRoot: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 539,
size: 535,
stateRoot: "0x0000000000000000000000000000000000000000000000000000000000000000",
//timestamp: 1595012243836,
timestamp: 6,
totalDifficulty: null,
//transactions: [],
transactionsRoot: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
Expand Down
2 changes: 1 addition & 1 deletion ts-tests/tests/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export async function startFrontierNode(specFilename: string): Promise<{ web3: W
`--execution=Native`, // Faster execution using native
`--no-telemetry`,
`--no-prometheus`,
`--manual-seal`,
`--sealing=Manual`,
`--no-grandpa`,
`--force-authoring`,
`-l${FRONTIER_LOG}`,
Expand Down

0 comments on commit 7b836a2

Please sign in to comment.