Make runtime api generate version and identifier information (#1226)

* Make `decl_runtime_apis!` implement `RuntimeApiInfo` for all runtime
apis

* Make the runtime side generate the info constants as well

* Make `RuntimeApiInfo` implementation use the correct generics

* Adds a test for the runtime api info stuff

* Remove duplicated code by using block from `test-client`

* Adds `compile_fail` tests for `api_version`

* Adds documentation for `api_version`

* Make `impl_runtime_apis!` generate `RUNTIME_API_VERSIONS`

* Update documentation and tests for `RUNTIME_API_VERSIONS`

* Implement `has_api` by using the `RuntimeApiInfo`

* Make `impl_runtime_apis` check that trait identifiers are unique

* Prefix all runtime api function with the corresponding trait

So `execute_block` will be called `Core_execute_block`.
This makes it possible to have traits implement a function with the
same name.

* Rebase master

* Update after master rebase
This commit is contained in:
Bastian Köcher
2018-12-10 11:48:07 +01:00
committed by GitHub
parent a4a67ccbe7
commit 1f6719346f
25 changed files with 661 additions and 396 deletions
@@ -110,40 +110,74 @@ mod adding_parameter_with_type_reference {
*/
}
mod missing_block_generic_parameter {
mod invalid_api_version {
/*!
```compile_fail
#[macro_use]
extern crate substrate_client;
extern crate sr_primitives as runtime_primitives;
extern crate substrate_primitives as primitives;
decl_runtime_apis! {
#[api_version]
pub trait Api {
fn test(data: u64);
}
}
fn main() {}
```
*/
}
mod invalid_api_version_2 {
/*!
```compile_fail
#[macro_use]
extern crate parity_codec_derive;
extern crate serde;
extern crate core;
extern crate substrate_client;
extern crate sr_primitives as runtime_primitives;
use primitives::hash::H256;
use runtime_primitives::traits::{BlakeTwo256, GetNodeBlockType, Extrinsic as ExtrinsicT};
// All the stuff we need to declare our `Block`
pub type BlockNumber = u64;
pub type DigestItem = runtime_primitives::generic::DigestItem<H256, u64>;
pub type Digest = runtime_primitives::generic::Digest<DigestItem>;
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
pub struct Extrinsic {}
impl serde::Serialize for Extrinsic {
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
unimplemented!()
decl_runtime_apis! {
#[api_version("1")]
pub trait Api {
fn test(data: u64);
}
}
impl ExtrinsicT for Extrinsic {
fn is_signed(&self) -> Option<bool> {
unimplemented!()
fn main() {}
```
*/
}
mod invalid_api_version_3 {
/*!
```compile_fail
#[macro_use]
extern crate substrate_client;
extern crate sr_primitives as runtime_primitives;
decl_runtime_apis! {
#[api_version()]
pub trait Api {
fn test(data: u64);
}
}
pub type Header = runtime_primitives::generic::Header<BlockNumber, BlakeTwo256, DigestItem>;
pub type Block = runtime_primitives::generic::Block<Header, Extrinsic>;
fn main() {}
```
*/
}
mod missing_block_generic_parameter {
/*!
```compile_fail
#[macro_use]
extern crate substrate_client;
extern crate substrate_test_client as test_client;
extern crate sr_primitives as runtime_primitives;
extern crate substrate_primitives as primitives;
use runtime_primitives::traits::GetNodeBlockType;
use test_client::runtime::Block;
/// The declaration of the `Runtime` type and the implementation of the `GetNodeBlockType`
/// trait are done by the `construct_runtime!` macro in a real runtime.
@@ -176,35 +210,12 @@ mod missing_path_for_trait {
```compile_fail
#[macro_use]
extern crate substrate_client;
extern crate substrate_test_client as test_client;
extern crate sr_primitives as runtime_primitives;
extern crate substrate_primitives as primitives;
#[macro_use]
extern crate parity_codec_derive;
extern crate serde;
extern crate core;
use primitives::hash::H256;
use runtime_primitives::traits::{BlakeTwo256, GetNodeBlockType, Extrinsic as ExtrinsicT};
// All the stuff we need to declare our `Block`
pub type BlockNumber = u64;
pub type DigestItem = runtime_primitives::generic::DigestItem<H256, u64>;
pub type Digest = runtime_primitives::generic::Digest<DigestItem>;
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
pub struct Extrinsic {}
impl serde::Serialize for Extrinsic
{
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
unimplemented!()
}
}
impl ExtrinsicT for Extrinsic {
fn is_signed(&self) -> Option<bool> {
unimplemented!()
}
}
pub type Header = runtime_primitives::generic::Header<BlockNumber, BlakeTwo256, DigestItem>;
pub type Block = runtime_primitives::generic::Block<Header, Extrinsic>;
use runtime_primitives::traits::GetNodeBlockType;
use test_client::runtime::Block;
/// The declaration of the `Runtime` type and the implementation of the `GetNodeBlockType`
/// trait are done by the `construct_runtime!` macro in a real runtime.
@@ -237,35 +248,12 @@ mod empty_impl_runtime_apis_call {
```compile_fail
#[macro_use]
extern crate substrate_client;
extern crate substrate_test_client as test_client;
extern crate sr_primitives as runtime_primitives;
extern crate substrate_primitives as primitives;
#[macro_use]
extern crate parity_codec_derive;
extern crate serde;
extern crate core;
use primitives::hash::H256;
use runtime_primitives::traits::{BlakeTwo256, GetNodeBlockType, Extrinsic as ExtrinsicT};
// All the stuff we need to declare our `Block`
pub type BlockNumber = u64;
pub type DigestItem = runtime_primitives::generic::DigestItem<H256, u64>;
pub type Digest = runtime_primitives::generic::Digest<DigestItem>;
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
pub struct Extrinsic {}
impl serde::Serialize for Extrinsic
{
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
unimplemented!()
}
}
impl ExtrinsicT for Extrinsic {
fn is_signed(&self) -> Option<bool> {
unimplemented!()
}
}
pub type Header = runtime_primitives::generic::Header<BlockNumber, BlakeTwo256, DigestItem>;
pub type Block = runtime_primitives::generic::Block<Header, Extrinsic>;
use runtime_primitives::traits::GetNodeBlockType;
use test_client::runtime::Block;
/// The declaration of the `Runtime` type and the implementation of the `GetNodeBlockType`
/// trait are done by the `construct_runtime!` macro in a real runtime.
@@ -292,35 +280,12 @@ mod type_reference_in_impl_runtime_apis_call {
```compile_fail
#[macro_use]
extern crate substrate_client;
extern crate substrate_test_client as test_client;
extern crate sr_primitives as runtime_primitives;
extern crate substrate_primitives as primitives;
#[macro_use]
extern crate parity_codec_derive;
extern crate serde;
extern crate core;
use primitives::hash::H256;
use runtime_primitives::traits::{BlakeTwo256, GetNodeBlockType, Extrinsic as ExtrinsicT};
// All the stuff we need to declare our `Block`
pub type BlockNumber = u64;
pub type DigestItem = runtime_primitives::generic::DigestItem<H256, u64>;
pub type Digest = runtime_primitives::generic::Digest<DigestItem>;
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
pub struct Extrinsic {}
impl serde::Serialize for Extrinsic
{
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
unimplemented!()
}
}
impl ExtrinsicT for Extrinsic {
fn is_signed(&self) -> Option<bool> {
unimplemented!()
}
}
pub type Header = runtime_primitives::generic::Header<BlockNumber, BlakeTwo256, DigestItem>;
pub type Block = runtime_primitives::generic::Block<Header, Extrinsic>;
use runtime_primitives::traits::GetNodeBlockType;
use test_client::runtime::Block;
/// The declaration of the `Runtime` type and the implementation of the `GetNodeBlockType`
/// trait are done by the `construct_runtime!` macro in a real runtime.
@@ -353,35 +318,12 @@ mod impl_incorrect_method_signature {
```compile_fail
#[macro_use]
extern crate substrate_client;
extern crate substrate_test_client as test_client;
extern crate sr_primitives as runtime_primitives;
extern crate substrate_primitives as primitives;
#[macro_use]
extern crate parity_codec_derive;
extern crate serde;
extern crate core;
use primitives::hash::H256;
use runtime_primitives::traits::{BlakeTwo256, GetNodeBlockType, Extrinsic as ExtrinsicT};
// All the stuff we need to declare our `Block`
pub type BlockNumber = u64;
pub type DigestItem = runtime_primitives::generic::DigestItem<H256, u64>;
pub type Digest = runtime_primitives::generic::Digest<DigestItem>;
#[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
pub struct Extrinsic {}
impl serde::Serialize for Extrinsic
{
fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
unimplemented!()
}
}
impl ExtrinsicT for Extrinsic {
fn is_signed(&self) -> Option<bool> {
unimplemented!()
}
}
pub type Header = runtime_primitives::generic::Header<BlockNumber, BlakeTwo256, DigestItem>;
pub type Block = runtime_primitives::generic::Block<Header, Extrinsic>;
use runtime_primitives::traits::GetNodeBlockType;
use test_client::runtime::Block;
/// The declaration of the `Runtime` type and the implementation of the `GetNodeBlockType`
/// trait are done by the `construct_runtime!` macro in a real runtime.
@@ -406,3 +348,51 @@ mod impl_incorrect_method_signature {
```
*/
}
mod impl_two_traits_with_same_name {
/*!
```compile_fail
#[macro_use]
extern crate substrate_client;
extern crate substrate_test_client as test_client;
extern crate sr_primitives as runtime_primitives;
extern crate substrate_primitives as primitives;
use runtime_primitives::traits::GetNodeBlockType;
use test_client::runtime::Block;
/// The declaration of the `Runtime` type and the implementation of the `GetNodeBlockType`
/// trait are done by the `construct_runtime!` macro in a real runtime.
struct Runtime {}
impl GetNodeBlockType for Runtime {
type NodeBlock = Block;
}
decl_runtime_apis! {
pub trait Api {
fn test(data: u64);
}
}
mod second {
decl_runtime_apis! {
pub trait Api {
fn test2(data: u64);
}
}
}
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() {}
```
*/
}
@@ -16,7 +16,7 @@
use utils::{
generate_crate_access, generate_hidden_includes, generate_runtime_mod_name_for_trait,
fold_fn_decl_for_client_side
fold_fn_decl_for_client_side, unwrap_or_error
};
use proc_macro;
@@ -27,12 +27,23 @@ use quote::quote;
use syn::{
spanned::Spanned, parse_macro_input, parse::{Parse, ParseStream, Result, Error},
fold::{self, Fold}, FnDecl, parse_quote, ItemTrait, Generics, GenericParam, Attribute,
visit::{Visit, self}, FnArg, Pat, TraitBound, Type
visit::{Visit, self}, FnArg, Pat, TraitBound, Type, Meta, NestedMeta, Lit
};
use std::collections::HashMap;
use blake2_rfc;
/// Unique identifier used to make the hidden includes unique for this macro.
const HIDDEN_INCLUDES_ID: &str = "DECL_RUNTIME_APIS";
/// The `core_trait` attribute.
const CORE_TRAIT_ATTRIBUTE: &str = "core_trait";
/// The `api_version` attribute.
const API_VERSION_ATTRIBUTE: &str = "api_version";
/// All attributes that we support in the declaratio of a runtime api trait.
const SUPPORTED_ATTRIBUTE_NAMES: &[&str] = &[CORE_TRAIT_ATTRIBUTE, API_VERSION_ATTRIBUTE];
/// The structure used for parsing the runtime api declarations.
struct RuntimeApiDecls {
decls: Vec<ItemTrait>,
@@ -59,15 +70,22 @@ fn extend_generics_with_block(generics: &mut Generics) {
generics.gt_token = Some(parse_quote!(>));
}
// Check if `core_trait` attribute is present and remove it. Returns if the attribute was found.
fn remove_core_trait_attribute(attrs: &mut Vec<Attribute>) -> bool {
let mut found = false;
/// 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| {
let res = v.path.is_ident("core_trait");
found |= res;
!res
match SUPPORTED_ATTRIBUTE_NAMES.iter().filter(|a| v.path.is_ident(a)).next() {
Some(attribute) => {
result.insert(*attribute, v.clone());
false
},
None => true,
}
});
found
result
}
/// Generate the decleration of the trait for the runtime.
@@ -78,7 +96,11 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> TokenStream {
let mut decl = decl.clone();
extend_generics_with_block(&mut decl.generics);
let mod_name = generate_runtime_mod_name_for_trait(&decl.ident);
remove_core_trait_attribute(&mut decl.attrs);
let found_attributes = remove_supported_attributes(&mut decl.attrs);
let api_version = unwrap_or_error(get_api_version(&found_attributes).map(|v| {
generate_runtime_api_version(v as u32)
}));
let id = generate_runtime_api_id(&decl.ident.to_string());
result.push(quote!(
#[doc(hidden)]
@@ -86,6 +108,10 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> TokenStream {
use super::*;
#decl
pub #api_version
pub #id
}
));
}
@@ -97,6 +123,7 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> TokenStream {
struct ToClientSideDecl<'a> {
block_id: &'a TokenStream,
crate_: &'a TokenStream,
found_attributes: &'a mut HashMap<&'static str, Attribute>,
}
impl<'a> Fold for ToClientSideDecl<'a> {
@@ -113,8 +140,9 @@ 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 = remove_core_trait_attribute(&mut input.attrs);
let is_core_trait = self.found_attributes.contains_key(CORE_TRAIT_ATTRIBUTE);
if is_core_trait {
// Add all the supertraits we want to have for `Core`.
@@ -124,7 +152,7 @@ impl<'a> Fold for ToClientSideDecl<'a> {
+ Send
+ Sync
+ #crate_::runtime_api::ConstructRuntimeApi<Block>
+ #crate_::runtime_api::ApiExt
+ #crate_::runtime_api::ApiExt<Block>
);
} else {
// Add the `Core` runtime api as super trait.
@@ -139,6 +167,77 @@ impl<'a> Fold for ToClientSideDecl<'a> {
}
}
/// Parse the given attribute as `API_VERSION_ATTRIBUTE`.
fn parse_runtime_api_version(version: &Attribute) -> Result<u64> {
let meta = version.parse_meta()?;
let err = Err(Error::new(
meta.span(),
&format!(
"Unexpected `{api_version}` attribute. The supported format is `{api_version}(1)`",
api_version = API_VERSION_ATTRIBUTE
)
)
);
match meta {
Meta::List(list) => {
if list.nested.len() > 1 && list.nested.is_empty() {
err
} else {
match list.nested.first().as_ref().map(|v| v.value()) {
Some(NestedMeta::Literal(Lit::Int(i))) => {
Ok(i.value())
},
_ => err,
}
}
},
_ => err,
}
}
/// 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 {
let mut res = [0; 8];
res.copy_from_slice(blake2_rfc::blake2b::blake2b(8, &[], trait_name.as_bytes()).as_bytes());
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: u64) -> TokenStream {
let trait_name = &trait_.ident;
let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
let id = generate_runtime_api_id(&trait_name.to_string());
let version = generate_runtime_api_version(version as u32);
let (impl_generics, ty_generics, where_clause) = trait_.generics.split_for_impl();
quote!(
#[cfg(any(feature = "std", test))]
impl #impl_generics #crate_::runtime_api::RuntimeApiInfo
for #trait_name #ty_generics #where_clause
{
#id
#version
}
)
}
/// 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<u64> {
match found_attributes.get(&API_VERSION_ATTRIBUTE) {
Some(attr) => parse_runtime_api_version(attr),
None => Ok(1),
}
}
/// Generate the decleration of the trait for the client side.
fn generate_client_side_decls(decls: &[ItemTrait]) -> TokenStream {
let mut result = Vec::new();
@@ -148,9 +247,24 @@ fn generate_client_side_decls(decls: &[ItemTrait]) -> TokenStream {
let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
let block_id = quote!( #crate_::runtime_api::BlockId<Block> );
let mut to_client_side = ToClientSideDecl { crate_: &crate_, block_id: &block_id };
let mut found_attributes = HashMap::new();
result.push(to_client_side.fold_item_trait(decl));
let decl = {
let mut to_client_side = ToClientSideDecl {
crate_: &crate_,
block_id: &block_id,
found_attributes: &mut found_attributes
};
to_client_side.fold_item_trait(decl)
};
let api_version = get_api_version(&found_attributes);
let runtime_info = unwrap_or_error(
api_version.map(|v| generate_runtime_info_impl(&decl, v))
);
result.push(quote!( #decl #runtime_info ));
}
quote!( #( #result )* )
@@ -30,7 +30,7 @@ use syn::{
fold::{self, Fold}, FnDecl, parse_quote, Pat
};
use std::iter;
use std::{collections::HashSet, iter};
/// Unique identifier used to make the hidden includes unique for this macro.
const HIDDEN_INCLUDES_ID: &str = "IMPL_RUNTIME_APIS";
@@ -165,11 +165,21 @@ fn extract_runtime_block_ident(trait_: &Path) -> Result<&TypePath> {
}
/// Generate all the implementation calls for the given functions.
fn generate_impl_calls(impls: &[ItemImpl], input: &Ident) -> Result<Vec<(Ident, TokenStream)>> {
fn generate_impl_calls(
impls: &[ItemImpl],
input: &Ident
) -> Result<Vec<(Ident, Ident, TokenStream)>> {
let mut impl_calls = Vec::new();
for impl_ in impls {
let impl_trait = extend_with_runtime_decl_path(extract_impl_trait(impl_)?.clone());
let impl_trait_path = extract_impl_trait(impl_)?;
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!"))?
.value()
.ident;
for item in &impl_.items {
match item {
@@ -181,7 +191,9 @@ fn generate_impl_calls(impls: &[ItemImpl], input: &Ident) -> Result<Vec<(Ident,
&impl_trait
)?;
impl_calls.push((method.sig.ident.clone(), impl_call));
impl_calls.push(
(impl_trait_ident.clone(), method.sig.ident.clone(), impl_call)
);
},
_ => {},
}
@@ -191,13 +203,19 @@ fn generate_impl_calls(impls: &[ItemImpl], input: &Ident) -> Result<Vec<(Ident,
Ok(impl_calls)
}
fn prefix_function_with_trait(trait_: &Ident, function: &Ident) -> String {
format!("{}_{}", trait_.to_string(), function.to_string())
}
/// 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("data", Span::call_site());
let impl_calls = generate_impl_calls(impls, &data)?.into_iter().map(|(fn_name, impl_)| {
let fn_name = fn_name.to_string();
quote!( #fn_name => Some({ #impl_ }), )
});
let impl_calls = generate_impl_calls(impls, &data)?
.into_iter()
.map(|(trait_, fn_name, impl_)| {
let name = prefix_function_with_trait(&trait_, &fn_name);
quote!( #name => Some({ #impl_ }), )
});
Ok(quote!(
#[cfg(feature = "std")]
@@ -214,30 +232,37 @@ fn generate_dispatch_function(impls: &[ItemImpl]) -> Result<TokenStream> {
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(|(fn_name, impl_)| {
quote!(
#[cfg(not(feature = "std"))]
#[no_mangle]
pub fn #fn_name(input_data: *mut u8, input_len: usize) -> u64 {
let mut #input = if input_len == 0 {
&[0u8; 0]
} else {
unsafe {
#c::runtime_api::slice::from_raw_parts(input_data, input_len)
}
};
let impl_calls = generate_impl_calls(impls, &input)?
.into_iter()
.map(|(trait_, fn_name, impl_)| {
let fn_name = Ident::new(
&prefix_function_with_trait(&trait_, &fn_name),
Span::call_site()
);
let output = { #impl_ };
let res = output.as_ptr() as u64 + ((output.len() as u64) << 32);
quote!(
#[cfg(not(feature = "std"))]
#[no_mangle]
pub fn #fn_name(input_data: *mut u8, input_len: usize) -> u64 {
let mut #input = if input_len == 0 {
&[0u8; 0]
} else {
unsafe {
#c::runtime_api::slice::from_raw_parts(input_data, input_len)
}
};
// Leak the output vector to avoid it being freed.
// This is fine in a WASM context since the heap
// will be discarded after the call.
::core::mem::forget(output);
res
}
)
});
let output = { #impl_ };
let res = output.as_ptr() as u64 + ((output.len() as u64) << 32);
// Leak the output vector to avoid it being freed.
// This is fine in a WASM context since the heap
// will be discarded after the call.
#c::runtime_api::mem::forget(output);
res
}
)
});
Ok(quote!( #( #impl_calls )* ))
}
@@ -272,7 +297,7 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
/// Implements all runtime apis for the client side.
#[cfg(any(feature = "std", test))]
pub struct RuntimeApi {
call: ::std::ptr::NonNull<#crate_::runtime_api::CallApiAt<#block>>,
call: ::std::ptr::NonNull<#crate_::runtime_api::CallRuntimeAt<#block>>,
commit_on_success: ::std::cell::RefCell<bool>,
initialised_block: ::std::cell::RefCell<Option<#block_id>>,
changes: ::std::cell::RefCell<#crate_::runtime_api::OverlayedChanges>,
@@ -287,11 +312,11 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
unsafe impl Sync for RuntimeApi {}
#[cfg(any(feature = "std", test))]
impl #crate_::runtime_api::ApiExt for RuntimeApi {
impl #crate_::runtime_api::ApiExt<#block> for RuntimeApi {
fn map_api_result<F: FnOnce(&Self) -> ::std::result::Result<R, E>, R, E>(
&self,
map_call: F
) -> ::std::result::Result<R, E> {
) -> ::std::result::Result<R, E> where Self: Sized {
*self.commit_on_success.borrow_mut() = false;
let res = map_call(self);
*self.commit_on_success.borrow_mut() = true;
@@ -300,17 +325,25 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
res
}
fn has_api<A: #crate_::runtime_api::RuntimeApiInfo + ?Sized>(
&self,
at: &#block_id
) -> #crate_::error::Result<bool> where Self: Sized {
unsafe { self.call.as_ref().runtime_version_at(at) }.map(|r| r.has_api::<A>())
}
}
#[cfg(any(feature = "std", test))]
impl #crate_::runtime_api::ConstructRuntimeApi<#block> for RuntimeApi {
fn construct_runtime_api<'a, T: #crate_::runtime_api::CallApiAt<#block>>(
fn construct_runtime_api<'a, T: #crate_::runtime_api::CallRuntimeAt<#block>>(
call: &'a T
) -> #crate_::runtime_api::ApiRef<'a, Self> {
) -> #crate_::runtime_api::ApiRef<'a, Self> where Self: Sized {
RuntimeApi {
call: unsafe {
::std::ptr::NonNull::new_unchecked(
call as &#crate_::runtime_api::CallApiAt<#block> as *const _ as *mut _
call as
&#crate_::runtime_api::CallRuntimeAt<#block> as *const _ as *mut _
)
},
commit_on_success: true.into(),
@@ -423,6 +456,7 @@ struct ApiRuntimeImplToApiRuntimeApiImpl<'a> {
node_block: &'a TokenStream,
runtime_block: &'a TypePath,
node_block_id: &'a TokenStream,
impl_trait_ident: &'a Ident,
}
impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
@@ -457,7 +491,7 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
*p = generate_unique_pattern(p.clone(), &mut generated_name_counter);
p
});
let name = input.sig.ident.to_string();
let name = prefix_function_with_trait(self.impl_trait_ident, &input.sig.ident);
// Generate the new method implementation that calls into the runime.
input.block = parse_quote!( { self.call_api_at(at, #name, &( #( #arg_names ),* )) } );
@@ -478,17 +512,26 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
}
}
/// 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 runtime_block = extract_runtime_block_ident(extract_impl_trait(&impl_)?)?;
let impl_trait = extract_impl_trait(&impl_)?;
let impl_trait_ident = &impl_trait
.segments
.last()
.ok_or_else(|| Error::new(impl_trait.span(), "Empty trait path not possible!"))?
.value()
.ident;
let runtime_block = extract_runtime_block_ident(impl_trait)?;
let (node_block, node_block_id) = generate_node_block_and_block_id_ty(&impl_.self_ty);
let mut visitor = ApiRuntimeImplToApiRuntimeApiImpl {
runtime_block,
node_block: &node_block,
node_block_id: &node_block_id,
impl_trait_ident: &impl_trait_ident,
};
result.push(visitor.fold_item_impl(impl_.clone()));
@@ -497,6 +540,48 @@ fn generate_api_impl_for_runtime_api(impls: &[ItemImpl]) -> Result<TokenStream>
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 processed_traits = HashSet::new();
for impl_ in impls {
let mut path = extend_with_runtime_decl_path(extract_impl_trait(&impl_)?.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 );
result.push(quote!( (#id, #version) ));
}
let c = generate_crate_access(HIDDEN_INCLUDES_ID);
Ok(quote!(
const RUNTIME_API_VERSIONS: #c::runtime_api::ApisVec =
#c::runtime_api::create_apis_vec!([ #( #result ),* ]);
))
}
/// 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
@@ -507,6 +592,7 @@ pub fn impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::Tok
let base_runtime_api = unwrap_or_error(generate_runtime_api_base_structures(&api_impls));
let api_impls_for_runtime = unwrap_or_error(generate_api_impl_for_runtime(&api_impls));
let api_impls_for_runtime_api = unwrap_or_error(generate_api_impl_for_runtime_api(&api_impls));
let runtime_api_versions = unwrap_or_error(generate_runtime_api_versions(&api_impls));
quote!(
#hidden_includes
@@ -517,6 +603,8 @@ pub fn impl_runtime_apis_impl(input: proc_macro::TokenStream) -> proc_macro::Tok
#api_impls_for_runtime_api
#runtime_api_versions
pub mod api {
use super::*;
+55 -29
View File
@@ -16,11 +16,12 @@
//! Macros for declaring and implementing runtime apis.
#![recursion_limit = "128"]
#![recursion_limit = "256"]
extern crate proc_macro;
extern crate proc_macro2;
extern crate quote;
extern crate syn;
extern crate blake2_rfc;
use proc_macro::TokenStream;
@@ -38,43 +39,27 @@ mod compile_fail_tests;
/// 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 implementation of the apis for the client side by generating the
/// `RuntimeApi` type. The `RuntimeApi` is hidden behind a `feature` called `std`.
/// 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
/// #[macro_use]
/// extern crate substrate_client;
/// extern crate sr_version as version;
///
/// use version::create_runtime_str;
/// # extern crate substrate_test_client as test_client;
/// # extern crate sr_primitives as runtime_primitives;
/// # extern crate substrate_primitives as primitives;
/// # #[macro_use]
/// # extern crate parity_codec_derive;
/// # extern crate serde;
/// # extern crate core;
/// #
/// # use primitives::hash::H256;
/// # use runtime_primitives::traits::{BlakeTwo256, GetNodeBlockType, Extrinsic as ExtrinsicT};
/// #
/// # // All the stuff we need to declare our `Block`
/// # pub type BlockNumber = u64;
/// # pub type DigestItem = runtime_primitives::generic::DigestItem<H256, u64>;
/// # pub type Digest = runtime_primitives::generic::Digest<DigestItem>;
/// # #[derive(Clone, PartialEq, Eq, Encode, Decode, Debug)]
/// # pub struct Extrinsic {}
/// #
/// # impl serde::Serialize for Extrinsic {
/// # fn serialize<S>(&self, seq: S) -> Result<S::Ok, S::Error> where S: ::serde::Serializer {
/// # unimplemented!()
/// # }
/// # }
/// # impl ExtrinsicT for Extrinsic {
/// # fn is_signed(&self) -> Option<bool> {
/// # unimplemented!()
/// # }
/// # }
/// # pub type Header = runtime_primitives::generic::Header<BlockNumber, BlakeTwo256, DigestItem>;
/// # pub type Block = runtime_primitives::generic::Block<Header, Extrinsic>;
/// # use runtime_primitives::traits::GetNodeBlockType;
/// # use test_client::runtime::Block;
/// #
/// # /// The declaration of the `Runtime` type and the implementation of the `GetNodeBlockType`
/// # /// trait are done by the `construct_runtime!` macro in a real runtime.
@@ -114,6 +99,17 @@ mod compile_fail_tests;
/// }
/// }
///
/// /// Runtime version. This needs to be declared for each runtime.
/// pub const VERSION: version::RuntimeVersion = version::RuntimeVersion {
/// spec_name: create_runtime_str!("node"),
/// impl_name: create_runtime_str!("test-node"),
/// authoring_version: 1,
/// spec_version: 1,
/// impl_version: 0,
/// // Here we are exposing the runtime api versions.
/// apis: RUNTIME_API_VERSIONS,
/// };
///
/// # fn main() {}
/// ```
#[proc_macro]
@@ -158,6 +154,36 @@ pub fn impl_runtime_apis(input: TokenStream) -> TokenStream {
///
/// # 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.
///
/// ```rust
/// #[macro_use]
/// extern crate substrate_client;
///
/// 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);
/// /// 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: &BlockId)` to
/// check if the runtime at the given block id implements the requested runtime api trait.
#[proc_macro]
pub fn decl_runtime_apis(input: TokenStream) -> TokenStream {
decl_runtime_apis::decl_runtime_apis_impl(input)