mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-13 18:41:05 +00:00
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:
@@ -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::*;
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user