From 190393d4768254a8698d399d191d05ff86a5f261 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bastian=20K=C3=B6cher?= Date: Wed, 6 Feb 2019 11:47:47 +0100 Subject: [PATCH] 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 --- substrate/Cargo.lock | 2 + .../basic-authorship/src/basic_authorship.rs | 8 +- substrate/core/client/Cargo.toml | 6 +- .../core/client/src/block_builder/api.rs | 44 ++- substrate/core/client/src/client.rs | 13 +- substrate/core/client/src/lib.rs | 2 + substrate/core/client/src/runtime_api.rs | 16 +- substrate/core/consensus/aura/src/lib.rs | 72 ++++- substrate/core/finality-grandpa/src/tests.rs | 72 +++-- substrate/core/rpc/src/state/tests.rs | 7 +- .../sr-api-macros/src/compile_fail_tests.rs | 32 ++ .../sr-api-macros/src/decl_runtime_apis.rs | 203 +++++++++++- .../sr-api-macros/src/impl_runtime_apis.rs | 107 ++++--- substrate/core/sr-api-macros/src/lib.rs | 13 +- substrate/core/sr-api-macros/src/utils.rs | 5 + .../core/sr-api-macros/tests/decl_and_impl.rs | 7 + .../core/sr-api-macros/tests/runtime_calls.rs | 20 ++ substrate/core/sr-version/src/lib.rs | 10 + substrate/core/test-runtime/Cargo.toml | 11 +- substrate/core/test-runtime/src/lib.rs | 298 +++++++++++++----- substrate/core/test-runtime/wasm/Cargo.lock | 2 + .../substrate_test_runtime.compact.wasm | Bin 59939 -> 60437 bytes substrate/node/runtime/src/lib.rs | 4 +- substrate/node/runtime/wasm/Cargo.lock | 1 + .../release/node_runtime.compact.wasm | Bin 826650 -> 826604 bytes 25 files changed, 731 insertions(+), 224 deletions(-) diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index a9eda33d0b..e845b1c8ac 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -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)", diff --git a/substrate/core/basic-authorship/src/basic_authorship.rs b/substrate/core/basic-authorship/src/basic_authorship.rs index 41625273cb..d504500fa2 100644 --- a/substrate/core/basic-authorship/src/basic_authorship.rs +++ b/substrate/core/basic-authorship/src/basic_authorship.rs @@ -96,10 +96,10 @@ impl AuthoringApi for SubstrateClient where let mut block_builder = self.new_block_at(at)?; let runtime_api = self.runtime_api(); - if runtime_api.has_api::>(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); diff --git a/substrate/core/client/Cargo.toml b/substrate/core/client/Cargo.toml index e21a3be127..79fd5bff9f 100644 --- a/substrate/core/client/Cargo.toml +++ b/substrate/core/client/Cargo.toml @@ -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", diff --git a/substrate/core/client/src/block_builder/api.rs b/substrate/core/client/src/block_builder/api.rs index 72cfea309f..f0f9a23122 100644 --- a/substrate/core/client/src/block_builder/api.rs +++ b/substrate/core/client/src/block_builder/api.rs @@ -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: ::Extrinsic) -> ApplyResult; @@ -32,6 +69,11 @@ decl_runtime_apis! { fn inherent_extrinsics(inherent: InherentData) -> Vec<::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() -> ::Hash; } diff --git a/substrate/core/client/src/client.rs b/substrate/core/client/src/client.rs index 2313c4733d..3d969285a9 100644 --- a/substrate/core/client/src/client.rs +++ b/substrate/core/client/src/client.rs @@ -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::>( - &BlockId::Number(client.info().unwrap().chain.best_number), - ).unwrap() - ); - } - #[test] fn authorities_call_works() { let client = test_client::new(); diff --git a/substrate/core/client/src/lib.rs b/substrate/core/client/src/lib.rs index 4a8f06dfde..671c453e96 100644 --- a/substrate/core/client/src/lib.rs +++ b/substrate/core/client/src/lib.rs @@ -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")] diff --git a/substrate/core/client/src/runtime_api.rs b/substrate/core/client/src/runtime_api.rs index 72371bc95f..95465d050b 100644 --- a/substrate/core/client/src/runtime_api.rs +++ b/substrate/core/client/src/runtime_api.rs @@ -69,7 +69,21 @@ pub trait ApiExt { fn has_api( &self, at: &BlockId - ) -> error::Result where Self: Sized; + ) -> error::Result where Self: Sized { + self.runtime_version_at(at).map(|v| v.has_api::()) + } + + /// Check if the given api is implemented and the version passes a predicate. + fn has_api_with bool>( + &self, + at: &BlockId, + pred: P, + ) -> error::Result where Self: Sized { + self.runtime_version_at(at).map(|v| v.has_api_with::(pred)) + } + + /// Returns the runtime version at the given block id. + fn runtime_version_at(&self, at: &BlockId) -> error::Result; } /// Something that can call into the runtime at a given block. diff --git a/substrate/core/consensus/aura/src/lib.rs b/substrate/core/consensus/aura/src/lib.rs index 81c257671d..a5442e59b1 100644 --- a/substrate/core/consensus/aura/src/lib.rs +++ b/substrate/core/consensus/aura/src/lib.rs @@ -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 AuraVerifier Ok(()) } } + + #[allow(deprecated)] + fn old_check_inherents( + &self, + block: B, + block_id: BlockId, + inherent_data: InherentData, + timestamp_now: u64, + ) -> Result<(), String> + where C: ProvideRuntimeApi, C::Api: BlockBuilderApi + { + 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 Verifier for AuraVerifier 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::, _>(&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); diff --git a/substrate/core/finality-grandpa/src/tests.rs b/substrate/core/finality-grandpa/src/tests.rs index c472c0d372..036d7e1870 100644 --- a/substrate/core/finality-grandpa/src/tests.rs +++ b/substrate/core/finality-grandpa/src/tests.rs @@ -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 for RuntimeApi { - fn version(&self, _: &BlockId) -> Result { - unimplemented!("Not required for testing!") - } - - fn authorities(&self, _: &BlockId) -> Result> { - unimplemented!("Not required for testing!") - } - - fn execute_block(&self, _: &BlockId, _: Block) -> Result<()> { - unimplemented!("Not required for testing!") - } - - fn initialise_block( + fn version_runtime_api_impl( &self, _: &BlockId, - _: &::Header - ) -> Result<()> { + _: Option<()>, + _: Vec + ) -> Result> { + unimplemented!("Not required for testing!") + } + + fn authorities_runtime_api_impl( + &self, + _: &BlockId, + _: Option<()>, + _: Vec + ) -> Result>> { + unimplemented!("Not required for testing!") + } + + fn execute_block_runtime_api_impl( + &self, + _: &BlockId, + _: Option<(Block)>, + _: Vec + ) -> Result> { + unimplemented!("Not required for testing!") + } + + fn initialise_block_runtime_api_impl( + &self, + _: &BlockId, + _: Option<&::Header>, + _: Vec, + ) -> Result> { unimplemented!("Not required for testing!") } } @@ -302,26 +319,31 @@ impl ApiExt for RuntimeApi { unimplemented!("Not required for testing!") } - fn has_api(&self, _: &BlockId) -> Result { + fn runtime_version_at(&self, _: &BlockId) -> Result { unimplemented!("Not required for testing!") } } impl GrandpaApi for RuntimeApi { - fn grandpa_authorities( + fn grandpa_authorities_runtime_api_impl( &self, - at: &BlockId - ) -> Result> { + at: &BlockId, + _: Option<()>, + _: Vec, + ) -> Result>> { 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, _: &DigestFor) - -> Result>>> - { + fn grandpa_pending_change_runtime_api_impl( + &self, + at: &BlockId, + _: Option<(&DigestFor)>, + _: Vec + ) -> Result>>>> { let parent_hash = match at { &BlockId::Hash(at) => at, _ => panic!("not requested by block hash!!"), @@ -329,7 +351,7 @@ impl GrandpaApi 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) } } diff --git a/substrate/core/rpc/src/state/tests.rs b/substrate/core/rpc/src/state/tests.rs index 87b3dfcc9f..8c5f18f24d 100644 --- a/substrate/core/rpc/src/state/tests.rs +++ b/substrate/core/rpc/src/state/tests.rs @@ -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]]}"# ); } diff --git a/substrate/core/sr-api-macros/src/compile_fail_tests.rs b/substrate/core/sr-api-macros/src/compile_fail_tests.rs index 7d7be065fa..195d46430f 100644 --- a/substrate/core/sr-api-macros/src/compile_fail_tests.rs +++ b/substrate/core/sr-api-macros/src/compile_fail_tests.rs @@ -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() {} + ``` + */ +} diff --git a/substrate/core/sr-api-macros/src/decl_runtime_apis.rs b/substrate/core/sr-api-macros/src/decl_runtime_apis.rs index 13498003c2..48e50b329c 100644 --- a/substrate/core/sr-api-macros/src/decl_runtime_apis.rs +++ b/substrate/core/sr-api-macros/src/decl_runtime_apis.rs @@ -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, } -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) -> Vec { + 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) { + 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 { + 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::>(), + 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 + ) -> #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::>(), + 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> { + 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 { - 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 ); 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 )* ) diff --git a/substrate/core/sr-api-macros/src/impl_runtime_apis.rs b/substrate/core/sr-api-macros/src/impl_runtime_apis.rs index 2909b07413..190ead3b9d 100644 --- a/substrate/core/sr-api-macros/src/impl_runtime_apis.rs +++ b/substrate/core/sr-api-macros/src/impl_runtime_apis.rs @@ -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( + fn runtime_version_at( &self, at: &#block_id - ) -> #crate_::error::Result where Self: Sized { - self.call.runtime_version_at(at).map(|r| r.has_api::()) + ) -> #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, - native_call: NC, - ) -> #crate_::error::Result { + native_call: Option, + ) -> #crate_::error::Result<#crate_::runtime_api::NativeOrEncoded> { let res = unsafe { self.call.call_api_at( at, @@ -345,21 +345,7 @@ fn generate_runtime_api_base_structures(impls: &[ItemImpl]) -> Result { - 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::>(); - 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::>() + }; + + 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::>(), + 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 + }; + + 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 ),* + ) + }) ) } ) diff --git a/substrate/core/sr-api-macros/src/lib.rs b/substrate/core/sr-api-macros/src/lib.rs index 52e96a57cc..b616835284 100644 --- a/substrate/core/sr-api-macros/src/lib.rs +++ b/substrate/core/sr-api-macros/src/lib.rs @@ -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); /// } diff --git a/substrate/core/sr-api-macros/src/utils.rs b/substrate/core/sr-api-macros/src/utils.rs index 9df59b30a7..d619fcb389 100644 --- a/substrate/core/sr-api-macros/src/utils.rs +++ b/substrate/core/sr-api-macros/src/utils.rs @@ -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 { diff --git a/substrate/core/sr-api-macros/tests/decl_and_impl.rs b/substrate/core/sr-api-macros/tests/decl_and_impl.rs index 1d87a15201..28c9d9d51a 100644 --- a/substrate/core/sr-api-macros/tests/decl_and_impl.rs +++ b/substrate/core/sr-api-macros/tests/decl_and_impl.rs @@ -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, &BlockId, Block) -> Result = RuntimeApiImpl::::something_with_block; + + #[allow(deprecated)] + let _same_name_before_version_2: + fn(&RuntimeApiImpl, &BlockId) -> Result = + RuntimeApiImpl::::same_name_before_version_2; } #[test] diff --git a/substrate/core/sr-api-macros/tests/runtime_calls.rs b/substrate/core/sr-api-macros/tests/runtime_calls.rs index d1d05eb48a..a4b36a6a21 100644 --- a/substrate/core/sr-api-macros/tests/runtime_calls.rs +++ b/substrate/core/sr-api-macros/tests/runtime_calls.rs @@ -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]); +} diff --git a/substrate/core/sr-version/src/lib.rs b/substrate/core/sr-version/src/lib.rs index 4a4c7174ba..58369dd1c2 100644 --- a/substrate/core/sr-version/src/lib.rs +++ b/substrate/core/sr-version/src/lib.rs @@ -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 bool>( + &self, + pred: P, + ) -> bool { + self.apis.iter().any(|(s, v)| { + s == &A::ID && pred(*v) + }) + } } #[cfg(feature = "std")] diff --git a/substrate/core/test-runtime/Cargo.toml b/substrate/core/test-runtime/Cargo.toml index 80c6677bdc..5fdf54ba9b 100644 --- a/substrate/core/test-runtime/Cargo.toml +++ b/substrate/core/test-runtime/Cargo.toml @@ -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" } diff --git a/substrate/core/test-runtime/src/lib.rs b/substrate/core/test-runtime/src/lib.rs index d804468a64..024532fbfb 100644 --- a/substrate/core/test-runtime/src/lib.rs +++ b/substrate/core/test-runtime/src/lib.rs @@ -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 Decode for DecodeFails { } } -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) -> Vec; - /// A function that always fails to convert a parameter between runtime and node. - fn fail_convert_parameter(param: DecodeFails); - /// A function that always fails to convert its return value between runtime and node. - fn fail_convert_return_value() -> DecodeFails; +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) -> Vec; + /// A function that always fails to convert a parameter between runtime and node. + fn fail_convert_parameter(param: DecodeFails); + /// A function that always fails to convert its return value between runtime and node. + fn fail_convert_return_value() -> DecodeFails; + /// A function for that the signature changed in version `2`. + #[changed_in(2)] + fn function_signature_changed() -> Vec; + /// 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) -> Vec; + /// A function that always fails to convert a parameter between runtime and node. + fn fail_convert_parameter(param: DecodeFails); + /// A function that always fails to convert its return value between runtime and node. + fn fail_convert_return_value() -> DecodeFails; + /// In wasm we just emulate the old behavior. + fn function_signature_changed() -> Vec; + } + } } } @@ -219,82 +248,177 @@ impl GetRuntimeBlockType for Runtime { type RuntimeBlock = Block; } -impl_runtime_apis! { - impl client_api::Core for Runtime { - fn version() -> RuntimeVersion { - version() +cfg_if! { + if #[cfg(feature = "std")] { + impl_runtime_apis! { + impl client_api::Core for Runtime { + fn version() -> RuntimeVersion { + version() + } + + fn authorities() -> Vec { + system::authorities() + } + + fn execute_block(block: Block) { + system::execute_block(block) + } + + fn initialise_block(header: &::Header) { + system::initialise_block(header) + } + } + + impl client_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + unimplemented!() + } + } + + impl client_api::TaggedTransactionQueue for Runtime { + fn validate_transaction(utx: ::Extrinsic) -> TransactionValidity { + system::validate_transaction(utx) + } + } + + impl block_builder_api::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyResult { + system::execute_transaction(extrinsic) + } + + fn finalise_block() -> ::Header { + system::finalise_block() + } + + fn inherent_extrinsics(_data: InherentData) -> Vec<::Extrinsic> { + vec![] + } + + fn check_inherents(_block: Block, _data: InherentData) -> CheckInherentsResult { + CheckInherentsResult::new() + } + + fn random_seed() -> ::Hash { + unimplemented!() + } + } + + impl self::TestAPI 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) -> Vec { + let mut vec = vec.clone(); + vec.iter_mut().for_each(|v| *v += 1); + vec + } + + fn fail_convert_parameter(_: DecodeFails) {} + + fn fail_convert_return_value() -> DecodeFails { + DecodeFails::new() + } + + fn function_signature_changed() -> u64 { + 1 + } + } + + impl consensus_aura::AuraApi for Runtime { + fn slot_duration() -> u64 { 1 } + } } + } else { + impl_runtime_apis! { + impl client_api::Core for Runtime { + fn version() -> RuntimeVersion { + version() + } - fn authorities() -> Vec { - system::authorities() + fn authorities() -> Vec { + system::authorities() + } + + fn execute_block(block: Block) { + system::execute_block(block) + } + + fn initialise_block(header: &::Header) { + system::initialise_block(header) + } + } + + impl client_api::Metadata for Runtime { + fn metadata() -> OpaqueMetadata { + unimplemented!() + } + } + + impl client_api::TaggedTransactionQueue for Runtime { + fn validate_transaction(utx: ::Extrinsic) -> TransactionValidity { + system::validate_transaction(utx) + } + } + + impl block_builder_api::BlockBuilder for Runtime { + fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyResult { + system::execute_transaction(extrinsic) + } + + fn finalise_block() -> ::Header { + system::finalise_block() + } + + fn inherent_extrinsics(_data: InherentData) -> Vec<::Extrinsic> { + vec![] + } + + fn check_inherents(_block: Block, _data: InherentData) -> CheckInherentsResult { + CheckInherentsResult::new() + } + + fn random_seed() -> ::Hash { + unimplemented!() + } + } + + impl self::TestAPI 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) -> Vec { + let mut vec = vec.clone(); + vec.iter_mut().for_each(|v| *v += 1); + vec + } + + fn fail_convert_parameter(_: DecodeFails) {} + + fn fail_convert_return_value() -> DecodeFails { + DecodeFails::new() + } + + fn function_signature_changed() -> Vec { + let mut vec = Vec::new(); + vec.push(1); + vec.push(2); + vec + } + } + + impl consensus_aura::AuraApi for Runtime { + fn slot_duration() -> u64 { 1 } + } } - - fn execute_block(block: Block) { - system::execute_block(block) - } - - fn initialise_block(header: &::Header) { - system::initialise_block(header) - } - } - - impl client_api::Metadata for Runtime { - fn metadata() -> OpaqueMetadata { - unimplemented!() - } - } - - impl client_api::TaggedTransactionQueue for Runtime { - fn validate_transaction(utx: ::Extrinsic) -> TransactionValidity { - system::validate_transaction(utx) - } - } - - impl block_builder_api::BlockBuilder for Runtime { - fn apply_extrinsic(extrinsic: ::Extrinsic) -> ApplyResult { - system::execute_transaction(extrinsic) - } - - fn finalise_block() -> ::Header { - system::finalise_block() - } - - fn inherent_extrinsics(_data: InherentData) -> Vec<::Extrinsic> { - vec![] - } - - fn check_inherents(_block: Block, _data: InherentData) -> CheckInherentsResult { - CheckInherentsResult::new() - } - - fn random_seed() -> ::Hash { - unimplemented!() - } - } - - impl self::TestAPI 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) -> Vec { - let mut vec = vec.clone(); - vec.iter_mut().for_each(|v| *v += 1); - vec - } - - fn fail_convert_parameter(_: DecodeFails) {} - - fn fail_convert_return_value() -> DecodeFails { - DecodeFails::new() - } - } - - impl consensus_aura::AuraApi for Runtime { - fn slot_duration() -> u64 { 1 } } } diff --git a/substrate/core/test-runtime/wasm/Cargo.lock b/substrate/core/test-runtime/wasm/Cargo.lock index 576c8674b3..a12e3185e5 100644 --- a/substrate/core/test-runtime/wasm/Cargo.lock +++ b/substrate/core/test-runtime/wasm/Cargo.lock @@ -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)", diff --git a/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm b/substrate/core/test-runtime/wasm/target/wasm32-unknown-unknown/release/substrate_test_runtime.compact.wasm index 9e05b9f7dd3927966aa437569febbfca56386ee1..96e58fb7eb3715886c7c9402f8c993a7b0a19265 100644 GIT binary patch delta 3919 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 diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 3e8e1dc395..218f324927 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -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, }; diff --git a/substrate/node/runtime/wasm/Cargo.lock b/substrate/node/runtime/wasm/Cargo.lock index c28857422b..08244710f9 100644 --- a/substrate/node/runtime/wasm/Cargo.lock +++ b/substrate/node/runtime/wasm/Cargo.lock @@ -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", diff --git a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 60132cd0ea16322c48c98152a92294404a34ee3b..4c583a3897a4d308c9aea0ee6012e8cd6bea9805 100644 GIT binary patch delta 20887 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