Skip to content

Commit

Permalink
Fix runtime api impl detection by construct runtime (#6665)
Browse files Browse the repository at this point in the history
Construct runtime uses autoref-based specialization to fetch the
metadata about the implemented runtime apis. This is done to not fail to
compile when there are no runtime apis implemented. However, there was
an issue with detecting runtime apis when they were implemented in a
different file. The problem is solved by moving the trait implemented by
`impl_runtime_apis!` to the metadata ir crate.


Closes: #6659

---------

Co-authored-by: GitHub Action <[email protected]>
  • Loading branch information
bkchr and actions-user authored Nov 29, 2024
1 parent 447902e commit 1e89a31
Show file tree
Hide file tree
Showing 9 changed files with 62 additions and 29 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions prdoc/pr_6665.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
title: Fix runtime api impl detection by construct runtime
doc:
- audience: Runtime Dev
description: |-
Construct runtime uses autoref-based specialization to fetch the metadata about the implemented runtime apis. This is done to not fail to compile when there are no runtime apis implemented. However, there was an issue with detecting runtime apis when they were implemented in a different file. The problem is solved by moving the trait implemented by `impl_runtime_apis!` to the metadata ir crate.


Closes: https://github.com/paritytech/polkadot-sdk/issues/6659
crates:
- name: frame-support-procedural
bump: patch
- name: sp-api-proc-macro
bump: patch
- name: sp-metadata-ir
bump: patch
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ pub fn expand_runtime_metadata(
<#extrinsic as #scrate::traits::SignedTransactionBuilder>::Extension
>();

use #scrate::__private::metadata_ir::InternalImplRuntimeApis;

#scrate::__private::metadata_ir::MetadataIR {
pallets: #scrate::__private::vec![ #(#pallets),* ],
extrinsic: #scrate::__private::metadata_ir::ExtrinsicMetadataIR {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,6 @@ fn construct_runtime_final_expansion(
// Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!`
// when both macros are called; and will resolve an empty `runtime_metadata` when only the `construct_runtime!`
// is called.

#[doc(hidden)]
trait InternalConstructRuntime {
#[inline(always)]
Expand All @@ -477,6 +476,8 @@ fn construct_runtime_final_expansion(
#[doc(hidden)]
impl InternalConstructRuntime for &#name {}

use #scrate::__private::metadata_ir::InternalImplRuntimeApis;

#outer_event

#outer_error
Expand Down
49 changes: 27 additions & 22 deletions substrate/frame/support/test/tests/runtime_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,34 +80,39 @@ sp_api::decl_runtime_apis! {
}
}

sp_api::impl_runtime_apis! {
impl self::Api<Block> for Runtime {
fn test(_data: u64) {
unimplemented!()
}
// Module to emulate having the implementation in a different file.
mod apis {
use super::{Block, BlockT, Runtime};

fn something_with_block(_: Block) -> Block {
unimplemented!()
}
sp_api::impl_runtime_apis! {
impl crate::Api<Block> for Runtime {
fn test(_data: u64) {
unimplemented!()
}

fn function_with_two_args(_: u64, _: Block) {
unimplemented!()
}
fn something_with_block(_: Block) -> Block {
unimplemented!()
}

fn same_name() {}
fn function_with_two_args(_: u64, _: Block) {
unimplemented!()
}

fn wild_card(_: u32) {}
}
fn same_name() {}

impl sp_api::Core<Block> for Runtime {
fn version() -> sp_version::RuntimeVersion {
unimplemented!()
}
fn execute_block(_: Block) {
unimplemented!()
fn wild_card(_: u32) {}
}
fn initialize_block(_: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
unimplemented!()

impl sp_api::Core<Block> for Runtime {
fn version() -> sp_version::RuntimeVersion {
unimplemented!()
}
fn execute_block(_: Block) {
unimplemented!()
}
fn initialize_block(_: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
unimplemented!()
}
}
}
}
Expand Down
6 changes: 1 addition & 5 deletions substrate/primitives/api/proc-macro/src/runtime_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -298,18 +298,14 @@ pub fn generate_impl_runtime_metadata(impls: &[ItemImpl]) -> Result<TokenStream2
// Therefore, the `Deref` trait will resolve the `runtime_metadata` from `impl_runtime_apis!`
// when both macros are called; and will resolve an empty `runtime_metadata` when only the
// `construct_runtime!` is called.

Ok(quote!(
#crate_::frame_metadata_enabled! {
#[doc(hidden)]
trait InternalImplRuntimeApis {
#[inline(always)]
impl #crate_::metadata_ir::InternalImplRuntimeApis for #runtime_name {
fn runtime_metadata(&self) -> #crate_::vec::Vec<#crate_::metadata_ir::RuntimeApiMetadataIR> {
#crate_::vec![ #( #metadata, )* ]
}
}
#[doc(hidden)]
impl InternalImplRuntimeApis for #runtime_name {}
}
))
}
3 changes: 2 additions & 1 deletion substrate/primitives/api/test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ sp-version = { workspace = true, default-features = true }
sp-tracing = { workspace = true, default-features = true }
sp-runtime = { workspace = true, default-features = true }
sp-consensus = { workspace = true, default-features = true }
sp-metadata-ir = { workspace = true, default-features = true }
sc-block-builder = { workspace = true, default-features = true }
codec = { workspace = true, default-features = true }
sp-state-machine = { workspace = true, default-features = true }
Expand All @@ -40,5 +41,5 @@ name = "bench"
harness = false

[features]
"enable-staging-api" = []
enable-staging-api = []
disable-ui-tests = []
2 changes: 2 additions & 0 deletions substrate/primitives/api/test/tests/decl_and_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,8 @@ fn mock_runtime_api_works_with_advanced() {

#[test]
fn runtime_api_metadata_matches_version_implemented() {
use sp_metadata_ir::InternalImplRuntimeApis;

let rt = Runtime {};
let runtime_metadata = rt.runtime_metadata();

Expand Down
10 changes: 10 additions & 0 deletions substrate/primitives/metadata-ir/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,16 @@ pub fn into_unstable(metadata: MetadataIR) -> RuntimeMetadataPrefixed {
latest.into()
}

/// INTERNAL USE ONLY
///
/// Special trait that is used together with `InternalConstructRuntime` by `construct_runtime!` to
/// fetch the runtime api metadata without exploding when there is no runtime api implementation
/// available.
#[doc(hidden)]
pub trait InternalImplRuntimeApis {
fn runtime_metadata(&self) -> alloc::vec::Vec<RuntimeApiMetadataIR>;
}

#[cfg(test)]
mod test {
use super::*;
Expand Down

0 comments on commit 1e89a31

Please sign in to comment.