Kitchen Node (Instant Seal)

nodes/kitchen-node Try on playground View on GitHub

This recipe demonstrates a general purpose Substrate node that supports most of the recipes' runtimes, and uses Instant Seal consensus.

The kitchen node serves as the first point of entry for most aspiring chefs when they first encounter the recipes. By default it builds with the super-runtime, but it can be used with most of the runtimes in the recipes. Changing the runtime is described below. It features the instant seal consensus which is perfect for testing and iterating on a runtime.

Installing a Runtime

Cargo Dependency

The Cargo.toml file specifies the runtime as a dependency. The file imports the super-runtime, and has dependencies on other runtimes commented out.

# Common runtime configured with most Recipes pallets.
runtime = { package = "super-runtime", path = "../../runtimes/super-runtime" }

# Runtime with custom weight and fee calculation.
# runtime = { package = "weight-fee-runtime", path = "../../runtimes/weight-fee-runtime"}

# Runtime with off-chain worker enabled.
# To use this runtime, compile the node with `ocw` feature enabled,
#   `cargo build --release --features ocw`.
# runtime = { package = "ocw-runtime", path = "../../runtimes/ocw-runtime" }

# Runtime with custom runtime-api (custom API only used in rpc-node)
#runtime = { package = "api-runtime", path = "../../runtimes/api-runtime" }

Installing a different runtime in the node is just a matter of commenting out the super-runtime line, and enabling another one. Try the weight-fee runtime for example. Of course cargo will complain if you try to import two crates under the name runtime.

It is worth noting that this node does not work with all of the recipes' runtimes. In particular, it is not compatible with the babe-grandpa runtime. That runtime uses the babe pallet which requires a node that will include a special PreRuntime DigestItem.

Building a Service with the Runtime

With a runtime of our choosing listed among our dependencies, we can wiring the nodes Service, the part of the node that coordinates communication between all other parts.

We begin by invoking the native_executor_instance! macro. This creates an executor which is responsible for executing transactions in the runtime and determining whether to run the native or Wasm version of the runtime.

native_executor_instance!(
	pub Executor,
	runtime::api::dispatch,
	runtime::native_version,
);

The remainder of the file will create the individual components of the node and connect them together. Most of this code is boilerplate taken from the Substrate Node Template. We will focus specifically on the unique consensus engine used here.

Instant Seal Consensus

The instant seal consensus engine, and its cousin the manual seal consensus engine, are both included in the same sc-consensus-manual-seal crate. Instant seal simply authors a new block whenever a new transaction is available in the queue. This is similar to Truffle Suite's Ganache in the Ethereum ecosystem, but without the UI.

The Cargo Dependencies

Installing the instant seal engine has three dependencies whereas the runtime had only one.

sc-consensus = '0.8.0-rc6'
sc-consensus-manual-seal = '0.8.0-rc6'
sp-consensus = '0.8.0-rc6'

The Import Queue

We begin in new_partial by creating a manual-seal import queue. Both instant seal and manual seal use the same import queue. This process is similar to, but simpler than, the basic-pow import queue.

let import_queue = sc_consensus_manual_seal::import_queue(
	Box::new(client.clone()),
	&task_manager.spawn_handle(),
	config.prometheus_registry(),
);

The Proposer

Now we pick up in the new_full function. All of the code in this portion is executed only if the node is an authority. Create a Proposer which will be responsible for creating proposing blocks in the chain.

let proposer = sc_basic_authorship::ProposerFactory::new(
	service.client().clone(),
	service.transaction_pool(),
);

The Authorship Task

As with every authoring engine, instant seal needs to be run as an async authoring task.

let authorship_future = sc_consensus_manual_seal::run_instant_seal(
	Box::new(client.clone()),
	proposer,
	client,
	transaction_pool.pool().clone(),
	select_chain,
	inherent_data_providers,
);

With the future created, we can now kick it off using the TaskManager's spawn_essential_handle method.

task_manager.spawn_essential_handle().spawn_blocking("instant-seal", authorship_future);

Manual Seal Consensus

The instant seal consensus engine used in this node is built on top of a similar manual seal engine. Manual seal listens for commands to come over the RPC instructing it to author blocks. To see this engine in use, check out the RPC node recipe.