Hello readers! In this blog post, we’ll look at what the Runtime API is and how we created and used it.

Each underlying node contains a runtime. The runtime contains the chain’s business logic. It defines which transactions are valid and which are not, and determines how the state of the chain changes in response to the transaction. To use the run-time upgrade, the run-time is compiled to Wasm. Everything except the runtime is called an “external node”, and an external node is not compiled into Wasm, it is compiled locally.

The outer node handles peer discovery, transaction pools, blocks and transaction gossip, consensus, and answering RPC calls from the outside world. In performing these tasks, external nodes sometimes need to query or provide information to the runtime. The Runtime API facilitates this communication between external nodes and the Runtime. In this blog post, we will write our own minimal runtime API.

Our example

In this example, we will write a tray named sum-storage with two storage entries, both U32s.

#! [allow(unused)] fn main() { decl_storage! { trait Store for Module<T: Config> as TemplateModule { Thing1 get(fn thing1): Option<u32>; Thing2 get(fn thing2): Option<u32>; }}}Copy the code

Substrate already provides a runtime API for querying stored values, which is why we can easily query our two stored values from the front end. Our runtime API will provide a way for external nodes to query the runtime of this and. Before we define the actual runtime API, let’s write a common helper function in the tray to do the summation.

#! [allow(unused)] fn main() { impl<T: Config> Module<T> { pub fn get_sum() -> u32 { Thing1::get() + Thing2::get() } } }Copy the code

Nothing we’ve done so far is specific to the runtime API. In the following sections, we will use this helper function in the implementation of the runtime API.

Define the API

The first step in adding a runtime API to your runtime is to define its interface using the Rust trait. This is done in the sum-storage/ Runtime-api/SRC /lib.rs file. This file can be placed anywhere you like, but because it defines an API that is closely related to a particular tray, it makes sense to put the API definitions in the tray’s directory.

The code that defines the API is so simple that it looks almost like any old Rust feature. An extra point is that it must be placed on decl_runtime_apis! This macro. This macro allows external nodes to query the runtime API on specific blocks. Although the runtime API provides only one function, you can write it as you like.

#! [allow(unused)] fn main() { sp_api::decl_runtime_apis! { pub trait SumStorageApi { fn get_sum() -> u32; }}}Copy the code

Implement the API

With our tray written and the runtime API defined, we can now implement the API for our runtime. This happens in the main runtime aggregation file. In our example, we have provided apI-Runtime in runtimes/api-runtime/ SRC /lib.rs.

As with defining the API, implementing a runtime API looks similar to implementing any old Rust feature, with the exception that the implementation must be implemented in IMPL_RUNtime_apis! This macro. Every runtime must use iml_RUNTIME_apis! Because the Core API is required. We’ll add an implementation for our own API, along with the other apis in this macro. Our implementation is simple because it simply calls the helper function of the tray we wrote earlier.

#! [allow(unused)] fn main() { impl_runtime_apis! { // --snip-- impl sum_storage_rpc_runtime_api::SumStorageApi<Block> for Runtime { fn get_sum() -> u32 { SumStorage::get_sum() } } } }Copy the code

You might want to know the Block type parameter, which appears here, but is not in our definition. All runtime apis have this type parameter to make it easy to query the runtime on any block. Read more documentation on this issue impl_RUNtime_apis! .

Call the runtime API

We have now successfully added a runtime API to our runtime. External nodes can now call this API to query the sum of the two stored values at run time. Given a reference to “client”, we can call it like this.

#! [allow(unused)] fn main() { let sum_at_block_fifty = client.runtime_api().get_sum(&50); }Copy the code

This blog post is about defining and implementing a custom runtime API. Stay in touch to explore this exciting topic! !