In a substrate runtime, take the node-template as an example, we have two definitions from Block
, among other types such as BlockId
but let's focus on Block
.. :
// runtime/src/lib.rs
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
pub mod opaque {
pub type Block = generic::Block<Header, UncheckedExtrinsic>;
// -- snip --
}
Now, as per my understanding, these opaque::*
types are used in the client side of things as a placeholder type to correctly check trait bounds, for instance, when instantiating an RPC server, you must make sure that your runtime has the correct RuntimeApi
s. For that you need this type information such as Block
as demonstrated in the trait bound below :
// node/src/rpc.rs
fn create_full<_>() where ... // -- snip --
C::Api: BlockBuilder<Block>, // Here, Block is runtime::opaque::Block
{ ... }
and from what I see, this is generally imported as:
use runtime::opaque::Block;
A brief description of opaque types is given just above the opaque
module as :
Opaque types. These are used by the CLI to instantiate machinery that don't need to know
the specifics of the runtime. They can then be made to be agnostic over specific formats
of data like extrinsics, allowing for them to continue syncing the network through upgrades
to even the core data structures.
My question:
Why use runtime::opaque::Block
instead of runtime::Block
? If I understand correctly, opaque::Block
and Block
must be aliases to the same type in the runtime or else we couldn't make sure that our trait bounds for RuntimeApis would be satisfied in the node-client code. In other words, I don't quite understand the rationale behind Opaque types as pasted above.