Make API backwards compatible with CC (#1697)

* Rework how a runtime api calls into the runtime

Now we generate a default implementation for each api call that calls
a generated method `method_runtime_api_impl`. This newly generated
method is the one that will be implemented by the `impl_runtime_apis`
macro in the runtime for the client side.

* Support `changed_in` to change runtime api function signatures

* Update documentation

* Fixes tests

* Implement checking the api version with a predicate

* Make the implementation backwards compatible with CC

* Update wasm files after merge

* Check for wasm runtime differences by building master and current branch

* Update spec_version and wasm files

* Fixes

* Revert my changes

* Remove `patch.crates-io` from test-runtime
This commit is contained in:
Bastian Köcher
2019-02-06 11:47:47 +01:00
committed by Gav Wood
parent 1ba7e35c18
commit 190393d476
25 changed files with 731 additions and 224 deletions
+2
View File
@@ -3487,6 +3487,7 @@ dependencies = [
"kvdb-memorydb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-api-macros 0.1.0",
@@ -3981,6 +3982,7 @@ dependencies = [
name = "substrate-test-runtime"
version = "0.1.0"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -96,10 +96,10 @@ impl<B, E, Block, RA> AuthoringApi for SubstrateClient<B, E, Block, RA> where
let mut block_builder = self.new_block_at(at)?;
let runtime_api = self.runtime_api();
if runtime_api.has_api::<BlockBuilderApi<Block>>(at)? {
runtime_api.inherent_extrinsics(at, inherent_data)?
.into_iter().try_for_each(|i| block_builder.push(i))?;
}
// We don't check the API versions any further here since the dispatch compatibility
// check should be enough.
runtime_api.inherent_extrinsics(at, inherent_data)?
.into_iter().try_for_each(|i| block_builder.push(i))?;
build_ctx(&mut block_builder);
+4 -2
View File
@@ -22,7 +22,8 @@ substrate-telemetry = { path = "../telemetry", optional = true }
hash-db = { version = "0.9" , optional = true }
kvdb = { git = "https://github.com/paritytech/parity-common", optional = true, rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d" }
codec = { package = "parity-codec", version = "3.0", default-features = false }
parity-codec = { version = "3.0", default-features = false }
parity-codec-derive = { version = "3.0", default-features = false }
primitives = { package = "substrate-primitives", path = "../primitives", default-features = false }
runtime-primitives = { package = "sr-primitives", path = "../sr-primitives", default-features = false }
runtime-version = { package = "sr-version", path = "../sr-version", default-features = false }
@@ -37,7 +38,8 @@ kvdb-memorydb = { git = "https://github.com/paritytech/parity-common", rev="b031
[features]
default = ["std"]
std = [
"codec/std",
"parity-codec/std",
"parity-codec-derive/std",
"consensus",
"primitives/std",
"inherents/std",
+43 -1
View File
@@ -16,13 +16,50 @@
//! The runtime api for building blocks.
use runtime_primitives::{traits::Block as BlockT, ApplyResult};
use runtime_primitives::{traits::Block as BlockT, ApplyResult, RuntimeString};
use rstd::vec::Vec;
use sr_api_macros::decl_runtime_apis;
pub use inherents::{InherentData, CheckInherentsResult};
use parity_codec_derive::{Encode, Decode};
/// The old representation of the inherent data.
#[doc(hide)]
#[derive(Encode, Decode)]
pub struct OldInherentData {
/// Current timestamp.
pub timestamp: u64,
/// Blank report.
pub consensus: (),
/// Aura expected slot. Can take any value during block construction.
pub aura_expected_slot: u64,
}
impl OldInherentData {
/// Create a new `BasicInherentData` instance.
pub fn new(timestamp: u64, expected_slot: u64) -> Self {
Self {
timestamp,
consensus: (),
aura_expected_slot: expected_slot,
}
}
}
/// Error type used while checking inherents.
#[doc(hide)]
#[derive(Encode, PartialEq)]
#[cfg_attr(feature = "std", derive(Decode))]
pub enum OldCheckInherentError {
/// The inherents are generally valid but a delay until the given timestamp
/// is required.
ValidAtTimestamp(u64),
/// Some other error has occurred.
Other(RuntimeString),
}
decl_runtime_apis! {
/// The `BlockBuilder` api trait that provides required functions for building a block for a runtime.
#[api_version(2)]
pub trait BlockBuilder {
/// Apply the given extrinsics.
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyResult;
@@ -32,6 +69,11 @@ decl_runtime_apis! {
fn inherent_extrinsics(inherent: InherentData) -> Vec<<Block as BlockT>::Extrinsic>;
/// Check that the inherents are valid. The inherent data will vary from chain to chain.
fn check_inherents(block: Block, data: InherentData) -> CheckInherentsResult;
/// Check that the inherents are valid. The inherent data will vary from chain to chain.
///
/// Old version that is used by the CC network.
#[changed_in(2)]
fn check_inherents(block: Block, data: OldInherentData) -> ::std::result::Result<(), OldCheckInherentError>;
/// Generate a random seed.
fn random_seed() -> <Block as BlockT>::Hash;
}
+1 -12
View File
@@ -1426,7 +1426,7 @@ pub(crate) mod tests {
use runtime_primitives::generic::DigestItem;
use test_client::{self, TestClient};
use consensus::BlockOrigin;
use test_client::client::{backend::Backend as TestBackend, runtime_api::ApiExt};
use test_client::client::backend::Backend as TestBackend;
use test_client::BlockBuilderExt;
use test_client::runtime::{self, Block, Transfer, RuntimeApi, TestAPI};
@@ -1523,17 +1523,6 @@ pub(crate) mod tests {
);
}
#[test]
fn runtime_api_has_test_api() {
let client = test_client::new();
assert!(
client.runtime_api().has_api::<TestAPI<Block>>(
&BlockId::Number(client.info().unwrap().chain.best_number),
).unwrap()
);
}
#[test]
fn authorities_call_works() {
let client = test_client::new();
+2
View File
@@ -20,6 +20,8 @@
#![warn(missing_docs)]
#![recursion_limit="128"]
extern crate parity_codec as codec;
#[macro_use]
pub mod runtime_api;
#[cfg(feature = "std")]
+15 -1
View File
@@ -69,7 +69,21 @@ pub trait ApiExt<Block: BlockT> {
fn has_api<A: RuntimeApiInfo + ?Sized>(
&self,
at: &BlockId<Block>
) -> error::Result<bool> where Self: Sized;
) -> error::Result<bool> where Self: Sized {
self.runtime_version_at(at).map(|v| v.has_api::<A>())
}
/// Check if the given api is implemented and the version passes a predicate.
fn has_api_with<A: RuntimeApiInfo + ?Sized, P: Fn(u32) -> bool>(
&self,
at: &BlockId<Block>,
pred: P,
) -> error::Result<bool> where Self: Sized {
self.runtime_version_at(at).map(|v| v.has_api_with::<A, _>(pred))
}
/// Returns the runtime version at the given block id.
fn runtime_version_at(&self, at: &BlockId<Block>) -> error::Result<RuntimeVersion>;
}
/// Something that can call into the runtime at a given block.
+65 -7
View File
@@ -34,7 +34,8 @@ use consensus_common::{
};
use consensus_common::import_queue::{Verifier, BasicQueue, SharedBlockImport, SharedJustificationImport};
use client::ChainHead;
use client::block_builder::api::BlockBuilder as BlockBuilderApi;
use client::block_builder::api::{BlockBuilder as BlockBuilderApi, self as block_builder_api};
use client::runtime_api::ApiExt;
use consensus_common::{ImportBlock, BlockOrigin};
use runtime_primitives::{generic, generic::BlockId, Justification};
use runtime_primitives::traits::{
@@ -465,6 +466,50 @@ impl<C, E> AuraVerifier<C, E>
Ok(())
}
}
#[allow(deprecated)]
fn old_check_inherents<B: Block>(
&self,
block: B,
block_id: BlockId<B>,
inherent_data: InherentData,
timestamp_now: u64,
) -> Result<(), String>
where C: ProvideRuntimeApi, C::Api: BlockBuilderApi<B>
{
use block_builder_api::{OldInherentData, OldCheckInherentError};
const MAX_TIMESTAMP_DRIFT_SECS: u64 = 60;
let (timestamp, slot) = AuraSlotCompatible::extract_timestamp_and_slot(&inherent_data).map_err(|e| format!("{:?}", e))?;
let inherent_data = OldInherentData::new(timestamp, slot);
let inherent_res = self.client.runtime_api().check_inherents_before_version_2(
&block_id,
block,
inherent_data,
).map_err(|e| format!("{:?}", e))?;
match inherent_res {
Ok(()) => Ok(()),
Err(OldCheckInherentError::ValidAtTimestamp(timestamp)) => {
// halt import until timestamp is valid.
// reject when too far ahead.
if timestamp > timestamp_now + MAX_TIMESTAMP_DRIFT_SECS {
return Err("Rejecting block too far in future".into());
}
let diff = timestamp.saturating_sub(timestamp_now);
info!(
target: "aura",
"halting for block {} seconds in the future",
diff
);
thread::sleep(Duration::from_secs(diff));
Ok(())
},
Err(OldCheckInherentError::Other(e)) => Err(e.into())
}
}
}
/// No-op extra verification.
@@ -519,12 +564,25 @@ impl<B: Block, C, E> Verifier<B> for AuraVerifier<C, E> where
inherent_data.aura_replace_inherent_data(slot_num);
let block = B::new(pre_header.clone(), inner_body);
self.check_inherents(
block.clone(),
BlockId::Hash(parent_hash),
inherent_data,
timestamp_now,
)?;
if self.client
.runtime_api()
.has_api_with::<BlockBuilderApi<B>, _>(&BlockId::Hash(parent_hash), |v| v < 2)
.map_err(|e| format!("{:?}", e))?
{
self.old_check_inherents(
block.clone(),
BlockId::Hash(parent_hash),
inherent_data,
timestamp_now,
)?;
} else {
self.check_inherents(
block.clone(),
BlockId::Hash(parent_hash),
inherent_data,
timestamp_now,
)?;
}
let (_, inner_body) = block.deconstruct();
body = Some(inner_body);
+47 -25
View File
@@ -34,8 +34,9 @@ use consensus_common::BlockOrigin;
use consensus_common::import_queue::{SharedBlockImport, SharedJustificationImport};
use std::collections::{HashMap, HashSet};
use std::result;
use runtime_primitives::traits::{ApiRef, ProvideRuntimeApi, RuntimeApiInfo};
use runtime_primitives::traits::{ApiRef, ProvideRuntimeApi};
use runtime_primitives::generic::BlockId;
use substrate_primitives::NativeOrEncoded;
use authorities::AuthoritySet;
@@ -273,23 +274,39 @@ impl ProvideRuntimeApi for TestApi {
}
impl Core<Block> for RuntimeApi {
fn version(&self, _: &BlockId<Block>) -> Result<RuntimeVersion> {
unimplemented!("Not required for testing!")
}
fn authorities(&self, _: &BlockId<Block>) -> Result<Vec<Ed25519AuthorityId>> {
unimplemented!("Not required for testing!")
}
fn execute_block(&self, _: &BlockId<Block>, _: Block) -> Result<()> {
unimplemented!("Not required for testing!")
}
fn initialise_block(
fn version_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: &<Block as BlockT>::Header
) -> Result<()> {
_: Option<()>,
_: Vec<u8>
) -> Result<NativeOrEncoded<RuntimeVersion>> {
unimplemented!("Not required for testing!")
}
fn authorities_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: Option<()>,
_: Vec<u8>
) -> Result<NativeOrEncoded<Vec<Ed25519AuthorityId>>> {
unimplemented!("Not required for testing!")
}
fn execute_block_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: Option<(Block)>,
_: Vec<u8>
) -> Result<NativeOrEncoded<()>> {
unimplemented!("Not required for testing!")
}
fn initialise_block_runtime_api_impl(
&self,
_: &BlockId<Block>,
_: Option<&<Block as BlockT>::Header>,
_: Vec<u8>,
) -> Result<NativeOrEncoded<()>> {
unimplemented!("Not required for testing!")
}
}
@@ -302,26 +319,31 @@ impl ApiExt<Block> for RuntimeApi {
unimplemented!("Not required for testing!")
}
fn has_api<A: RuntimeApiInfo + ?Sized>(&self, _: &BlockId<Block>) -> Result<bool> {
fn runtime_version_at(&self, _: &BlockId<Block>) -> Result<RuntimeVersion> {
unimplemented!("Not required for testing!")
}
}
impl GrandpaApi<Block> for RuntimeApi {
fn grandpa_authorities(
fn grandpa_authorities_runtime_api_impl(
&self,
at: &BlockId<Block>
) -> Result<Vec<(Ed25519AuthorityId, u64)>> {
at: &BlockId<Block>,
_: Option<()>,
_: Vec<u8>,
) -> Result<NativeOrEncoded<Vec<(Ed25519AuthorityId, u64)>>> {
if at == &BlockId::Number(0) {
Ok(self.inner.genesis_authorities.clone())
Ok(self.inner.genesis_authorities.clone()).map(NativeOrEncoded::Native)
} else {
panic!("should generally only request genesis authorities")
}
}
fn grandpa_pending_change(&self, at: &BlockId<Block>, _: &DigestFor<Block>)
-> Result<Option<ScheduledChange<NumberFor<Block>>>>
{
fn grandpa_pending_change_runtime_api_impl(
&self,
at: &BlockId<Block>,
_: Option<(&DigestFor<Block>)>,
_: Vec<u8>
) -> Result<NativeOrEncoded<Option<ScheduledChange<NumberFor<Block>>>>> {
let parent_hash = match at {
&BlockId::Hash(at) => at,
_ => panic!("not requested by block hash!!"),
@@ -329,7 +351,7 @@ impl GrandpaApi<Block> for RuntimeApi {
// we take only scheduled changes at given block number where there are no
// extrinsics.
Ok(self.inner.scheduled_changes.lock().get(&parent_hash).map(|c| c.clone()))
Ok(self.inner.scheduled_changes.lock().get(&parent_hash).map(|c| c.clone())).map(NativeOrEncoded::Native)
}
}
+1 -6
View File
@@ -215,14 +215,9 @@ fn should_return_runtime_version() {
let client = Arc::new(test_client::new());
let api = State::new(client.clone(), Subscriptions::new(core.executor()));
assert_matches!(
api.runtime_version(None.into()),
Ok(ref ver) if ver == &runtime::VERSION
);
assert_eq!(
::serde_json::to_string(&api.runtime_version(None.into()).unwrap()).unwrap(),
r#"{"specName":"test","implName":"parity-test","authoringVersion":1,"specVersion":1,"implVersion":1,"apis":[["0xdf6acb689907609b",1],["0x37e397fc7c91f5e4",1],["0xd2bc9897eed08f15",1],["0x40fe3ad401f8959a",1],["0xc6e9a76309f39b09",1],["0xdd718d5cc53262d4",1]]}"#
r#"{"specName":"test","implName":"parity-test","authoringVersion":1,"specVersion":1,"implVersion":1,"apis":[["0xdf6acb689907609b",1],["0x37e397fc7c91f5e4",1],["0xd2bc9897eed08f15",1],["0x40fe3ad401f8959a",2],["0xc6e9a76309f39b09",1],["0xdd718d5cc53262d4",1]]}"#
);
}
@@ -378,3 +378,35 @@ mod impl_two_traits_with_same_name {
```
*/
}
mod changed_at_unknown_version {
/*!
```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 {
#[changed_in(2)]
fn test(data: u64);
fn test(data: u64);
}
}
fn main() {}
```
*/
}
@@ -18,6 +18,7 @@ use utils::{
generate_crate_access, generate_hidden_includes, generate_runtime_mod_name_for_trait,
fold_fn_decl_for_client_side, unwrap_or_error, extract_parameter_names_types_and_borrows,
generate_native_call_generator_fn_name, return_type_extract_type,
generate_method_runtime_api_impl_name
};
use proc_macro;
@@ -27,8 +28,9 @@ use quote::quote;
use syn::{
spanned::Spanned, parse_macro_input, parse::{Parse, ParseStream, Result, Error}, ReturnType,
fold::{self, Fold}, FnDecl, parse_quote, ItemTrait, Generics, GenericParam, Attribute,
visit::{Visit, self}, FnArg, Pat, TraitBound, Meta, NestedMeta, Lit, TraitItem, Ident, Type
fold::{self, Fold}, parse_quote, ItemTrait, Generics, GenericParam, Attribute, FnArg,
visit::{Visit, self}, Pat, TraitBound, Meta, NestedMeta, Lit, TraitItem, Ident, Type,
TraitItemMethod
};
use std::collections::HashMap;
@@ -44,9 +46,16 @@ const HIDDEN_INCLUDES_ID: &str = "DECL_RUNTIME_APIS";
/// The `core_trait` attribute.
const CORE_TRAIT_ATTRIBUTE: &str = "core_trait";
/// The `api_version` attribute.
/// Is used to set the current version of the trait.
const API_VERSION_ATTRIBUTE: &str = "api_version";
/// The `changed_in` attribute.
/// Is used when the function signature changed between different versions of a trait.
/// This attribute should be placed on the old signature of the function.
const CHANGED_IN_ATTRIBUTE: &str = "changed_in";
/// All attributes that we support in the declaratio of a runtime api trait.
const SUPPORTED_ATTRIBUTE_NAMES: &[&str] = &[CORE_TRAIT_ATTRIBUTE, API_VERSION_ATTRIBUTE];
const SUPPORTED_ATTRIBUTE_NAMES: &[&str] = &[
CORE_TRAIT_ATTRIBUTE, API_VERSION_ATTRIBUTE, CHANGED_IN_ATTRIBUTE
];
/// The structure used for parsing the runtime api declarations.
struct RuntimeApiDecls {
@@ -279,6 +288,20 @@ fn generate_runtime_decls(decls: &[ItemTrait]) -> TokenStream {
generate_runtime_api_version(v as u32)
}));
let id = generate_runtime_api_id(&decl.ident.to_string());
// Remove methods that have the `changed_in` attribute as they are not required for the
// runtime anymore.
decl.items = decl.items.iter_mut().filter_map(|i| match i {
TraitItem::Method(ref mut method) => {
if remove_supported_attributes(&mut method.attrs).contains_key(CHANGED_IN_ATTRIBUTE) {
None
} else {
Some(TraitItem::Method(method.clone()))
}
}
r => Some(r.clone()),
}).collect();
let native_call_generators = unwrap_or_error(generate_native_call_generators(&decl));
result.push(quote!(
@@ -306,19 +329,164 @@ struct ToClientSideDecl<'a> {
block_id: &'a TokenStream,
crate_: &'a TokenStream,
found_attributes: &'a mut HashMap<&'static str, Attribute>,
/// Any error that we found while converting this declaration.
errors: &'a mut Vec<TokenStream>,
}
impl<'a> Fold for ToClientSideDecl<'a> {
fn fold_fn_decl(&mut self, input: FnDecl) -> FnDecl {
let input = fold_fn_decl_for_client_side(
input,
impl<'a> ToClientSideDecl<'a> {
fn fold_item_trait_items(&mut self, items: Vec<TraitItem>) -> Vec<TraitItem> {
let mut result = Vec::new();
items.into_iter().for_each(|i| match i {
TraitItem::Method(method) => {
let (fn_decl, fn_impl) = self.fold_trait_item_method(method);
result.push(fn_decl.into());
if let Some(fn_impl) = fn_impl {
result.push(fn_impl.into());
}
},
r => result.push(r),
});
result
}
fn fold_trait_item_method(&mut self, method: TraitItemMethod) -> (TraitItemMethod, Option<TraitItemMethod>) {
let fn_impl = self.create_method_runtime_api_impl(method.clone());
let fn_decl = self.create_method_decl(method);
(fn_decl, fn_impl)
}
/// Takes the given method and creates a `method_runtime_api_impl` method that will be
/// implemented in the runtime for the client side.
fn create_method_runtime_api_impl(&mut self, mut method: TraitItemMethod) -> Option<TraitItemMethod> {
if remove_supported_attributes(&mut method.attrs).contains_key(CHANGED_IN_ATTRIBUTE) {
return None;
}
let fn_decl = &method.sig.decl;
let ret_type = return_type_extract_type(&fn_decl.output);
// Get types and if the value is borrowed from all parameters.
// If there is an error, we push it as the block to the user.
let param_types = match extract_parameter_names_types_and_borrows(fn_decl) {
Ok(res) => res.into_iter().map(|v| {
let ty = v.1;
let borrow = v.2;
quote!( #borrow #ty )
}).collect::<Vec<_>>(),
Err(e) => {
self.errors.push(e.to_compile_error());
Vec::new()
}
};
let name = generate_method_runtime_api_impl_name(&method.sig.ident);
let block_id = self.block_id;
let crate_ = self.crate_;
Some(
parse_quote!{
#[doc(hidden)]
fn #name(
&self,
at: &#block_id,
params: Option<( #( #param_types ),* )>,
params_encoded: Vec<u8>
) -> #crate_::error::Result<#crate_::runtime_api::NativeOrEncoded<#ret_type>>;
}
)
}
/// Takes the method declared by the user and creates the declaration we require for the runtime
/// api client side. This method will call by default the `method_runtime_api_impl` for doing
/// the actual call into the runtime.
fn create_method_decl(&mut self, mut method: TraitItemMethod) -> TraitItemMethod {
let params = match extract_parameter_names_types_and_borrows(&method.sig.decl) {
Ok(res) => res.into_iter().map(|v| v.0).collect::<Vec<_>>(),
Err(e) => {
self.errors.push(e.to_compile_error());
Vec::new()
}
};
let params2 = params.clone();
let ret_type = return_type_extract_type(&method.sig.decl.output);
method.sig.decl = fold_fn_decl_for_client_side(
method.sig.decl.clone(),
&self.block_id,
&self.crate_
);
let name_impl = generate_method_runtime_api_impl_name(&method.sig.ident);
let crate_ = self.crate_;
fold::fold_fn_decl(self, input)
let found_attributes = remove_supported_attributes(&mut method.attrs);
// If the method has a `changed_in` attribute, we need to alter the method name to
// `method_before_version_VERSION`.
let (native_handling, param_tuple) = match get_changed_in(&found_attributes) {
Ok(Some(version)) => {
// Make sure that the `changed_in` version is at least the current `api_version`.
if get_api_version(&self.found_attributes).ok() < Some(version) {
self.errors.push(
Error::new(
method.span(),
"`changed_in` version can not be greater than the `api_version`",
).to_compile_error()
);
}
let ident = Ident::new(
&format!("{}_before_version_{}", method.sig.ident, version),
method.sig.ident.span()
);
method.sig.ident = ident;
method.attrs.push(parse_quote!( #[deprecated] ));
let panic = format!("Calling `{}` should not return a native value!", method.sig.ident);
(quote!( panic!(#panic) ), quote!( None ))
},
Ok(None) => (quote!( Ok(n) ), quote!( Some(( #( #params2 ),* )) )),
Err(e) => {
self.errors.push(e.to_compile_error());
(quote!( unimplemented!() ), quote!( None ))
}
};
let function_name = method.sig.ident.to_string();
// Generate the default implementation that calls the `method_runtime_api_impl` method.
method.default = Some(
parse_quote! {
{
let runtime_api_impl_params_encoded =
#crate_::runtime_api::Encode::encode(&( #( &#params ),* ));
self.#name_impl(at, #param_tuple, runtime_api_impl_params_encoded)
.and_then(|r|
match r {
#crate_::runtime_api::NativeOrEncoded::Native(n) => {
#native_handling
},
#crate_::runtime_api::NativeOrEncoded::Encoded(r) => {
<#ret_type as #crate_::runtime_api::Decode>::decode(&mut &r[..])
.ok_or_else(||
#crate_::error::ErrorKind::CallResultDecode(
#function_name
).into()
)
}
}
)
}
}
);
method
}
}
impl<'a> Fold for ToClientSideDecl<'a> {
fn fold_item_trait(&mut self, mut input: ItemTrait) -> ItemTrait {
extend_generics_with_block(&mut input.generics);
@@ -344,6 +512,7 @@ impl<'a> Fold for ToClientSideDecl<'a> {
// The client side trait is only required when compiling with the feature `std` or `test`.
input.attrs.push(parse_quote!( #[cfg(any(feature = "std", test))] ));
input.items = self.fold_item_trait_items(input.items);
fold::fold_item_trait(self, input)
}
@@ -412,12 +581,16 @@ fn generate_runtime_info_impl(trait_: &ItemTrait, version: u64) -> TokenStream {
)
}
/// Get changed in version from the user given attribute or `Ok(None)`, if no attribute was given.
fn get_changed_in(found_attributes: &HashMap<&'static str, Attribute>) -> Result<Option<u64>> {
found_attributes.get(&CHANGED_IN_ATTRIBUTE)
.map(|v| parse_runtime_api_version(v).map(Some))
.unwrap_or(Ok(None))
}
/// Get the api version from the user given attribute or `Ok(1)`, if no attribute was given.
fn get_api_version(found_attributes: &HashMap<&'static str, Attribute>) -> Result<u64> {
match found_attributes.get(&API_VERSION_ATTRIBUTE) {
Some(attr) => parse_runtime_api_version(attr),
None => Ok(1),
}
found_attributes.get(&API_VERSION_ATTRIBUTE).map(parse_runtime_api_version).unwrap_or(Ok(1))
}
/// Generate the decleration of the trait for the client side.
@@ -430,12 +603,14 @@ 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 found_attributes = HashMap::new();
let mut errors = Vec::new();
let decl = {
let mut to_client_side = ToClientSideDecl {
crate_: &crate_,
block_id: &block_id,
found_attributes: &mut found_attributes
found_attributes: &mut found_attributes,
errors: &mut errors,
};
to_client_side.fold_item_trait(decl)
};
@@ -446,7 +621,7 @@ fn generate_client_side_decls(decls: &[ItemTrait]) -> TokenStream {
api_version.map(|v| generate_runtime_info_impl(&decl, v))
);
result.push(quote!( #decl #runtime_info ));
result.push(quote!( #decl #runtime_info #( #errors )* ));
}
quote!( #( #result )* )
@@ -16,8 +16,8 @@
use utils::{
unwrap_or_error, generate_crate_access, generate_hidden_includes,
generate_runtime_mod_name_for_trait, fold_fn_decl_for_client_side, generate_unique_pattern,
extract_parameter_names_types_and_borrows, generate_native_call_generator_fn_name
generate_runtime_mod_name_for_trait, generate_method_runtime_api_impl_name,
extract_parameter_names_types_and_borrows, generate_native_call_generator_fn_name, return_type_extract_type
};
use proc_macro;
@@ -28,7 +28,7 @@ use quote::quote;
use syn::{
spanned::Spanned, parse_macro_input, Ident, Type, ItemImpl, MethodSig, Path,
ImplItem, parse::{Parse, ParseStream, Result, Error}, PathArguments, GenericArgument, TypePath,
fold::{self, Fold}, FnDecl, parse_quote, FnArg
fold::{self, Fold}, parse_quote
};
use std::{collections::HashSet, iter};
@@ -300,11 +300,11 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
res
}
fn has_api<A: #crate_::runtime_api::RuntimeApiInfo + ?Sized>(
fn runtime_version_at(
&self,
at: &#block_id
) -> #crate_::error::Result<bool> where Self: Sized {
self.call.runtime_version_at(at).map(|r| r.has_api::<A>())
) -> #crate_::error::Result<#crate_::runtime_api::RuntimeVersion> {
self.call.runtime_version_at(at)
}
}
@@ -336,8 +336,8 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
at: &#block_id,
function: &'static str,
args: Vec<u8>,
native_call: NC,
) -> #crate_::error::Result<R> {
native_call: Option<NC>,
) -> #crate_::error::Result<#crate_::runtime_api::NativeOrEncoded<R>> {
let res = unsafe {
self.call.call_api_at(
at,
@@ -345,21 +345,7 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result<TokenStrea
args,
&mut *self.changes.borrow_mut(),
&mut *self.initialised_block.borrow_mut(),
Some(native_call),
).and_then(|r|
match r {
#crate_::runtime_api::NativeOrEncoded::Native(n) => {
Ok(n)
},
#crate_::runtime_api::NativeOrEncoded::Encoded(r) => {
R::decode(&mut &r[..])
.ok_or_else(||
#crate_::error::ErrorKind::CallResultDecode(
function
).into()
)
}
}
native_call,
)
};
@@ -446,50 +432,69 @@ impl<'a> Fold for ApiRuntimeImplToApiRuntimeApiImpl<'a> {
fold::fold_type_path(self, new_ty_path)
}
fn fold_fn_decl(&mut self, input: FnDecl) -> FnDecl {
let input = fold_fn_decl_for_client_side(
input,
&self.node_block_id,
&generate_crate_access(HIDDEN_INCLUDES_ID)
);
fold::fold_fn_decl(self, input)
}
fn fold_impl_item_method(&mut self, mut input: syn::ImplItemMethod) -> syn::ImplItemMethod {
let block = {
let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
let mut generated_name_counter = 0;
// Replace `_` with unique patterns and collect all patterns.
let arg_names = input.sig.decl.inputs.iter_mut().filter_map(|i| match i {
FnArg::Captured(ref mut arg) => Some(&mut arg.pat),
_ => None,
}).map(|p| {
*p = generate_unique_pattern(p.clone(), &mut generated_name_counter);
p.clone()
}).collect::<Vec<_>>();
let runtime_mod_path = self.runtime_mod_path;
let runtime = self.runtime_type;
let arg_names2 = arg_names.clone();
let fn_name = prefix_function_with_trait(self.impl_trait_ident, &input.sig.ident);
let native_call_generator_ident =
generate_native_call_generator_fn_name(&input.sig.ident);
let trait_generic_arguments = self.trait_generic_arguments;
let node_block = self.node_block;
let crate_ = generate_crate_access(HIDDEN_INCLUDES_ID);
let block_id = self.node_block_id;
// Generate the access to the native parameters
let param_tuple_access = if input.sig.decl.inputs.len() == 1 {
vec![ quote!( p ) ]
} else {
input.sig.decl.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.decl) {
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.decl.inputs = parse_quote! {
&self, at: &#block_id, params: Option<( #( #param_types ),* )>, params_encoded: Vec<u8>
};
input.sig.ident = generate_method_runtime_api_impl_name(&input.sig.ident);
let ret_type = return_type_extract_type(&input.sig.decl.output);
// Generate the correct return type.
input.sig.decl.output = parse_quote!(
-> #crate_::error::Result<#crate_::runtime_api::NativeOrEncoded<#ret_type>>
);
// Generate the new method implementation that calls into the runime.
parse_quote!(
{
let args = #crate_::runtime_api::Encode::encode(&( #( &#arg_names ),* ));
// Get the error to the user (if we have one).
#( #error )*
self.call_api_at(
at,
#fn_name,
args,
#runtime_mod_path #native_call_generator_ident ::
<#runtime, #node_block #(, #trait_generic_arguments )*> (
#( #arg_names2 ),*
)
params_encoded,
params.map(|p| {
#runtime_mod_path #native_call_generator_ident ::
<#runtime, #node_block #(, #trait_generic_arguments )*> (
#( #param_tuple_access ),*
)
})
)
}
)
+11 -2
View File
@@ -159,7 +159,11 @@ pub fn impl_runtime_apis(input: TokenStream) -> TokenStream {
///
/// 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.
/// version is provided. We also support chaning the signature of a method. This signature
/// change is highlighted with the `#[changed_in(2)]` attribute above a method. A method that is
/// tagged with this attribute is callable by the name `METHOD_before_version_VERSION`. This
/// method will only support calling into wasm, trying to call into native will fail (change the
/// spec version!). Such a method also does not need to be implemented in the runtime.
///
/// ```rust
/// #[macro_use]
@@ -171,8 +175,13 @@ pub fn impl_runtime_apis(input: TokenStream) -> TokenStream {
/// pub trait Balance {
/// /// Get the balance.
/// fn get_balance() -> u64;
/// /// Set the balance.
/// /// Set balance.
/// fn set_balance(val: u64);
/// /// Set balance, old version.
/// ///
/// /// Is callable by `set_balance_before_version_2`.
/// #[changed_in(2)]
/// fn set_balance(val: u8);
/// /// In version 2, we added this new function.
/// fn increase_balance(val: u64);
/// }
@@ -58,6 +58,11 @@ pub fn generate_runtime_mod_name_for_trait(trait_: &Ident) -> Ident {
Ident::new(&format!("runtime_decl_for_{}", trait_.to_string()), Span::call_site())
}
/// Generates a name for a method that needs to be implemented in the runtime for the client side.
pub fn generate_method_runtime_api_impl_name(method: &Ident) -> Ident {
Ident::new(&format!("{}_runtime_api_impl", method.to_string()), Span::call_site())
}
/// Get the type of a `syn::ReturnType`.
pub fn return_type_extract_type(rt: &syn::ReturnType) -> Type {
match rt {
@@ -28,6 +28,8 @@ decl_runtime_apis! {
#[api_version(2)]
pub trait ApiWithCustomVersion {
fn same_name();
#[changed_in(2)]
fn same_name() -> String;
}
}
@@ -77,6 +79,11 @@ fn test_client_side_function_signature() {
let _something_with_block:
fn(&RuntimeApiImpl<TestClient>, &BlockId<Block>, Block) -> Result<Block> =
RuntimeApiImpl::<TestClient>::something_with_block;
#[allow(deprecated)]
let _same_name_before_version_2:
fn(&RuntimeApiImpl<TestClient>, &BlockId<Block>) -> Result<String> =
RuntimeApiImpl::<TestClient>::same_name_before_version_2;
}
#[test]
@@ -58,3 +58,23 @@ fn calling_native_runtime_function_with_non_decodable_return_value() {
let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
runtime_api.fail_convert_return_value(&block_id).unwrap();
}
#[test]
fn calling_native_runtime_signature_changed_function() {
let client = test_client::new_with_api_execution_strat(ExecutionStrategy::NativeWhenPossible);
let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
assert_eq!(runtime_api.function_signature_changed(&block_id).unwrap(), 1);
}
#[test]
fn calling_wasm_runtime_signature_changed_old_function() {
let client = test_client::new_with_api_execution_strat(ExecutionStrategy::AlwaysWasm);
let runtime_api = client.runtime_api();
let block_id = BlockId::Number(client.info().unwrap().chain.best_number);
#[allow(deprecated)]
let res = runtime_api.function_signature_changed_before_version_2(&block_id).unwrap();
assert_eq!(&res, &[1, 2]);
}
+10
View File
@@ -136,6 +136,16 @@ impl RuntimeVersion {
s == &A::ID && *v == A::VERSION
})
}
/// Check if the given api is implemented and the version passes a predicate.
pub fn has_api_with<A: RuntimeApiInfo + ?Sized, P: Fn(u32) -> bool>(
&self,
pred: P,
) -> bool {
self.apis.iter().any(|(s, v)| {
s == &A::ID && pred(*v)
})
}
}
#[cfg(feature = "std")]
+1 -10
View File
@@ -21,6 +21,7 @@ runtime_io = { package = "sr-io", path = "../sr-io", default-features = false }
runtime_primitives = { package = "sr-primitives", path = "../sr-primitives", default-features = false }
runtime_version = { package = "sr-version", path = "../sr-version", default-features = false }
srml-support = { path = "../../srml/support", default-features = false }
cfg-if = "0.1.6"
[dev-dependencies]
substrate-executor = { path = "../executor" }
@@ -44,13 +45,3 @@ std = [
"runtime_version/std",
"consensus_aura/std",
]
[patch.crates-io]
hash-db = { git = "https://github.com/paritytech/trie" }
hash256-std-hasher = { git = "https://github.com/paritytech/trie" }
keccak-hasher = { git = "https://github.com/paritytech/trie" }
memory-db = { git = "https://github.com/paritytech/trie" }
trie-bench = { git = "https://github.com/paritytech/trie" }
trie-db = { git = "https://github.com/paritytech/trie" }
trie-root = { git = "https://github.com/paritytech/trie" }
trie-standardmap = { git = "https://github.com/paritytech/trie" }
+211 -87
View File
@@ -46,6 +46,7 @@ use primitives::{Ed25519AuthorityId, OpaqueMetadata};
#[cfg(any(feature = "std", test))]
use runtime_version::NativeVersion;
use inherents::{CheckInherentsResult, InherentData};
use cfg_if::cfg_if;
/// Test runtime version.
pub const VERSION: RuntimeVersion = RuntimeVersion {
@@ -193,19 +194,47 @@ impl<B: BlockT> Decode for DecodeFails<B> {
}
}
decl_runtime_apis! {
pub trait TestAPI {
/// Return the balance of the given account id.
fn balance_of(id: AccountId) -> u64;
/// A benchmkark function that adds one to the given value and returns the result.
fn benchmark_add_one(val: &u64) -> u64;
/// A benchmark function that adds one to each value in the given vector and returns the
/// result.
fn benchmark_vector_add_one(vec: &Vec<u64>) -> Vec<u64>;
/// A function that always fails to convert a parameter between runtime and node.
fn fail_convert_parameter(param: DecodeFails<Block>);
/// A function that always fails to convert its return value between runtime and node.
fn fail_convert_return_value() -> DecodeFails<Block>;
cfg_if! {
if #[cfg(feature = "std")] {
decl_runtime_apis! {
#[api_version(2)]
pub trait TestAPI {
/// Return the balance of the given account id.
fn balance_of(id: AccountId) -> u64;
/// A benchmkark function that adds one to the given value and returns the result.
fn benchmark_add_one(val: &u64) -> u64;
/// A benchmark function that adds one to each value in the given vector and returns the
/// result.
fn benchmark_vector_add_one(vec: &Vec<u64>) -> Vec<u64>;
/// A function that always fails to convert a parameter between runtime and node.
fn fail_convert_parameter(param: DecodeFails<Block>);
/// A function that always fails to convert its return value between runtime and node.
fn fail_convert_return_value() -> DecodeFails<Block>;
/// A function for that the signature changed in version `2`.
#[changed_in(2)]
fn function_signature_changed() -> Vec<u64>;
/// The new signature.
fn function_signature_changed() -> u64;
}
}
} else {
decl_runtime_apis! {
pub trait TestAPI {
/// Return the balance of the given account id.
fn balance_of(id: AccountId) -> u64;
/// A benchmkark function that adds one to the given value and returns the result.
fn benchmark_add_one(val: &u64) -> u64;
/// A benchmark function that adds one to each value in the given vector and returns the
/// result.
fn benchmark_vector_add_one(vec: &Vec<u64>) -> Vec<u64>;
/// A function that always fails to convert a parameter between runtime and node.
fn fail_convert_parameter(param: DecodeFails<Block>);
/// A function that always fails to convert its return value between runtime and node.
fn fail_convert_return_value() -> DecodeFails<Block>;
/// In wasm we just emulate the old behavior.
fn function_signature_changed() -> Vec<u64>;
}
}
}
}
@@ -219,82 +248,177 @@ impl GetRuntimeBlockType for Runtime {
type RuntimeBlock = Block;
}
impl_runtime_apis! {
impl client_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion {
version()
cfg_if! {
if #[cfg(feature = "std")] {
impl_runtime_apis! {
impl client_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion {
version()
}
fn authorities() -> Vec<Ed25519AuthorityId> {
system::authorities()
}
fn execute_block(block: Block) {
system::execute_block(block)
}
fn initialise_block(header: &<Block as BlockT>::Header) {
system::initialise_block(header)
}
}
impl client_api::Metadata<Block> for Runtime {
fn metadata() -> OpaqueMetadata {
unimplemented!()
}
}
impl client_api::TaggedTransactionQueue<Block> for Runtime {
fn validate_transaction(utx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
system::validate_transaction(utx)
}
}
impl block_builder_api::BlockBuilder<Block> for Runtime {
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyResult {
system::execute_transaction(extrinsic)
}
fn finalise_block() -> <Block as BlockT>::Header {
system::finalise_block()
}
fn inherent_extrinsics(_data: InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
vec![]
}
fn check_inherents(_block: Block, _data: InherentData) -> CheckInherentsResult {
CheckInherentsResult::new()
}
fn random_seed() -> <Block as BlockT>::Hash {
unimplemented!()
}
}
impl self::TestAPI<Block> for Runtime {
fn balance_of(id: AccountId) -> u64 {
system::balance_of(id)
}
fn benchmark_add_one(val: &u64) -> u64 {
val + 1
}
fn benchmark_vector_add_one(vec: &Vec<u64>) -> Vec<u64> {
let mut vec = vec.clone();
vec.iter_mut().for_each(|v| *v += 1);
vec
}
fn fail_convert_parameter(_: DecodeFails<Block>) {}
fn fail_convert_return_value() -> DecodeFails<Block> {
DecodeFails::new()
}
fn function_signature_changed() -> u64 {
1
}
}
impl consensus_aura::AuraApi<Block> for Runtime {
fn slot_duration() -> u64 { 1 }
}
}
} else {
impl_runtime_apis! {
impl client_api::Core<Block> for Runtime {
fn version() -> RuntimeVersion {
version()
}
fn authorities() -> Vec<Ed25519AuthorityId> {
system::authorities()
fn authorities() -> Vec<Ed25519AuthorityId> {
system::authorities()
}
fn execute_block(block: Block) {
system::execute_block(block)
}
fn initialise_block(header: &<Block as BlockT>::Header) {
system::initialise_block(header)
}
}
impl client_api::Metadata<Block> for Runtime {
fn metadata() -> OpaqueMetadata {
unimplemented!()
}
}
impl client_api::TaggedTransactionQueue<Block> for Runtime {
fn validate_transaction(utx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
system::validate_transaction(utx)
}
}
impl block_builder_api::BlockBuilder<Block> for Runtime {
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyResult {
system::execute_transaction(extrinsic)
}
fn finalise_block() -> <Block as BlockT>::Header {
system::finalise_block()
}
fn inherent_extrinsics(_data: InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
vec![]
}
fn check_inherents(_block: Block, _data: InherentData) -> CheckInherentsResult {
CheckInherentsResult::new()
}
fn random_seed() -> <Block as BlockT>::Hash {
unimplemented!()
}
}
impl self::TestAPI<Block> for Runtime {
fn balance_of(id: AccountId) -> u64 {
system::balance_of(id)
}
fn benchmark_add_one(val: &u64) -> u64 {
val + 1
}
fn benchmark_vector_add_one(vec: &Vec<u64>) -> Vec<u64> {
let mut vec = vec.clone();
vec.iter_mut().for_each(|v| *v += 1);
vec
}
fn fail_convert_parameter(_: DecodeFails<Block>) {}
fn fail_convert_return_value() -> DecodeFails<Block> {
DecodeFails::new()
}
fn function_signature_changed() -> Vec<u64> {
let mut vec = Vec::new();
vec.push(1);
vec.push(2);
vec
}
}
impl consensus_aura::AuraApi<Block> for Runtime {
fn slot_duration() -> u64 { 1 }
}
}
fn execute_block(block: Block) {
system::execute_block(block)
}
fn initialise_block(header: &<Block as BlockT>::Header) {
system::initialise_block(header)
}
}
impl client_api::Metadata<Block> for Runtime {
fn metadata() -> OpaqueMetadata {
unimplemented!()
}
}
impl client_api::TaggedTransactionQueue<Block> for Runtime {
fn validate_transaction(utx: <Block as BlockT>::Extrinsic) -> TransactionValidity {
system::validate_transaction(utx)
}
}
impl block_builder_api::BlockBuilder<Block> for Runtime {
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyResult {
system::execute_transaction(extrinsic)
}
fn finalise_block() -> <Block as BlockT>::Header {
system::finalise_block()
}
fn inherent_extrinsics(_data: InherentData) -> Vec<<Block as BlockT>::Extrinsic> {
vec![]
}
fn check_inherents(_block: Block, _data: InherentData) -> CheckInherentsResult {
CheckInherentsResult::new()
}
fn random_seed() -> <Block as BlockT>::Hash {
unimplemented!()
}
}
impl self::TestAPI<Block> for Runtime {
fn balance_of(id: AccountId) -> u64 {
system::balance_of(id)
}
fn benchmark_add_one(val: &u64) -> u64 {
val + 1
}
fn benchmark_vector_add_one(vec: &Vec<u64>) -> Vec<u64> {
let mut vec = vec.clone();
vec.iter_mut().for_each(|v| *v += 1);
vec
}
fn fail_convert_parameter(_: DecodeFails<Block>) {}
fn fail_convert_return_value() -> DecodeFails<Block> {
DecodeFails::new()
}
}
impl consensus_aura::AuraApi<Block> for Runtime {
fn slot_duration() -> u64 { 1 }
}
}
+2
View File
@@ -1155,6 +1155,7 @@ dependencies = [
"kvdb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-api-macros 0.1.0",
@@ -1320,6 +1321,7 @@ dependencies = [
name = "substrate-test-runtime"
version = "0.1.0"
dependencies = [
"cfg-if 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
"hex-literal 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
+2 -2
View File
@@ -66,8 +66,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion {
spec_name: create_runtime_str!("node"),
impl_name: create_runtime_str!("substrate-node"),
authoring_version: 10,
spec_version: 20,
impl_version: 20,
spec_version: 21,
impl_version: 21,
apis: RUNTIME_API_VERSIONS,
};
+1
View File
@@ -1525,6 +1525,7 @@ dependencies = [
"kvdb 0.1.0 (git+https://github.com/paritytech/parity-common?rev=b0317f649ab2c665b7987b8475878fc4d2e1f81d)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 3.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
"parking_lot 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
"slog 2.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-api-macros 0.1.0",