From 813d99f47d17400d421663807b9e08fca75cbaf2 Mon Sep 17 00:00:00 2001 From: Frank Bell <60948618+evilrobot-01@users.noreply.github.com> Date: Wed, 29 Nov 2023 13:23:16 +0000 Subject: [PATCH] feat: delayed finalization (#214) Adds `--finalize-delay-sec` cli argument, based on the great work by @shunsukew at https://github.com/inkdevhub/swanky-node/pull/61. Manual testing by starting node with/without option, along with example contract upload: ```shell # build example contract cargo contract build --manifest-path=../ink-examples/erc20/Cargo.toml # start node cargo run # upload contract cargo contract upload --suri //Alice --execute --manifest-path=../ink-examples/erc20/Cargo.toml # check finalized head remains at genesis sleep 1 test $(curl -sH "Content-Type: application/json" -d '{"id":"1", "jsonrpc":"2.0", "method": "chainHead_unstable_genesisHash", "params":[]}' http://localhost:9944 | jq .result) \ = $(curl -sH "Content-Type: application/json" -d '{"id":"1", "jsonrpc":"2.0", "method": "chain_getFinalizedHead", "params":[]}' http://localhost:9944 | jq .result) && echo PASS || echo FAIL # start node (with delayed finalization) cargo run -- --finalize-delay-sec 1 # upload contract cargo contract upload --suri //Alice --execute --manifest-path=../ink-examples/erc20/Cargo.toml # check finalized head matches chain head sleep 1 test $(curl -sH "Content-Type: application/json" -d '{"id":"1", "jsonrpc":"2.0", "method": "chain_getHead", "params":[]}' http://localhost:9944 | jq .result) \ = $(curl -sH "Content-Type: application/json" -d '{"id":"1", "jsonrpc":"2.0", "method": "chain_getFinalizedHead", "params":[]}' http://localhost:9944 | jq .result) && echo PASS || echo FAIL ``` Closes #160 --- README.md | 7 +++++++ node/src/cli.rs | 4 ++++ node/src/command.rs | 2 +- node/src/service/dev.rs | 20 +++++++++++++++++--- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 1d9c5c6..6c31e66 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,13 @@ for a production deployment, but a great fit for development and testing:_ [#42](https://github.com/paritytech/substrate-contracts-node/pull/42). Hereby blocks are authored immediately at every transaction, so there is none of the typical six seconds block time associated with `grandpa` or `aura`. + * By default, either manual or instant seal does not result in block finalization unless the `engine_finalizeBlock` + RPC is executed. However, it is possible to configure the finalization of sealed blocks to occur after a certain + amount of time by setting the `--finalize-delay-sec` option to a specific value, which specifies the number of seconds + to delay before finalizing the blocks. The default value is 1 second. + ```shell + ./target/release/substrate-contracts-node --finalize-delay-sec 5 + ``` * _If no CLI arguments are passed the node is started in development mode by default._ * A custom logging filter is applied by default that hides block production noise diff --git a/node/src/cli.rs b/node/src/cli.rs index a4efcf6..522254e 100644 --- a/node/src/cli.rs +++ b/node/src/cli.rs @@ -83,6 +83,10 @@ pub struct Cli { /// Relay chain arguments #[arg(raw = true)] pub relay_chain_args: Vec, + + /// The number of seconds to delay before finalizing blocks. + #[arg(long, default_value_t = 1)] + pub finalize_delay_sec: u8, } #[derive(Debug)] diff --git a/node/src/command.rs b/node/src/command.rs index 9a8a9a5..7be6a9d 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -225,7 +225,7 @@ pub fn run() -> Result<()> { runner.run_node_until_exit(|config| async move { if config.chain_spec.name() == "Development" { // TODO - return service::dev::new_full(config).map_err(sc_cli::Error::Service); + return service::dev::new_full(config, cli.finalize_delay_sec.into()).map_err(sc_cli::Error::Service); } let hwbench = (!cli.no_hardware_benchmarks) diff --git a/node/src/service/dev.rs b/node/src/service/dev.rs index bf0689d..274ac49 100644 --- a/node/src/service/dev.rs +++ b/node/src/service/dev.rs @@ -101,7 +101,10 @@ pub fn new_partial( }) } -pub fn new_full(config: Configuration) -> Result { +pub fn new_full( + config: Configuration, + finalize_delay_sec: u64, +) -> Result { let sc_service::PartialComponents { client, backend, @@ -195,7 +198,7 @@ pub fn new_full(config: Configuration) -> Result { let params = sc_consensus_manual_seal::InstantSealParams { block_import: client.clone(), env: proposer, - client, + client: client.clone(), pool: transaction_pool, select_chain, consensus_data_provider: None, @@ -205,10 +208,21 @@ pub fn new_full(config: Configuration) -> Result { }; let authorship_future = sc_consensus_manual_seal::run_instant_seal(params); - task_manager .spawn_essential_handle() .spawn_blocking("instant-seal", None, authorship_future); + + let delayed_finalize_params = sc_consensus_manual_seal::DelayedFinalizeParams { + client, + spawn_handle: task_manager.spawn_handle(), + delay_sec: finalize_delay_sec, + }; + task_manager.spawn_essential_handle().spawn_blocking( + "delayed_finalize", + None, + sc_consensus_manual_seal::run_delayed_finalize(delayed_finalize_params), + ); + network_starter.start_network(); Ok(task_manager) }