Files
pezkuwi-subxt/substrate/primitives/api/proc-macro/src/impl_runtime_apis.rs
T
cheme 4c651637f2 Inner hashing of value in state trie (runtime versioning). (#9732)
* starting

* Updated from other branch.

* setting flag

* flag in storage struct

* fix flagging to access and insert.

* added todo to fix

* also missing serialize meta to storage proof

* extract meta.

* Isolate old trie layout.

* failing test that requires storing in meta when old hash scheme is used.

* old hash compatibility

* Db migrate.

* runing tests with both states when interesting.

* fix chain spec test with serde default.

* export state (missing trie function).

* Pending using new branch, lacking genericity on layout resolution.

* extract and set global meta

* Update to branch 4

* fix iterator with root flag (no longer insert node).

* fix trie root hashing of root

* complete basic backend.

* Remove old_hash meta from proof that do not use inner_hashing.

* fix trie test for empty (force layout on empty deltas).

* Root update fix.

* debug on meta

* Use trie key iteration that do not include value in proofs.

* switch default test ext to use inner hash.

* small integration test, and fix tx cache mgmt in ext.
test  failing

* Proof scenario at state-machine level.

* trace for db upgrade

* try different param

* act more like iter_from.

* Bigger batches.

* Update trie dependency.

* drafting codec changes and refact

* before removing unused branch no value alt hashing.
more work todo rename all flag var to alt_hash, and remove extrinsic
replace by storage query at every storage_root call.

* alt hashing only for branch with value.

* fix trie tests

* Hash of value include the encoded size.

* removing fields(broken)

* fix trie_stream to also include value length in inner hash.

* triedbmut only using alt type if inner hashing.

* trie_stream to also only use alt hashing type when actually alt hashing.

* Refactor meta state, logic should work with change of trie treshold.

* Remove NoMeta variant.

* Remove state_hashed trigger specific functions.

* pending switching to using threshold, new storage root api does not
make much sense.

* refactoring to use state from backend (not possible payload changes).

* Applying from previous state

* Remove default from storage, genesis need a special build.

* rem empty space

* Catch problem: when using triedb with default: we should not revert
nodes: otherwhise thing as trie codec cannot decode-encode without
changing state.

* fix compilation

* Right logic to avoid switch on reencode when default layout.

* Clean up some todos

* remove trie meta from root upstream

* update upstream and fix benches.

* split some long lines.

* UPdate trie crate to work with new design.

* Finish update to refactored upstream.

* update to latest triedb changes.

* Clean up.

* fix executor test.

* rust fmt from master.

* rust format.

* rustfmt

* fix

* start host function driven versioning

* update state-machine part

* still need access to state version from runtime

* state hash in mem: wrong

* direction likely correct, but passing call to code exec for genesis
init seem awkward.

* state version serialize in runtime, wrong approach, just initialize it
with no threshold for core api < 4 seems more proper.

* stateversion from runtime version (core api >= 4).

* update trie, fix tests

* unused import

* clean some TODOs

* Require RuntimeVersionOf for executor

* use RuntimeVersionOf to resolve genesis state version.

* update runtime version test

* fix state-machine tests

* TODO

* Use runtime version from storage wasm with fast sync.

* rustfmt

* fmt

* fix test

* revert useless changes.

* clean some unused changes

* fmt

* removing useless trait function.

* remove remaining reference to state_hash

* fix some imports

* Follow chain state version management.

* trie update, fix and constant threshold for trie layouts.

* update deps

* Update to latest trie pr changes.

* fix benches

* Verify proof requires right layout.

* update trie_root

* Update trie deps to  latest

* Update to latest trie versioning

* Removing patch

* update lock

* extrinsic for sc-service-test using layout v0.

* Adding RuntimeVersionOf to CallExecutor works.

* fmt

* error when resolving version and no wasm in storage.

* use existing utils to instantiate runtime code.

* Patch to delay runtime switch.

* Revert "Patch to delay runtime switch."

This reverts commit 67e55fee468f1a0cda853f5362b22e0d775786da.

* useless closure

* remove remaining state_hash variables.

* Remove outdated comment

* useless inner hash

* fmt

* fmt and opt-in feature to apply state change.

* feature gate core version, use new test feature for node and test node

* Use a 'State' api version instead of Core one.

* fix merge of test function

* use blake macro.

* Fix state api (require declaring the api in runtime).

* Opt out feature, fix macro for io to select a given version
instead of latest.

* run test nodes on new state.

* fix

* Apply review change (docs and error).

* fmt

* use explicit runtime_interface in doc test

* fix ui test

* fix doc test

* fmt

* use default for path and specname when resolving version.

* small review related changes.

* doc value size requirement.

* rename old_state feature

* Remove macro changes

* feature rename

* state version as host function parameter

* remove flag for client api

* fix tests

* switch storage chain proof to V1

* host functions, pass by state version enum

* use WrappedRuntimeCode

* start

* state_version in runtime version

* rust fmt

* Update storage proof of max size.

* fix runtime version rpc test

* right intent of convert from compat

* fix doc test

* fix doc test

* split proof

* decode without replay, and remove some reexports.

* Decode with compatibility by default.

* switch state_version to u8. And remove RuntimeVersionBasis.

* test

* use api when reading embedded version

* fix decode with apis

* extract core version instead

* test fix

* unused import

* review changes.

Co-authored-by: kianenigma <kian@parity.io>
2021-12-24 08:54:07 +00:00

749 lines
22 KiB
Rust

// This file is part of Substrate.
// Copyright (C) 2018-2021 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_all_signature_types, extract_block_type_from_trait_path, extract_impl_trait,
extract_parameter_names_types_and_borrows, generate_call_api_at_fn_name, generate_crate_access,
generate_hidden_includes, generate_method_runtime_api_impl_name,
generate_native_call_generator_fn_name, generate_runtime_mod_name_for_trait,
prefix_function_with_trait, return_type_extract_type, AllowSelfRefInParameters,
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,
Attribute, GenericArgument, Ident, ImplItem, ItemImpl, Path, PathArguments, Signature, Type,
TypePath,
};
use std::collections::HashSet;
/// Unique identifier used to make the hidden includes unique for this macro.
const HIDDEN_INCLUDES_ID: &str = "IMPL_RUNTIME_APIS";
/// 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,
) -> Result<TokenStream> {
let params =
extract_parameter_names_types_and_borrows(signature, AllowSelfRefInParameters::No)?;
let c = generate_crate_access(HIDDEN_INCLUDES_ID);
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);
Ok(quote!(
let (#( #pnames ),*) : ( #( #ptypes ),* ) =
match #c::DecodeLimit::decode_all_with_depth_limit(
#c::MAX_EXTRINSIC_DEPTH,
&#input,
) {
Ok(res) => res,
Err(e) => panic!("Bad input data provided to {}: {}", #fn_name_str, e),
};
#[allow(deprecated)]
<#runtime as #impl_trait>::#fn_name(#( #pborrow #pnames2 ),*)
))
}
/// 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 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::Method(method) = item {
let impl_call =
generate_impl_call(&method.sig, &impl_.self_ty, input, &impl_trait)?;
impl_calls.push((
impl_trait_ident.clone(),
method.sig.ident.clone(),
impl_call,
filter_cfg_attrs(&impl_.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(HIDDEN_INCLUDES_ID);
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!(
#[cfg(feature = "std")]
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(HIDDEN_INCLUDES_ID);
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!(
#( #attrs )*
#[cfg(not(feature = "std"))]
#[no_mangle]
pub unsafe fn #fn_name(input_data: *mut u8, input_len: usize) -> u64 {
let mut #input = if input_len == 0 {
&[0u8; 0]
} else {
unsafe {
#c::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(HIDDEN_INCLUDES_ID);
Ok(quote!(
pub struct RuntimeApi {}
/// Implements all runtime apis for the client side.
#[cfg(any(feature = "std", test))]
pub struct RuntimeApiImpl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block> + 'static> {
call: &'static C,
commit_on_success: std::cell::RefCell<bool>,
changes: std::cell::RefCell<#crate_::OverlayedChanges>,
storage_transaction_cache: std::cell::RefCell<
#crate_::StorageTransactionCache<Block, C::StateBackend>
>,
recorder: Option<#crate_::ProofRecorder<Block>>,
}
// `RuntimeApi` itself is not threadsafe. However, an instance is only available in a
// `ApiRef` object and `ApiRef` also has an associated lifetime. This lifetimes makes it
// impossible to move `RuntimeApi` into another thread.
#[cfg(any(feature = "std", test))]
unsafe impl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block>> Send
for RuntimeApiImpl<Block, C>
{}
#[cfg(any(feature = "std", test))]
unsafe impl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block>> Sync
for RuntimeApiImpl<Block, C>
{}
#[cfg(any(feature = "std", test))]
impl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block>> #crate_::ApiExt<Block> for
RuntimeApiImpl<Block, C>
{
type StateBackend = C::StateBackend;
fn execute_in_transaction<F: FnOnce(&Self) -> #crate_::TransactionOutcome<R>, R>(
&self,
call: F,
) -> R where Self: Sized {
self.changes.borrow_mut().start_transaction();
*self.commit_on_success.borrow_mut() = false;
let res = call(self);
*self.commit_on_success.borrow_mut() = true;
self.commit_or_rollback(matches!(res, #crate_::TransactionOutcome::Commit(_)));
res.into_inner()
}
fn has_api<A: #crate_::RuntimeApiInfo + ?Sized>(
&self,
at: &#crate_::BlockId<Block>,
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
self.call
.runtime_version_at(at)
.map(|v| v.has_api_with(&A::ID, |v| v == A::VERSION))
}
fn has_api_with<A: #crate_::RuntimeApiInfo + ?Sized, P: Fn(u32) -> bool>(
&self,
at: &#crate_::BlockId<Block>,
pred: P,
) -> std::result::Result<bool, #crate_::ApiError> where Self: Sized {
self.call
.runtime_version_at(at)
.map(|v| v.has_api_with(&A::ID, pred))
}
fn api_version<A: #crate_::RuntimeApiInfo + ?Sized>(
&self,
at: &#crate_::BlockId<Block>,
) -> std::result::Result<Option<u32>, #crate_::ApiError> where Self: Sized {
self.call
.runtime_version_at(at)
.map(|v| v.api_version(&A::ID))
}
fn record_proof(&mut self) {
self.recorder = Some(Default::default());
}
fn proof_recorder(&self) -> Option<#crate_::ProofRecorder<Block>> {
self.recorder.clone()
}
fn extract_proof(&mut self) -> Option<#crate_::StorageProof> {
self.recorder
.take()
.map(|recorder| recorder.to_storage_proof())
}
fn into_storage_changes(
&self,
backend: &Self::StateBackend,
parent_hash: Block::Hash,
) -> std::result::Result<
#crate_::StorageChanges<C::StateBackend, Block>,
String
> where Self: Sized {
let at = #crate_::BlockId::Hash(parent_hash.clone());
let state_version = self.call
.runtime_version_at(&at)
.map(|v| v.state_version())
.map_err(|e| format!("Failed to get state version: {:?}", e))?;
self.changes.replace(Default::default()).into_storage_changes(
backend,
parent_hash,
self.storage_transaction_cache.replace(Default::default()),
state_version,
)
}
}
#[cfg(any(feature = "std", test))]
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) },
commit_on_success: true.into(),
changes: Default::default(),
recorder: Default::default(),
storage_transaction_cache: Default::default(),
}.into()
}
}
#[cfg(any(feature = "std", test))]
impl<Block: #crate_::BlockT, C: #crate_::CallApiAt<Block>> RuntimeApiImpl<Block, C> {
fn call_api_at<
R: #crate_::Encode + #crate_::Decode + PartialEq,
F: FnOnce(
&C,
&std::cell::RefCell<#crate_::OverlayedChanges>,
&std::cell::RefCell<#crate_::StorageTransactionCache<Block, C::StateBackend>>,
&Option<#crate_::ProofRecorder<Block>>,
) -> std::result::Result<#crate_::NativeOrEncoded<R>, E>,
E,
>(
&self,
call_api_at: F,
) -> std::result::Result<#crate_::NativeOrEncoded<R>, E> {
if *self.commit_on_success.borrow() {
self.changes.borrow_mut().start_transaction();
}
let res = call_api_at(
&self.call,
&self.changes,
&self.storage_transaction_cache,
&self.recorder,
);
self.commit_or_rollback(res.is_ok());
res
}
fn commit_or_rollback(&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";
if *self.commit_on_success.borrow() {
if commit {
self.changes.borrow_mut().commit_transaction().expect(proof);
} else {
self.changes.borrow_mut().rollback_transaction().expect(proof);
}
}
}
}
))
}
/// 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_
}
/// 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 mut impl_ = impl_.clone();
let trait_ = extract_impl_trait(&impl_, RequireQualifiedTraitPath::Yes)?.clone();
let trait_ = extend_with_runtime_decl_path(trait_);
impl_.trait_.as_mut().unwrap().1 = trait_;
impl_.attrs = filter_cfg_attrs(&impl_.attrs);
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,
runtime_mod_path: &'a Path,
runtime_type: &'a Type,
trait_generic_arguments: &'a [GenericArgument],
impl_trait: &'a Ident,
}
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!(__SR_API_BLOCK__) } else { input };
fold::fold_type_path(self, new_ty_path)
}
fn fold_impl_item_method(&mut self, mut input: syn::ImplItemMethod) -> syn::ImplItemMethod {
let block = {
let runtime_mod_path = self.runtime_mod_path;
let runtime = self.runtime_type;
let native_call_generator_ident =
generate_native_call_generator_fn_name(&input.sig.ident);
let call_api_at_call = generate_call_api_at_fn_name(&input.sig.ident);
let trait_generic_arguments = self.trait_generic_arguments;
let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
// Generate the access to the native parameters
let param_tuple_access = if input.sig.inputs.len() == 1 {
vec![quote!(p)]
} else {
input
.sig
.inputs
.iter()
.enumerate()
.map(|(i, _)| {
let i = syn::Index::from(i);
quote!( p.#i )
})
.collect::<Vec<_>>()
};
let (param_types, error) = match extract_parameter_names_types_and_borrows(
&input.sig,
AllowSelfRefInParameters::No,
) {
Ok(res) => (
res.into_iter()
.map(|v| {
let ty = v.1;
let borrow = v.2;
quote!( #borrow #ty )
})
.collect::<Vec<_>>(),
None,
),
Err(e) => (Vec::new(), Some(e.to_compile_error())),
};
// Rewrite the input parameters.
input.sig.inputs = parse_quote! {
&self,
at: &#crate_::BlockId<__SR_API_BLOCK__>,
context: #crate_::ExecutionContext,
params: Option<( #( #param_types ),* )>,
params_encoded: Vec<u8>,
};
input.sig.ident =
generate_method_runtime_api_impl_name(&self.impl_trait, &input.sig.ident);
let ret_type = return_type_extract_type(&input.sig.output);
// Generate the correct return type.
input.sig.output = parse_quote!(
-> std::result::Result<#crate_::NativeOrEncoded<#ret_type>, #crate_::ApiError>
);
// Generate the new method implementation that calls into the runtime.
parse_quote!(
{
// Get the error to the user (if we have one).
#error
self.call_api_at(
|
call_runtime_at,
changes,
storage_transaction_cache,
recorder
| {
#runtime_mod_path #call_api_at_call(
call_runtime_at,
at,
params_encoded,
changes,
storage_transaction_cache,
params.map(|p| {
#runtime_mod_path #native_call_generator_ident ::
<#runtime, __SR_API_BLOCK__ #(, #trait_generic_arguments )*> (
#( #param_tuple_access ),*
)
}),
context,
recorder,
)
}
)
}
)
};
let mut input = fold::fold_impl_item_method(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
}
fn fold_item_impl(&mut self, mut input: ItemImpl) -> ItemImpl {
// All this `UnwindSafe` magic below here is required for this rust bug:
// https://github.com/rust-lang/rust/issues/24159
// Before we directly had the final block type and rust could determine that it is unwind
// safe, but now we just have a generic parameter `Block`.
let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
// Implement the trait for the `RuntimeApiImpl`
input.self_ty =
Box::new(parse_quote!( RuntimeApiImpl<__SR_API_BLOCK__, RuntimeApiImplCall> ));
input.generics.params.push(parse_quote!(
__SR_API_BLOCK__: #crate_::BlockT + std::panic::UnwindSafe +
std::panic::RefUnwindSafe
));
input.generics.params.push(
parse_quote!( RuntimeApiImplCall: #crate_::CallApiAt<__SR_API_BLOCK__> + 'static ),
);
let where_clause = input.generics.make_where_clause();
where_clause.predicates.push(parse_quote! {
RuntimeApiImplCall::StateBackend:
#crate_::StateBackend<#crate_::HashFor<__SR_API_BLOCK__>>
});
// Require that all types used in the function signatures are unwind safe.
extract_all_signature_types(&input.items).iter().for_each(|i| {
where_clause.predicates.push(parse_quote! {
#i: std::panic::UnwindSafe + std::panic::RefUnwindSafe
});
});
where_clause.predicates.push(parse_quote! {
__SR_API_BLOCK__::Header: std::panic::UnwindSafe + std::panic::RefUnwindSafe
});
input.attrs = filter_cfg_attrs(&input.attrs);
// The implementation for the `RuntimeApiImpl` is only required when compiling with
// the feature `std` or `test`.
input.attrs.push(parse_quote!( #[cfg(any(feature = "std", test))] ));
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 impl_trait = &impl_trait_path
.segments
.last()
.ok_or_else(|| Error::new(impl_trait_path.span(), "Empty trait path not possible!"))?
.clone();
let runtime_block = extract_block_type_from_trait_path(impl_trait_path)?;
let runtime_type = &impl_.self_ty;
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 trait_generic_arguments = match impl_trait.arguments {
PathArguments::Parenthesized(_) | PathArguments::None => vec![],
PathArguments::AngleBracketed(ref b) => b.args.iter().cloned().collect(),
};
let mut visitor = ApiRuntimeImplToApiRuntimeApiImpl {
runtime_block,
runtime_mod_path: &runtime_mod_path,
runtime_type: &*runtime_type,
trait_generic_arguments: &trait_generic_arguments,
impl_trait: &impl_trait.ident,
};
result.push(visitor.fold_item_impl(impl_.clone()));
}
Ok(quote!( #( #result )* ))
}
/// 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::with_capacity(impls.len());
let mut sections = Vec::with_capacity(impls.len());
let mut processed_traits = HashSet::new();
let c = generate_crate_access(HIDDEN_INCLUDES_ID);
for impl_ in impls {
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 !processed_traits.insert(trait_) {
return Err(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!",
))
}
let id: Path = parse_quote!( #path ID );
let version: Path = parse_quote!( #path VERSION );
let attrs = filter_cfg_attrs(&impl_.attrs);
result.push(quote!(
#( #attrs )*
(#id, #version)
));
sections.push(quote!(
#( #attrs )*
const _: () = {
// All sections with the same name are going to be merged by concatenation.
#[cfg(not(feature = "std"))]
#[link_section = "runtime_apis"]
static SECTION_CONTENTS: [u8; 12] = #c::serialize_runtime_api_info(#id, #version);
};
));
}
Ok(quote!(
const RUNTIME_API_VERSIONS: #c::ApisVec = #c::create_apis_vec!([ #( #result ),* ]);
#( #sections )*
))
}
/// 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: api_impls } = parse_macro_input!(input as RuntimeApiImpls);
impl_runtime_apis_impl_inner(&api_impls)
.unwrap_or_else(|e| e.to_compile_error())
.into()
}
fn impl_runtime_apis_impl_inner(api_impls: &[ItemImpl]) -> Result<TokenStream> {
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 hidden_includes = generate_hidden_includes(HIDDEN_INCLUDES_ID);
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)?;
Ok(quote!(
#hidden_includes
#base_runtime_api
#api_impls_for_runtime
#api_impls_for_runtime_api
#runtime_api_versions
pub mod api {
use super::*;
#dispatch_impl
#wasm_interface
}
))
}
// Filters all attributes except the cfg ones.
fn filter_cfg_attrs(attrs: &[Attribute]) -> Vec<Attribute> {
attrs.into_iter().filter(|a| a.path.is_ident("cfg")).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 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_attrs(&attrs);
assert_eq!(filtered.len(), 2);
assert_eq!(cfg_std, filtered[0]);
assert_eq!(cfg_benchmarks, filtered[1]);
}
}