Let’s distill the construct_runtime! macro
The construct_runtime!
macro is an essential part of building a Substrate
runtime. This macro is responsible for tying together the various pallets that
compose a runtime, making them interact with each other, and exposing their APIs
to the outside world (like RPC, extrinsics, etc.). It's like the glue that
binds your blockchain together.
In this article, we’ll take a look at what happens when this macro is not provided with sufficient information required for your runtime compile, as well as in-depth information about this macro and how to configure it to make sure your runtime works as intended.
To help us measure our progress and improve Substrate in Bits content, please fill out our living feedback form. It will only take 2 minutes of your time. Thank you!
Reproducing errors
Environment and project set up
To follow along with this tutorial, ensure that you have the Rust toolchain installed.
- Visit the substrate official documentation page for the installation processes.
- Clone the project repository.
git clone https://github.com/abdbee/construct-runtime.git
- Navigate into the project’s directory.
cd construct-runtime
- Run the command below to compile the node.
cargo build --release
While attempting to compile the node above, you’ll encounter an error similar to the one below:
error[E0277]: the trait bound `RuntimeEvent: From<pallet_identity::Event<Runtime>>` is not satisfied
--> /workspace/NextSimpleStarter/construct_runtime/runtime/src/lib.rs:266:22
|
266 | type RuntimeEvent = RuntimeEvent;
| ^^^^^^^^^^^^ the trait `From<pallet_identity::Event<Runtime>>` is not implemented for `RuntimeEvent`
|
= help: the following other types implement trait `From<T>`:
<RuntimeEvent as From<frame_system::Event<Runtime>>>
<RuntimeEvent as From<pallet_balances::Event<Runtime>>>
<RuntimeEvent as From<pallet_grandpa::Event>>
<RuntimeEvent as From<pallet_sudo::Event<Runtime>>>
<RuntimeEvent as From<pallet_template::Event<Runtime>>>
<RuntimeEvent as From<pallet_transaction_payment::Event<Runtime>>>
note: required by a bound in `pallet_identity::Config::RuntimeEvent`
--> /workspace/.cargo/git/checkouts/substrate-7e08433d4c370a21/98f2e34/frame/identity/src/lib.rs:108:22
|
108 | type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;
| ^^^^^^^^^^^^^^^^^ required by this bound in `Config::RuntimeEvent`
For more information about this error, try `rustc --explain E0277`.
error: could not compile `node-template-runtime` (lib) due to previous error
This error is saying that the From<pallet_identity::Event<Runtime>>
is not
implemented for RuntimeEvent
. It's telling you that the RuntimeEvent
type
is expected to implement the From<pallet_identity::Event<Runtime>>
trait,
but it doesn't.
The construct_runtime!
macro in Substrate creates an overarching enum type,
RuntimeEvent
, which represents all possible events that can be emitted by
pallets included in your runtime. When you include a pallet, for example,
pallet_identity
, in the construct_runtime!
macro, the macro automatically
adds a variant to this RuntimeEvent
enum for the pallet's events. The
RuntimeEvent
enum then implements the From
trait of the specific pallet's
event type (in this case, From<pallet_identity::Event<Runtime>>
) This trait
implementation enables automatic conversion (or "upcasting") from a
pallet-specific event type (pallet_identity::Event<Runtime>
) to the generic
RuntimeEvent
type. This allows any pallet event to be treated as a generic
event in the runtime.
But since pallet_identity
is not included in the construct_runtime!
macro,
the macro does not include the pallet_identity::Event<Runtime>
variant in
the Event
enum. Consequently, the From<pallet_identity::Event<Runtime>>
trait is not implemented for the RuntimeEvent
enum.
However, the implementation of pallet_identity::Config
for your runtime
expects that the RuntimeEvent
type implements the
From<pallet_identity::Event<Runtime>>
trait. Since this is not the case,
the compiler throws an error.
Solving the error
-
Go to the lib.rs file of your node’s runtime.
-
Add
pallet_identity
to theconstruct_runtime!
macro.
construct_runtime!(
pub struct Runtime where
Block = Block,
NodeBlock = opaque::Block,
UncheckedExtrinsic = UncheckedExtrinsic,
{
// ----- *snip* ------
Identity: pallet_identity, //<------ add this line
}
);
Including pallet_identity
in the construct_runtime macro does a couple of things.
-
It adds it as a variant in the
RuntimeEvents
enum -
It implements
From<pallet_identity::Event<Runtime>>
for theRuntimeEvents
type, which satisfies the trait requirements forpallet_identity::Config::RuntimeEvents
-
Re-compile the node
cargo build --release
Going in-depth
The construct_runtime!
macro in Substrate is a declarative macro that enables
you to compose your blockchain runtime by specifying a selection of individual
pallets. The macro generates boilerplate code to glue these pallets together
into a cohesive whole.
Each pallet declaration includes:
- The unique
Identifier
that represents the pallet. - The
path::to::pallet
, which specifies where the pallet's definition can be found. - An optional
::<InstanceN>
part if the pallet supports multiple instances. - A comma-separated list of parts enclosed in
{}
. These parts, likePallet
,Call
,Storage
,Event<T>
,Origin<T>
,Config<T>
, etc., represent different components of the pallet. They are used to export the pallet's functionalities correctly in the runtime and its metadata.
YourPallet: pallet_your_pallet::{Pallet, Call, Storage, Event<T>, Config<T>},
In this example, YourPallet
is the unique identifier for the pallet, and
pallet_your_pallet
is the path to the pallet's definition. The parts within {}
represent different components of the pallet that you want to include in the
runtime, such as the pallet's main logic (Pallet
), extrinsic calls (Call
),
storage (Storage
), events (Event<T>
), and configuration (Config<T>
).
You can also assign an index to each pallet using = $n
, which is used to
encode the pallet's variants in types like OriginCaller
, Call
, and Event
.
This is particularly useful for larger-scale projects, where multiple pallets
may be interacting, as it helps trace the flow of events and calls. It also
allows for better error tracing and debugging, as it's immediately clear from
the encoded data which pallet an event or call is associated with.
The macro is also responsible for associating your runtime’s concrete type
implementations with the pallet’s types. For example, if a pallet requires a
Currency
trait with an associated type Balance
, and you've implemented
Currency
for your runtime with Balance
as u32
, the construct_runtime!
macro ensures that Currency::Balance
in that pallet is replaced with u32
.
For each included pallet, the construct_runtime!
macro generates a type alias
mapping the pallet to the runtime. This allows the pallet's functionalities to
be accessed under the given identifier in the runtime and makes it easier to
differentiate between multiple instances of the same pallet. For example, an
alias for one instance of the collective
pallet might be “Council” and
another alias could be “Technical Committee”. These aliases ensure that you
can easily identify and interact with the intended pallet.
The construct_runtime!
macro also handles events emitted by the pallets. It
creates a RuntimeEvent
enum type that includes variants for each pallet's
events. RuntimeEvent
implements the From
trait of the respective pallet's
event types, allowing the events to be converted into the overarching
RuntimeEvent
type.
This enables a unified way of handling events from different pallets. However,
if you remove a pallet from the construct_runtime!
macro, the associated
From
trait implementation is also removed. Consequently, the
construct_runtime!
macro will no longer generate the variant corresponding to
that pallet’s event type. This would mean that events from that pallet are no
longer acknowledged by the runtime's RuntimeEvent
enum. More crucially, the
macro will not generate the From
trait implementation for the pallet’s event
type on the RuntimeEvent
enum. This implementation is crucial as it allows
events coming from the pallet to be transformed into the generic RuntimeEvent
type.
As a result, if there is still code in your runtime that deals with such a
pallet and its events directly, it will fail to compile because
RuntimeEvents
no longer acknowledges the event type of that pallet.
Summary
In this Substrate in Bits content, we delved into the construct_runtime!
macro, a crucial component in building a Substrate runtime. This macro
facilitates the assembly of various pallets that constitute a runtime,
orchestrates their interaction, and exposes their APIs for external use,
thereby serving as the “adhesive” that binds a runtime together.
The article extensively explained what transpires when this macro lacks
sufficient information for runtime compilation. It also provides an in-depth
view of the macro's configuration and its role in ensuring smooth operation of
your runtime with a step-by-step tutorial to reproduce and resolve a common
error encountered when a pallet's event variant is missing in the
construct_runtime!
macro, using the pallet_identity
as an example.
The construct_runtime!
macro's function is broken down in detail, from
pallet declaration, which includes elements like the unique Identifier, path
to the pallet, optional instances, and different components, to assigning an
index to each pallet for ease of debugging and tracing events. We also
discussed how the macro associates the runtime's concrete type implementations
with the pallet's types and handles events emitted by the pallets by generating
a RuntimeEvent
enum type. If a pallet is removed from the
construct_runtime!
macro, the associated From
trait implementation is also
omitted, leading to potential compilation errors.
Thus, understanding the construct_runtime!
macro is vital for developers
working with the Substrate framework, as it brings the various moving parts of
the blockchain together.
To learn more about the concepts discussed in this guide, here are some resources that we recommend:
We’re inviting you to fill out our living feedback form to help us measure our progress and improve Substrate in Bits content. It will only take 2 minutes of your time. Thank you!