feat: initialize Kurdistan SDK - independent fork of Polkadot SDK

This commit is contained in:
2025-12-13 15:44:15 +03:00
commit e4778b4576
6838 changed files with 1847450 additions and 0 deletions
+76
View File
@@ -0,0 +1,76 @@
[package]
name = "sp-api"
version = "26.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Substrate runtime api primitives"
readme = "README.md"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { workspace = true }
docify = { workspace = true }
hash-db = { optional = true, workspace = true, default-features = true }
log = { workspace = true }
scale-info = { features = ["derive"], workspace = true }
sp-api-proc-macro = { workspace = true }
sp-core = { workspace = true }
sp-externalities = { optional = true, workspace = true }
sp-metadata-ir = { optional = true, workspace = true }
sp-runtime = { workspace = true }
sp-runtime-interface = { workspace = true }
sp-state-machine = { optional = true, workspace = true }
sp-trie = { optional = true, workspace = true }
sp-version = { workspace = true }
thiserror = { optional = true, workspace = true }
[dev-dependencies]
sp-test-primitives = { workspace = true }
[features]
default = ["std"]
std = [
"codec/std",
"hash-db",
"log/std",
"scale-info/std",
"sp-api-proc-macro/std",
"sp-core/std",
"sp-externalities",
"sp-externalities?/std",
"sp-metadata-ir?/std",
"sp-runtime-interface/std",
"sp-runtime/std",
"sp-state-machine/std",
"sp-test-primitives/std",
"sp-trie/std",
"sp-version/std",
"thiserror",
]
# Special feature to disable logging completely.
#
# By default `sp-api` initializes the `RuntimeLogger` for each runtime api function. However,
# logging functionality increases the code size. It is recommended to enable this feature when
# building a runtime for registering it on chain.
#
# This sets the max logging level to `off` for `log`.
disable-logging = ["log/max_level_off"]
# Do not report the documentation in the metadata.
no-metadata-docs = ["sp-api-proc-macro/no-metadata-docs"]
frame-metadata = ["sp-metadata-ir"]
runtime-benchmarks = [
"sp-runtime-interface/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"sp-state-machine?/runtime-benchmarks",
"sp-test-primitives/runtime-benchmarks",
"sp-trie?/runtime-benchmarks",
"sp-version/runtime-benchmarks",
]
+17
View File
@@ -0,0 +1,17 @@
Substrate runtime api
The Substrate runtime api is the crucial interface between the node and the runtime.
Every call that goes into the runtime is done with a runtime api. The runtime apis are not fixed.
Every Substrate user can define its own apis with
[`decl_runtime_apis`](https://docs.rs/sp-api/latest/sp_api/macro.decl_runtime_apis.html) and implement them in
the runtime with [`impl_runtime_apis`](https://docs.rs/sp-api/latest/sp_api/macro.impl_runtime_apis.html).
Every Substrate runtime needs to implement the [`Core`] runtime api. This api provides the basic
functionality that every runtime needs to export.
Besides the macros and the [`Core`] runtime api, this crates provides the [`Metadata`] runtime
api, the [`ApiExt`] trait, the [`CallApiAt`] trait and the [`ConstructRuntimeApi`] trait.
On a meta level this implies, the client calls the generated API from the client perspective.
License: Apache-2.0
@@ -0,0 +1,43 @@
[package]
name = "sp-api-proc-macro"
version = "15.0.0"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
homepage.workspace = true
repository.workspace = true
description = "Macros for declaring and implementing runtime apis."
documentation = "https://docs.rs/sp-api-proc-macro"
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[lib]
proc-macro = true
[dependencies]
Inflector = { workspace = true }
blake2 = { workspace = true }
expander = { workspace = true }
proc-macro-crate = { workspace = true }
proc-macro2 = { workspace = true }
quote = { workspace = true }
syn = { features = [
"extra-traits",
"fold",
"full",
"visit",
"visit-mut",
], workspace = true }
[dev-dependencies]
assert_matches = { workspace = true }
[features]
# Required for the doc tests
default = ["std"]
std = ["blake2/std"]
no-metadata-docs = []
@@ -0,0 +1,39 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// The ident used for the block generic parameter.
pub const BLOCK_GENERIC_IDENT: &str = "Block";
/// The `core_trait` attribute.
pub const CORE_TRAIT_ATTRIBUTE: &str = "core_trait";
/// The `api_version` attribute.
///
/// Is used to set the current version of the trait.
pub const API_VERSION_ATTRIBUTE: &str = "api_version";
/// The `changed_in` attribute.
///
/// Is used when the function signature changed between different versions of a trait.
/// This attribute should be placed on the old signature of the function.
pub const CHANGED_IN_ATTRIBUTE: &str = "changed_in";
/// The `renamed` attribute.
///
/// Is used when a trait method was renamed.
pub const RENAMED_ATTRIBUTE: &str = "renamed";
/// All attributes that we support in the declaration of a runtime api trait.
pub const SUPPORTED_ATTRIBUTE_NAMES: &[&str] =
&[CORE_TRAIT_ATTRIBUTE, API_VERSION_ATTRIBUTE, CHANGED_IN_ATTRIBUTE, RENAMED_ATTRIBUTE];
@@ -0,0 +1,751 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::{
common::{
API_VERSION_ATTRIBUTE, BLOCK_GENERIC_IDENT, CHANGED_IN_ATTRIBUTE, CORE_TRAIT_ATTRIBUTE,
RENAMED_ATTRIBUTE, SUPPORTED_ATTRIBUTE_NAMES,
},
utils::{
extract_parameter_names_types_and_borrows, fold_fn_decl_for_client_side,
generate_crate_access, generate_runtime_mod_name_for_trait, parse_runtime_api_version,
prefix_function_with_trait, replace_wild_card_parameter_names, return_type_extract_type,
versioned_trait_name, AllowSelfRefInParameters,
},
};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use std::collections::{BTreeMap, HashMap};
use syn::{
fold::{self, Fold},
parse::{Error, Parse, ParseStream, Result},
parse_macro_input, parse_quote,
spanned::Spanned,
token::Comma,
visit::{self, Visit},
Attribute, FnArg, GenericParam, Generics, Ident, ItemTrait, LitInt, LitStr, TraitBound,
TraitItem, TraitItemFn,
};
/// The structure used for parsing the runtime api declarations.
struct RuntimeApiDecls {
decls: Vec<ItemTrait>,
}
impl Parse for RuntimeApiDecls {
fn parse(input: ParseStream) -> Result<Self> {
let mut decls = Vec::new();
while !input.is_empty() {
decls.push(ItemTrait::parse(input)?);
}
Ok(Self { decls })
}
}
/// Extend the given generics with `Block: BlockT` as first generic parameter.
fn extend_generics_with_block(generics: &mut Generics) {
let c = generate_crate_access();
generics.lt_token = Some(Default::default());
generics.params.insert(0, parse_quote!( Block: #c::BlockT ));
generics.gt_token = Some(Default::default());
}
/// Remove all attributes from the vector that are supported by us in the declaration of a runtime
/// api trait. The returned hashmap contains all found attribute names as keys and the rest of the
/// attribute body as `TokenStream`.
fn remove_supported_attributes(attrs: &mut Vec<Attribute>) -> HashMap<&'static str, Attribute> {
let mut result = HashMap::new();
attrs.retain(|v| match SUPPORTED_ATTRIBUTE_NAMES.iter().find(|a| v.path().is_ident(a)) {
Some(attribute) => {
result.insert(*attribute, v.clone());
false
},
None => true,
});
result
}
/// Versioned API traits are used to catch missing methods when implementing a specific version of a
/// versioned API. They contain all non-versioned methods (aka stable methods) from the main trait
/// and all versioned methods for the specific version. This means that there is one trait for each
/// version mentioned in the trait definition. For example:
/// ```ignore
/// // The trait version implicitly is 1
/// decl_runtime_apis!(
/// trait SomeApi {
/// fn method1(); // this is a 'stable method'
///
/// #[api_version(2)]
/// fn method2();
///
/// #[api_version(2)]
/// fn method3();
///
/// #[api_version(3)]
/// fn method4();
/// }
/// );
/// ```
/// This trait has got three different versions. The function below will generate the following
/// code:
/// ```
/// trait SomeApiV1 {
/// // in V1 only the stable methods are required. The rest has got default implementations.
/// fn method1();
/// }
///
/// trait SomeApiV2 {
/// // V2 contains all methods from V1 and V2. V3 not required so they are skipped.
/// fn method1();
/// fn method2();
/// fn method3();
/// }
///
/// trait SomeApiV3 {
/// // And V3 contains all methods from the trait.
/// fn method1();
/// fn method2();
/// fn method3();
/// fn method4();
/// }
/// ```
fn generate_versioned_api_traits(
api: ItemTrait,
methods: BTreeMap<u32, Vec<TraitItemFn>>,
) -> Vec<ItemTrait> {
let mut result = Vec::<ItemTrait>::new();
for (version, _) in &methods {
let mut versioned_trait = api.clone();
versioned_trait.ident = versioned_trait_name(&versioned_trait.ident, *version);
versioned_trait.items = Vec::new();
// Add the methods from the current version and all previous one. Versions are sorted so
// it's safe to stop early.
for (_, m) in methods.iter().take_while(|(v, _)| v <= &version) {
versioned_trait.items.extend(m.iter().cloned().map(|m| TraitItem::Fn(m)));
}
result.push(versioned_trait);
}
result
}
/// Try to parse the given `Attribute` as `renamed` attribute.
fn parse_renamed_attribute(renamed: &Attribute) -> Result<(String, u32)> {
let err = || {
Error::new(
renamed.span(),
&format!(
"Unexpected `{RENAMED_ATTRIBUTE}` attribute. \
The supported format is `{RENAMED_ATTRIBUTE}(\"old_name\", version_it_was_renamed)`",
),
)
};
renamed
.parse_args_with(|input: ParseStream| {
let old_name: LitStr = input.parse()?;
let _comma: Comma = input.parse()?;
let version: LitInt = input.parse()?;
if !input.is_empty() {
return Err(input.error("No more arguments expected"));
}
Ok((old_name.value(), version.base10_parse()?))
})
.map_err(|_| err())
}
/// Generate the declaration of the trait for the runtime.
fn generate_runtime_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
let mut result = Vec::new();
for decl in decls {
let mut decl = decl.clone();
let decl_span = decl.span();
extend_generics_with_block(&mut decl.generics);
let mod_name = generate_runtime_mod_name_for_trait(&decl.ident);
let found_attributes = remove_supported_attributes(&mut decl.attrs);
let api_version = get_api_version(&found_attributes).map(generate_runtime_api_version)?;
let id = generate_runtime_api_id(&decl.ident.to_string());
let trait_api_version = get_api_version(&found_attributes)?;
let mut methods_by_version: BTreeMap<u32, Vec<TraitItemFn>> = BTreeMap::new();
// Process the items in the declaration. The filter_map function below does a lot of stuff
// because the method attributes are stripped at this point
decl.items.iter_mut().for_each(|i| {
match i {
TraitItem::Fn(ref mut method) => {
let method_attrs = remove_supported_attributes(&mut method.attrs);
let mut method_version = trait_api_version;
// validate the api version for the method (if any) and generate default
// implementation for versioned methods
if let Some(version_attribute) = method_attrs.get(API_VERSION_ATTRIBUTE) {
method_version = match parse_runtime_api_version(version_attribute) {
Ok(method_api_ver) if method_api_ver < trait_api_version => {
let method_ver = method_api_ver.to_string();
let trait_ver = trait_api_version.to_string();
let mut err1 = Error::new(
version_attribute.span(),
format!(
"Method version `{}` is older than (or equal to) trait version `{}`.\
Methods can't define versions older than the trait version.",
method_ver,
trait_ver,
),
);
let err2 = match found_attributes.get(&API_VERSION_ATTRIBUTE) {
Some(attr) =>
Error::new(attr.span(), "Trait version is set here."),
None => Error::new(
decl_span,
"Trait version is not set so it is implicitly equal to 1.",
),
};
err1.combine(err2);
result.push(err1.to_compile_error());
trait_api_version
},
Ok(method_api_ver) => method_api_ver,
Err(e) => {
result.push(e.to_compile_error());
trait_api_version
},
};
}
// Any method with the `changed_in` attribute isn't required for the runtime
// anymore.
if !method_attrs.contains_key(CHANGED_IN_ATTRIBUTE) {
// Make sure we replace all the wild card parameter names.
replace_wild_card_parameter_names(&mut method.sig);
// partition methods by api version
methods_by_version.entry(method_version).or_default().push(method.clone());
}
},
_ => (),
}
});
let versioned_methods_iter = methods_by_version
.iter()
.flat_map(|(&version, methods)| methods.iter().map(move |method| (method, version)));
let metadata =
crate::runtime_metadata::generate_decl_runtime_metadata(&decl, versioned_methods_iter);
let versioned_api_traits = generate_versioned_api_traits(decl.clone(), methods_by_version);
let main_api_ident = decl.ident.clone();
let versioned_ident = &versioned_api_traits
.first()
.expect("There should always be at least one version.")
.ident;
result.push(quote!(
#[doc(hidden)]
#[allow(dead_code)]
#[allow(deprecated)]
pub mod #mod_name {
pub use super::*;
#( #versioned_api_traits )*
pub use #versioned_ident as #main_api_ident;
#metadata
pub #api_version
pub #id
}
));
}
Ok(quote!( #( #result )* ))
}
/// Modify the given runtime api declaration to be usable on the client side.
struct ToClientSideDecl<'a> {
block_hash: &'a TokenStream,
crate_: &'a TokenStream,
found_attributes: &'a mut HashMap<&'static str, Attribute>,
/// Any error that we found while converting this declaration.
errors: &'a mut Vec<TokenStream>,
trait_: &'a Ident,
}
impl<'a> ToClientSideDecl<'a> {
/// Process the given [`ItemTrait`].
fn process(mut self, decl: ItemTrait) -> ItemTrait {
let mut decl = self.fold_item_trait(decl);
let block_hash = self.block_hash;
let crate_ = self.crate_;
// Add the special method that will be implemented by the `impl_runtime_apis!` macro
// to enable functions to call into the runtime.
decl.items.push(parse_quote! {
/// !!INTERNAL USE ONLY!!
#[doc(hidden)]
fn __runtime_api_internal_call_api_at(
&self,
at: #block_hash,
params: std::vec::Vec<u8>,
fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError>;
});
decl
}
}
impl<'a> ToClientSideDecl<'a> {
fn fold_item_trait_items(
&mut self,
items: Vec<TraitItem>,
trait_generics_num: usize,
) -> Vec<TraitItem> {
let mut result = Vec::new();
items.into_iter().for_each(|i| match i {
TraitItem::Fn(method) => {
let fn_decl = self.create_method_decl(method, trait_generics_num);
result.push(fn_decl.into());
},
r => result.push(r),
});
result
}
/// Takes the method declared by the user and creates the declaration we require for the runtime
/// api client side. This method will call by default the `method_runtime_api_impl` for doing
/// the actual call into the runtime.
fn create_method_decl(
&mut self,
mut method: TraitItemFn,
trait_generics_num: usize,
) -> TraitItemFn {
let params = match extract_parameter_names_types_and_borrows(
&method.sig,
AllowSelfRefInParameters::No,
) {
Ok(res) => res.into_iter().map(|v| v.0).collect::<Vec<_>>(),
Err(e) => {
self.errors.push(e.to_compile_error());
Vec::new()
},
};
let ret_type = return_type_extract_type(&method.sig.output);
fold_fn_decl_for_client_side(&mut method.sig, self.block_hash, self.crate_);
let crate_ = self.crate_;
let found_attributes = remove_supported_attributes(&mut method.attrs);
// Parse the renamed attributes.
let mut renames = Vec::new();
for (_, a) in found_attributes.iter().filter(|a| a.0 == &RENAMED_ATTRIBUTE) {
match parse_renamed_attribute(a) {
Ok((old_name, version)) => {
renames.push((version, prefix_function_with_trait(&self.trait_, &old_name)));
},
Err(e) => self.errors.push(e.to_compile_error()),
}
}
renames.sort_by(|l, r| r.cmp(l));
let (versions, old_names) = renames.into_iter().fold(
(Vec::new(), Vec::new()),
|(mut versions, mut old_names), (version, old_name)| {
versions.push(version);
old_names.push(old_name);
(versions, old_names)
},
);
// Generate the function name before we may rename it below to
// `function_name_before_version_{}`.
let function_name = prefix_function_with_trait(&self.trait_, &method.sig.ident);
// If the method has a `changed_in` attribute, we need to alter the method name to
// `method_before_version_VERSION`.
match get_changed_in(&found_attributes) {
Ok(Some(version)) => {
// Make sure that the `changed_in` version is at least the current `api_version`.
if get_api_version(self.found_attributes).ok() < Some(version) {
self.errors.push(
Error::new(
method.span(),
"`changed_in` version can not be greater than the `api_version`",
)
.to_compile_error(),
);
}
let ident = Ident::new(
&format!("{}_before_version_{}", method.sig.ident, version),
method.sig.ident.span(),
);
method.sig.ident = ident;
method.attrs.push(parse_quote!( #[deprecated] ));
},
Ok(None) => {},
Err(e) => {
self.errors.push(e.to_compile_error());
},
};
// The module where the runtime relevant stuff is declared.
let trait_name = &self.trait_;
let runtime_mod = generate_runtime_mod_name_for_trait(trait_name);
let underscores = (0..trait_generics_num).map(|_| quote!(_));
// Generate the default implementation that calls the `method_runtime_api_impl` method.
method.default = Some(parse_quote! {
{
let __runtime_api_impl_params_encoded__ =
#crate_::Encode::encode(&( #( &#params ),* ));
<Self as #trait_name<#( #underscores ),*>>::__runtime_api_internal_call_api_at(
self,
__runtime_api_at_param__,
__runtime_api_impl_params_encoded__,
&|_version| {
#(
// Check if we need to call the function by an old name.
if _version.apis.iter().any(|(s, v)| {
s == &#runtime_mod::ID && *v < #versions
}) {
return #old_names
}
)*
#function_name
}
)
.and_then(|r|
std::result::Result::map_err(
<#ret_type as #crate_::Decode>::decode(&mut &r[..]),
|err| #crate_::ApiError::FailedToDecodeReturnValue {
function: #function_name,
error: err,
raw: r.clone(),
}
)
)
}
});
method
}
}
impl<'a> Fold for ToClientSideDecl<'a> {
fn fold_item_trait(&mut self, mut input: ItemTrait) -> ItemTrait {
extend_generics_with_block(&mut input.generics);
*self.found_attributes = remove_supported_attributes(&mut input.attrs);
// Check if this is the `Core` runtime api trait.
let is_core_trait = self.found_attributes.contains_key(CORE_TRAIT_ATTRIBUTE);
let block_ident = Ident::new(BLOCK_GENERIC_IDENT, Span::call_site());
if is_core_trait {
// Add all the supertraits we want to have for `Core`.
input.supertraits = parse_quote!('static + Send);
} else {
// Add the `Core` runtime api as super trait.
let crate_ = &self.crate_;
input.supertraits.push(parse_quote!( #crate_::Core<#block_ident> ));
}
input.items = self.fold_item_trait_items(input.items, input.generics.params.len());
fold::fold_item_trait(self, input)
}
}
/// Generates the identifier as const variable for the given `trait_name`
/// by hashing the `trait_name`.
fn generate_runtime_api_id(trait_name: &str) -> TokenStream {
use blake2::digest::{consts::U8, Digest};
let mut res = [0; 8];
res.copy_from_slice(blake2::Blake2b::<U8>::digest(trait_name).as_slice());
quote!( const ID: [u8; 8] = [ #( #res ),* ]; )
}
/// Generates the const variable that holds the runtime api version.
fn generate_runtime_api_version(version: u32) -> TokenStream {
quote!( const VERSION: u32 = #version; )
}
/// Generates the implementation of `RuntimeApiInfo` for the given trait.
fn generate_runtime_info_impl(trait_: &ItemTrait, version: u32) -> TokenStream {
let trait_name = &trait_.ident;
let crate_ = generate_crate_access();
let id = generate_runtime_api_id(&trait_name.to_string());
let version = generate_runtime_api_version(version as u32);
let impl_generics = trait_.generics.type_params().map(|t| {
let ident = &t.ident;
let colon_token = &t.colon_token;
let bounds = &t.bounds;
quote! { #ident #colon_token #bounds }
});
let ty_generics = trait_.generics.type_params().map(|t| {
let ident = &t.ident;
quote! { #ident }
});
let maybe_allow_attrs = trait_.attrs.iter().filter(|attr| attr.path().is_ident("allow"));
let maybe_allow_deprecated = trait_.attrs.iter().filter_map(|attr| {
attr.path().is_ident("deprecated").then(|| quote! { #[allow(deprecated)] })
});
quote!(
#crate_::std_enabled! {
#( #maybe_allow_attrs )*
#( #maybe_allow_deprecated )*
impl < #( #impl_generics, )* > #crate_::RuntimeApiInfo
for dyn #trait_name < #( #ty_generics, )* >
{
#id
#version
}
}
)
}
/// Get changed in version from the user given attribute or `Ok(None)`, if no attribute was given.
fn get_changed_in(found_attributes: &HashMap<&'static str, Attribute>) -> Result<Option<u32>> {
found_attributes
.get(&CHANGED_IN_ATTRIBUTE)
.map(|v| parse_runtime_api_version(v).map(Some))
.unwrap_or(Ok(None))
}
/// Get the api version from the user given attribute or `Ok(1)`, if no attribute was given.
fn get_api_version(found_attributes: &HashMap<&'static str, Attribute>) -> Result<u32> {
found_attributes
.get(&API_VERSION_ATTRIBUTE)
.map(parse_runtime_api_version)
.unwrap_or(Ok(1))
}
/// Generate the declaration of the trait for the client side.
fn generate_client_side_decls(decls: &[ItemTrait]) -> Result<TokenStream> {
let mut result = Vec::new();
for decl in decls {
let decl = decl.clone();
let crate_ = generate_crate_access();
let block_hash = quote!( <Block as #crate_::BlockT>::Hash );
let mut found_attributes = HashMap::new();
let mut errors = Vec::new();
let trait_ = decl.ident.clone();
let decl = ToClientSideDecl {
crate_: &crate_,
block_hash: &block_hash,
found_attributes: &mut found_attributes,
errors: &mut errors,
trait_: &trait_,
}
.process(decl);
let api_version = get_api_version(&found_attributes);
let runtime_info = api_version.map(|v| generate_runtime_info_impl(&decl, v))?;
result.push(quote!(
#crate_::std_enabled! {
#[allow(deprecated)]
#decl
}
#runtime_info
#( #errors )*
));
}
Ok(quote!( #( #result )* ))
}
/// Checks that a trait declaration is in the format we expect.
struct CheckTraitDecl {
errors: Vec<Error>,
}
impl CheckTraitDecl {
/// Check the given trait.
///
/// All errors will be collected in `self.errors`.
fn check(&mut self, trait_: &ItemTrait) {
self.check_method_declarations(trait_.items.iter().filter_map(|i| match i {
TraitItem::Fn(method) => Some(method),
_ => None,
}));
visit::visit_item_trait(self, trait_);
}
/// Check that the given method declarations are correct.
///
/// Any error is stored in `self.errors`.
fn check_method_declarations<'a>(&mut self, methods: impl Iterator<Item = &'a TraitItemFn>) {
let mut method_to_signature_changed = HashMap::<Ident, Vec<Option<u32>>>::new();
methods.into_iter().for_each(|method| {
let attributes = remove_supported_attributes(&mut method.attrs.clone());
let changed_in = match get_changed_in(&attributes) {
Ok(r) => r,
Err(e) => {
self.errors.push(e);
return;
},
};
method_to_signature_changed
.entry(method.sig.ident.clone())
.or_default()
.push(changed_in);
if method.default.is_some() {
self.errors.push(Error::new(
method.default.span(),
"A runtime API function cannot have a default implementation!",
));
}
});
method_to_signature_changed.into_iter().for_each(|(f, changed)| {
// If `changed_in` is `None`, it means it is the current "default" method that calls
// into the latest implementation.
if changed.iter().filter(|c| c.is_none()).count() == 0 {
self.errors.push(Error::new(
f.span(),
"There is no 'default' method with this name (without `changed_in` attribute).\n\
The 'default' method is used to call into the latest implementation.",
));
}
});
}
}
impl<'ast> Visit<'ast> for CheckTraitDecl {
fn visit_fn_arg(&mut self, input: &'ast FnArg) {
if let FnArg::Receiver(_) = input {
self.errors.push(Error::new(input.span(), "`self` as argument not supported."))
}
visit::visit_fn_arg(self, input);
}
fn visit_generic_param(&mut self, input: &'ast GenericParam) {
match input {
GenericParam::Type(ty) if ty.ident == BLOCK_GENERIC_IDENT =>
self.errors.push(Error::new(
input.span(),
"`Block: BlockT` generic parameter will be added automatically by the \
`decl_runtime_apis!` macro!",
)),
_ => {},
}
visit::visit_generic_param(self, input);
}
fn visit_trait_bound(&mut self, input: &'ast TraitBound) {
if let Some(last_ident) = input.path.segments.last().map(|v| &v.ident) {
if last_ident == "BlockT" || last_ident == BLOCK_GENERIC_IDENT {
self.errors.push(Error::new(
input.span(),
"`Block: BlockT` generic parameter will be added automatically by the \
`decl_runtime_apis!` macro! If you try to use a different trait than the \
substrate `Block` trait, please rename it locally.",
))
}
}
visit::visit_trait_bound(self, input)
}
}
/// Check that the trait declarations are in the format we expect.
fn check_trait_decls(decls: &[ItemTrait]) -> Result<()> {
let mut checker = CheckTraitDecl { errors: Vec::new() };
decls.iter().for_each(|decl| checker.check(decl));
if let Some(err) = checker.errors.pop() {
Err(checker.errors.into_iter().fold(err, |mut err, other| {
err.combine(other);
err
}))
} else {
Ok(())
}
}
/// The implementation of the `decl_runtime_apis!` macro.
pub fn decl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Parse all trait declarations
let RuntimeApiDecls { decls: api_decls } = parse_macro_input!(input as RuntimeApiDecls);
decl_runtime_apis_impl_inner(&api_decls)
.unwrap_or_else(|e| e.to_compile_error())
.into()
}
fn decl_runtime_apis_impl_inner(api_decls: &[ItemTrait]) -> Result<TokenStream> {
check_trait_decls(api_decls)?;
let runtime_decls = generate_runtime_decls(api_decls)?;
let client_side_decls = generate_client_side_decls(api_decls)?;
let decl = quote! {
#runtime_decls
#client_side_decls
};
let decl = expander::Expander::new("decl_runtime_apis")
.dry(std::env::var("EXPAND_MACROS").is_err())
.verbose(true)
.write_to_out_dir(decl)
.expect("Does not fail because of IO in OUT_DIR; qed");
Ok(decl)
}
@@ -0,0 +1,950 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::utils::{
extract_api_version, extract_block_type_from_trait_path, extract_impl_trait,
extract_parameter_names_types_and_borrows, generate_crate_access,
generate_runtime_mod_name_for_trait, prefix_function_with_trait, versioned_trait_name,
AllowSelfRefInParameters, ApiVersion, RequireQualifiedTraitPath,
};
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{
fold::{self, Fold},
parse::{Error, Parse, ParseStream, Result},
parse_macro_input, parse_quote,
spanned::Spanned,
visit_mut::{self, VisitMut},
Attribute, Ident, ImplItem, ItemImpl, Path, Signature, Type, TypePath,
};
use std::collections::HashMap;
/// The structure used for parsing the runtime api implementations.
struct RuntimeApiImpls {
impls: Vec<ItemImpl>,
}
impl Parse for RuntimeApiImpls {
fn parse(input: ParseStream) -> Result<Self> {
let mut impls = Vec::new();
while !input.is_empty() {
impls.push(ItemImpl::parse(input)?);
}
if impls.is_empty() {
Err(Error::new(Span::call_site(), "No api implementation given!"))
} else {
Ok(Self { impls })
}
}
}
/// Generates the call to the implementation of the requested function.
/// The generated code includes decoding of the input arguments and encoding of the output.
fn generate_impl_call(
signature: &Signature,
runtime: &Type,
input: &Ident,
impl_trait: &Path,
api_version: &ApiVersion,
) -> Result<TokenStream> {
let params =
extract_parameter_names_types_and_borrows(signature, AllowSelfRefInParameters::No)?;
let c = generate_crate_access();
let fn_name = &signature.ident;
let fn_name_str = fn_name.to_string();
let pnames = params.iter().map(|v| &v.0);
let pnames2 = params.iter().map(|v| &v.0);
let ptypes = params.iter().map(|v| &v.1);
let pborrow = params.iter().map(|v| &v.2);
let decode_params = if params.is_empty() {
quote!(
if !#input.is_empty() {
panic!(
"Bad input data provided to {}: expected no parameters, but input buffer is not empty. Nothing bad happened: someone sent an invalid transaction to the node.",
#fn_name_str
);
}
)
} else {
let let_binding = if params.len() == 1 {
quote! {
let #( #pnames )* : #( #ptypes )*
}
} else {
quote! {
let ( #( #pnames ),* ) : ( #( #ptypes ),* )
}
};
quote!(
#let_binding =
match #c::Decode::decode(
&mut #input,
) {
Ok(res) => res,
Err(e) => panic!("Bad input data provided to {}: {}. Nothing bad happened: someone sent an invalid transaction to the node.", #fn_name_str, e),
};
)
};
let fn_calls = if let Some(feature_gated) = &api_version.feature_gated {
let pnames = pnames2;
let pnames2 = pnames.clone();
let pborrow2 = pborrow.clone();
let feature_name = &feature_gated.0;
let impl_trait_fg = extend_with_api_version(impl_trait.clone(), Some(feature_gated.1));
let impl_trait = extend_with_api_version(impl_trait.clone(), api_version.custom);
quote!(
#[cfg(feature = #feature_name)]
#[allow(deprecated)]
let r = <#runtime as #impl_trait_fg>::#fn_name(#( #pborrow #pnames ),*);
#[cfg(not(feature = #feature_name))]
#[allow(deprecated)]
let r = <#runtime as #impl_trait>::#fn_name(#( #pborrow2 #pnames2 ),*);
r
)
} else {
let pnames = pnames2;
let impl_trait = extend_with_api_version(impl_trait.clone(), api_version.custom);
quote!(
#[allow(deprecated)]
<#runtime as #impl_trait>::#fn_name(#( #pborrow #pnames ),*)
)
};
Ok(quote!(
#decode_params
#fn_calls
))
}
/// Generate all the implementation calls for the given functions.
fn generate_impl_calls(
impls: &[ItemImpl],
input: &Ident,
) -> Result<Vec<(Ident, Ident, TokenStream, Vec<Attribute>)>> {
let mut impl_calls = Vec::new();
for impl_ in impls {
let trait_api_ver = extract_api_version(&impl_.attrs, impl_.span())?;
let impl_trait_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?;
let impl_trait = extend_with_runtime_decl_path(impl_trait_path.clone());
let impl_trait_ident = &impl_trait_path
.segments
.last()
.ok_or_else(|| Error::new(impl_trait_path.span(), "Empty trait path not possible!"))?
.ident;
for item in &impl_.items {
if let ImplItem::Fn(method) = item {
let impl_call = generate_impl_call(
&method.sig,
&impl_.self_ty,
input,
&impl_trait,
&trait_api_ver,
)?;
let mut attrs = filter_cfg_and_allow_attrs(&impl_.attrs);
// Add any `#[cfg(feature = X)]` attributes of the method to result
attrs.extend(filter_cfg_and_allow_attrs(&method.attrs));
impl_calls.push((
impl_trait_ident.clone(),
method.sig.ident.clone(),
impl_call,
attrs,
));
}
}
}
Ok(impl_calls)
}
/// Generate the dispatch function that is used in native to call into the runtime.
fn generate_dispatch_function(impls: &[ItemImpl]) -> Result<TokenStream> {
let data = Ident::new("_sp_api_input_data_", Span::call_site());
let c = generate_crate_access();
let impl_calls =
generate_impl_calls(impls, &data)?
.into_iter()
.map(|(trait_, fn_name, impl_, attrs)| {
let name = prefix_function_with_trait(&trait_, &fn_name);
quote!(
#( #attrs )*
#name => Some(#c::Encode::encode(&{ #impl_ })),
)
});
Ok(quote!(
#c::std_enabled! {
pub fn dispatch(method: &str, mut #data: &[u8]) -> Option<Vec<u8>> {
match method {
#( #impl_calls )*
_ => None,
}
}
}
))
}
/// Generate the interface functions that are used to call into the runtime in wasm.
fn generate_wasm_interface(impls: &[ItemImpl]) -> Result<TokenStream> {
let input = Ident::new("input", Span::call_site());
let c = generate_crate_access();
let impl_calls =
generate_impl_calls(impls, &input)?
.into_iter()
.map(|(trait_, fn_name, impl_, attrs)| {
let fn_name =
Ident::new(&prefix_function_with_trait(&trait_, &fn_name), Span::call_site());
quote!(
#c::std_disabled! {
#( #attrs )*
#[no_mangle]
#[cfg_attr(any(target_arch = "riscv32", target_arch = "riscv64"), #c::__private::polkavm_export(abi = #c::__private::polkavm_abi))]
pub unsafe extern fn #fn_name(input_data: *mut u8, input_len: usize) -> u64 {
let mut #input = if input_len == 0 {
&[0u8; 0]
} else {
unsafe {
::core::slice::from_raw_parts(input_data, input_len)
}
};
#c::init_runtime_logger();
let output = (move || { #impl_ })();
#c::to_substrate_wasm_fn_return_value(&output)
}
}
)
});
Ok(quote!( #( #impl_calls )* ))
}
fn generate_runtime_api_base_structures() -> Result<TokenStream> {
let crate_ = generate_crate_access();
Ok(quote!(
pub struct RuntimeApi {}
#crate_::std_enabled! {
/// Implements all runtime apis for the client side.
pub struct RuntimeApiImpl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block> + 'static> {
call: &'static C,
transaction_depth: std::cell::RefCell<u16>,
changes: std::cell::RefCell<#crate_::OverlayedChanges<#crate_::HashingFor<Block>>>,
recorder: std::option::Option<#crate_::ProofRecorder<Block>>,
call_context: #crate_::CallContext,
extensions: std::cell::RefCell<#crate_::Extensions>,
extensions_generated_for: std::cell::RefCell<std::option::Option<Block::Hash>>,
}
#[automatically_derived]
impl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block>> #crate_::ApiExt<Block> for
RuntimeApiImpl<Block, C>
{
fn execute_in_transaction<F: FnOnce(&Self) -> #crate_::TransactionOutcome<R>, R>(
&self,
call: F,
) -> R where Self: Sized {
self.start_transaction();
*std::cell::RefCell::borrow_mut(&self.transaction_depth) += 1;
let res = call(self);
std::cell::RefCell::borrow_mut(&self.transaction_depth)
.checked_sub(1)
.expect("Transactions are opened and closed together; qed");
self.commit_or_rollback_transaction(
std::matches!(res, #crate_::TransactionOutcome::Commit(_))
);
res.into_inner()
}
fn has_api<A: #crate_::RuntimeApiInfo + ?Sized>(
&self,
at: <Block as #crate_::BlockT>::Hash,
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
#crate_::CallApiAt::<Block>::runtime_version_at(self.call, at)
.map(|v| #crate_::RuntimeVersion::has_api_with(&v, &A::ID, |v| v == A::VERSION))
}
fn has_api_with<A: #crate_::RuntimeApiInfo + ?Sized, P: Fn(u32) -> bool>(
&self,
at: <Block as #crate_::BlockT>::Hash,
pred: P,
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
#crate_::CallApiAt::<Block>::runtime_version_at(self.call, at)
.map(|v| #crate_::RuntimeVersion::has_api_with(&v, &A::ID, pred))
}
fn api_version<A: #crate_::RuntimeApiInfo + ?Sized>(
&self,
at: <Block as #crate_::BlockT>::Hash,
) -> std::result::Result<Option<u32>, #crate_::ApiError> where Self: Sized {
#crate_::CallApiAt::<Block>::runtime_version_at(self.call, at)
.map(|v| #crate_::RuntimeVersion::api_version(&v, &A::ID))
}
fn record_proof(&mut self) {
self.recorder = std::option::Option::Some(std::default::Default::default());
}
fn record_proof_with_recorder(&mut self, recorder: #crate_::ProofRecorder<Block>) {
self.recorder = std::option::Option::Some(recorder);
}
fn proof_recorder(&self) -> std::option::Option<#crate_::ProofRecorder<Block>> {
std::clone::Clone::clone(&self.recorder)
}
fn extract_proof(
&mut self,
) -> std::option::Option<#crate_::StorageProof> {
let recorder = std::option::Option::take(&mut self.recorder);
std::option::Option::map(recorder, |recorder| {
#crate_::ProofRecorder::<Block>::drain_storage_proof(recorder)
})
}
fn into_storage_changes<B: #crate_::StateBackend<#crate_::HashingFor<Block>>>(
&self,
backend: &B,
parent_hash: Block::Hash,
) -> ::core::result::Result<
#crate_::StorageChanges<Block>,
String
> where Self: Sized {
let state_version = #crate_::CallApiAt::<Block>::runtime_version_at(self.call, std::clone::Clone::clone(&parent_hash))
.map(|v| #crate_::RuntimeVersion::state_version(&v))
.map_err(|e| format!("Failed to get state version: {}", e))?;
#crate_::OverlayedChanges::drain_storage_changes(
&mut std::cell::RefCell::borrow_mut(&self.changes),
backend,
state_version,
)
}
fn set_call_context(&mut self, call_context: #crate_::CallContext) {
self.call_context = call_context;
}
fn register_extension<E: #crate_::Extension>(&mut self, extension: E) {
std::cell::RefCell::borrow_mut(&self.extensions).register(extension);
}
}
#[automatically_derived]
impl<Block: #crate_::BlockT, C> #crate_::ConstructRuntimeApi<Block, C>
for RuntimeApi
where
C: #crate_::CallApiAt<Block> + 'static,
{
type RuntimeApi = RuntimeApiImpl<Block, C>;
fn construct_runtime_api<'a>(
call: &'a C,
) -> #crate_::ApiRef<'a, Self::RuntimeApi> {
RuntimeApiImpl {
call: unsafe { std::mem::transmute(call) },
transaction_depth: 0.into(),
changes: std::default::Default::default(),
recorder: std::default::Default::default(),
call_context: #crate_::CallContext::Offchain,
extensions: std::default::Default::default(),
extensions_generated_for: std::default::Default::default(),
}.into()
}
}
#[automatically_derived]
impl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block>> RuntimeApiImpl<Block, C> {
fn commit_or_rollback_transaction(&self, commit: bool) {
let proof = "\
We only close a transaction when we opened one ourself.
Other parts of the runtime that make use of transactions (state-machine)
also balance their transactions. The runtime cannot close client initiated
transactions; qed";
let res = if commit {
let res = if let Some(recorder) = &self.recorder {
#crate_::ProofRecorder::<Block>::commit_transaction(&recorder)
} else {
Ok(())
};
let res2 = #crate_::OverlayedChanges::commit_transaction(
&mut std::cell::RefCell::borrow_mut(&self.changes)
);
#crate_::Extensions::commit_transaction(
&mut std::cell::RefCell::borrow_mut(&self.extensions),
#crate_::TransactionType::Host,
);
// Will panic on an `Err` below, however we should call commit
// on the recorder and the changes together.
std::result::Result::and(res, std::result::Result::map_err(res2, drop))
} else {
let res = if let Some(recorder) = &self.recorder {
#crate_::ProofRecorder::<Block>::rollback_transaction(&recorder)
} else {
Ok(())
};
let res2 = #crate_::OverlayedChanges::rollback_transaction(
&mut std::cell::RefCell::borrow_mut(&self.changes)
);
#crate_::Extensions::rollback_transaction(
&mut std::cell::RefCell::borrow_mut(&self.extensions),
#crate_::TransactionType::Host,
);
// Will panic on an `Err` below, however we should call commit
// on the recorder and the changes together.
std::result::Result::and(res, std::result::Result::map_err(res2, drop))
};
std::result::Result::expect(res, proof);
}
fn start_transaction(&self) {
#crate_::OverlayedChanges::start_transaction(
&mut std::cell::RefCell::borrow_mut(&self.changes)
);
if let Some(recorder) = &self.recorder {
#crate_::ProofRecorder::<Block>::start_transaction(&recorder);
}
#crate_::Extensions::start_transaction(
&mut std::cell::RefCell::borrow_mut(&self.extensions),
#crate_::TransactionType::Host,
);
}
}
}
))
}
/// Extend the given trait path with module that contains the declaration of the trait for the
/// runtime.
fn extend_with_runtime_decl_path(mut trait_: Path) -> Path {
let runtime = {
let trait_name = &trait_
.segments
.last()
.as_ref()
.expect("Trait path should always contain at least one item; qed")
.ident;
generate_runtime_mod_name_for_trait(trait_name)
};
let pos = trait_.segments.len() - 1;
trait_.segments.insert(pos, runtime.into());
trait_
}
fn extend_with_api_version(mut trait_: Path, version: Option<u32>) -> Path {
let version = if let Some(v) = version {
v
} else {
// nothing to do
return trait_;
};
let trait_name = &mut trait_
.segments
.last_mut()
.expect("Trait path should always contain at least one item; qed")
.ident;
*trait_name = versioned_trait_name(trait_name, version);
trait_
}
/// Adds a feature guard to `attributes`.
///
/// Depending on `enable`, the feature guard either enables ('feature = "something"`) or disables
/// (`not(feature = "something")`).
fn add_feature_guard(attrs: &mut Vec<Attribute>, feature_name: &str, enable: bool) {
let attr = match enable {
true => parse_quote!(#[cfg(feature = #feature_name)]),
false => parse_quote!(#[cfg(not(feature = #feature_name))]),
};
attrs.push(attr);
}
/// Generates the implementations of the apis for the runtime.
fn generate_api_impl_for_runtime(impls: &[ItemImpl]) -> Result<TokenStream> {
let mut impls_prepared = Vec::new();
// We put `runtime` before each trait to get the trait that is intended for the runtime and
// we put the `RuntimeBlock` as first argument for the trait generics.
for impl_ in impls.iter() {
let trait_api_ver = extract_api_version(&impl_.attrs, impl_.span())?;
let mut impl_ = impl_.clone();
impl_.attrs = filter_cfg_and_allow_attrs(&impl_.attrs);
let trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone();
let trait_ = extend_with_runtime_decl_path(trait_);
// If the trait api contains feature gated version - there are staging methods in it. Handle
// them explicitly here by adding staging implementation with `#cfg(feature = ...)` and
// stable implementation with `#[cfg(not(feature = ...))]`.
if let Some(feature_gated) = trait_api_ver.feature_gated {
let mut feature_gated_impl = impl_.clone();
add_feature_guard(&mut feature_gated_impl.attrs, &feature_gated.0, true);
feature_gated_impl.trait_.as_mut().unwrap().1 =
extend_with_api_version(trait_.clone(), Some(feature_gated.1));
impls_prepared.push(feature_gated_impl);
// Finally add `#[cfg(not(feature = ...))]` for the stable implementation (which is
// generated outside this if).
add_feature_guard(&mut impl_.attrs, &feature_gated.0, false);
}
// Generate stable trait implementation.
let trait_ = extend_with_api_version(trait_, trait_api_ver.custom);
impl_.trait_.as_mut().unwrap().1 = trait_;
impls_prepared.push(impl_);
}
Ok(quote!( #( #impls_prepared )* ))
}
/// Auxiliary data structure that is used to convert `impl Api for Runtime` to
/// `impl Api for RuntimeApi`.
/// This requires us to replace the runtime `Block` with the node `Block`,
/// `impl Api for Runtime` with `impl Api for RuntimeApi` and replace the method implementations
/// with code that calls into the runtime.
struct ApiRuntimeImplToApiRuntimeApiImpl<'a> {
runtime_block: &'a TypePath,
}
impl<'a> ApiRuntimeImplToApiRuntimeApiImpl<'a> {
/// Process the given item implementation.
fn process(mut self, input: ItemImpl) -> ItemImpl {
let mut input = self.fold_item_impl(input);
let crate_ = generate_crate_access();
// Delete all functions, because all of them are default implemented by
// `decl_runtime_apis!`. We only need to implement the `__runtime_api_internal_call_api_at`
// function.
input.items.clear();
input.items.push(parse_quote! {
fn __runtime_api_internal_call_api_at(
&self,
at: <__SrApiBlock__ as #crate_::BlockT>::Hash,
params: std::vec::Vec<u8>,
fn_name: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError> {
// If we are not already in a transaction, we should create a new transaction
// and then commit/roll it back at the end!
let transaction_depth = *std::cell::RefCell::borrow(&self.transaction_depth);
if transaction_depth == 0 {
self.start_transaction();
}
let res = (|| {
let version = #crate_::CallApiAt::<__SrApiBlock__>::runtime_version_at(
self.call,
at,
)?;
match &mut *std::cell::RefCell::borrow_mut(&self.extensions_generated_for) {
Some(generated_for) => {
if *generated_for != at {
return std::result::Result::Err(
#crate_::ApiError::UsingSameInstanceForDifferentBlocks
)
}
},
generated_for @ None => {
#crate_::CallApiAt::<__SrApiBlock__>::initialize_extensions(
self.call,
at,
&mut std::cell::RefCell::borrow_mut(&self.extensions),
)?;
*generated_for = Some(at);
}
}
let params = #crate_::CallApiAtParams {
at,
function: (*fn_name)(version),
arguments: params,
overlayed_changes: &self.changes,
call_context: self.call_context,
recorder: &self.recorder,
extensions: &self.extensions,
};
#crate_::CallApiAt::<__SrApiBlock__>::call_api_at(
self.call,
params,
)
})();
if transaction_depth == 0 {
self.commit_or_rollback_transaction(std::result::Result::is_ok(&res));
}
res
}
});
input
}
}
impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
fn fold_type_path(&mut self, input: TypePath) -> TypePath {
let new_ty_path =
if input == *self.runtime_block { parse_quote!(__SrApiBlock__) } else { input };
fold::fold_type_path(self, new_ty_path)
}
fn fold_item_impl(&mut self, mut input: ItemImpl) -> ItemImpl {
let crate_ = generate_crate_access();
// Implement the trait for the `RuntimeApiImpl`
input.self_ty =
Box::new(parse_quote!( RuntimeApiImpl<__SrApiBlock__, RuntimeApiImplCall> ));
input.generics.params.push(parse_quote!(
__SrApiBlock__: #crate_::BlockT
));
input
.generics
.params
.push(parse_quote!( RuntimeApiImplCall: #crate_::CallApiAt<__SrApiBlock__> + 'static ));
let where_clause = input.generics.make_where_clause();
where_clause.predicates.push(parse_quote! {
RuntimeApiImplCall::StateBackend:
#crate_::StateBackend<#crate_::HashingFor<__SrApiBlock__>>
});
where_clause.predicates.push(parse_quote! { &'static RuntimeApiImplCall: Send });
input.attrs = filter_cfg_and_allow_attrs(&input.attrs);
fold::fold_item_impl(self, input)
}
}
/// Generate the implementations of the runtime apis for the `RuntimeApi` type.
fn generate_api_impl_for_runtime_api(impls: &[ItemImpl]) -> Result<TokenStream> {
let mut result = Vec::with_capacity(impls.len());
for impl_ in impls {
let impl_trait_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?;
let runtime_block = extract_block_type_from_trait_path(impl_trait_path)?;
let mut runtime_mod_path = extend_with_runtime_decl_path(impl_trait_path.clone());
// remove the trait to get just the module path
runtime_mod_path.segments.pop();
let mut processed_impl =
ApiRuntimeImplToApiRuntimeApiImpl { runtime_block }.process(impl_.clone());
processed_impl.attrs.push(parse_quote!(#[automatically_derived]));
result.push(processed_impl);
}
let crate_ = generate_crate_access();
Ok(quote!( #crate_::std_enabled! { #( #result )* } ))
}
fn populate_runtime_api_versions(
result: &mut Vec<TokenStream>,
sections: &mut Vec<TokenStream>,
attrs: Vec<Attribute>,
id: Path,
version: TokenStream,
crate_access: &TokenStream,
) {
result.push(quote!(
#( #attrs )*
(#id, #version)
));
sections.push(quote!(
#crate_access::std_disabled! {
#( #attrs )*
const _: () = {
// All sections with the same name are going to be merged by concatenation.
#[link_section = "runtime_apis"]
static SECTION_CONTENTS: [u8; 12] = #crate_access::serialize_runtime_api_info(#id, #version);
};
}
));
}
/// Generates `RUNTIME_API_VERSIONS` that holds all version information about the implemented
/// runtime apis.
fn generate_runtime_api_versions(impls: &[ItemImpl]) -> Result<TokenStream> {
let mut result = Vec::<TokenStream>::with_capacity(impls.len());
let mut sections = Vec::<TokenStream>::with_capacity(impls.len());
let mut processed_traits = HashMap::new();
let c = generate_crate_access();
for impl_ in impls {
let versions = extract_api_version(&impl_.attrs, impl_.span())?;
let api_ver = versions.custom.map(|a| a as u32);
let mut path = extend_with_runtime_decl_path(
extract_impl_trait(impl_, RequireQualifiedTraitPath::Yes)?.clone(),
);
// Remove the trait
let trait_ = path
.segments
.pop()
.expect("extract_impl_trait already checks that this is valid; qed")
.into_value()
.ident;
let span = trait_.span();
if let Some(other_span) = processed_traits.insert(trait_, span) {
let mut error = Error::new(
span,
"Two traits with the same name detected! \
The trait name is used to generate its ID. \
Please rename one trait at the declaration!",
);
error.combine(Error::new(other_span, "First trait implementation."));
return Err(error);
}
let id: Path = parse_quote!( #path ID );
let mut attrs = filter_cfg_and_allow_attrs(&impl_.attrs);
// Handle API versioning
// If feature gated version is set - handle it first
if let Some(feature_gated) = versions.feature_gated {
let feature_gated_version = feature_gated.1 as u32;
// the attributes for the feature gated staging api
let mut feature_gated_attrs = attrs.clone();
add_feature_guard(&mut feature_gated_attrs, &feature_gated.0, true);
populate_runtime_api_versions(
&mut result,
&mut sections,
feature_gated_attrs,
id.clone(),
quote!( #feature_gated_version ),
&c,
);
// Add `#[cfg(not(feature ...))]` to the initial attributes. If the staging feature flag
// is not set we want to set the stable api version
add_feature_guard(&mut attrs, &feature_gated.0, false);
}
// Now add the stable api version to the versions list. If the api has got staging functions
// there might be a `#[cfg(not(feature ...))]` attribute attached to the stable version.
let base_api_version = quote!( #path VERSION );
let api_ver = api_ver.map(|a| quote!( #a )).unwrap_or_else(|| base_api_version);
populate_runtime_api_versions(&mut result, &mut sections, attrs, id, api_ver, &c);
}
Ok(quote!(
pub const RUNTIME_API_VERSIONS: #c::ApisVec = #c::create_apis_vec!([ #( #result ),* ]);
#( #sections )*
))
}
/// replaces `Self` with explicit `ItemImpl.self_ty`.
struct ReplaceSelfImpl {
self_ty: Box<Type>,
}
impl ReplaceSelfImpl {
/// Replace `Self` with `ItemImpl.self_ty`
fn replace(&mut self, trait_: &mut ItemImpl) {
visit_mut::visit_item_impl_mut(self, trait_)
}
}
impl VisitMut for ReplaceSelfImpl {
fn visit_type_mut(&mut self, ty: &mut syn::Type) {
match ty {
Type::Path(p) if p.path.is_ident("Self") => {
*ty = *self.self_ty.clone();
},
ty => syn::visit_mut::visit_type_mut(self, ty),
}
}
}
/// Rename `Self` to `ItemImpl.self_ty` in all items.
fn rename_self_in_trait_impls(impls: &mut [ItemImpl]) {
impls.iter_mut().for_each(|i| {
let mut checker = ReplaceSelfImpl { self_ty: i.self_ty.clone() };
checker.replace(i);
});
}
/// The implementation of the `impl_runtime_apis!` macro.
pub fn impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Parse all impl blocks
let RuntimeApiImpls { impls: mut api_impls } = parse_macro_input!(input as RuntimeApiImpls);
impl_runtime_apis_impl_inner(&mut api_impls)
.unwrap_or_else(|e| e.to_compile_error())
.into()
}
fn impl_runtime_apis_impl_inner(api_impls: &mut [ItemImpl]) -> Result<TokenStream> {
rename_self_in_trait_impls(api_impls);
let dispatch_impl = generate_dispatch_function(api_impls)?;
let api_impls_for_runtime = generate_api_impl_for_runtime(api_impls)?;
let base_runtime_api = generate_runtime_api_base_structures()?;
let runtime_api_versions = generate_runtime_api_versions(api_impls)?;
let wasm_interface = generate_wasm_interface(api_impls)?;
let api_impls_for_runtime_api = generate_api_impl_for_runtime_api(api_impls)?;
let runtime_metadata = crate::runtime_metadata::generate_impl_runtime_metadata(api_impls)?;
let impl_ = quote!(
#base_runtime_api
#api_impls_for_runtime
#api_impls_for_runtime_api
#runtime_api_versions
#runtime_metadata
pub mod api {
use super::*;
#dispatch_impl
#wasm_interface
}
);
let impl_ = expander::Expander::new("impl_runtime_apis")
.dry(std::env::var("EXPAND_MACROS").is_err())
.verbose(true)
.write_to_out_dir(impl_)
.expect("Does not fail because of IO in OUT_DIR; qed");
Ok(impl_)
}
// Filters all attributes except the cfg and allow ones.
fn filter_cfg_and_allow_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
attrs
.iter()
.filter(|a| a.path().is_ident("cfg") || a.path().is_ident("allow"))
.cloned()
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn filter_non_cfg_attributes() {
let cfg_std: Attribute = parse_quote!(#[cfg(feature = "std")]);
let cfg_benchmarks: Attribute = parse_quote!(#[cfg(feature = "runtime-benchmarks")]);
let allow: Attribute = parse_quote!(#[allow(non_camel_case_types)]);
let attrs = vec![
cfg_std.clone(),
parse_quote!(#[derive(Debug)]),
parse_quote!(#[test]),
cfg_benchmarks.clone(),
parse_quote!(#[allow(non_camel_case_types)]),
];
let filtered = filter_cfg_and_allow_attrs(&attrs);
assert_eq!(filtered.len(), 3);
assert_eq!(cfg_std, filtered[0]);
assert_eq!(cfg_benchmarks, filtered[1]);
assert_eq!(allow, filtered[2]);
}
#[test]
fn impl_trait_rename_self_param() {
let code = quote::quote! {
impl client::Core<Block> for Runtime {
fn initialize_block(header: &HeaderFor<Self>) -> Output<Self> {
let _: HeaderFor<Self> = header.clone();
example_fn::<Self>(header)
}
}
};
let expected = quote::quote! {
impl client::Core<Block> for Runtime {
fn initialize_block(header: &HeaderFor<Runtime>) -> Output<Runtime> {
let _: HeaderFor<Runtime> = header.clone();
example_fn::<Runtime>(header)
}
}
};
// Parse the items
let RuntimeApiImpls { impls: mut api_impls } =
syn::parse2::<RuntimeApiImpls>(code).unwrap();
// Run the renamer which is being run first in the `impl_runtime_apis!` macro.
rename_self_in_trait_impls(&mut api_impls);
let result: TokenStream = quote::quote! { #(#api_impls)* };
assert_eq!(result.to_string(), expected.to_string());
}
}
@@ -0,0 +1,44 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Macros for declaring and implementing runtime apis.
#![recursion_limit = "512"]
use proc_macro::TokenStream;
mod common;
mod decl_runtime_apis;
mod impl_runtime_apis;
mod mock_impl_runtime_apis;
mod runtime_metadata;
mod utils;
#[proc_macro]
pub fn impl_runtime_apis(input: TokenStream) -> TokenStream {
impl_runtime_apis::impl_runtime_apis_impl(input)
}
#[proc_macro]
pub fn mock_impl_runtime_apis(input: TokenStream) -> TokenStream {
mock_impl_runtime_apis::mock_impl_runtime_apis_impl(input)
}
#[proc_macro]
pub fn decl_runtime_apis(input: TokenStream) -> TokenStream {
decl_runtime_apis::decl_runtime_apis_impl(input)
}
@@ -0,0 +1,447 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::utils::{
extract_block_type_from_trait_path, extract_impl_trait,
extract_parameter_names_types_and_borrows, generate_crate_access, return_type_extract_type,
AllowSelfRefInParameters, RequireQualifiedTraitPath,
};
use proc_macro2::{Span, TokenStream};
use quote::{quote, quote_spanned};
use syn::{
fold::{self, Fold},
parse::{Error, Parse, ParseStream, Result},
parse_macro_input, parse_quote,
spanned::Spanned,
Attribute, ItemImpl, Pat, Type, TypePath,
};
/// The `advanced` attribute.
///
/// If this attribute is given to a function, the function gets access to the `Hash` as first
/// parameter and needs to return a `Result` with the appropriate error type.
const ADVANCED_ATTRIBUTE: &str = "advanced";
/// The structure used for parsing the runtime api implementations.
struct RuntimeApiImpls {
impls: Vec<ItemImpl>,
}
impl Parse for RuntimeApiImpls {
fn parse(input: ParseStream) -> Result<Self> {
let mut impls = Vec::new();
while !input.is_empty() {
impls.push(ItemImpl::parse(input)?);
}
if impls.is_empty() {
Err(Error::new(Span::call_site(), "No api implementation given!"))
} else {
Ok(Self { impls })
}
}
}
/// Implement the `ApiExt` trait and the `Core` runtime api.
fn implement_common_api_traits(block_type: TypePath, self_ty: Type) -> Result<TokenStream> {
let crate_ = generate_crate_access();
Ok(quote!(
impl #crate_::ApiExt<#block_type> for #self_ty {
fn execute_in_transaction<F: FnOnce(&Self) -> #crate_::TransactionOutcome<R>, R>(
&self,
call: F,
) -> R where Self: Sized {
call(self).into_inner()
}
fn has_api<A: #crate_::RuntimeApiInfo + ?Sized>(
&self,
_: <Block as #crate_::BlockT>::Hash,
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
Ok(true)
}
fn has_api_with<A: #crate_::RuntimeApiInfo + ?Sized, P: Fn(u32) -> bool>(
&self,
_: <Block as #crate_::BlockT>::Hash,
pred: P,
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
Ok(pred(A::VERSION))
}
fn api_version<A: #crate_::RuntimeApiInfo + ?Sized>(
&self,
_: <Block as #crate_::BlockT>::Hash,
) -> std::result::Result<Option<u32>, #crate_::ApiError> where Self: Sized {
Ok(Some(A::VERSION))
}
fn record_proof(&mut self) {
unimplemented!("`record_proof` not implemented for runtime api mocks")
}
fn record_proof_with_recorder(&mut self, _: #crate_::ProofRecorder<#block_type>) {
unimplemented!("`record_proof_with_recorder` not implemented for runtime api mocks")
}
fn extract_proof(
&mut self,
) -> Option<#crate_::StorageProof> {
unimplemented!("`extract_proof` not implemented for runtime api mocks")
}
fn proof_recorder(&self) -> Option<#crate_::ProofRecorder<#block_type>> {
unimplemented!("`proof_recorder` not implemented for runtime api mocks")
}
fn into_storage_changes<B: #crate_::StateBackend<#crate_::HashingFor<#block_type>>>(
&self,
_: &B,
_: <#block_type as #crate_::BlockT>::Hash,
) -> std::result::Result<
#crate_::StorageChanges<#block_type>,
String
> where Self: Sized {
unimplemented!("`into_storage_changes` not implemented for runtime api mocks")
}
fn set_call_context(&mut self, _: #crate_::CallContext) {
unimplemented!("`set_call_context` not implemented for runtime api mocks")
}
fn register_extension<E: #crate_::Extension>(&mut self, _: E) {
unimplemented!("`register_extension` not implemented for runtime api mocks")
}
}
impl #crate_::Core<#block_type> for #self_ty {
fn __runtime_api_internal_call_api_at(
&self,
_: <#block_type as #crate_::BlockT>::Hash,
_: std::vec::Vec<u8>,
_: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError> {
unimplemented!("`__runtime_api_internal_call_api_at` not implemented for runtime api mocks")
}
fn version(
&self,
_: <#block_type as #crate_::BlockT>::Hash,
) -> std::result::Result<#crate_::RuntimeVersion, #crate_::ApiError> {
unimplemented!("`Core::version` not implemented for runtime api mocks")
}
fn execute_block(
&self,
_: <#block_type as #crate_::BlockT>::Hash,
_: <#block_type as #crate_::BlockT>::LazyBlock,
) -> std::result::Result<(), #crate_::ApiError> {
unimplemented!("`Core::execute_block` not implemented for runtime api mocks")
}
fn initialize_block(
&self,
_: <#block_type as #crate_::BlockT>::Hash,
_: &<#block_type as #crate_::BlockT>::Header,
) -> std::result::Result<#crate_::__private::ExtrinsicInclusionMode, #crate_::ApiError> {
unimplemented!("`Core::initialize_block` not implemented for runtime api mocks")
}
}
))
}
/// Returns if the advanced attribute is present in the given `attributes`.
///
/// If the attribute was found, it will be automatically removed from the vec.
fn has_advanced_attribute(attributes: &mut Vec<Attribute>) -> bool {
let mut found = false;
attributes.retain(|attr| {
if attr.path().is_ident(ADVANCED_ATTRIBUTE) {
found = true;
false
} else {
true
}
});
found
}
/// Get the name and type of the `at` parameter that is passed to a runtime api function.
///
/// If `is_advanced` is `false`, the name is `_`.
fn get_at_param_name(
is_advanced: bool,
param_names: &mut Vec<Pat>,
param_types_and_borrows: &mut Vec<(TokenStream, bool)>,
function_span: Span,
default_hash_type: &TokenStream,
) -> Result<(TokenStream, TokenStream)> {
if is_advanced {
if param_names.is_empty() {
return Err(Error::new(
function_span,
format!(
"If using the `{}` attribute, it is required that the function \
takes at least one argument, the `Hash`.",
ADVANCED_ATTRIBUTE,
),
));
}
// `param_names` and `param_types` have the same length, so if `param_names` is not empty
// `param_types` can not be empty as well.
let ptype_and_borrows = param_types_and_borrows.remove(0);
let span = ptype_and_borrows.1.span();
if ptype_and_borrows.1 {
return Err(Error::new(span, "`Hash` needs to be taken by value and not by reference!"));
}
let name = param_names.remove(0);
Ok((quote!( #name ), ptype_and_borrows.0))
} else {
Ok((quote!(_), default_hash_type.clone()))
}
}
/// Auxiliary structure to fold a runtime api trait implementation into the expected format.
///
/// This renames the methods, changes the method parameters and extracts the error type.
struct FoldRuntimeApiImpl<'a> {
/// The block type that is being used.
block_type: &'a TypePath,
}
impl<'a> FoldRuntimeApiImpl<'a> {
/// Process the given [`syn::ItemImpl`].
fn process(mut self, impl_item: syn::ItemImpl) -> syn::ItemImpl {
let mut impl_item = self.fold_item_impl(impl_item);
let crate_ = generate_crate_access();
let block_type = self.block_type;
impl_item.items.push(parse_quote! {
fn __runtime_api_internal_call_api_at(
&self,
_: <#block_type as #crate_::BlockT>::Hash,
_: std::vec::Vec<u8>,
_: &dyn Fn(#crate_::RuntimeVersion) -> &'static str,
) -> std::result::Result<std::vec::Vec<u8>, #crate_::ApiError> {
unimplemented!(
"`__runtime_api_internal_call_api_at` not implemented for runtime api mocks. \
Calling deprecated methods is not supported by mocked runtime api."
)
}
});
impl_item
}
}
impl<'a> Fold for FoldRuntimeApiImpl<'a> {
fn fold_impl_item_fn(&mut self, mut input: syn::ImplItemFn) -> syn::ImplItemFn {
let block = {
let crate_ = generate_crate_access();
let is_advanced = has_advanced_attribute(&mut input.attrs);
let mut errors = Vec::new();
let (mut param_names, mut param_types_and_borrows) =
match extract_parameter_names_types_and_borrows(
&input.sig,
AllowSelfRefInParameters::YesButIgnore,
) {
Ok(res) => (
res.iter().map(|v| v.0.clone()).collect::<Vec<_>>(),
res.iter()
.map(|v| {
let ty = &v.1;
let borrow = &v.2;
(quote_spanned!(ty.span() => #borrow #ty ), v.2.is_some())
})
.collect::<Vec<_>>(),
),
Err(e) => {
errors.push(e.to_compile_error());
(Default::default(), Default::default())
},
};
let block_type = &self.block_type;
let hash_type = quote!( <#block_type as #crate_::BlockT>::Hash );
let (at_param_name, hash_type) = match get_at_param_name(
is_advanced,
&mut param_names,
&mut param_types_and_borrows,
input.span(),
&hash_type,
) {
Ok(res) => res,
Err(e) => {
errors.push(e.to_compile_error());
(quote!(_), hash_type)
},
};
let param_types = param_types_and_borrows.iter().map(|v| &v.0);
// Rewrite the input parameters.
input.sig.inputs = parse_quote! {
&self,
#at_param_name: #hash_type,
#( #param_names: #param_types ),*
};
// When using advanced, the user needs to declare the correct return type on its own,
// otherwise do it for the user.
if !is_advanced {
let ret_type = return_type_extract_type(&input.sig.output);
// Generate the correct return type.
input.sig.output = parse_quote!(
-> std::result::Result<#ret_type, #crate_::ApiError>
);
}
let orig_block = input.block.clone();
let construct_return_value = if is_advanced {
quote!( (move || #orig_block)() )
} else {
quote! {
let __fn_implementation__ = move || #orig_block;
Ok(__fn_implementation__())
}
};
// Generate the new method implementation that calls into the runtime.
parse_quote!(
{
// Get the error to the user (if we have one).
#( #errors )*
#construct_return_value
}
)
};
let mut input = fold::fold_impl_item_fn(self, input);
// We need to set the block, after we modified the rest of the ast, otherwise we would
// modify our generated block as well.
input.block = block;
input
}
}
/// Result of [`generate_runtime_api_impls`].
struct GeneratedRuntimeApiImpls {
/// All the runtime api implementations.
impls: TokenStream,
/// The block type that is being used by the runtime apis.
block_type: TypePath,
/// The type the traits are implemented for.
self_ty: Type,
}
/// Generate the runtime api implementations from the given trait implementations.
///
/// This folds the method names, changes the method parameters, method return type,
/// extracts the error type, self type and the block type.
fn generate_runtime_api_impls(impls: &[ItemImpl]) -> Result<GeneratedRuntimeApiImpls> {
let mut result = Vec::with_capacity(impls.len());
let mut global_block_type: Option<TypePath> = None;
let mut self_ty: Option<Box<Type>> = None;
for impl_ in impls {
let impl_trait_path = extract_impl_trait(impl_, RequireQualifiedTraitPath::No)?;
let block_type = extract_block_type_from_trait_path(impl_trait_path)?;
self_ty = match self_ty.take() {
Some(self_ty) =>
if self_ty == impl_.self_ty {
Some(self_ty)
} else {
let mut error = Error::new(
impl_.self_ty.span(),
"Self type should not change between runtime apis",
);
error.combine(Error::new(self_ty.span(), "First self type found here"));
return Err(error);
},
None => Some(impl_.self_ty.clone()),
};
global_block_type = match global_block_type.take() {
Some(global_block_type) =>
if global_block_type == *block_type {
Some(global_block_type)
} else {
let mut error = Error::new(
block_type.span(),
"Block type should be the same between all runtime apis.",
);
error.combine(Error::new(
global_block_type.span(),
"First block type found here",
));
return Err(error);
},
None => Some(block_type.clone()),
};
result.push(FoldRuntimeApiImpl { block_type }.process(impl_.clone()));
}
Ok(GeneratedRuntimeApiImpls {
impls: quote!( #( #result )* ),
block_type: global_block_type.expect("There is a least one runtime api; qed"),
self_ty: *self_ty.expect("There is at least one runtime api; qed"),
})
}
/// The implementation of the `mock_impl_runtime_apis!` macro.
pub fn mock_impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
// Parse all impl blocks
let RuntimeApiImpls { impls: api_impls } = parse_macro_input!(input as RuntimeApiImpls);
mock_impl_runtime_apis_impl_inner(&api_impls)
.unwrap_or_else(|e| e.to_compile_error())
.into()
}
fn mock_impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result<TokenStream> {
let GeneratedRuntimeApiImpls { impls, block_type, self_ty } =
generate_runtime_api_impls(api_impls)?;
let api_traits = implement_common_api_traits(block_type, self_ty)?;
Ok(quote!(
#impls
#api_traits
))
}
@@ -0,0 +1,312 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use proc_macro2::TokenStream as TokenStream2;
use quote::quote;
use syn::{parse_quote, spanned::Spanned, ItemImpl, ItemTrait, Result};
use crate::utils::{
extract_api_version, extract_impl_trait, filter_cfg_attributes, generate_crate_access,
generate_runtime_mod_name_for_trait, get_doc_literals, RequireQualifiedTraitPath,
};
/// Get the type parameter argument without lifetime or mutability
/// of a runtime metadata function.
///
/// In the following example, both the `AccountId` and `Nonce` generic
/// type parameters must implement `scale_info::TypeInfo` because they
/// are added into the metadata using `scale_info::meta_type`.
///
/// ```ignore
/// trait ExampleAccountNonceApi<AccountId, Nonce> {
/// fn account_nonce<'a>(account: &'a AccountId) -> Nonce;
/// }
/// ```
///
/// Instead of returning `&'a AccountId` for the first parameter, this function
/// returns `AccountId` to place bounds around it.
fn get_type_param(ty: &syn::Type) -> syn::Type {
// Remove the lifetime and mutability of the type T to
// place bounds around it.
let ty_elem = match &ty {
syn::Type::Reference(reference) => &reference.elem,
syn::Type::Ptr(ptr) => &ptr.elem,
syn::Type::Slice(slice) => &slice.elem,
syn::Type::Array(arr) => &arr.elem,
_ => ty,
};
ty_elem.clone()
}
/// Extract the documentation from the provided attributes.
///
/// It takes into account the `no-metadata-docs` feature.
fn collect_docs(attrs: &[syn::Attribute], crate_: &TokenStream2) -> TokenStream2 {
if cfg!(feature = "no-metadata-docs") {
quote!(#crate_::vec![])
} else {
let docs = get_doc_literals(&attrs);
quote!(#crate_::vec![ #( #docs, )* ])
}
}
/// Generate the runtime metadata of the provided trait.
///
/// The metadata is exposed as a generic function on the hidden module
/// of the trait generated by the `decl_runtime_apis`.
pub fn generate_decl_runtime_metadata<'a>(
decl: &ItemTrait,
versioned_methods_iter: impl Iterator<Item = (&'a syn::TraitItemFn, u32)>,
) -> TokenStream2 {
let crate_ = generate_crate_access();
let mut methods = Vec::new();
// Ensure that any function parameter that relies on the `BlockT` bounds
// also has `TypeInfo + 'static` bounds (required by `scale_info::meta_type`).
//
// For example, if a runtime API defines a method that has an input:
// `fn func(input: <Block as BlockT>::Header)`
// then the runtime metadata will imply `<Block as BlockT>::Header: TypeInfo + 'static`.
//
// This restricts the bounds at the metadata level, without needing to modify the `BlockT`
// itself, since the concrete implementations are already satisfying `TypeInfo`.
let mut where_clause = Vec::new();
for (method, version) in versioned_methods_iter {
let mut inputs = Vec::new();
let signature = &method.sig;
for input in &signature.inputs {
// Exclude `self` from metadata collection.
let syn::FnArg::Typed(typed) = input else { continue };
let pat = &typed.pat;
let name = quote!(#pat).to_string();
let ty = &typed.ty;
where_clause.push(get_type_param(ty));
inputs.push(quote!(
#crate_::metadata_ir::RuntimeApiMethodParamMetadataIR {
name: #name,
ty: #crate_::scale_info::meta_type::<#ty>(),
}
));
}
let output = match &signature.output {
syn::ReturnType::Default => quote!(#crate_::scale_info::meta_type::<()>()),
syn::ReturnType::Type(_, ty) => {
where_clause.push(get_type_param(ty));
quote!(#crate_::scale_info::meta_type::<#ty>())
},
};
// String method name including quotes for constructing `v15::RuntimeApiMethodMetadata`.
let method_name = signature.ident.to_string();
let docs = collect_docs(&method.attrs, &crate_);
// Include the method metadata only if its `cfg` features are enabled.
let attrs = filter_cfg_attributes(&method.attrs);
let deprecation = match crate::utils::get_deprecation(&crate_, &method.attrs) {
Ok(deprecation) => deprecation,
Err(e) => return e.into_compile_error(),
};
// Methods are filtered so that only those whose version is <= the `impl_version` passed to
// `runtime_metadata` are kept in the metadata we hand back.
methods.push(quote!(
#( #attrs )*
if #version <= impl_version {
Some(#crate_::metadata_ir::RuntimeApiMethodMetadataIR {
name: #method_name,
inputs: #crate_::vec![ #( #inputs, )* ],
output: #output,
docs: #docs,
deprecation_info: #deprecation,
})
} else {
None
}
));
}
let trait_name_ident = &decl.ident;
let trait_name = trait_name_ident.to_string();
let docs = collect_docs(&decl.attrs, &crate_);
let deprecation = match crate::utils::get_deprecation(&crate_, &decl.attrs) {
Ok(deprecation) => deprecation,
Err(e) => return e.into_compile_error(),
};
let attrs = filter_cfg_attributes(&decl.attrs);
// The trait generics where already extended with `Block: BlockT`.
let mut generics = decl.generics.clone();
for generic_param in generics.params.iter_mut() {
let syn::GenericParam::Type(ty) = generic_param else { continue };
// Default type parameters are not allowed in functions.
ty.eq_token = None;
ty.default = None;
}
where_clause
.into_iter()
.map(|ty| parse_quote!(#ty: #crate_::scale_info::TypeInfo + 'static))
.for_each(|w| generics.make_where_clause().predicates.push(w));
let (impl_generics, _, where_clause) = generics.split_for_impl();
quote!(
#crate_::frame_metadata_enabled! {
#( #attrs )*
#[inline(always)]
pub fn runtime_metadata #impl_generics (impl_version: u32) -> #crate_::metadata_ir::RuntimeApiMetadataIR
#where_clause
{
#crate_::metadata_ir::RuntimeApiMetadataIR {
name: #trait_name,
methods: [ #( #methods, )* ]
.into_iter()
.filter_map(|maybe_m| maybe_m)
.collect(),
docs: #docs,
deprecation_info: #deprecation,
version: impl_version.into(),
}
}
}
)
}
/// Implement the `runtime_metadata` function on the runtime that
/// generates the metadata for the given traits.
///
/// The metadata of each trait is extracted from the generic function
/// exposed by `generate_decl_runtime_metadata`.
pub fn generate_impl_runtime_metadata(impls: &[ItemImpl]) -> Result<TokenStream2> {
if impls.is_empty() {
return Ok(quote!());
}
let crate_ = generate_crate_access();
// Get the name of the runtime for which the traits are implemented.
let runtime_name = &impls
.get(0)
.expect("Traits should contain at least one implementation; qed")
.self_ty;
let mut metadata = Vec::new();
for impl_ in impls {
let mut trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone();
// Implementation traits are always references with a path `impl client::Core<generics> ...`
// The trait name is the last segment of this path.
let trait_name_ident = &trait_
.segments
.last()
.as_ref()
.expect("Trait path should always contain at least one item; qed")
.ident;
// Extract the generics from the trait to pass to the `runtime_metadata`
// function on the hidden module.
let generics = trait_
.segments
.iter()
.find_map(|segment| {
if let syn::PathArguments::AngleBracketed(generics) = &segment.arguments {
Some(generics.clone())
} else {
None
}
})
.expect("Trait path should always contain at least one generic parameter; qed");
let mod_name = generate_runtime_mod_name_for_trait(&trait_name_ident);
// Get absolute path to the `runtime_decl_for_` module by replacing the last segment.
if let Some(segment) = trait_.segments.last_mut() {
*segment = parse_quote!(#mod_name);
}
// Build a call to request runtime metadata for the appropriate API version.
let runtime_metadata_call = {
let api_version = extract_api_version(&impl_.attrs, impl_.span())?;
// If we've annotated an api_version, that defines the methods we need to impl.
// If we haven't, then by default we are implementing methods for whatever the
// base version of the declared runtime API is.
let base_version = if let Some(version) = api_version.custom {
quote! { #version }
} else {
quote! { #trait_::VERSION }
};
// Handle the case where eg `#[cfg_attr(feature = "foo", api_version(4))]` is
// present by using that version only when the feature is enabled and falling
// back to the above version if not.
if let Some(cfg_version) = api_version.feature_gated {
let cfg_feature = cfg_version.0;
let cfg_version = cfg_version.1;
quote! {{
if cfg!(feature = #cfg_feature) {
#trait_::runtime_metadata::#generics(#cfg_version)
} else {
#trait_::runtime_metadata::#generics(#base_version)
}
}}
} else {
quote! {
#trait_::runtime_metadata::#generics(#base_version)
}
}
};
let attrs = filter_cfg_attributes(&impl_.attrs);
metadata.push(quote!(
#( #attrs )*
#runtime_metadata_call
));
}
// Each runtime must expose the `runtime_metadata()` to fetch the runtime API metadata.
// The function is implemented by calling `impl_runtime_apis!`.
//
// However, the `construct_runtime!` may be called without calling `impl_runtime_apis!`.
// Rely on the `Deref` trait to differentiate between a runtime that implements
// APIs (by macro impl_runtime_apis!) and a runtime that is simply created (by macro
// construct_runtime!).
//
// Both `InternalConstructRuntime` and `InternalImplRuntimeApis` expose a `runtime_metadata()`
// function. `InternalConstructRuntime` is implemented by the `construct_runtime!` for Runtime
// references (`& Runtime`), while `InternalImplRuntimeApis` is implemented by the
// `impl_runtime_apis!` for Runtime (`Runtime`).
//
// 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)]
impl #crate_::metadata_ir::InternalImplRuntimeApis for #runtime_name {
fn runtime_metadata(&self) -> #crate_::vec::Vec<#crate_::metadata_ir::RuntimeApiMetadataIR> {
#crate_::vec![ #( #metadata, )* ]
}
}
}
))
}
@@ -0,0 +1,504 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::common::API_VERSION_ATTRIBUTE;
use inflector::Inflector;
use proc_macro2::{Span, TokenStream};
use proc_macro_crate::{crate_name, FoundCrate};
use quote::{format_ident, quote};
use syn::{
parenthesized, parse_quote, punctuated::Punctuated, spanned::Spanned, token::And, Attribute,
Error, Expr, ExprLit, FnArg, GenericArgument, Ident, ItemImpl, Lit, LitInt, LitStr, Meta,
MetaNameValue, Pat, Path, PathArguments, Result, ReturnType, Signature, Token, Type, TypePath,
};
/// Generates the access to the `sc_client` crate.
pub fn generate_crate_access() -> TokenStream {
match crate_name("sp-api") {
Ok(FoundCrate::Itself) => quote!(sp_api::__private),
Ok(FoundCrate::Name(renamed_name)) => {
let renamed_name = Ident::new(&renamed_name, Span::call_site());
quote!(#renamed_name::__private)
},
Err(e) => {
if let Ok(FoundCrate::Name(name)) =
crate_name(&"pezkuwi-sdk-frame").or_else(|_| crate_name(&"frame"))
{
let path = format!("{}::deps::sp_api::__private", name);
let path = syn::parse_str::<syn::Path>(&path).expect("is a valid path; qed");
quote!( #path )
} else if let Ok(FoundCrate::Name(name)) = crate_name(&"pezkuwi-sdk") {
let path = format!("{}::sp_api::__private", name);
let path = syn::parse_str::<syn::Path>(&path).expect("is a valid path; qed");
quote!( #path )
} else {
let err = Error::new(Span::call_site(), e).to_compile_error();
quote!( #err )
}
},
}
}
/// Generates the name of the module that contains the trait declaration for the runtime.
pub fn generate_runtime_mod_name_for_trait(trait_: &Ident) -> Ident {
Ident::new(
&format!("runtime_decl_for_{}", trait_.to_string().to_snake_case()),
Span::call_site(),
)
}
/// Get the type of a `syn::ReturnType`.
pub fn return_type_extract_type(rt: &ReturnType) -> Type {
match rt {
ReturnType::Default => parse_quote!(()),
ReturnType::Type(_, ref ty) => *ty.clone(),
}
}
/// Replace the `_` (wild card) parameter names in the given signature with unique identifiers.
pub fn replace_wild_card_parameter_names(input: &mut Signature) {
let mut generated_pattern_counter = 0;
input.inputs.iter_mut().for_each(|arg| {
if let FnArg::Typed(arg) = arg {
arg.pat =
Box::new(sanitize_pattern((*arg.pat).clone(), &mut generated_pattern_counter));
}
});
}
/// Fold the given `Signature` to make it usable on the client side.
pub fn fold_fn_decl_for_client_side(
input: &mut Signature,
block_hash: &TokenStream,
crate_: &TokenStream,
) {
replace_wild_card_parameter_names(input);
// Add `&self, at:& Block::Hash` as parameters to each function at the beginning.
input.inputs.insert(0, parse_quote!( __runtime_api_at_param__: #block_hash ));
input.inputs.insert(0, parse_quote!(&self));
// Wrap the output in a `Result`
input.output = {
let ty = return_type_extract_type(&input.output);
parse_quote!( -> std::result::Result<#ty, #crate_::ApiError> )
};
}
/// Sanitize the given pattern.
///
/// - `_` patterns are changed to a variable based on `counter`.
/// - `mut something` removes the `mut`.
pub fn sanitize_pattern(pat: Pat, counter: &mut u32) -> Pat {
match pat {
Pat::Wild(_) => {
let generated_name =
Ident::new(&format!("__runtime_api_generated_name_{}__", counter), pat.span());
*counter += 1;
parse_quote!( #generated_name )
},
Pat::Ident(mut pat) => {
pat.mutability = None;
pat.into()
},
_ => pat,
}
}
/// Allow `&self` in parameters of a method.
pub enum AllowSelfRefInParameters {
/// Allows `&self` in parameters, but doesn't return it as part of the parameters.
YesButIgnore,
No,
}
/// Extracts the name, the type and `&` or ``(if it is a reference or not)
/// for each parameter in the given function signature.
pub fn extract_parameter_names_types_and_borrows(
sig: &Signature,
allow_self: AllowSelfRefInParameters,
) -> Result<Vec<(Pat, Type, Option<And>)>> {
let mut result = Vec::new();
let mut generated_pattern_counter = 0;
for input in sig.inputs.iter() {
match input {
FnArg::Typed(arg) => {
let (ty, borrow) = match &*arg.ty {
Type::Reference(t) => ((*t.elem).clone(), Some(t.and_token)),
t => (t.clone(), None),
};
let name = sanitize_pattern((*arg.pat).clone(), &mut generated_pattern_counter);
result.push((name, ty, borrow));
},
FnArg::Receiver(_) if matches!(allow_self, AllowSelfRefInParameters::No) =>
return Err(Error::new(input.span(), "`self` parameter not supported!")),
FnArg::Receiver(recv) =>
if recv.mutability.is_some() || recv.reference.is_none() {
return Err(Error::new(recv.span(), "Only `&self` is supported!"));
},
}
}
Ok(result)
}
/// Prefix the given function with the trait name.
pub fn prefix_function_with_trait<F: ToString>(trait_: &Ident, function: &F) -> String {
format!("{}_{}", trait_, function.to_string())
}
/// Extracts the block type from a trait path.
///
/// It is expected that the block type is the first type in the generic arguments.
pub fn extract_block_type_from_trait_path(trait_: &Path) -> Result<&TypePath> {
let span = trait_.span();
let generics = trait_
.segments
.last()
.ok_or_else(|| Error::new(span, "Empty path not supported"))?;
match &generics.arguments {
PathArguments::AngleBracketed(ref args) => args
.args
.first()
.and_then(|v| match v {
GenericArgument::Type(Type::Path(ref block)) => Some(block),
_ => None,
})
.ok_or_else(|| Error::new(args.span(), "Missing `Block` generic parameter.")),
PathArguments::None => {
let span = trait_.segments.last().as_ref().unwrap().span();
Err(Error::new(span, "Missing `Block` generic parameter."))
},
PathArguments::Parenthesized(_) =>
Err(Error::new(generics.arguments.span(), "Unexpected parentheses in path!")),
}
}
/// Should a qualified trait path be required?
///
/// e.g. `path::Trait` is qualified and `Trait` is not.
pub enum RequireQualifiedTraitPath {
Yes,
No,
}
/// Extract the trait that is implemented by the given `ItemImpl`.
pub fn extract_impl_trait(impl_: &ItemImpl, require: RequireQualifiedTraitPath) -> Result<&Path> {
impl_
.trait_
.as_ref()
.map(|v| &v.1)
.ok_or_else(|| Error::new(impl_.span(), "Only implementation of traits are supported!"))
.and_then(|p| {
if p.segments.len() > 1 || matches!(require, RequireQualifiedTraitPath::No) {
Ok(p)
} else {
Err(Error::new(
p.span(),
"The implemented trait has to be referenced with a path, \
e.g. `impl client::Core for Runtime`.",
))
}
})
}
/// Parse the given attribute as `API_VERSION_ATTRIBUTE`.
pub fn parse_runtime_api_version(version: &Attribute) -> Result<u32> {
let version = version.parse_args::<syn::LitInt>().map_err(|_| {
Error::new(
version.span(),
&format!(
"Unexpected `{api_version}` attribute. The supported format is `{api_version}(1)`",
api_version = API_VERSION_ATTRIBUTE
),
)
})?;
version.base10_parse()
}
/// Each versioned trait is named 'ApiNameVN' where N is the specific version. E.g. TeyrchainHostV2
pub fn versioned_trait_name(trait_ident: &Ident, version: u32) -> Ident {
format_ident!("{}V{}", trait_ident, version)
}
/// Extract the documentation from the provided attributes.
pub fn get_doc_literals(attrs: &[syn::Attribute]) -> Vec<syn::Lit> {
use quote::ToTokens;
attrs
.iter()
.filter_map(|attr| {
let syn::Meta::NameValue(meta) = &attr.meta else { return None };
let Ok(lit) = syn::parse2::<syn::Lit>(meta.value.to_token_stream()) else {
unreachable!("non-lit doc attribute values do not exist");
};
meta.path.get_ident().filter(|ident| *ident == "doc").map(|_| lit)
})
.collect()
}
/// Filters all attributes except the cfg ones.
pub fn filter_cfg_attributes(attrs: &[syn::Attribute]) -> Vec<syn::Attribute> {
attrs.iter().filter(|a| a.path().is_ident("cfg")).cloned().collect()
}
fn deprecation_msg_formatter(msg: &str) -> String {
format!(
r#"{msg}
help: the following are the possible correct uses
|
| #[deprecated = "reason"]
|
| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
| #[deprecated]
|"#
)
}
fn parse_deprecated_meta(crate_: &TokenStream, attr: &syn::Attribute) -> Result<TokenStream> {
match &attr.meta {
Meta::List(meta_list) => {
let parsed = meta_list
.parse_args_with(Punctuated::<MetaNameValue, Token![,]>::parse_terminated)
.map_err(|e| Error::new(attr.span(), e.to_string()))?;
let (note, since) = parsed.iter().try_fold((None, None), |mut acc, item| {
let value = match &item.value {
Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }) => Ok(lit),
_ => Err(Error::new(
attr.span(),
deprecation_msg_formatter(
"Invalid deprecation attribute: expected string literal",
),
)),
}?;
if item.path.is_ident("note") {
acc.0.replace(value);
} else if item.path.is_ident("since") {
acc.1.replace(value);
}
Ok::<(Option<&syn::Lit>, Option<&syn::Lit>), Error>(acc)
})?;
note.map_or_else(
|| Err(Error::new(attr.span(), deprecation_msg_formatter(
"Invalid deprecation attribute: missing `note`"))),
|note| {
let since = if let Some(str) = since {
quote! { Some(#str) }
} else {
quote! { None }
};
let doc = quote! { #crate_::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #note, since: #since }};
Ok(doc)
},
)
},
Meta::NameValue(MetaNameValue {
value: Expr::Lit(ExprLit { lit: lit @ Lit::Str(_), .. }),
..
}) => {
// #[deprecated = "lit"]
let doc = quote! { #crate_::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #lit, since: None } };
Ok(doc)
},
Meta::Path(_) => {
// #[deprecated]
Ok(quote! { #crate_::metadata_ir::ItemDeprecationInfoIR::DeprecatedWithoutNote })
},
_ => Err(Error::new(
attr.span(),
deprecation_msg_formatter("Invalid deprecation attribute: expected string literal"),
)),
}
}
/// collects deprecation attribute if its present.
pub fn get_deprecation(crate_: &TokenStream, attrs: &[syn::Attribute]) -> Result<TokenStream> {
attrs
.iter()
.find(|a| a.path().is_ident("deprecated"))
.map(|a| parse_deprecated_meta(&crate_, a))
.unwrap_or_else(|| Ok(quote! {#crate_::metadata_ir::ItemDeprecationInfoIR::NotDeprecated}))
}
/// Represents an API version.
pub struct ApiVersion {
/// Corresponds to `#[api_version(X)]` attribute.
pub custom: Option<u32>,
/// Corresponds to `#[cfg_attr(feature = "enable-staging-api", api_version(99))]`
/// attribute. `String` is the feature name, `u32` the staging api version.
pub feature_gated: Option<(String, u32)>,
}
/// Extracts the value of `API_VERSION_ATTRIBUTE` and handles errors.
/// Returns:
/// - Err if the version is malformed
/// - `ApiVersion` on success. If a version is set or not is determined by the fields of
/// `ApiVersion`
pub fn extract_api_version(attrs: &[Attribute], span: Span) -> Result<ApiVersion> {
// First fetch all `API_VERSION_ATTRIBUTE` values (should be only one)
let api_ver = attrs
.iter()
.filter(|a| a.path().is_ident(API_VERSION_ATTRIBUTE))
.collect::<Vec<_>>();
if api_ver.len() > 1 {
return Err(Error::new(
span,
format!(
"Found multiple #[{}] attributes for an API implementation. \
Each runtime API can have only one version.",
API_VERSION_ATTRIBUTE
),
));
}
// Parse the runtime version if there exists one.
Ok(ApiVersion {
custom: api_ver.first().map(|v| parse_runtime_api_version(v)).transpose()?,
feature_gated: extract_cfg_api_version(attrs, span)?,
})
}
/// Parse feature flagged api_version.
/// E.g. `#[cfg_attr(feature = "enable-staging-api", api_version(99))]`
fn extract_cfg_api_version(attrs: &[Attribute], span: Span) -> Result<Option<(String, u32)>> {
let cfg_attrs = attrs.iter().filter(|a| a.path().is_ident("cfg_attr")).collect::<Vec<_>>();
let mut cfg_api_version_attr = Vec::new();
for cfg_attr in cfg_attrs {
let mut feature_name = None;
let mut api_version = None;
cfg_attr.parse_nested_meta(|m| {
if m.path.is_ident("feature") {
let a = m.value()?;
let b: LitStr = a.parse()?;
feature_name = Some(b.value());
} else if m.path.is_ident(API_VERSION_ATTRIBUTE) {
let content;
parenthesized!(content in m.input);
let ver: LitInt = content.parse()?;
api_version = Some(ver.base10_parse::<u32>()?);
}
Ok(())
})?;
// If there is a cfg attribute containing api_version - save if for processing
if let (Some(feature_name), Some(api_version)) = (feature_name, api_version) {
cfg_api_version_attr.push((feature_name, api_version, cfg_attr.span()));
}
}
if cfg_api_version_attr.len() > 1 {
let mut err = Error::new(span, format!("Found multiple feature gated api versions (cfg attribute with nested `{}` attribute). This is not supported.", API_VERSION_ATTRIBUTE));
for (_, _, attr_span) in cfg_api_version_attr {
err.combine(Error::new(attr_span, format!("`{}` found here", API_VERSION_ATTRIBUTE)));
}
return Err(err);
}
Ok(cfg_api_version_attr
.into_iter()
.next()
.map(|(feature, name, _)| (feature, name)))
}
#[cfg(test)]
mod tests {
use assert_matches::assert_matches;
use super::*;
#[test]
fn check_get_doc_literals() {
const FIRST: &'static str = "hello";
const SECOND: &'static str = "WORLD";
let doc: Attribute = parse_quote!(#[doc = #FIRST]);
let doc_world: Attribute = parse_quote!(#[doc = #SECOND]);
let attrs = vec![
doc.clone(),
parse_quote!(#[derive(Debug)]),
parse_quote!(#[test]),
parse_quote!(#[allow(non_camel_case_types)]),
doc_world.clone(),
];
let docs = get_doc_literals(&attrs);
assert_eq!(docs.len(), 2);
assert_matches!(&docs[0], syn::Lit::Str(val) if val.value() == FIRST);
assert_matches!(&docs[1], syn::Lit::Str(val) if val.value() == SECOND);
}
#[test]
fn check_filter_cfg_attributes() {
let cfg_std: Attribute = parse_quote!(#[cfg(feature = "std")]);
let cfg_benchmarks: Attribute = parse_quote!(#[cfg(feature = "runtime-benchmarks")]);
let attrs = vec![
cfg_std.clone(),
parse_quote!(#[derive(Debug)]),
parse_quote!(#[test]),
cfg_benchmarks.clone(),
parse_quote!(#[allow(non_camel_case_types)]),
];
let filtered = filter_cfg_attributes(&attrs);
assert_eq!(filtered.len(), 2);
assert_eq!(cfg_std, filtered[0]);
assert_eq!(cfg_benchmarks, filtered[1]);
}
#[test]
fn check_deprecated_attr() {
const FIRST: &'static str = "hello";
const SECOND: &'static str = "WORLD";
let simple: Attribute = parse_quote!(#[deprecated]);
let simple_path: Attribute = parse_quote!(#[deprecated = #FIRST]);
let meta_list: Attribute = parse_quote!(#[deprecated(note = #FIRST)]);
let meta_list_with_since: Attribute =
parse_quote!(#[deprecated(note = #FIRST, since = #SECOND)]);
let extra_fields: Attribute =
parse_quote!(#[deprecated(note = #FIRST, since = #SECOND, extra = "Test")]);
assert_eq!(
get_deprecation(&quote! { crate }, &[simple]).unwrap().to_string(),
quote! { crate::metadata_ir::ItemDeprecationInfoIR::DeprecatedWithoutNote }.to_string()
);
assert_eq!(
get_deprecation(&quote! { crate }, &[simple_path]).unwrap().to_string(),
quote! { crate::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #FIRST, since: None } }.to_string()
);
assert_eq!(
get_deprecation(&quote! { crate }, &[meta_list]).unwrap().to_string(),
quote! { crate::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #FIRST, since: None } }.to_string()
);
assert_eq!(
get_deprecation(&quote! { crate }, &[meta_list_with_since]).unwrap().to_string(),
quote! { crate::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string()
);
assert_eq!(
get_deprecation(&quote! { crate }, &[extra_fields]).unwrap().to_string(),
quote! { crate::metadata_ir::ItemDeprecationInfoIR::Deprecated { note: #FIRST, since: Some(#SECOND) }}.to_string()
);
}
}
+857
View File
@@ -0,0 +1,857 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Substrate runtime api
//!
//! The Substrate runtime api is the interface between the node and the runtime. There isn't a fixed
//! set of runtime apis, instead it is up to the user to declare and implement these runtime apis.
//! The declaration of a runtime api is normally done outside of a runtime, while the implementation
//! of it has to be done in the runtime. We provide the [`decl_runtime_apis!`] macro for declaring
//! a runtime api and the [`impl_runtime_apis!`] for implementing them. The macro docs provide more
//! information on how to use them and what kind of attributes we support.
//!
//! It is required that each runtime implements at least the [`Core`] runtime api. This runtime api
//! provides all the core functions that Substrate expects from a runtime.
//!
//! # Versioning
//!
//! Runtime apis support versioning. Each runtime api itself has a version attached. It is also
//! supported to change function signatures or names in a non-breaking way. For more information on
//! versioning check the [`decl_runtime_apis!`] macro.
//!
//! All runtime apis and their versions are returned as part of the [`RuntimeVersion`]. This can be
//! used to check which runtime api version is currently provided by the on-chain runtime.
//!
//! # Testing
//!
//! For testing we provide the [`mock_impl_runtime_apis!`] macro that lets you implement a runtime
//! api for a mocked object to use it in tests.
//!
//! # Logging
//!
//! Substrate supports logging from the runtime in native and in wasm. For that purpose it provides
//! the [`RuntimeLogger`](sp_runtime::runtime_logger::RuntimeLogger). This runtime logger is
//! automatically enabled for each call into the runtime through the runtime api. As logging
//! introduces extra code that isn't actually required for the logic of your runtime and also
//! increases the final wasm blob size, it is recommended to disable the logging for on-chain
//! wasm blobs. This can be done by enabling the `disable-logging` feature of this crate. Be aware
//! that this feature instructs `log` and `tracing` to disable logging at compile time by setting
//! the `max_level_off` feature for these crates. So, you should not enable this feature for a
//! native build as otherwise the node will not output any log messages.
//!
//! # How does it work?
//!
//! Each runtime api is declared as a trait with functions. When compiled to WASM, each implemented
//! runtime api function is exported as a function with the following naming scheme
//! `${TRAIT_NAME}_${FUNCTION_NAME}`. Such a function has the following signature
//! `(ptr: *u8, length: u32) -> u64`. It takes a pointer to an `u8` array and its length as an
//! argument. This `u8` array is expected to be the SCALE encoded parameters of the function as
//! defined in the trait. The return value is an `u64` that represents `length << 32 | pointer` of
//! an `u8` array. This return value `u8` array contains the SCALE encoded return value as defined
//! by the trait function. The macros take care to encode the parameters and to decode the return
//! value.
#![cfg_attr(not(feature = "std"), no_std)]
// Make doc tests happy
extern crate self as sp_api;
extern crate alloc;
/// Private exports used by the macros.
///
/// This is seen as internal API and can change at any point.
#[doc(hidden)]
pub mod __private {
#[cfg(feature = "std")]
mod std_imports {
pub use hash_db::Hasher;
pub use sp_core::traits::CallContext;
pub use sp_externalities::{Extension, Extensions, TransactionType};
pub use sp_runtime::StateVersion;
pub use sp_state_machine::{
Backend as StateBackend, InMemoryBackend, OverlayedChanges, StorageProof, TrieBackend,
TrieBackendBuilder,
};
}
#[cfg(feature = "std")]
pub use std_imports::*;
pub use crate::*;
pub use alloc::vec;
pub use codec::{self, Decode, DecodeLimit, Encode};
pub use core::{mem, slice};
pub use scale_info;
pub use sp_core::offchain;
#[cfg(not(feature = "std"))]
pub use sp_core::to_substrate_wasm_fn_return_value;
#[cfg(feature = "frame-metadata")]
pub use sp_metadata_ir::{self as metadata_ir, frame_metadata as metadata};
pub use sp_runtime::{
generic::BlockId,
traits::{Block as BlockT, Hash as HashT, HashingFor, Header as HeaderT, NumberFor},
transaction_validity::TransactionValidity,
ExtrinsicInclusionMode, TransactionOutcome,
};
pub use sp_version::{create_apis_vec, ApiId, ApisVec, RuntimeVersion};
#[cfg(all(any(target_arch = "riscv32", target_arch = "riscv64"), substrate_runtime))]
pub use sp_runtime_interface::polkavm::{polkavm_abi, polkavm_export};
}
#[cfg(feature = "std")]
pub use sp_core::traits::CallContext;
use sp_core::OpaqueMetadata;
#[cfg(feature = "std")]
use sp_externalities::{Extension, Extensions};
#[cfg(feature = "std")]
use sp_runtime::traits::HashingFor;
#[cfg(feature = "std")]
pub use sp_runtime::TransactionOutcome;
use sp_runtime::{traits::Block as BlockT, ExtrinsicInclusionMode};
#[cfg(feature = "std")]
pub use sp_state_machine::StorageProof;
#[cfg(feature = "std")]
use sp_state_machine::{backend::AsTrieBackend, Backend as StateBackend, OverlayedChanges};
use sp_version::RuntimeVersion;
#[cfg(feature = "std")]
use std::cell::RefCell;
/// Declares given traits as runtime apis.
///
/// The macro will create two declarations, one for using on the client side and one for using
/// on the runtime side. The declaration for the runtime side is hidden in its own module.
/// The client side declaration gets two extra parameters per function,
/// `&self` and `at: Block::Hash`. The runtime side declaration will match the given trait
/// declaration. Besides one exception, the macro adds an extra generic parameter `Block:
/// BlockT` to the client side and the runtime side. This generic parameter is usable by the
/// user.
///
/// For implementing these macros you should use the
/// [`impl_runtime_apis!`] macro.
///
/// # Example
///
/// ```rust
/// sp_api::decl_runtime_apis! {
/// /// Declare the api trait.
/// pub trait Balance {
/// /// Get the balance.
/// fn get_balance() -> u64;
/// /// Set the balance.
/// fn set_balance(val: u64);
/// }
///
/// /// You can declare multiple api traits in one macro call.
/// /// In one module you can call the macro at maximum one time.
/// pub trait BlockBuilder {
/// /// The macro adds an explicit `Block: BlockT` generic parameter for you.
/// /// You can use this generic parameter as you would defined it manually.
/// fn build_block() -> Block;
/// }
/// }
///
/// # fn main() {}
/// ```
///
/// # Runtime api trait versioning
///
/// To support versioning of the traits, the macro supports the attribute `#[api_version(1)]`.
/// The attribute supports any `u32` as version. By default, each trait is at version `1`, if
/// no version is provided. We also support changing the signature of a method. This signature
/// change is highlighted with the `#[changed_in(2)]` attribute above a method. A method that
/// is tagged with this attribute is callable by the name `METHOD_before_version_VERSION`. This
/// method will only support calling into wasm, trying to call into native will fail (change
/// the spec version!). Such a method also does not need to be implemented in the runtime. It
/// is required that there exist the "default" of the method without the `#[changed_in(_)]`
/// attribute, this method will be used to call the current default implementation.
///
/// ```rust
/// sp_api::decl_runtime_apis! {
/// /// Declare the api trait.
/// #[api_version(2)]
/// pub trait Balance {
/// /// Get the balance.
/// fn get_balance() -> u64;
/// /// Set balance.
/// fn set_balance(val: u64);
/// /// Set balance, old version.
/// ///
/// /// Is callable by `set_balance_before_version_2`.
/// #[changed_in(2)]
/// fn set_balance(val: u16);
/// /// In version 2, we added this new function.
/// fn increase_balance(val: u64);
/// }
/// }
///
/// # fn main() {}
/// ```
///
/// To check if a given runtime implements a runtime api trait, the `RuntimeVersion` has the
/// function `has_api<A>()`. Also the `ApiExt` provides a function `has_api<A>(at: Hash)`
/// to check if the runtime at the given block id implements the requested runtime api trait.
///
/// # Declaring multiple api versions
///
/// Optionally multiple versions of the same api can be declared. This is useful for
/// development purposes. For example you want to have a testing version of the api which is
/// available only on a testnet. You can define one stable and one development version. This
/// can be done like this:
/// ```rust
/// sp_api::decl_runtime_apis! {
/// /// Declare the api trait.
/// #[api_version(2)]
/// pub trait Balance {
/// /// Get the balance.
/// fn get_balance() -> u64;
/// /// Set the balance.
/// fn set_balance(val: u64);
/// /// Transfer the balance to another user id
/// #[api_version(3)]
/// fn transfer_balance(uid: u64);
/// }
/// }
///
/// # fn main() {}
/// ```
/// The example above defines two api versions - 2 and 3. Version 2 contains `get_balance` and
/// `set_balance`. Version 3 additionally contains `transfer_balance`, which is not available
/// in version 2. Version 2 in this case is considered the default/base version of the api.
/// More than two versions can be defined this way. For example:
/// ```rust
/// sp_api::decl_runtime_apis! {
/// /// Declare the api trait.
/// #[api_version(2)]
/// pub trait Balance {
/// /// Get the balance.
/// fn get_balance() -> u64;
/// /// Set the balance.
/// fn set_balance(val: u64);
/// /// Transfer the balance to another user id
/// #[api_version(3)]
/// fn transfer_balance(uid: u64);
/// /// Clears the balance
/// #[api_version(4)]
/// fn clear_balance();
/// }
/// }
///
/// # fn main() {}
/// ```
/// Note that the latest version (4 in our example above) always contains all methods from all
/// the versions before.
///
/// ## Note on deprecation.
///
/// - Usage of `deprecated` attribute will propagate deprecation information to the metadata.
/// - For general usage examples of `deprecated` attribute please refer to <https://doc.rust-lang.org/nightly/reference/attributes/diagnostics.html#the-deprecated-attribute>
pub use sp_api_proc_macro::decl_runtime_apis;
/// Tags given trait implementations as runtime apis.
///
/// All traits given to this macro, need to be declared with the
/// [`decl_runtime_apis!`](macro.decl_runtime_apis.html) macro. The implementation of the trait
/// should follow the declaration given to the
/// [`decl_runtime_apis!`](macro.decl_runtime_apis.html) macro, besides the `Block` type that
/// is required as first generic parameter for each runtime api trait. When implementing a
/// runtime api trait, it is required that the trait is referenced by a path, e.g. `impl
/// my_trait::MyTrait for Runtime`. The macro will use this path to access the declaration of
/// the trait for the runtime side.
///
/// The macro also generates the api implementations for the client side and provides it
/// through the `RuntimeApi` type. The `RuntimeApi` is hidden behind a `feature` called `std`.
///
/// To expose version information about all implemented api traits, the constant
/// `RUNTIME_API_VERSIONS` is generated. This constant should be used to instantiate the `apis`
/// field of `RuntimeVersion`.
///
/// # Example
///
/// ```rust
/// extern crate alloc;
/// #
/// # use sp_runtime::{ExtrinsicInclusionMode, traits::Block as BlockT};
/// # use sp_test_primitives::Block;
/// #
/// # /// The declaration of the `Runtime` type is done by the `construct_runtime!` macro
/// # /// in a real runtime.
/// # pub enum Runtime {}
/// #
/// # sp_api::decl_runtime_apis! {
/// # /// Declare the api trait.
/// # pub trait Balance {
/// # /// Get the balance.
/// # fn get_balance() -> u64;
/// # /// Set the balance.
/// # fn set_balance(val: u64);
/// # }
/// # pub trait BlockBuilder {
/// # fn build_block() -> Block;
/// # }
/// # }
///
/// /// All runtime api implementations need to be done in one call of the macro!
/// sp_api::impl_runtime_apis! {
/// # impl sp_api::Core<Block> for Runtime {
/// # fn version() -> sp_version::RuntimeVersion {
/// # unimplemented!()
/// # }
/// # fn execute_block(_block: <Block as BlockT>::LazyBlock) {}
/// # fn initialize_block(_header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode {
/// # unimplemented!()
/// # }
/// # }
///
/// impl self::Balance<Block> for Runtime {
/// fn get_balance() -> u64 {
/// 1
/// }
/// fn set_balance(_bal: u64) {
/// // Store the balance
/// }
/// }
///
/// impl self::BlockBuilder<Block> for Runtime {
/// fn build_block() -> Block {
/// unimplemented!("Please implement me!")
/// }
/// }
/// }
///
/// /// Runtime version. This needs to be declared for each runtime.
/// pub const VERSION: sp_version::RuntimeVersion = sp_version::RuntimeVersion {
/// spec_name: alloc::borrow::Cow::Borrowed("node"),
/// impl_name: alloc::borrow::Cow::Borrowed("test-node"),
/// authoring_version: 1,
/// spec_version: 1,
/// impl_version: 0,
/// // Here we are exposing the runtime api versions.
/// apis: RUNTIME_API_VERSIONS,
/// transaction_version: 1,
/// system_version: 1,
/// };
///
/// # fn main() {}
/// ```
///
/// # Implementing specific api version
///
/// If `decl_runtime_apis!` declares multiple versions for an api `impl_runtime_apis!`
/// should specify which version it implements by adding `api_version` attribute to the
/// `impl` block. If omitted - the base/default version is implemented. Here is an example:
/// ```ignore
/// sp_api::impl_runtime_apis! {
/// #[api_version(3)]
/// impl self::Balance<Block> for Runtime {
/// // implementation
/// }
/// }
/// ```
/// In this case `Balance` api version 3 is being implemented for `Runtime`. The `impl` block
/// must contain all methods declared in version 3 and below.
///
/// # Conditional version implementation
///
/// `impl_runtime_apis!` supports `cfg_attr` attribute for conditional compilation. For example
/// let's say you want to implement a staging version of the runtime api and put it behind a
/// feature flag. You can do it this way:
/// ```ignore
/// pub enum Runtime {}
/// sp_api::decl_runtime_apis! {
/// pub trait ApiWithStagingMethod {
/// fn stable_one(data: u64);
///
/// #[api_version(99)]
/// fn staging_one();
/// }
/// }
///
/// sp_api::impl_runtime_apis! {
/// #[cfg_attr(feature = "enable-staging-api", api_version(99))]
/// impl self::ApiWithStagingMethod<Block> for Runtime {
/// fn stable_one(_: u64) {}
///
/// #[cfg(feature = "enable-staging-api")]
/// fn staging_one() {}
/// }
/// }
/// ```
///
/// [`decl_runtime_apis!`] declares two version of the api - 1 (the default one, which is
/// considered stable in our example) and 99 (which is considered staging). In
/// `impl_runtime_apis!` a `cfg_attr` attribute is attached to the `ApiWithStagingMethod`
/// implementation. If the code is compiled with `enable-staging-api` feature a version 99 of
/// the runtime api will be built which will include `staging_one`. Note that `staging_one`
/// implementation is feature gated by `#[cfg(feature = ... )]` attribute.
///
/// If the code is compiled without `enable-staging-api` version 1 (the default one) will be
/// built which doesn't include `staging_one`.
///
/// `cfg_attr` can also be used together with `api_version`. For the next snippet will build
/// version 99 if `enable-staging-api` is enabled and version 2 otherwise because both
/// `cfg_attr` and `api_version` are attached to the impl block:
/// ```ignore
/// #[cfg_attr(feature = "enable-staging-api", api_version(99))]
/// #[api_version(2)]
/// impl self::ApiWithStagingAndVersionedMethods<Block> for Runtime {
/// // impl skipped
/// }
/// ```
pub use sp_api_proc_macro::impl_runtime_apis;
/// Mocks given trait implementations as runtime apis.
///
/// Accepts similar syntax as [`impl_runtime_apis!`] and generates simplified mock
/// implementations of the given runtime apis. The difference in syntax is that the trait does
/// not need to be referenced by a qualified path, methods accept the `&self` parameter and the
/// error type can be specified as associated type. If no error type is specified [`String`] is
/// used as error type.
///
/// Besides implementing the given traits, the [`Core`] and [`ApiExt`] are implemented
/// automatically.
///
/// # Example
///
/// ```rust
/// # use sp_runtime::traits::Block as BlockT;
/// # use sp_test_primitives::Block;
/// #
/// # sp_api::decl_runtime_apis! {
/// # /// Declare the api trait.
/// # pub trait Balance {
/// # /// Get the balance.
/// # fn get_balance() -> u64;
/// # /// Set the balance.
/// # fn set_balance(val: u64);
/// # }
/// # pub trait BlockBuilder {
/// # fn build_block() -> Block;
/// # }
/// # }
/// struct MockApi {
/// balance: u64,
/// }
///
/// /// All runtime api mock implementations need to be done in one call of the macro!
/// sp_api::mock_impl_runtime_apis! {
/// impl Balance<Block> for MockApi {
/// /// Here we take the `&self` to access the instance.
/// fn get_balance(&self) -> u64 {
/// self.balance
/// }
/// fn set_balance(_bal: u64) {
/// // Store the balance
/// }
/// }
///
/// impl BlockBuilder<Block> for MockApi {
/// fn build_block() -> Block {
/// unimplemented!("Not Required in tests")
/// }
/// }
/// }
///
/// # fn main() {}
/// ```
///
/// # `advanced` attribute
///
/// This attribute can be placed above individual function in the mock implementation to
/// request more control over the function declaration. From the client side each runtime api
/// function is called with the `at` parameter that is a [`Hash`](sp_runtime::traits::Hash).
/// When using the `advanced` attribute, the macro expects that the first parameter of the
/// function is this `at` parameter. Besides that the macro also doesn't do the automatic
/// return value rewrite, which means that full return value must be specified. The full return
/// value is constructed like [`Result`]`<<ReturnValue>, Error>` while `ReturnValue` being the
/// return value that is specified in the trait declaration.
///
/// ## Example
/// ```rust
/// # use sp_runtime::traits::Block as BlockT;
/// # use sp_test_primitives::Block;
/// # use codec;
/// #
/// # sp_api::decl_runtime_apis! {
/// # /// Declare the api trait.
/// # pub trait Balance {
/// # /// Get the balance.
/// # fn get_balance() -> u64;
/// # /// Set the balance.
/// # fn set_balance(val: u64);
/// # }
/// # }
/// struct MockApi {
/// balance: u64,
/// }
///
/// sp_api::mock_impl_runtime_apis! {
/// impl Balance<Block> for MockApi {
/// #[advanced]
/// fn get_balance(&self, at: <Block as BlockT>::Hash) -> Result<u64, sp_api::ApiError> {
/// println!("Being called at: {}", at);
///
/// Ok(self.balance.into())
/// }
/// #[advanced]
/// fn set_balance(at: <Block as BlockT>::Hash, val: u64) -> Result<(), sp_api::ApiError> {
/// println!("Being called at: {}", at);
///
/// Ok(().into())
/// }
/// }
/// }
///
/// # fn main() {}
/// ```
pub use sp_api_proc_macro::mock_impl_runtime_apis;
/// A type that records all accessed trie nodes and generates a proof out of it.
#[cfg(feature = "std")]
pub type ProofRecorder<B> = sp_trie::recorder::Recorder<HashingFor<B>>;
#[cfg(feature = "std")]
pub type ProofRecorderIgnoredNodes<B> = sp_trie::recorder::IgnoredNodes<<B as BlockT>::Hash>;
#[cfg(feature = "std")]
pub type StorageChanges<Block> = sp_state_machine::StorageChanges<HashingFor<Block>>;
/// Something that can be constructed to a runtime api.
#[cfg(feature = "std")]
pub trait ConstructRuntimeApi<Block: BlockT, C: CallApiAt<Block>> {
/// The actual runtime api that will be constructed.
type RuntimeApi: ApiExt<Block>;
/// Construct an instance of the runtime api.
fn construct_runtime_api(call: &C) -> ApiRef<'_, Self::RuntimeApi>;
}
#[docify::export]
/// Init the [`RuntimeLogger`](sp_runtime::runtime_logger::RuntimeLogger).
pub fn init_runtime_logger() {
#[cfg(not(feature = "disable-logging"))]
sp_runtime::runtime_logger::RuntimeLogger::init();
}
/// An error describing which API call failed.
#[cfg(feature = "std")]
#[derive(Debug, thiserror::Error)]
pub enum ApiError {
#[error("Failed to decode return value of {function}: {error} raw data: {raw:?}")]
FailedToDecodeReturnValue {
function: &'static str,
#[source]
error: codec::Error,
raw: Vec<u8>,
},
#[error("Failed to convert return value from runtime to node of {function}")]
FailedToConvertReturnValue {
function: &'static str,
#[source]
error: codec::Error,
},
#[error("Failed to convert parameter `{parameter}` from node to runtime of {function}")]
FailedToConvertParameter {
function: &'static str,
parameter: &'static str,
#[source]
error: codec::Error,
},
#[error("The given `StateBackend` isn't a `TrieBackend`.")]
StateBackendIsNotTrie,
#[error(transparent)]
Application(#[from] Box<dyn std::error::Error + Send + Sync>),
#[error("Api called for an unknown Block: {0}")]
UnknownBlock(String),
#[error("Using the same api instance to call into multiple independent blocks.")]
UsingSameInstanceForDifferentBlocks,
}
/// Extends the runtime api implementation with some common functionality.
#[cfg(feature = "std")]
pub trait ApiExt<Block: BlockT> {
/// Execute the given closure inside a new transaction.
///
/// Depending on the outcome of the closure, the transaction is committed or rolled-back.
///
/// The internal result of the closure is returned afterwards.
fn execute_in_transaction<F: FnOnce(&Self) -> TransactionOutcome<R>, R>(&self, call: F) -> R
where
Self: Sized;
/// Checks if the given api is implemented and versions match.
fn has_api<A: RuntimeApiInfo + ?Sized>(&self, at_hash: Block::Hash) -> Result<bool, ApiError>
where
Self: Sized;
/// Check if the given api is implemented and the version passes a predicate.
fn has_api_with<A: RuntimeApiInfo + ?Sized, P: Fn(u32) -> bool>(
&self,
at_hash: Block::Hash,
pred: P,
) -> Result<bool, ApiError>
where
Self: Sized;
/// Returns the version of the given api.
fn api_version<A: RuntimeApiInfo + ?Sized>(
&self,
at_hash: Block::Hash,
) -> Result<Option<u32>, ApiError>
where
Self: Sized;
/// Start recording all accessed trie nodes.
///
/// The recorded trie nodes can be converted into a proof using [`Self::extract_proof`].
fn record_proof(&mut self);
/// Start recording all accessed trie nodes using the given proof recorder.
///
/// The recorded trie nodes can be converted into a proof using [`Self::extract_proof`].
fn record_proof_with_recorder(&mut self, recorder: ProofRecorder<Block>);
/// Extract the recorded proof.
///
/// This stops the proof recording.
///
/// If `record_proof` was not called before, this will return `None`.
fn extract_proof(&mut self) -> Option<StorageProof>;
/// Returns the current active proof recorder.
fn proof_recorder(&self) -> Option<ProofRecorder<Block>>;
/// Convert the api object into the storage changes that were done while executing runtime
/// api functions.
///
/// After executing this function, all collected changes are reset.
fn into_storage_changes<B: StateBackend<HashingFor<Block>>>(
&self,
backend: &B,
parent_hash: Block::Hash,
) -> Result<StorageChanges<Block>, String>
where
Self: Sized;
/// Set the [`CallContext`] to be used by the runtime api calls done by this instance.
fn set_call_context(&mut self, call_context: CallContext);
/// Register an [`Extension`] that will be accessible while executing a runtime api call.
fn register_extension<E: Extension>(&mut self, extension: E);
}
/// Parameters for [`CallApiAt::call_api_at`].
#[cfg(feature = "std")]
pub struct CallApiAtParams<'a, Block: BlockT> {
/// The block id that determines the state that should be setup when calling the function.
pub at: Block::Hash,
/// The name of the function that should be called.
pub function: &'static str,
/// The encoded arguments of the function.
pub arguments: Vec<u8>,
/// The overlayed changes that are on top of the state.
pub overlayed_changes: &'a RefCell<OverlayedChanges<HashingFor<Block>>>,
/// The call context of this call.
pub call_context: CallContext,
/// The optional proof recorder for recording storage accesses.
pub recorder: &'a Option<ProofRecorder<Block>>,
/// The extensions that should be used for this call.
pub extensions: &'a RefCell<Extensions>,
}
/// Something that can call into an api at a given block.
#[cfg(feature = "std")]
pub trait CallApiAt<Block: BlockT> {
/// The state backend that is used to store the block states.
type StateBackend: StateBackend<HashingFor<Block>> + AsTrieBackend<HashingFor<Block>>;
/// Calls the given api function with the given encoded arguments at the given block and returns
/// the encoded result.
fn call_api_at(&self, params: CallApiAtParams<Block>) -> Result<Vec<u8>, ApiError>;
/// Returns the runtime version at the given block.
fn runtime_version_at(&self, at_hash: Block::Hash) -> Result<RuntimeVersion, ApiError>;
/// Get the state `at` the given block.
fn state_at(&self, at: Block::Hash) -> Result<Self::StateBackend, ApiError>;
/// Initialize the `extensions` for the given block `at` by using the global extensions factory.
fn initialize_extensions(
&self,
at: Block::Hash,
extensions: &mut Extensions,
) -> Result<(), ApiError>;
}
#[cfg(feature = "std")]
impl<Block: BlockT, T: CallApiAt<Block>> CallApiAt<Block> for std::sync::Arc<T> {
type StateBackend = T::StateBackend;
fn call_api_at(&self, params: CallApiAtParams<Block>) -> Result<Vec<u8>, ApiError> {
(**self).call_api_at(params)
}
fn runtime_version_at(
&self,
at_hash: <Block as BlockT>::Hash,
) -> Result<RuntimeVersion, ApiError> {
(**self).runtime_version_at(at_hash)
}
fn state_at(&self, at: <Block as BlockT>::Hash) -> Result<Self::StateBackend, ApiError> {
(**self).state_at(at)
}
fn initialize_extensions(
&self,
at: <Block as BlockT>::Hash,
extensions: &mut Extensions,
) -> Result<(), ApiError> {
(**self).initialize_extensions(at, extensions)
}
}
/// Auxiliary wrapper that holds an api instance and binds it to the given lifetime.
#[cfg(feature = "std")]
pub struct ApiRef<'a, T>(T, std::marker::PhantomData<&'a ()>);
#[cfg(feature = "std")]
impl<'a, T> From<T> for ApiRef<'a, T> {
fn from(api: T) -> Self {
ApiRef(api, Default::default())
}
}
#[cfg(feature = "std")]
impl<'a, T> std::ops::Deref for ApiRef<'a, T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
#[cfg(feature = "std")]
impl<'a, T> std::ops::DerefMut for ApiRef<'a, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.0
}
}
/// Something that provides a runtime api.
#[cfg(feature = "std")]
pub trait ProvideRuntimeApi<Block: BlockT> {
/// The concrete type that provides the api.
type Api: ApiExt<Block>;
/// Returns the runtime api.
/// The returned instance will keep track of modifications to the storage. Any successful
/// call to an api function, will `commit` its changes to an internal buffer. Otherwise,
/// the modifications will be `discarded`. The modifications will not be applied to the
/// storage, even on a `commit`.
fn runtime_api(&self) -> ApiRef<'_, Self::Api>;
}
/// Something that provides information about a runtime api.
#[cfg(feature = "std")]
pub trait RuntimeApiInfo {
/// The identifier of the runtime api.
const ID: [u8; 8];
/// The version of the runtime api.
const VERSION: u32;
}
/// The number of bytes required to encode a [`RuntimeApiInfo`].
///
/// 8 bytes for `ID` and 4 bytes for a version.
pub const RUNTIME_API_INFO_SIZE: usize = 12;
/// Crude and simple way to serialize the `RuntimeApiInfo` into a bunch of bytes.
pub const fn serialize_runtime_api_info(id: [u8; 8], version: u32) -> [u8; RUNTIME_API_INFO_SIZE] {
let version = version.to_le_bytes();
let mut r = [0; RUNTIME_API_INFO_SIZE];
r[0] = id[0];
r[1] = id[1];
r[2] = id[2];
r[3] = id[3];
r[4] = id[4];
r[5] = id[5];
r[6] = id[6];
r[7] = id[7];
r[8] = version[0];
r[9] = version[1];
r[10] = version[2];
r[11] = version[3];
r
}
/// Deserialize the runtime API info serialized by [`serialize_runtime_api_info`].
pub fn deserialize_runtime_api_info(bytes: [u8; RUNTIME_API_INFO_SIZE]) -> ([u8; 8], u32) {
let id: [u8; 8] = bytes[0..8]
.try_into()
.expect("the source slice size is equal to the dest array length; qed");
let version = u32::from_le_bytes(
bytes[8..12]
.try_into()
.expect("the source slice size is equal to the array length; qed"),
);
(id, version)
}
decl_runtime_apis! {
/// The `Core` runtime api that every Substrate runtime needs to implement.
#[core_trait]
#[api_version(5)]
pub trait Core {
/// Returns the version of the runtime.
fn version() -> RuntimeVersion;
/// Execute the given block.
fn execute_block(block: Block::LazyBlock);
/// Initialize a block with the given header.
#[changed_in(5)]
#[renamed("initialise_block", 2)]
fn initialize_block(header: &<Block as BlockT>::Header);
/// Initialize a block with the given header and return the runtime executive mode.
fn initialize_block(header: &<Block as BlockT>::Header) -> ExtrinsicInclusionMode;
}
/// The `Metadata` api trait that returns metadata for the runtime.
#[api_version(2)]
pub trait Metadata {
/// Returns the metadata of a runtime.
fn metadata() -> OpaqueMetadata;
/// Returns the metadata at a given version.
///
/// If the given `version` isn't supported, this will return `None`.
/// Use [`Self::metadata_versions`] to find out about supported metadata version of the runtime.
fn metadata_at_version(version: u32) -> Option<OpaqueMetadata>;
/// Returns the supported metadata versions.
///
/// This can be used to call `metadata_at_version`.
fn metadata_versions() -> alloc::vec::Vec<u32>;
}
}
sp_core::generate_feature_enabled_macro!(std_enabled, feature = "std", $);
sp_core::generate_feature_enabled_macro!(std_disabled, not(feature = "std"), $);
sp_core::generate_feature_enabled_macro!(frame_metadata_enabled, feature = "frame-metadata", $);
+55
View File
@@ -0,0 +1,55 @@
[package]
name = "sp-api-test"
version = "2.0.1"
authors.workspace = true
edition.workspace = true
license = "Apache-2.0"
publish = false
homepage.workspace = true
repository.workspace = true
[lints]
workspace = true
[package.metadata.docs.rs]
targets = ["x86_64-unknown-linux-gnu"]
[[bench]]
name = "bench"
harness = false
[dependencies]
codec = { workspace = true, default-features = true }
rustversion = { workspace = true }
sc-block-builder = { workspace = true, default-features = true }
scale-info = { features = ["derive"], workspace = true }
sp-api = { workspace = true, default-features = true }
sp-consensus = { workspace = true, default-features = true }
sp-externalities = { workspace = true, default-features = true }
sp-metadata-ir = { workspace = true, default-features = true }
sp-runtime = { workspace = true, default-features = true }
sp-state-machine = { workspace = true, default-features = true }
sp-tracing = { workspace = true, default-features = true }
sp-version = { workspace = true, default-features = true }
substrate-test-runtime-client = { workspace = true }
trybuild = { workspace = true }
[dev-dependencies]
criterion = { workspace = true, default-features = true }
futures = { workspace = true }
log = { workspace = true, default-features = true }
sp-core = { workspace = true, default-features = true }
static_assertions = { workspace = true, default-features = true }
[features]
enable-staging-api = []
disable-ui-tests = []
runtime-benchmarks = [
"sc-block-builder/runtime-benchmarks",
"sp-api/runtime-benchmarks",
"sp-consensus/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"sp-state-machine/runtime-benchmarks",
"sp-version/runtime-benchmarks",
"substrate-test-runtime-client/runtime-benchmarks",
]
@@ -0,0 +1,71 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use criterion::{criterion_group, criterion_main, Criterion};
use sp_api::ProvideRuntimeApi;
use substrate_test_runtime_client::{
runtime::TestAPI, DefaultTestClientBuilderExt, TestClientBuilder, TestClientBuilderExt,
};
fn sp_api_benchmark(c: &mut Criterion) {
c.bench_function("add one with same runtime api", |b| {
let client = substrate_test_runtime_client::new();
let runtime_api = client.runtime_api();
let best_hash = client.chain_info().best_hash;
b.iter(|| runtime_api.benchmark_add_one(best_hash, &1))
});
c.bench_function("add one with recreating runtime api", |b| {
let client = substrate_test_runtime_client::new();
let best_hash = client.chain_info().best_hash;
b.iter(|| client.runtime_api().benchmark_add_one(best_hash, &1))
});
c.bench_function("vector add one with same runtime api", |b| {
let client = substrate_test_runtime_client::new();
let runtime_api = client.runtime_api();
let best_hash = client.chain_info().best_hash;
let data = vec![0; 1000];
b.iter_with_large_drop(|| runtime_api.benchmark_vector_add_one(best_hash, &data))
});
c.bench_function("vector add one with recreating runtime api", |b| {
let client = substrate_test_runtime_client::new();
let best_hash = client.chain_info().best_hash;
let data = vec![0; 1000];
b.iter_with_large_drop(|| client.runtime_api().benchmark_vector_add_one(best_hash, &data))
});
c.bench_function("calling function by function pointer in wasm", |b| {
let client = TestClientBuilder::new().build();
let best_hash = client.chain_info().best_hash;
b.iter(|| client.runtime_api().benchmark_indirect_call(best_hash).unwrap())
});
c.bench_function("calling function", |b| {
let client = TestClientBuilder::new().build();
let best_hash = client.chain_info().best_hash;
b.iter(|| client.runtime_api().benchmark_direct_call(best_hash).unwrap())
});
}
criterion_group!(benches, sp_api_benchmark);
criterion_main!(benches);
@@ -0,0 +1,378 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use sp_api::{
decl_runtime_apis, impl_runtime_apis, mock_impl_runtime_apis, ApiError, ApiExt, RuntimeApiInfo,
};
use sp_runtime::traits::Block as BlockT;
use substrate_test_runtime_client::runtime::{Block, Hash};
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
/// runtime.
pub struct Runtime {}
decl_runtime_apis! {
#[deprecated]
pub trait Api {
fn test(data: u64);
fn something_with_block(block: Block) -> Block;
fn function_with_two_args(data: u64, block: Block);
fn same_name();
fn wild_card(_: u32);
}
#[api_version(2)]
pub trait ApiWithCustomVersion {
fn same_name();
#[changed_in(2)]
fn same_name() -> String;
}
#[api_version(2)]
pub trait ApiWithMultipleVersions {
fn stable_one(data: u64);
#[api_version(3)]
fn new_one();
#[api_version(4)]
fn glory_one();
}
pub trait ApiWithStagingMethod {
fn stable_one(data: u64);
#[api_version(99)]
fn staging_one();
}
pub trait ApiWithStagingAndVersionedMethods {
fn stable_one(data: u64);
#[api_version(2)]
fn new_one();
#[api_version(99)]
fn staging_one();
}
#[api_version(2)]
pub trait ApiWithStagingAndChangedBase {
fn stable_one(data: u64);
fn new_one();
#[api_version(99)]
fn staging_one();
}
}
impl_runtime_apis! {
#[allow(deprecated)]
impl self::Api<Block> for Runtime {
fn test(_: u64) {
unimplemented!()
}
// Ensure that we accept `mut`
fn something_with_block(mut _block: Block) -> Block {
unimplemented!()
}
fn function_with_two_args(_: u64, _: Block) {
unimplemented!()
}
fn same_name() {}
fn wild_card(_: u32) {}
}
impl self::ApiWithCustomVersion<Block> for Runtime {
fn same_name() {}
}
#[api_version(3)]
impl self::ApiWithMultipleVersions<Block> for Runtime {
fn stable_one(_: u64) {}
fn new_one() {}
}
#[cfg_attr(feature = "enable-staging-api", api_version(99))]
impl self::ApiWithStagingMethod<Block> for Runtime {
fn stable_one(_: u64) {}
#[cfg(feature = "enable-staging-api")]
fn staging_one() { }
}
#[cfg_attr(feature = "enable-staging-api", api_version(99))]
#[api_version(2)]
impl self::ApiWithStagingAndVersionedMethods<Block> for Runtime {
fn stable_one(_: u64) {}
fn new_one() {}
#[cfg(feature = "enable-staging-api")]
fn staging_one() {}
}
#[cfg_attr(feature = "enable-staging-api", api_version(99))]
impl self::ApiWithStagingAndChangedBase<Block> for Runtime {
fn stable_one(_: u64) {}
fn new_one() {}
#[cfg(feature = "enable-staging-api")]
fn staging_one() {}
}
impl sp_api::Core<Block> for Runtime {
fn version() -> sp_version::RuntimeVersion {
unimplemented!()
}
fn execute_block(_: <Block as BlockT>::LazyBlock) {
unimplemented!()
}
fn initialize_block(_: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
unimplemented!()
}
}
}
struct MockApi {
block: Option<Block>,
}
mock_impl_runtime_apis! {
#[allow(deprecated)]
impl Api<Block> for MockApi {
fn test(_: u64) {
unimplemented!()
}
fn something_with_block(&self, _: Block) -> Block {
self.block.clone().unwrap()
}
fn function_with_two_args(_: u64, _: Block) {
unimplemented!()
}
#[advanced]
fn same_name(_: <Block as BlockT>::Hash) -> Result<(), ApiError> {
Ok(().into())
}
#[advanced]
fn wild_card(at: <Block as BlockT>::Hash, _: u32) -> Result<(), ApiError> {
if Hash::repeat_byte(0x0f) == at {
// yeah
Ok(().into())
} else {
Err((Box::from("Test error") as Box<dyn std::error::Error + Send + Sync>).into())
}
}
}
impl ApiWithCustomVersion<Block> for MockApi {
fn same_name() {}
}
}
type TestClient = substrate_test_runtime_client::client::Client<
substrate_test_runtime_client::Backend,
substrate_test_runtime_client::ExecutorDispatch,
Block,
RuntimeApi,
>;
#[test]
#[allow(deprecated)]
fn test_client_side_function_signature() {
let _test: fn(
&RuntimeApiImpl<Block, TestClient>,
<Block as BlockT>::Hash,
u64,
) -> Result<(), ApiError> = RuntimeApiImpl::<Block, TestClient>::test;
let _something_with_block: fn(
&RuntimeApiImpl<Block, TestClient>,
<Block as BlockT>::Hash,
Block,
) -> Result<Block, ApiError> = RuntimeApiImpl::<Block, TestClient>::something_with_block;
#[allow(deprecated)]
let _same_name_before_version_2: fn(
&RuntimeApiImpl<Block, TestClient>,
<Block as BlockT>::Hash,
) -> Result<String, ApiError> = RuntimeApiImpl::<Block, TestClient>::same_name_before_version_2;
}
#[test]
#[allow(deprecated)]
fn check_runtime_api_info() {
assert_eq!(&<dyn Api::<Block>>::ID, &runtime_decl_for_api::ID);
assert_eq!(<dyn Api::<Block>>::VERSION, runtime_decl_for_api::VERSION);
assert_eq!(<dyn Api::<Block>>::VERSION, 1);
assert_eq!(
<dyn ApiWithCustomVersion::<Block>>::VERSION,
runtime_decl_for_api_with_custom_version::VERSION,
);
assert_eq!(
&<dyn ApiWithCustomVersion::<Block>>::ID,
&runtime_decl_for_api_with_custom_version::ID,
);
assert_eq!(<dyn ApiWithCustomVersion::<Block>>::VERSION, 2);
// The stable version of the API
assert_eq!(<dyn ApiWithMultipleVersions::<Block>>::VERSION, 2);
assert_eq!(<dyn ApiWithStagingMethod::<Block>>::VERSION, 1);
assert_eq!(<dyn ApiWithStagingAndVersionedMethods::<Block>>::VERSION, 1);
assert_eq!(<dyn ApiWithStagingAndChangedBase::<Block>>::VERSION, 2);
}
fn check_runtime_api_versions_contains<T: RuntimeApiInfo + ?Sized>() {
assert!(RUNTIME_API_VERSIONS.iter().any(|v| v == &(T::ID, T::VERSION)));
}
fn check_staging_runtime_api_versions<T: RuntimeApiInfo + ?Sized>(_staging_ver: u32) {
// Staging APIs should contain staging version if the feature is set...
#[cfg(feature = "enable-staging-api")]
assert!(RUNTIME_API_VERSIONS.iter().any(|v| v == &(T::ID, _staging_ver)));
//... otherwise the base version should be set
#[cfg(not(feature = "enable-staging-api"))]
check_runtime_api_versions_contains::<dyn ApiWithStagingMethod<Block>>();
}
#[allow(unused_assignments)]
fn check_staging_multiver_runtime_api_versions<T: RuntimeApiInfo + ?Sized>(
_staging_ver: u32,
_stable_ver: u32,
) {
// Staging APIs should contain staging version if the feature is set...
#[cfg(feature = "enable-staging-api")]
assert!(RUNTIME_API_VERSIONS.iter().any(|v| v == &(T::ID, _staging_ver)));
//... otherwise the base version should be set
#[cfg(not(feature = "enable-staging-api"))]
assert!(RUNTIME_API_VERSIONS.iter().any(|v| v == &(T::ID, _stable_ver)));
}
#[test]
#[allow(deprecated)]
fn check_runtime_api_versions() {
check_runtime_api_versions_contains::<dyn Api<Block>>();
check_runtime_api_versions_contains::<dyn ApiWithCustomVersion<Block>>();
assert!(RUNTIME_API_VERSIONS
.iter()
.any(|v| v == &(<dyn ApiWithMultipleVersions<Block>>::ID, 3)));
check_staging_runtime_api_versions::<dyn ApiWithStagingMethod<Block>>(99);
check_staging_multiver_runtime_api_versions::<dyn ApiWithStagingAndVersionedMethods<Block>>(
99, 2,
);
check_staging_runtime_api_versions::<dyn ApiWithStagingAndChangedBase<Block>>(99);
check_runtime_api_versions_contains::<dyn sp_api::Core<Block>>();
}
#[test]
#[allow(deprecated)]
fn mock_runtime_api_has_api() {
let mock = MockApi { block: None };
assert!(mock.has_api::<dyn ApiWithCustomVersion<Block>>(Hash::default()).unwrap());
assert!(mock.has_api::<dyn Api<Block>>(Hash::default()).unwrap());
}
#[test]
#[should_panic(expected = "Calling deprecated methods is not supported by mocked runtime api.")]
fn mock_runtime_api_panics_on_calling_old_version() {
let mock = MockApi { block: None };
#[allow(deprecated)]
let _ = mock.same_name_before_version_2(Hash::default());
}
#[test]
#[allow(deprecated)]
fn mock_runtime_api_works_with_advanced() {
let mock = MockApi { block: None };
Api::<Block>::same_name(&mock, Hash::default()).unwrap();
mock.wild_card(Hash::repeat_byte(0x0f), 1).unwrap();
assert_eq!(
"Test error".to_string(),
mock.wild_card(Hash::repeat_byte(0x01), 1).unwrap_err().to_string(),
);
}
#[test]
fn runtime_api_metadata_matches_version_implemented() {
use sp_metadata_ir::InternalImplRuntimeApis;
let rt = Runtime {};
let runtime_metadata = rt.runtime_metadata();
// Check that the metadata for some runtime API matches expectation.
let assert_has_api_with_methods = |api_name: &str, api_methods: &[&str]| {
let Some(api) = runtime_metadata.iter().find(|api| api.name == api_name) else {
panic!("Can't find runtime API '{api_name}'");
};
if api.methods.len() != api_methods.len() {
panic!(
"Wrong number of methods in '{api_name}'; expected {} methods but got {}: {:?}",
api_methods.len(),
api.methods.len(),
api.methods
);
}
for expected_name in api_methods {
if !api.methods.iter().any(|method| &method.name == expected_name) {
panic!("Can't find API method '{expected_name}' in '{api_name}'");
}
}
};
assert_has_api_with_methods("ApiWithCustomVersion", &["same_name"]);
assert_has_api_with_methods("ApiWithMultipleVersions", &["stable_one", "new_one"]);
assert_has_api_with_methods(
"ApiWithStagingMethod",
&[
"stable_one",
#[cfg(feature = "enable-staging-api")]
"staging_one",
],
);
assert_has_api_with_methods(
"ApiWithStagingAndVersionedMethods",
&[
"stable_one",
"new_one",
#[cfg(feature = "enable-staging-api")]
"staging_one",
],
);
assert_has_api_with_methods(
"ApiWithStagingAndChangedBase",
&[
"stable_one",
"new_one",
#[cfg(feature = "enable-staging-api")]
"staging_one",
],
);
}
@@ -0,0 +1,276 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{
panic::UnwindSafe,
sync::{
atomic::{AtomicUsize, Ordering},
Arc,
},
};
use sc_block_builder::BlockBuilderBuilder;
use sp_api::{ApiExt, Core, ProvideRuntimeApi};
use sp_externalities::{decl_extension, TransactionType};
use sp_runtime::{
traits::{HashingFor, Header as HeaderT},
TransactionOutcome,
};
use sp_state_machine::{create_proof_check_backend, execution_proof_check_on_trie_backend};
use substrate_test_runtime_client::{
prelude::*,
runtime::{Block, Header, TestAPI, Transfer},
DefaultTestClientBuilderExt, TestClient, TestClientBuilder,
};
use codec::Encode;
use sp_consensus::SelectChain;
use substrate_test_runtime_client::sc_executor::WasmExecutor;
#[test]
fn calling_runtime_function() {
let client = TestClientBuilder::new().build();
let runtime_api = client.runtime_api();
let best_hash = client.chain_info().best_hash;
assert_eq!(runtime_api.benchmark_add_one(best_hash, &1).unwrap(), 2);
}
#[test]
fn calling_native_runtime_signature_changed_function() {
let client = TestClientBuilder::new().build();
let runtime_api = client.runtime_api();
let best_hash = client.chain_info().best_hash;
assert_eq!(runtime_api.function_signature_changed(best_hash).unwrap(), 1);
}
#[test]
fn use_trie_function() {
let client = TestClientBuilder::new().build();
let runtime_api = client.runtime_api();
let best_hash = client.chain_info().best_hash;
assert_eq!(runtime_api.use_trie(best_hash).unwrap(), 2);
}
#[test]
fn initialize_block_works() {
let client = TestClientBuilder::new().build();
let runtime_api = client.runtime_api();
let best_hash = client.chain_info().best_hash;
runtime_api
.initialize_block(
best_hash,
&Header::new(
1,
Default::default(),
Default::default(),
Default::default(),
Default::default(),
),
)
.unwrap();
assert_eq!(runtime_api.get_block_number(best_hash).unwrap(), 1);
}
#[test]
fn record_proof_works() {
let (client, longest_chain) = TestClientBuilder::new().build_with_longest_chain();
let storage_root =
*futures::executor::block_on(longest_chain.best_chain()).unwrap().state_root();
let runtime_code = sp_core::traits::RuntimeCode {
code_fetcher: &sp_core::traits::WrappedRuntimeCode(
client.code_at(client.chain_info().best_hash).unwrap().into(),
),
hash: vec![1],
heap_pages: None,
};
let transaction = Transfer {
amount: 1000,
nonce: 0,
from: Sr25519Keyring::Alice.into(),
to: Sr25519Keyring::Bob.into(),
}
.into_unchecked_extrinsic();
// Build the block and record proof
let mut builder = BlockBuilderBuilder::new(&client)
.on_parent_block(client.chain_info().best_hash)
.with_parent_block_number(client.chain_info().best_number)
.enable_proof_recording()
.build()
.unwrap();
builder.push(transaction.clone()).unwrap();
let (block, _, proof) = builder.build().expect("Bake block").into_inner();
let backend = create_proof_check_backend::<HashingFor<Block>>(
storage_root,
proof.expect("Proof was generated"),
)
.expect("Creates proof backend.");
// Use the proof backend to execute `execute_block`.
let mut overlay = Default::default();
let executor: WasmExecutor = WasmExecutor::builder().build();
execution_proof_check_on_trie_backend(
&backend,
&mut overlay,
&executor,
"Core_execute_block",
&block.encode(),
&runtime_code,
)
.expect("Executes block while using the proof backend");
}
#[test]
fn call_runtime_api_with_multiple_arguments() {
let client = TestClientBuilder::new().build();
let data = vec![1, 2, 4, 5, 6, 7, 8, 8, 10, 12];
let best_hash = client.chain_info().best_hash;
client
.runtime_api()
.test_multiple_arguments(best_hash, data.clone(), data.clone(), data.len() as u32)
.unwrap();
}
#[test]
fn disable_logging_works() {
if std::env::var("RUN_TEST").is_ok() {
sp_tracing::try_init_simple();
let mut builder = TestClientBuilder::new();
builder.genesis_init_mut().set_wasm_code(
substrate_test_runtime_client::runtime::wasm_binary_logging_disabled_unwrap().to_vec(),
);
let client = builder.build();
let runtime_api = client.runtime_api();
runtime_api
.do_trace_log(client.chain_info().genesis_hash)
.expect("Logging should not fail");
log::error!("Logging from native works");
} else {
let executable = std::env::current_exe().unwrap();
let output = std::process::Command::new(executable)
.env("RUN_TEST", "1")
.env("RUST_LOG", "info")
.args(&["--nocapture", "disable_logging_works"])
.output()
.unwrap();
let output = dbg!(String::from_utf8(output.stderr).unwrap());
assert!(!output.contains("Hey I'm runtime"));
assert!(output.contains("Logging from native works"));
}
}
// Certain logic like the transaction handling is not unwind safe.
//
// Ensure that the type is not unwind safe!
static_assertions::assert_not_impl_any!(<TestClient as ProvideRuntimeApi<_>>::Api: UnwindSafe);
#[derive(Default)]
struct TransactionTesterInner {
started: AtomicUsize,
committed: AtomicUsize,
rolled_back: AtomicUsize,
}
decl_extension! {
struct TransactionTester(Arc<TransactionTesterInner>);
impl TransactionTester {
fn start_transaction(&mut self, ty: TransactionType) {
assert_eq!(ty, TransactionType::Host);
self.0.started.fetch_add(1, Ordering::Relaxed);
}
fn commit_transaction(&mut self, ty: TransactionType) {
assert_eq!(ty, TransactionType::Host);
self.0.committed.fetch_add(1, Ordering::Relaxed);
}
fn rollback_transaction(&mut self, ty: TransactionType) {
assert_eq!(ty, TransactionType::Host);
self.0.rolled_back.fetch_add(1, Ordering::Relaxed);
}
}
}
#[test]
fn ensure_transactional_works() {
const KEY: &[u8] = b"test";
let client = TestClientBuilder::new().build();
let best_hash = client.chain_info().best_hash;
let transaction_tester = Arc::new(TransactionTesterInner::default());
let mut runtime_api = client.runtime_api();
runtime_api.register_extension(TransactionTester(transaction_tester.clone()));
runtime_api.execute_in_transaction(|api| {
api.write_key_value(best_hash, KEY.to_vec(), vec![1, 2, 3], false).unwrap();
api.execute_in_transaction(|api| {
api.write_key_value(best_hash, KEY.to_vec(), vec![1, 2, 3, 4], false).unwrap();
TransactionOutcome::Commit(())
});
TransactionOutcome::Commit(())
});
let changes = runtime_api
.into_storage_changes(&client.state_at(best_hash).unwrap(), best_hash)
.unwrap();
assert_eq!(changes.main_storage_changes[0].1, Some(vec![1, 2, 3, 4]));
let mut runtime_api = client.runtime_api();
runtime_api.register_extension(TransactionTester(transaction_tester.clone()));
runtime_api.execute_in_transaction(|api| {
assert!(api.write_key_value(best_hash, KEY.to_vec(), vec![1, 2, 3], true).is_err());
TransactionOutcome::Commit(())
});
let changes = runtime_api
.into_storage_changes(&client.state_at(best_hash).unwrap(), best_hash)
.unwrap();
assert_eq!(changes.main_storage_changes[0].1, Some(vec![1, 2, 3]));
let mut runtime_api = client.runtime_api();
runtime_api.register_extension(TransactionTester(transaction_tester.clone()));
runtime_api.execute_in_transaction(|api| {
assert!(api.write_key_value(best_hash, KEY.to_vec(), vec![1, 2], true).is_err());
TransactionOutcome::Rollback(())
});
let changes = runtime_api
.into_storage_changes(&client.state_at(best_hash).unwrap(), best_hash)
.unwrap();
assert!(changes.main_storage_changes.is_empty());
assert_eq!(transaction_tester.started.load(Ordering::Relaxed), 4);
assert_eq!(transaction_tester.committed.load(Ordering::Relaxed), 3);
assert_eq!(transaction_tester.rolled_back.load(Ordering::Relaxed), 1);
}
@@ -0,0 +1,36 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#[rustversion::attr(not(stable), ignore)]
#[cfg(not(feature = "disable-ui-tests"))]
#[test]
fn ui() {
// Only run the ui tests when `RUN_UI_TESTS` is set.
if std::env::var("RUN_UI_TESTS").is_err() {
return;
}
// As trybuild is using `cargo check`, we don't need the real WASM binaries.
std::env::set_var("SKIP_WASM_BUILD", "1");
// Warnings are part of our UI.
std::env::set_var("CARGO_ENCODED_RUSTFLAGS", "--deny=warnings");
let t = trybuild::TestCases::new();
t.compile_fail("tests/ui/*.rs");
t.pass("tests/ui/positive_cases/*.rs");
}
@@ -0,0 +1,24 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
sp_api::decl_runtime_apis! {
pub trait Api {
fn test(&self);
}
}
fn main() {}
@@ -0,0 +1,5 @@
error: `self` as argument not supported.
--> tests/ui/adding_self_parameter.rs:20:11
|
20 | fn test(&self);
| ^
@@ -0,0 +1,30 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
/// runtime.
struct Runtime {}
sp_api::decl_runtime_apis! {
#[api_version(2)]
pub trait Api {
#[changed_in(2)]
fn test(data: u64);
}
}
fn main() {}
@@ -0,0 +1,6 @@
error: There is no 'default' method with this name (without `changed_in` attribute).
The 'default' method is used to call into the latest implementation.
--> tests/ui/changed_in_no_default_method.rs:26:6
|
26 | fn test(data: u64);
| ^^^^
@@ -0,0 +1,30 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
/// runtime.
struct Runtime {}
sp_api::decl_runtime_apis! {
pub trait Api {
#[changed_in(2)]
fn test(data: u64);
fn test(data: u64);
}
}
fn main() {}
@@ -0,0 +1,5 @@
error: `changed_in` version can not be greater than the `api_version`
--> tests/ui/changed_in_unknown_version.rs:25:3
|
25 | fn test(data: u64);
| ^^
@@ -0,0 +1,24 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
sp_api::decl_runtime_apis! {
pub trait Api<Block: BlockT> {
fn test();
}
}
fn main() {}
@@ -0,0 +1,11 @@
error: `Block: BlockT` generic parameter will be added automatically by the `decl_runtime_apis!` macro! If you try to use a different trait than the substrate `Block` trait, please rename it locally.
--> tests/ui/declaring_old_block.rs:19:23
|
19 | pub trait Api<Block: BlockT> {
| ^^^^^^
error: `Block: BlockT` generic parameter will be added automatically by the `decl_runtime_apis!` macro!
--> tests/ui/declaring_old_block.rs:19:16
|
19 | pub trait Api<Block: BlockT> {
| ^^^^^
@@ -0,0 +1,24 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
sp_api::decl_runtime_apis! {
pub trait Api<B: BlockT> {
fn test();
}
}
fn main() {}
@@ -0,0 +1,5 @@
error: `Block: BlockT` generic parameter will be added automatically by the `decl_runtime_apis!` macro! If you try to use a different trait than the substrate `Block` trait, please rename it locally.
--> tests/ui/declaring_own_block_with_different_name.rs:19:19
|
19 | pub trait Api<B: BlockT> {
| ^^^^^^
@@ -0,0 +1,29 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
sp_api::decl_runtime_apis! {
pub trait Api {
#[deprecated(unknown_kw = "test")]
fn test();
#[deprecated(since = 5)]
fn test2();
#[deprecated = 5]
fn test3();
}
}
fn main() {}
@@ -0,0 +1,43 @@
error: Invalid deprecation attribute: missing `note`
help: the following are the possible correct uses
|
| #[deprecated = "reason"]
|
| #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
| #[deprecated]
|
--> tests/ui/deprecation_info.rs:20:3
|
20 | #[deprecated(unknown_kw = "test")]
| ^
error: malformed `deprecated` attribute input
--> tests/ui/deprecation_info.rs:24:3
|
24 | #[deprecated = 5]
| ^^^^^^^^^^^^^^^^^
|
help: the following are the possible correct uses
|
24 - #[deprecated = 5]
24 + #[deprecated = "reason"]
|
24 - #[deprecated = 5]
24 + #[deprecated(/*opt*/ since = "version", /*opt*/ note = "reason")]
|
24 - #[deprecated = 5]
24 + #[deprecated]
|
error[E0541]: unknown meta item 'unknown_kw'
--> tests/ui/deprecation_info.rs:20:16
|
20 | #[deprecated(unknown_kw = "test")]
| ^^^^^^^^^^^^^^^^^^^ expected one of `since`, `note`
error[E0565]: literal in `deprecated` value must be a string
--> tests/ui/deprecation_info.rs:22:24
|
22 | #[deprecated(since = 5)]
| ^
@@ -0,0 +1,30 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
/// runtime.
struct Runtime {}
sp_api::decl_runtime_apis! {
pub trait Api {
fn test(data: u64);
}
}
sp_api::impl_runtime_apis! {}
fn main() {}
@@ -0,0 +1,7 @@
error: No api implementation given!
--> tests/ui/empty_impl_runtime_apis_call.rs:28:1
|
28 | sp_api::impl_runtime_apis! {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `sp_api::impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -0,0 +1,49 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use sp_runtime::traits::Block as BlockT;
use substrate_test_runtime_client::runtime::Block;
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
/// runtime.
struct Runtime {}
sp_api::decl_runtime_apis! {
pub trait Api {
fn test(data: u64);
}
}
sp_api::impl_runtime_apis! {
impl self::Api<Block> for Runtime {
fn test(data: String) {}
}
impl sp_api::Core<Block> for Runtime {
fn version() -> sp_api::RuntimeVersion {
unimplemented!()
}
fn execute_block(_: <Block as BlockT>::LazyBlock) {
unimplemented!()
}
fn initialize_block(_: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
unimplemented!()
}
}
}
fn main() {}
@@ -0,0 +1,66 @@
error[E0603]: struct `RuntimeVersion` is private
--> tests/ui/impl_incorrect_method_signature.rs:37:27
|
37 | fn version() -> sp_api::RuntimeVersion {
| ^^^^^^^^^^^^^^ private struct
|
note: the struct `RuntimeVersion` is defined here
--> $WORKSPACE/substrate/primitives/api/src/lib.rs
|
| use sp_version::RuntimeVersion;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider importing this struct instead
|
37 - fn version() -> sp_api::RuntimeVersion {
37 + fn version() -> sp_version::RuntimeVersion {
|
help: import `RuntimeVersion` directly
|
37 - fn version() -> sp_api::RuntimeVersion {
37 + fn version() -> sp_version::RuntimeVersion {
|
error[E0053]: method `test` has an incompatible type for trait
--> tests/ui/impl_incorrect_method_signature.rs:33:17
|
33 | fn test(data: String) {}
| ^^^^^^ expected `u64`, found `std::string::String`
|
note: type in trait
--> tests/ui/impl_incorrect_method_signature.rs:27:17
|
27 | fn test(data: u64);
| ^^^
= note: expected signature `fn(u64)`
found signature `fn(std::string::String)`
help: change the parameter type to match the trait
|
33 - fn test(data: String) {}
33 + fn test(data: u64) {}
|
error[E0308]: mismatched types
--> tests/ui/impl_incorrect_method_signature.rs:33:11
|
31 | / sp_api::impl_runtime_apis! {
32 | | impl self::Api<Block> for Runtime {
33 | | fn test(data: String) {}
| | ^^^^ expected `u64`, found `String`
... |
47 | | }
| |_- arguments to this function are incorrect
|
note: associated function defined here
--> tests/ui/impl_incorrect_method_signature.rs:27:6
|
27 | fn test(data: u64);
| ^^^^ ----
error: unused variable: `data`
--> tests/ui/impl_incorrect_method_signature.rs:33:11
|
33 | fn test(data: String) {}
| ^^^^ help: if this is intentional, prefix it with an underscore: `_data`
|
= note: `-D unused-variables` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(unused_variables)]`
@@ -0,0 +1,54 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use sp_runtime::traits::Block as BlockT;
use substrate_test_runtime_client::runtime::Block;
struct Runtime {}
sp_api::decl_runtime_apis! {
#[api_version(2)]
pub trait Api {
fn test1();
fn test2();
#[api_version(3)]
fn test3();
}
}
sp_api::impl_runtime_apis! {
#[api_version(4)]
impl self::Api<Block> for Runtime {
fn test1() {}
fn test2() {}
fn test3() {}
}
impl sp_api::Core<Block> for Runtime {
fn version() -> sp_version::RuntimeVersion {
unimplemented!()
}
fn execute_block(_: <Block as BlockT>::LazyBlock) {
unimplemented!()
}
fn initialize_block(_: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
unimplemented!()
}
}
}
fn main() {}
@@ -0,0 +1,8 @@
error[E0405]: cannot find trait `ApiV4` in module `self::runtime_decl_for_api`
--> tests/ui/impl_missing_version.rs:35:13
|
25 | pub trait Api {
| ------------- similarly named trait `ApiV2` defined here
...
35 | impl self::Api<Block> for Runtime {
| ^^^ help: a trait with a similar name exists: `ApiV2`
@@ -0,0 +1,47 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
/// runtime.
struct Runtime {}
sp_api::decl_runtime_apis! {
pub trait Api {
fn test(data: u64);
}
}
#[allow(unused_imports)]
mod second {
sp_api::decl_runtime_apis! {
pub trait Api {
fn test2(data: u64);
}
}
}
sp_api::impl_runtime_apis! {
impl self::Api<Block> for Runtime {
fn test(data: u64) {}
}
impl second::Api<Block> for Runtime {
fn test2(data: u64) {}
}
}
fn main() {}
@@ -0,0 +1,11 @@
error: Two traits with the same name detected! The trait name is used to generate its ID. Please rename one trait at the declaration!
--> tests/ui/impl_two_traits_with_same_name.rs:42:15
|
42 | impl second::Api<Block> for Runtime {
| ^^^
error: First trait implementation.
--> tests/ui/impl_two_traits_with_same_name.rs:38:13
|
38 | impl self::Api<Block> for Runtime {
| ^^^
@@ -0,0 +1,25 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
sp_api::decl_runtime_apis! {
#[api_version]
pub trait Api {
fn test(data: u64);
}
}
fn main() {}
@@ -0,0 +1,5 @@
error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
--> tests/ui/invalid_api_version_1.rs:19:2
|
19 | #[api_version]
| ^
@@ -0,0 +1,25 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
sp_api::decl_runtime_apis! {
#[api_version("1")]
pub trait Api {
fn test(data: u64);
}
}
fn main() {}
@@ -0,0 +1,5 @@
error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
--> tests/ui/invalid_api_version_2.rs:19:2
|
19 | #[api_version("1")]
| ^
@@ -0,0 +1,25 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
sp_api::decl_runtime_apis! {
#[api_version()]
pub trait Api {
fn test(data: u64);
}
}
fn main() {}
@@ -0,0 +1,5 @@
error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
--> tests/ui/invalid_api_version_3.rs:19:2
|
19 | #[api_version()]
| ^
@@ -0,0 +1,25 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
sp_api::decl_runtime_apis! {
pub trait Api {
#[api_version("1")]
fn test(data: u64);
}
}
fn main() {}
@@ -0,0 +1,5 @@
error: Unexpected `api_version` attribute. The supported format is `api_version(1)`
--> tests/ui/invalid_api_version_4.rs:20:3
|
20 | #[api_version("1")]
| ^
@@ -0,0 +1,26 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
sp_api::decl_runtime_apis! {
#[api_version(2)]
pub trait Api {
#[api_version(1)]
fn test(data: u64);
}
}
fn main() {}
@@ -0,0 +1,11 @@
error: Method version `1` is older than (or equal to) trait version `2`.Methods can't define versions older than the trait version.
--> tests/ui/method_ver_lower_than_trait_ver.rs:21:3
|
21 | #[api_version(1)]
| ^
error: Trait version is set here.
--> tests/ui/method_ver_lower_than_trait_ver.rs:19:2
|
19 | #[api_version(2)]
| ^
@@ -0,0 +1,36 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
/// runtime.
struct Runtime {}
sp_api::decl_runtime_apis! {
pub trait Api {
fn test(data: u64);
}
}
sp_api::impl_runtime_apis! {
impl self::Api for Runtime {
fn test(data: u64) {
unimplemented!()
}
}
}
fn main() {}
@@ -0,0 +1,5 @@
error: Missing `Block` generic parameter.
--> tests/ui/missing_block_generic_parameter.rs:29:13
|
29 | impl self::Api for Runtime {
| ^^^
@@ -0,0 +1,36 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
/// runtime.
struct Runtime {}
sp_api::decl_runtime_apis! {
pub trait Api {
fn test(data: u64);
}
}
sp_api::impl_runtime_apis! {
impl Api<Block> for Runtime {
fn test(data: u64) {
unimplemented!()
}
}
}
fn main() {}
@@ -0,0 +1,5 @@
error: The implemented trait has to be referenced with a path, e.g. `impl client::Core for Runtime`.
--> tests/ui/missing_path_for_trait.rs:29:7
|
29 | impl Api<Block> for Runtime {
| ^^^
@@ -0,0 +1,53 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use sp_runtime::traits::Block as BlockT;
use substrate_test_runtime_client::runtime::Block;
struct Runtime {}
sp_api::decl_runtime_apis! {
#[api_version(2)]
pub trait Api {
fn test1();
fn test2();
#[api_version(3)]
fn test3();
}
}
sp_api::impl_runtime_apis! {
#[api_version(3)]
impl self::Api<Block> for Runtime {
fn test1() {}
fn test2() {}
}
impl sp_api::Core<Block> for Runtime {
fn version() -> sp_version::RuntimeVersion {
unimplemented!()
}
fn execute_block(_: <Block as BlockT>::LazyBlock) {
unimplemented!()
}
fn initialize_block(_: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
unimplemented!()
}
}
}
fn main() {}
@@ -0,0 +1,8 @@
error[E0046]: not all trait items implemented, missing: `test3`
--> tests/ui/missing_versioned_method.rs:35:2
|
29 | fn test3();
| ----------- `test3` from trait
...
35 | impl self::Api<Block> for Runtime {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `test3` in implementation
@@ -0,0 +1,56 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use sp_runtime::traits::Block as BlockT;
use substrate_test_runtime_client::runtime::Block;
struct Runtime {}
sp_api::decl_runtime_apis! {
#[api_version(2)]
pub trait Api {
fn test1();
fn test2();
#[api_version(3)]
fn test3();
#[api_version(4)]
fn test4();
}
}
sp_api::impl_runtime_apis! {
#[api_version(4)]
impl self::Api<Block> for Runtime {
fn test1() {}
fn test2() {}
fn test4() {}
}
impl sp_api::Core<Block> for Runtime {
fn version() -> sp_version::RuntimeVersion {
unimplemented!()
}
fn execute_block(_: <Block as BlockT>::LazyBlock) {
unimplemented!()
}
fn initialize_block(_: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
unimplemented!()
}
}
}
fn main() {}
@@ -0,0 +1,8 @@
error[E0046]: not all trait items implemented, missing: `test3`
--> tests/ui/missing_versioned_method_multiple_vers.rs:37:2
|
29 | fn test3();
| ----------- `test3` from trait
...
37 | impl self::Api<Block> for Runtime {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `test3` in implementation
@@ -0,0 +1,38 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use substrate_test_runtime_client::runtime::Block;
use sp_api::ApiError;
sp_api::decl_runtime_apis! {
pub trait Api {
fn test();
}
}
struct MockApi;
sp_api::mock_impl_runtime_apis! {
impl Api<Block> for MockApi {
#[advanced]
fn test(&self, _: &Hash) -> Result<(), ApiError> {
Ok(().into())
}
}
}
fn main() {}
@@ -0,0 +1,12 @@
error: `Hash` needs to be taken by value and not by reference!
--> tests/ui/mock_advanced_hash_by_reference.rs:29:1
|
29 | / sp_api::mock_impl_runtime_apis! {
30 | | impl Api<Block> for MockApi {
31 | | #[advanced]
32 | | fn test(&self, _: &Hash) -> Result<(), ApiError> {
... |
36 | | }
| |_^
|
= note: this error originates in the macro `sp_api::mock_impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -0,0 +1,38 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use substrate_test_runtime_client::runtime::Block;
use sp_api::ApiError;
sp_api::decl_runtime_apis! {
pub trait Api {
fn test();
}
}
struct MockApi;
sp_api::mock_impl_runtime_apis! {
impl Api<Block> for MockApi {
#[advanced]
fn test(&self) -> Result<(), ApiError> {
Ok(().into())
}
}
}
fn main() {}
@@ -0,0 +1,5 @@
error: If using the `advanced` attribute, it is required that the function takes at least one argument, the `Hash`.
--> tests/ui/mock_advanced_missing_hash.rs:32:3
|
32 | fn test(&self) -> Result<(), ApiError> {
| ^^
@@ -0,0 +1,42 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
struct Block2;
sp_api::decl_runtime_apis! {
pub trait Api {
fn test(data: u64);
}
pub trait Api2 {
fn test(data: u64);
}
}
struct MockApi;
sp_api::mock_impl_runtime_apis! {
impl Api<Block> for MockApi {
fn test(data: u64) {}
}
impl Api2<Block2> for MockApi {
fn test(data: u64) {}
}
}
fn main() {}
@@ -0,0 +1,11 @@
error: Block type should be the same between all runtime apis.
--> tests/ui/mock_only_one_block_type.rs:37:12
|
37 | impl Api2<Block2> for MockApi {
| ^^^^^^
error: First block type found here
--> tests/ui/mock_only_one_block_type.rs:33:11
|
33 | impl Api<Block> for MockApi {
| ^^^^^
@@ -0,0 +1,41 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
sp_api::decl_runtime_apis! {
pub trait Api {
fn test(data: u64);
}
pub trait Api2 {
fn test(data: u64);
}
}
struct MockApi;
struct MockApi2;
sp_api::mock_impl_runtime_apis! {
impl Api<Block> for MockApi {
fn test(data: u64) {}
}
impl Api2<Block> for MockApi2 {
fn test(data: u64) {}
}
}
fn main() {}
@@ -0,0 +1,11 @@
error: Self type should not change between runtime apis
--> tests/ui/mock_only_one_self_type.rs:36:23
|
36 | impl Api2<Block> for MockApi2 {
| ^^^^^^^^
error: First self type found here
--> tests/ui/mock_only_one_self_type.rs:32:22
|
32 | impl Api<Block> for MockApi {
| ^^^^^^^
@@ -0,0 +1,37 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use substrate_test_runtime_client::runtime::Block;
sp_api::decl_runtime_apis! {
pub trait Api {
fn test(data: u64);
fn test2(data: u64);
}
}
struct MockApi;
sp_api::mock_impl_runtime_apis! {
impl Api<Block> for MockApi {
fn test(self, data: u64) {}
fn test2(&mut self, data: u64) {}
}
}
fn main() {}
@@ -0,0 +1,46 @@
error: Only `&self` is supported!
--> tests/ui/mock_only_self_reference.rs:31:11
|
31 | fn test(self, data: u64) {}
| ^^^^
error: Only `&self` is supported!
--> tests/ui/mock_only_self_reference.rs:33:12
|
33 | fn test2(&mut self, data: u64) {}
| ^
error[E0050]: method `test` has 2 parameters but the declaration in trait `Api::test` has 3
--> tests/ui/mock_only_self_reference.rs:29:1
|
20 | / sp_api::decl_runtime_apis! {
21 | | pub trait Api {
22 | | fn test(data: u64);
| |_________________________- trait requires 3 parameters
...
29 | / sp_api::mock_impl_runtime_apis! {
30 | | impl Api<Block> for MockApi {
31 | | fn test(self, data: u64) {}
... |
35 | | }
| |_^ expected 3 parameters, found 2
|
= note: this error originates in the macro `sp_api::mock_impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0050]: method `test2` has 2 parameters but the declaration in trait `Api::test2` has 3
--> tests/ui/mock_only_self_reference.rs:29:1
|
20 | / sp_api::decl_runtime_apis! {
21 | | pub trait Api {
22 | | fn test(data: u64);
23 | | fn test2(data: u64);
| |__________________________- trait requires 3 parameters
...
29 | / sp_api::mock_impl_runtime_apis! {
30 | | impl Api<Block> for MockApi {
31 | | fn test(self, data: u64) {}
... |
35 | | }
| |_^ expected 3 parameters, found 2
|
= note: this error originates in the macro `sp_api::mock_impl_runtime_apis` (in Nightly builds, run with -Z macro-backtrace for more info)
@@ -0,0 +1,26 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
sp_api::decl_runtime_apis! {
pub trait Api {
fn test() {
println!("Hey, I'm a default implementation!");
}
}
}
fn main() {}
@@ -0,0 +1,8 @@
error: A runtime API function cannot have a default implementation!
--> tests/ui/no_default_implementation.rs:20:13
|
20 | fn test() {
| ___________________^
21 | | println!("Hey, I'm a default implementation!");
22 | | }
| |_________^
@@ -0,0 +1,60 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use codec::{Decode, Encode};
use scale_info::TypeInfo;
use sp_runtime::traits::Block as BlockT;
use substrate_test_runtime_client::runtime::Block;
struct Runtime {}
pub trait CustomTrait: Encode + Decode + TypeInfo {}
#[derive(Encode, Decode, TypeInfo)]
pub struct SomeImpl;
impl CustomTrait for SomeImpl {}
#[derive(Encode, Decode, TypeInfo)]
pub struct SomeOtherType<C: CustomTrait>(C);
sp_api::decl_runtime_apis! {
pub trait Api<A> where A: CustomTrait {
fn test() -> A;
fn test2() -> SomeOtherType<A>;
}
}
sp_api::impl_runtime_apis! {
impl self::Api<Block, SomeImpl> for Runtime {
fn test() -> SomeImpl { SomeImpl }
fn test2() -> SomeOtherType<SomeImpl> { SomeOtherType(SomeImpl) }
}
impl sp_api::Core<Block> for Runtime {
fn version() -> sp_version::RuntimeVersion {
unimplemented!()
}
fn execute_block(_: <Block as BlockT>::LazyBlock) {
unimplemented!()
}
fn initialize_block(_: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
unimplemented!()
}
}
}
fn main() {}
@@ -0,0 +1,55 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use sp_runtime::traits::Block as BlockT;
use substrate_test_runtime_client::runtime::Block;
struct Runtime {}
sp_api::decl_runtime_apis! {
#[api_version(2)]
pub trait Api {
fn test1();
fn test2();
#[api_version(3)]
fn test3();
#[api_version(4)]
fn test4();
}
}
sp_api::impl_runtime_apis! {
#[api_version(2)]
impl self::Api<Block> for Runtime {
fn test1() {}
fn test2() {}
}
impl sp_api::Core<Block> for Runtime {
fn version() -> sp_version::RuntimeVersion {
unimplemented!()
}
fn execute_block(_: <Block as BlockT>::LazyBlock) {
unimplemented!()
}
fn initialize_block(_: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
unimplemented!()
}
}
}
fn main() {}
@@ -0,0 +1,51 @@
// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use sp_runtime::traits::Block as BlockT;
use substrate_test_runtime_client::runtime::Block;
/// The declaration of the `Runtime` type is done by the `construct_runtime!` macro in a real
/// runtime.
struct Runtime {}
sp_api::decl_runtime_apis! {
pub trait Api {
fn test(data: u64);
}
}
sp_api::impl_runtime_apis! {
impl self::Api<Block> for Runtime {
fn test(data: &u64) {
unimplemented!()
}
}
impl sp_api::Core<Block> for Runtime {
fn version() -> sp_api::RuntimeVersion {
unimplemented!()
}
fn execute_block(_: <Block as BlockT>::LazyBlock) {
unimplemented!()
}
fn initialize_block(_: &<Block as BlockT>::Header) -> sp_runtime::ExtrinsicInclusionMode {
unimplemented!()
}
}
}
fn main() {}
@@ -0,0 +1,67 @@
error[E0603]: struct `RuntimeVersion` is private
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:39:27
|
39 | fn version() -> sp_api::RuntimeVersion {
| ^^^^^^^^^^^^^^ private struct
|
note: the struct `RuntimeVersion` is defined here
--> $WORKSPACE/substrate/primitives/api/src/lib.rs
|
| use sp_version::RuntimeVersion;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
help: consider importing this struct instead
|
39 - fn version() -> sp_api::RuntimeVersion {
39 + fn version() -> sp_version::RuntimeVersion {
|
help: import `RuntimeVersion` directly
|
39 - fn version() -> sp_api::RuntimeVersion {
39 + fn version() -> sp_version::RuntimeVersion {
|
error[E0053]: method `test` has an incompatible type for trait
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:33:17
|
33 | fn test(data: &u64) {
| ^^^^ expected `u64`, found `&u64`
|
note: type in trait
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:27:17
|
27 | fn test(data: u64);
| ^^^
= note: expected signature `fn(_)`
found signature `fn(&_)`
help: change the parameter type to match the trait
|
33 - fn test(data: &u64) {
33 + fn test(data: u64) {
|
error[E0308]: mismatched types
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:33:11
|
31 | / sp_api::impl_runtime_apis! {
32 | | impl self::Api<Block> for Runtime {
33 | | fn test(data: &u64) {
| | ^^^^^^^ expected `u64`, found `&u64`
34 | | unimplemented!()
... |
49 | | }
| |_- arguments to this function are incorrect
|
note: associated function defined here
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:27:6
|
27 | fn test(data: u64);
| ^^^^ ----
error: unused variable: `data`
--> tests/ui/type_reference_in_impl_runtime_apis_call.rs:33:11
|
33 | fn test(data: &u64) {
| ^^^^ help: if this is intentional, prefix it with an underscore: `_data`
|
= note: `-D unused-variables` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(unused_variables)]`