Refactor the runtime API to use traits. (#878)

* Add missing `As` imports.

* Adds new API traits that will be used by the client and runtime

* Switch consensus to new API's

* Switches transaction-pool to new API's

* Move runtime api stuff into its own crate

* Adds `impl_apis!` macro for implementing the new API traits

* Make `metadata` return directly a blob

* Runtime replace `impl_stubs!` with `impl_apis!`

* Switches to none feature based approach for declaring the different API traits

* Fixes compilation error

* Fixes errors

* Make the `decl_apis!` trait usable from the outside

* Make the `test-client` use the new API traits

* Remove last `impl_stubs!` bits and move some of them into wasm executor for tests

* A little bit more documentation
This commit is contained in:
Bastian Köcher
2018-10-09 10:58:29 +02:00
committed by Gav Wood
parent fb058ae235
commit 2c65ad6c7b
25 changed files with 1058 additions and 256 deletions
+1
View File
@@ -17,6 +17,7 @@ parity-codec = "2.0"
substrate-executor = { path = "../executor" }
substrate-primitives = { path = "../primitives" }
sr-primitives = { path = "../sr-primitives" }
sr-api = { path = "../sr-api" }
substrate-state-machine = { path = "../state-machine" }
substrate-keyring = { path = "../keyring" }
substrate-trie = { path = "../trie" }
+123 -1
View File
@@ -17,12 +17,14 @@
//! Substrate Client
use std::sync::Arc;
use error::Error;
use futures::sync::mpsc;
use parking_lot::{Mutex, RwLock};
use primitives::AuthorityId;
use runtime_primitives::{bft::Justification, generic::{BlockId, SignedBlock, Block as RuntimeBlock}};
use runtime_primitives::traits::{Block as BlockT, Header as HeaderT, Zero, As, NumberFor, CurrentHeight, BlockNumberToHash};
use runtime_primitives::BuildStorage;
use runtime_primitives::{ApplyResult, BuildStorage};
use runtime_api as api;
use primitives::{Blake2Hasher, H256, ChangesTrieConfiguration};
use primitives::storage::{StorageKey, StorageData};
use primitives::storage::well_known_keys;
@@ -1043,6 +1045,126 @@ impl<B, E, Block> BlockBody<Block> for Client<B, E, Block>
}
}
impl<B, E, Block> api::Core<Block, AuthorityId> for Client<B, E, Block> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT,
{
type Error = Error;
fn version(&self, at: &BlockId<Block>) -> Result<RuntimeVersion, Self::Error> {
self.call_api_at(at, "version", &())
}
fn authorities(&self, at: &BlockId<Block>) -> Result<Vec<AuthorityId>, Self::Error> {
bft::Authorities::authorities(self, at).map_err(Into::into)
}
fn execute_block(&self, at: &BlockId<Block>, block: Block) -> Result<(), Self::Error> {
self.call_api_at(at, "execute_block", &(block))
}
}
impl<B, E, Block> api::Metadata<Block> for Client<B, E, Block> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT,
{
type Error = Error;
fn metadata(&self, at: &BlockId<Block>) -> Result<Vec<u8>, Self::Error> {
self.call_api_at(at, "metadata", &())
}
}
impl<B, E, Block> api::BlockBuilder<Block> for Client<B, E, Block> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT,
{
type Error = Error;
fn initialise_block(&self, at: &BlockId<Block>, header: <Block as BlockT>::Header) -> Result<(), Self::Error> {
self.call_api_at(at, "initialise_block", &(header))
}
fn apply_extrinsic(&self, at: &BlockId<Block>, extrinsic: <Block as BlockT>::Extrinsic) -> Result<ApplyResult, Self::Error> {
self.call_api_at(at, "apply_extrinsic", &(extrinsic))
}
fn finalise_block(&self, at: &BlockId<Block>) -> Result<<Block as BlockT>::Header, Self::Error> {
self.call_api_at(at, "finalise_block", &())
}
fn inherent_extrinsics<InherentExtrinsic: Encode + Decode, UncheckedExtrinsic: Encode + Decode>(
&self, at: &BlockId<Block>, inherent: InherentExtrinsic
) -> Result<Vec<UncheckedExtrinsic>, Self::Error> {
self.call_api_at(at, "inherent_extrinsics", &(inherent))
}
fn random_seed(&self, at: &BlockId<Block>) -> Result<<Block as BlockT>::Hash, Self::Error> {
self.call_api_at(at, "random_seed", &())
}
}
impl<B, E, Block> api::OldTxQueue<Block> for Client<B, E, Block> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT,
{
type Error = Error;
fn account_nonce<AccountId: Encode + Decode, Index: Encode + Decode>(
&self, at: &BlockId<Block>, account: AccountId
) -> Result<Index, Self::Error> {
self.call_api_at(at, "account_nonce", &(account))
}
fn lookup_address<Address: Encode + Decode, AccountId: Encode + Decode>(
&self, at: &BlockId<Block>, address: Address
) -> Result<Option<AccountId>, Self::Error> {
self.call_api_at(at, "lookup_address", &(address))
}
}
impl<B, E, Block> api::NewTxQueue<Block> for Client<B, E, Block> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT,
{
type Error = Error;
fn validate_transaction<TransactionValidity: Encode + Decode>(
&self, at: &BlockId<Block>, tx: <Block as BlockT>::Extrinsic
) -> Result<TransactionValidity, Self::Error> {
self.call_api_at(at, "validate_transaction", &(tx))
}
}
impl<B, E, Block> api::Miscellaneous<Block> for Client<B, E, Block> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher>,
Block: BlockT,
{
type Error = Error;
fn validator_count(&self, at: &BlockId<Block>) -> Result<u32, Self::Error> {
self.call_api_at(at, "validator_count", &())
}
fn validators<AccountId: Encode + Decode>(
&self, at: &BlockId<Block>
) -> Result<Vec<AccountId>, Self::Error> {
self.call_api_at(at, "validators", &())
}
fn timestamp<Moment: Encode + Decode>(
&self, at: &BlockId<Block>
) -> Result<Moment, Self::Error> {
self.call_api_at(at, "timestamp", &())
}
}
#[cfg(test)]
pub(crate) mod tests {
use std::collections::HashMap;
+5
View File
@@ -19,8 +19,13 @@
use std;
use state_machine;
use runtime_primitives::ApplyError;
use bft;
error_chain! {
links {
BFT(bft::error::Error, bft::error::ErrorKind) #[doc="BFT error"];
}
errors {
/// Backend error.
Backend(s: String) {
+6
View File
@@ -36,6 +36,7 @@ extern crate parking_lot;
extern crate hash_db;
extern crate heapsize;
extern crate kvdb;
extern crate sr_api;
#[macro_use] extern crate error_chain;
#[macro_use] extern crate log;
@@ -67,3 +68,8 @@ pub use client::{
pub use notifications::{StorageEventStream, StorageChangeSet};
pub use state_machine::ExecutionStrategy;
pub use leaves::LeafSet;
/// Traits for interfacing with the runtime from the client.
pub mod runtime_api {
pub use sr_api::*;
}
+44 -15
View File
@@ -4,8 +4,8 @@
#![feature(alloc)]
extern crate alloc;
use alloc::vec::Vec;
use alloc::slice;
#[macro_use]
extern crate sr_io as runtime_io;
extern crate sr_sandbox as sandbox;
extern crate substrate_primitives;
@@ -15,8 +15,37 @@ use runtime_io::{
twox_128, twox_256, ed25519_verify, enumerated_trie_root
};
macro_rules! impl_stubs {
( $( $new_name:ident $($nodecode:ident)* => $invoke:expr ),* ) => {
$(
impl_stubs!(@METHOD $new_name $($nodecode)* => $invoke);
)*
};
( @METHOD $new_name:ident => $invoke:expr ) => {
#[no_mangle]
pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 {
let input: &[u8] = if input_len == 0 {
&[0u8; 0]
} else {
unsafe {
slice::from_raw_parts(input_data, input_len)
}
};
let output: Vec<u8> = $invoke(input);
let res = output.as_ptr() as u64 + ((output.len() as u64) << 32);
// Leak the output vector to avoid it being freed.
// This is fine in a WASM context since the heap
// will be discarded after the call.
::core::mem::forget(output);
res
}
};
}
impl_stubs!(
test_data_in NO_DECODE => |input| {
test_data_in => |input| {
print("set_storage");
set_storage(b"input", input);
@@ -29,22 +58,22 @@ impl_stubs!(
print("finished!");
b"all ok!".to_vec()
},
test_clear_prefix NO_DECODE => |input| {
test_clear_prefix => |input| {
clear_prefix(input);
b"all ok!".to_vec()
},
test_empty_return NO_DECODE => |_| Vec::new(),
test_panic NO_DECODE => |_| panic!("test panic"),
test_conditional_panic NO_DECODE => |input: &[u8]| {
test_empty_return => |_| Vec::new(),
test_panic => |_| panic!("test panic"),
test_conditional_panic => |input: &[u8]| {
if input.len() > 0 {
panic!("test panic")
}
input.to_vec()
},
test_blake2_256 NO_DECODE => |input| blake2_256(input).to_vec(),
test_twox_256 NO_DECODE => |input| twox_256(input).to_vec(),
test_twox_128 NO_DECODE => |input| twox_128(input).to_vec(),
test_ed25519_verify NO_DECODE => |input: &[u8]| {
test_blake2_256 => |input| blake2_256(input).to_vec(),
test_twox_256 => |input| twox_256(input).to_vec(),
test_twox_128 => |input| twox_128(input).to_vec(),
test_ed25519_verify => |input: &[u8]| {
let mut pubkey = [0; 32];
let mut sig = [0; 64];
@@ -54,14 +83,14 @@ impl_stubs!(
let msg = b"all ok!";
[ed25519_verify(&sig, &msg[..], &pubkey) as u8].to_vec()
},
test_enumerated_trie_root NO_DECODE => |_| {
test_enumerated_trie_root => |_| {
enumerated_trie_root::<substrate_primitives::Blake2Hasher>(&[&b"zero"[..], &b"one"[..], &b"two"[..]]).to_vec()
},
test_sandbox NO_DECODE => |code: &[u8]| {
test_sandbox => |code: &[u8]| {
let ok = execute_sandboxed(code, &[]).is_ok();
[ok as u8].to_vec()
},
test_sandbox_args NO_DECODE => |code: &[u8]| {
test_sandbox_args => |code: &[u8]| {
let ok = execute_sandboxed(
code,
&[
@@ -71,7 +100,7 @@ impl_stubs!(
).is_ok();
[ok as u8].to_vec()
},
test_sandbox_return_val NO_DECODE => |code: &[u8]| {
test_sandbox_return_val => |code: &[u8]| {
let result = execute_sandboxed(
code,
&[
@@ -81,7 +110,7 @@ impl_stubs!(
let ok = if let Ok(sandbox::ReturnValue::Value(sandbox::TypedValue::I32(0x1337))) = result { true } else { false };
[ok as u8].to_vec()
},
test_sandbox_instantiate NO_DECODE => |code: &[u8]| {
test_sandbox_instantiate => |code: &[u8]| {
let env_builder = sandbox::EnvironmentDefinitionBuilder::new();
let code = match sandbox::Instance::new(code, &env_builder, &mut ()) {
Ok(_) => 0,
+19
View File
@@ -0,0 +1,19 @@
[package]
name = "sr-api"
version = "0.1.0"
authors = ["Parity Technologies <admin@parity.io>"]
[dependencies]
parity-codec = { version = "2.0", default-features = false }
sr-std = { path = "../sr-std", default-features = false }
sr-primitives = { path = "../sr-primitives", default-features = false }
sr-version = { path = "../sr-version", default-features = false }
[features]
default = ["std"]
std = [
"sr-std/std",
"parity-codec/std",
"sr-primitives/std",
"sr-version/std",
]
+589
View File
@@ -0,0 +1,589 @@
// Copyright 2018 Parity Technologies (UK) Ltd.
// This file is part of Substrate.
// Substrate is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Substrate is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
//! API's for interfacing with the runtime via native/wasm.
#![cfg_attr(not(feature = "std"), no_std)]
extern crate sr_std as rstd;
extern crate sr_primitives as primitives;
#[doc(hidden)]
pub extern crate parity_codec as codec;
extern crate sr_version as runtime_version;
#[doc(hidden)]
pub use primitives::{ApplyResult, traits::Block as BlockT, generic::BlockId};
use runtime_version::RuntimeVersion;
use rstd::vec::Vec;
#[doc(hidden)]
pub use rstd::slice;
#[doc(hidden)]
pub use codec::{Encode, Decode};
/// Declare the given API traits.
///
/// # Example:
///
/// ```nocompile
/// decl_apis!{
/// pub trait Test<Event> {
/// fn test<AccountId>(event: Event) -> AccountId;
/// }
/// }
/// ```
///
/// Will result in the following declaration:
///
/// ```nocompile
/// mod runtime {
/// pub trait Test<Event, AccountId> {
/// fn test(event: Event) -> AccountId;
/// }
/// }
///
/// pub trait Test<Block: BlockT, Event> {
/// type Error;
/// fn test<AccountId: Encode + Decode>(&self, at: &BlockId<Block>, event: Event) -> Result<Event, Self::Error>;
/// }
/// ```
///
/// The declarations generated in the `runtime` module will be used by `impl_apis!` for implementing
/// the traits for a runtime. The other declarations should be used for implementing the interface
/// in the client.
#[macro_export]
macro_rules! decl_apis {
(
$(
$( #[$attr:meta] )*
pub trait $name:ident $(< $( $generic_param:ident $( : $generic_bound:ident )* ),* >)* {
$(
fn $fn_name:ident $( < $( $fn_generic:ident ),* > )* (
$( $param_name:ident : $param_type:ty )*
) $( -> $return_ty:ty)*;
)*
}
)*
) => {
$(
decl_apis!(
@ADD_BLOCK_GENERIC
$( #[$attr] )*
pub trait $name $(< $( $generic_param $( : $generic_bound )* ),* >)* {
$(
fn $fn_name $( < $( $fn_generic ),* > )* ($( $param_name : $param_type )* ) $( -> $return_ty )*;
)*
};
;
;
$( $( $generic_param $( : $generic_bound )* ),* )*
);
)*
decl_apis! {
@GENERATE_RUNTIME_TRAITS
$(
$( #[$attr] )*
pub trait $name $(< $( $generic_param $( : $generic_bound )* ),* >)* {
$(
fn $fn_name $( < $( $fn_generic ),* > )* ($( $param_name : $param_type )* ) $( -> $return_ty )*;
)*
};
)*
}
};
(@ADD_BLOCK_GENERIC
$( #[$attr:meta] )*
pub trait $name:ident $(< $( $generic_param_orig:ident $( : $generic_bound_orig:ident )* ),* >)* {
$(
fn $fn_name:ident $( < $( $fn_generic:ident ),* > )* (
$( $param_name:ident : $param_type:ty )*
) $( -> $return_ty:ty)*;
)*
};
;
$( $generic_param_parsed:ident $( : $generic_bound_parsed:ident )* ),*;
Block: BlockT
$(, $generic_param_rest:ident $( : $generic_bound_rest:ident )* )*
) => {
decl_apis!(
@ADD_BLOCK_GENERIC
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
$(
fn $fn_name $( < $( $fn_generic ),* > )* (
$( $param_name : $param_type )*
) $( -> $return_ty )*;
)*
};
Found;
$( $generic_param_parsed $( : $generic_bound_parsed )* , )* Block: $crate::BlockT;
$( $generic_param_rest $( : $generic_bound_rest )* ),*
);
};
(@ADD_BLOCK_GENERIC
$( #[$attr:meta] )*
pub trait $name:ident $(< $( $generic_param_orig:ident $( : $generic_bound_orig:ident )* ),* >)* {
$(
fn $fn_name:ident $( < $( $fn_generic:ident ),* > )* (
$( $param_name:ident : $param_type:ty )*
) $( -> $return_ty:ty )*;
)*
};
$( $block_found:ident )*;
$( $generic_param_parsed:ident $( : $generic_bound_parsed:path )* ),*;
$generic_param:ident $( : $generic_bound:ident )*
$(, $generic_param_rest:ident $( : $generic_bound_rest:ident )* )*
) => {
decl_apis!(
@ADD_BLOCK_GENERIC
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
$(
fn $fn_name $( < $( $fn_generic ),* > )* (
$( $param_name : $param_type )*
) $( -> $return_ty )*;
)*
};
$( $block_found )*;
$( $generic_param_parsed $( : $generic_bound_parsed )* , )* $generic_param $( : $generic_bound )*;
$( $generic_param_rest $( : $generic_bound_rest )* ),*
);
};
(@ADD_BLOCK_GENERIC
$( #[$attr:meta] )*
pub trait $name:ident $(< $( $generic_param_orig:ident $( : $generic_bound_orig:ident )* ),* >)* {
$(
fn $fn_name:ident $( < $( $fn_generic:ident ),* > )* (
$( $param_name:ident : $param_type:ty )*
) $( -> $return_ty:ty )*;
)*
};
Found;
$( $generic_param_parsed:ident $( : $generic_bound_parsed:path )* ),*;
) => {
decl_apis!(
@GENERATE_FNS
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
$(
fn $fn_name $( < $( $fn_generic ),* > )* (
$( $param_name : $param_type )*
) $( -> $return_ty )*;
)*
};
$( $generic_param_parsed $( : $generic_bound_parsed )* ),*;
{};
$( $( $return_ty )*; )*
);
};
(@ADD_BLOCK_GENERIC
$( #[$attr:meta] )*
pub trait $name:ident $(< $( $generic_param_orig:ident $( : $generic_bound_orig:ident )* ),* >)* {
$(
fn $fn_name:ident $( < $( $fn_generic:ident ),* > )* (
$( $param_name:ident : $param_type:ty )*
) $( -> $return_ty:ty )*;
)*
};
;
$( $generic_param_parsed:ident $( : $generic_bound_parsed:ident )* ),*;
) => {
decl_apis!(
@GENERATE_FNS
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
$(
fn $fn_name $( < $( $fn_generic ),* > )* (
$( $param_name : $param_type )*
) $( -> $return_ty )*;
)*
};
// We need to add the required generic Block parameter
Block: $crate::BlockT $(, $generic_param_parsed $( : $generic_bound_parsed )* )*;
{};
$( $( $return_ty )*; )*
);
};
(@GENERATE_FNS
$( #[$attr:meta] )*
pub trait $name:ident $(< $( $generic_param_orig:ident $( : $generic_bound_orig:ident )* ),* >)* {
$(
fn $fn_name:ident $( < $( $fn_generic:ident ),* > )* (
$( $param_name:ident : $param_type:ty )*
) $( -> $return_ty:ty)*;
)*
};
$( $generic_param_parsed:ident $( : $generic_bound_parsed:path )* ),*;
{ $( $result_return_ty:ty; )* };
$return_ty_current:ty;
$( $( $return_ty_rest:ty )*; )*
) => {
decl_apis!(
@GENERATE_FNS
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
$(
fn $fn_name $( < $( $fn_generic ),* > )* (
$( $param_name : $param_type )*
) $( -> $return_ty )*;
)*
};
$( $generic_param_parsed $( : $generic_bound_parsed )* ),*;
{ $( $result_return_ty; )* Result<$return_ty_current, Self::Error>; };
$( $( $return_ty_rest )*; )*
);
};
(@GENERATE_FNS
$( #[$attr:meta] )*
pub trait $name:ident $(< $( $generic_param_orig:ident $( : $generic_bound_orig:ident )* ),* >)* {
$(
fn $fn_name:ident $( < $( $fn_generic:ident ),* > )* (
$( $param_name:ident : $param_type:ty )*
) $( -> $return_ty:ty)*;
)*
};
$( $generic_param_parsed:ident $( : $generic_bound_parsed:path )* ),*;
{ $( $result_return_ty:ty; )* };
;
$( $( $return_ty_rest:ty )*; )*
) => {
decl_apis!(
@GENERATE_FNS
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
$(
fn $fn_name $( < $( $fn_generic ),* > )* (
$( $param_name : $param_type )*
) $( -> $return_ty )*;
)*
};
$( $generic_param_parsed $( : $generic_bound_parsed )* ),*;
{ $( $result_return_ty; )* Result<(), Self::Error>; };
$( $( $return_ty_rest )*; )*
);
};
(@GENERATE_FNS
$( #[$attr:meta] )*
pub trait $name:ident $(< $( $generic_param_orig:ident $( : $generic_bound_orig:ident )* ),* >)* {
$(
fn $fn_name:ident $( < $( $fn_generic:ident ),* > )* (
$( $param_name:ident : $param_type:ty )*
) $( -> $return_ty:ty)*;
)*
};
$( $generic_param_parsed:ident $( : $generic_bound_parsed:path )* ),*;
{ $( $result_return_ty:ty; )* };
) => {
decl_apis!(
@GENERATE_CLIENT_TRAITS
$( #[$attr] )*
pub trait $name $(< $( $generic_param_orig $( : $generic_bound_orig )* ),* >)* {
$(
fn $fn_name $( < $( $fn_generic ),* > )* (
$( $param_name : $param_type )*
) $( -> $return_ty )*;
)*
};
$( $generic_param_parsed $( : $generic_bound_parsed )* ),*;
{ $( $result_return_ty; )* };
$( $( $generic_param_orig $( : $generic_bound_orig )*, )* )* $( $( $( $fn_generic, )* )* )*;
);
};
(@GENERATE_CLIENT_TRAITS
$( #[$attr:meta] )*
pub trait $name:ident $(< $( $generic_param_orig:ident $( : $generic_bound_orig:ident )* ),* >)* {
$(
fn $fn_name:ident $( < $( $fn_generic:ident ),* > )* (
$( $param_name:ident : $param_type:ty )*
) $( -> $return_ty:ty)*;
)*
};
$( $generic_param_parsed:ident $( : $generic_bound_parsed:path )* ),*;
{ $( $result_return_ty:ty; )* };
$( $generic_param_joined:ident $( : $generic_bound_joined:ident )*, )*;
) => {
$( #[$attr] )*
pub trait $name < $( $generic_param_parsed $( : $generic_bound_parsed )* ),* > {
type Error;
$(
fn $fn_name $( < $( $fn_generic: $crate::Encode + $crate::Decode ),* > )* (
&self, at: &$crate::BlockId<Block> $(, $param_name: $param_type )*
) -> $result_return_ty;
)*
}
};
(@GENERATE_RUNTIME_TRAITS
$(
$( #[$attr:meta] )*
pub trait $name:ident $(< $( $generic_param:ident $( : $generic_bound:ident )* ),* >)* {
$(
fn $fn_name:ident $( < $( $fn_generic:ident ),* > )* (
$( $param_name:ident : $param_type:ty )*
) $( -> $return_ty:ty)*;
)*
};
)*
) => {
decl_apis! {
@GENERATE_RUNTIME_TRAITS_WITH_JOINED_GENERICS
$(
$( #[$attr] )*
pub trait $name < $( $( $generic_param $( : $generic_bound )*, )* )* $( $( $( $fn_generic, )* )* )* > {
$(
fn $fn_name ($( $param_name: $param_type ),*) $( -> $return_ty )*;
)*
}
)*
}
};
(@GENERATE_RUNTIME_TRAITS_WITH_JOINED_GENERICS
$(
$( #[$attr:meta] )*
pub trait $name:ident < $( $generic_param:ident $( : $generic_bound:ident )*, )* > {
$(
fn $fn_name:ident($( $param_name:ident : $param_type:ty )*) $( -> $return_ty:ty)*;
)*
}
)*
) => {
pub mod runtime {
use super::*;
$(
$( #[$attr] )*
pub trait $name < $( $generic_param $( : $generic_bound )* ),* > {
$(
fn $fn_name ($( $param_name: $param_type ),*) $( -> $return_ty )*;
)*
}
)*
}
};
}
decl_apis! {
/// The `Core` api trait that is mandantory for each runtime.
pub trait Core<Block: BlockT, AuthorityId> {
fn version() -> RuntimeVersion;
fn authorities() -> Vec<AuthorityId>;
fn execute_block(block: Block);
}
/// The `Metadata` api trait that returns metadata for the runtime.
pub trait Metadata {
fn metadata() -> Vec<u8>;
}
/// The `BlockBuilder` api trait that provides required functions for building a block for a runtime.
pub trait BlockBuilder<Block: BlockT> {
fn initialise_block(header: <Block as BlockT>::Header);
fn apply_extrinsic(extrinsic: <Block as BlockT>::Extrinsic) -> ApplyResult;
fn finalise_block() -> <Block as BlockT>::Header;
fn inherent_extrinsics<InherentExtrinsic, UncheckedExtrinsic>(inherent: InherentExtrinsic) -> Vec<UncheckedExtrinsic>;
fn random_seed() -> <Block as BlockT>::Hash;
}
/// The `OldTxQueue` api trait for interfering with the old transaction queue.
pub trait OldTxQueue {
fn account_nonce<AccountId, Index>(account: AccountId) -> Index;
fn lookup_address<Address, LookupId>(address: Address) -> Option<LookupId>;
}
/// The `NewTxQueue` api trait for interfering with the new transaction queue.
pub trait NewTxQueue<Block: BlockT> {
fn validate_transaction<TransactionValidity>(tx: <Block as BlockT>::Extrinsic) -> TransactionValidity;
}
/// The `Miscellaneous` api trait for getting miscellaneous information from the runtime.
pub trait Miscellaneous {
fn validator_count() -> u32;
fn validators<AccountId>() -> Vec<AccountId>;
fn timestamp<Moment>() -> Moment;
}
}
/// Implement the given API's for the given runtime.
/// All desired API's need to be implemented in one `impl_apis!` call.
/// Besides generating the implementation for the runtime, there will be also generated an
/// auxiliary module named `api` that contains function for inferring with the API in native/wasm.
/// It is important to use the traits from the `runtime` module with this macro.
///
/// # Example:
///
/// ```nocompile
/// #[macro_use]
/// extern crate sr_api as runtime_api;
///
/// use runtime_api::runtime::{Core, OldTxQueue};
///
/// impl_apis! {
/// impl Core<Block, AccountId> for Runtime {
/// fn version() -> RuntimeVersion { 1 }
/// fn authorities() -> Vec<AuthorityId> { vec![1] }
/// fn execute_block(block: Block) {
/// //comment
/// let block = call_arbitrary_code(block);
/// execute(block);
/// }
/// }
///
/// impl OldTxQueue<AccountId, Index, Address, LookupId> for Runtime {
/// fn account_nonce(account: AccountId) -> Index {
/// 0
/// }
/// fn lookup_address(address: Address) -> Option<LookupId> {
/// None
/// }
/// }
/// }
///
/// fn main() {}
/// ```
#[macro_export]
macro_rules! impl_apis {
(
impl $trait_name:ident $( < $( $generic:ident ),* > )* for $runtime:ident {
$(
fn $fn_name:ident ( $( $arg_name:ident : $arg_ty:ty ),* ) $( -> $return_ty:ty )* {
$( $impl:tt )*
}
)*
}
$( $rest:tt )*
) => {
impl $trait_name $( < $( $generic ),* > )* for $runtime {
$(
fn $fn_name ( $( $arg_name : $arg_ty ),* ) $( -> $return_ty )* {
$( $impl )*
}
)*
}
impl_apis! {
$runtime;
$( $fn_name ( $( $arg_name: $arg_ty ),* ); )*;
$( $rest )*
}
};
(
$runtime:ident;
$( $fn_name_parsed:ident ( $( $arg_name_parsed:ident : $arg_ty_parsed:ty ),* ); )*;
impl $trait_name:ident $( < $( $generic:ident ),* > )* for $runtime_ignore:ident {
$(
fn $fn_name:ident ( $( $arg_name:ident : $arg_ty:ty ),* ) $( -> $return_ty:ty )* {
$( $impl:tt )*
}
)*
}
$( $rest:tt )*
) => {
impl $trait_name $( < $( $generic ),* > )* for $runtime {
$(
fn $fn_name ( $( $arg_name : $arg_ty ),* ) $( -> $return_ty )* {
$( $impl )*
}
)*
}
impl_apis! {
$runtime;
$( $fn_name_parsed ( $( $arg_name_parsed: $arg_ty_parsed )* ); )*
$( $fn_name ( $( $arg_name: $arg_ty ),* ); )*;
$( $rest )*
}
};
(
$runtime:ident;
$( $fn_name:ident ( $( $arg_name:ident : $arg_ty:ty ),* ); )*;
) => {
pub mod api {
use super::*;
#[cfg(feature = "std")]
pub fn dispatch(method: &str, mut data: &[u8]) -> Option<Vec<u8>> {
match method {
$(
stringify!($fn_name) => {
Some({impl_apis! {
@GENERATE_IMPL_CALL
$runtime;
$fn_name;
$( $arg_name : $arg_ty ),*;
data;
}})
}
)*
_ => None,
}
}
$(
#[cfg(not(feature = "std"))]
#[no_mangle]
pub fn $fn_name(input_data: *mut u8, input_len: usize) -> u64 {
let mut input = if input_len == 0 {
&[0u8; 0]
} else {
unsafe {
$crate::slice::from_raw_parts(input_data, input_len)
}
};
let output = { impl_apis! {
@GENERATE_IMPL_CALL
$runtime;
$fn_name;
$( $arg_name : $arg_ty ),*;
input;
}};
let res = output.as_ptr() as u64 + ((output.len() as u64) << 32);
// Leak the output vector to avoid it being freed.
// This is fine in a WASM context since the heap
// will be discarded after the call.
::core::mem::forget(output);
res
}
)*
}
};
(@GENERATE_IMPL_CALL
$runtime:ident;
$fn_name:ident;
$arg_name:ident : $arg_ty:ty;
$input:ident;
) => {
let $arg_name : $arg_ty = match $crate::codec::Decode::decode(&mut $input) {
Some(input) => input,
None => panic!("Bad input data provided to {}", stringify!($fn_name)),
};
let output = $runtime::$fn_name($arg_name);
$crate::codec::Encode::encode(&output)
};
(@GENERATE_IMPL_CALL
$runtime:ident;
$fn_name:ident;
$( $arg_name:ident : $arg_ty:ty ),*;
$input:ident;
) => {
let ( $( $arg_name ),* ) : ($( $arg_ty ),*) = match $crate::codec::Decode::decode(&mut $input) {
Some(input) => input,
None => panic!("Bad input data provided to {}", stringify!($fn_name)),
};
let output = $runtime::$fn_name($( $arg_name ),*);
$crate::codec::Encode::encode(&output)
};
}
-28
View File
@@ -181,34 +181,6 @@ pub fn print<T: Printable + Sized>(value: T) {
value.print();
}
#[macro_export]
macro_rules! impl_stubs {
( $( $new_name:ident $($nodecode:ident)* => $invoke: expr ),*) => {
/// Dispatch logic for the native runtime.
pub fn dispatch(method: &str, data: &[u8]) -> Option<Vec<u8>> {
match method {
$(
stringify!($new_name) => { impl_stubs!(@METHOD data $new_name $($nodecode)* => $invoke) }
)*
_ => None,
}
}
};
(@METHOD $data: ident $new_name: ident NO_DECODE => $invoke:expr) => {
Some($invoke($data))
};
(@METHOD $data: ident $new_name: ident => $invoke:expr) => {{
let mut data = $data;
let input = match $crate::codec::Decode::decode(&mut data) {
Some(input) => input,
None => panic!("Bad input data provided to {}", stringify!($new_name)),
};
let output = $invoke(input);
Some($crate::codec::Encode::encode(&output))
}}
}
#[cfg(test)]
mod std_tests {
use super::*;
-57
View File
@@ -285,60 +285,3 @@ impl Printable for u64 {
pub fn print<T: Printable + Sized>(value: T) {
value.print();
}
#[macro_export]
macro_rules! impl_stubs {
( $( $new_name:ident $($nodecode:ident)* => $invoke:expr ),* ) => {
$(
impl_stubs!(@METHOD $new_name $($nodecode)* => $invoke);
)*
};
( @METHOD $new_name:ident NO_DECODE => $invoke:expr ) => {
#[no_mangle]
pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 {
let input: &[u8] = if input_len == 0 {
&[0u8; 0]
} else {
unsafe {
$crate::slice::from_raw_parts(input_data, input_len)
}
};
let output: $crate::rstd::vec::Vec<u8> = $invoke(input);
let res = output.as_ptr() as u64 + ((output.len() as u64) << 32);
// Leak the output vector to avoid it being freed.
// This is fine in a WASM context since the heap
// will be discarded after the call.
::core::mem::forget(output);
res
}
};
( @METHOD $new_name:ident => $invoke:expr ) => {
#[no_mangle]
pub fn $new_name(input_data: *mut u8, input_len: usize) -> u64 {
let mut input = if input_len == 0 {
&[0u8; 0]
} else {
unsafe {
$crate::slice::from_raw_parts(input_data, input_len)
}
};
let input = match $crate::codec::Decode::decode(&mut input) {
Some(input) => input,
None => panic!("Bad input data provided to {}", stringify!($name)),
};
let output = ($invoke)(input);
let output = $crate::codec::Encode::encode(&output);
let res = output.as_ptr() as u64 + ((output.len() as u64) << 32);
// Leak the output vector to avoid it being freed.
// This is fine in a WASM context since the heap
// will be discarded after the call.
::core::mem::forget(output);
res
}
}
}
+3
View File
@@ -13,6 +13,7 @@ parity-codec-derive = { version = "2.0", default-features = false }
substrate-primitives = { path = "../primitives", default-features = false }
sr-std = { path = "../sr-std", default-features = false }
sr-io = { path = "../sr-io", default-features = false }
sr-version = { path = "../sr-version", default-features = false }
log = {version = "0.4", optional = true }
[dev-dependencies]
@@ -27,6 +28,8 @@ std = [
"log",
"sr-std/std",
"sr-io/std",
"sr-version/std",
"parity-codec/std",
"substrate-primitives/std",
]
api-for-runtime = []
+1
View File
@@ -37,6 +37,7 @@ extern crate num_traits;
extern crate integer_sqrt;
extern crate sr_std as rstd;
extern crate sr_io as runtime_io;
extern crate sr_version as runtime_version;
#[doc(hidden)]
pub extern crate parity_codec as codec;
extern crate substrate_primitives;
+2
View File
@@ -12,6 +12,7 @@ parity-codec = { version = "2.0", default-features = false }
parity-codec-derive = { version = "2.0", default-features = false }
substrate-keyring = { path = "../keyring", optional = true }
substrate-primitives = { path = "../primitives", default-features = false }
sr-api = { path = "../sr-api", default-features = false }
sr-std = { path = "../sr-std", default-features = false }
sr-io = { path = "../sr-io", default-features = false }
sr-primitives = { path = "../sr-primitives", default-features = false }
@@ -27,6 +28,7 @@ std = [
"serde_derive",
"substrate-keyring",
"parity-codec/std",
"sr-api/std",
"sr-std/std",
"sr-io/std",
"srml-support/std",
+58 -13
View File
@@ -33,6 +33,7 @@ extern crate srml_support as runtime_support;
#[macro_use]
extern crate parity_codec_derive;
#[macro_use]
extern crate sr_api as runtime_api;
extern crate sr_io as runtime_io;
#[macro_use]
extern crate sr_version as runtime_version;
@@ -52,10 +53,12 @@ pub mod system;
use rstd::prelude::*;
use codec::{Encode, Decode};
use runtime_primitives::traits::{BlindCheckable, BlakeTwo256};
use runtime_primitives::Ed25519Signature;
use runtime_api::runtime::*;
use runtime_primitives::traits::{BlindCheckable, BlakeTwo256, Block as BlockT};
use runtime_primitives::{ApplyResult, Ed25519Signature};
use runtime_version::RuntimeVersion;
pub use primitives::hash::H256;
use primitives::AuthorityId;
#[cfg(any(feature = "std", test))]
use runtime_version::NativeVersion;
@@ -149,15 +152,57 @@ pub fn changes_trie_config() -> primitives::ChangesTrieConfiguration {
}
}
pub mod api {
use system;
impl_stubs!(
version => |()| super::version(),
authorities => |()| system::authorities(),
initialise_block => |header| system::initialise_block(header),
execute_block => |block| system::execute_block(block),
apply_extrinsic => |utx| system::execute_transaction(utx),
finalise_block => |()| system::finalise_block(),
balance_of => |a| system::balance_of(a)
);
mod test_api {
decl_apis! {
pub trait TestAPI {
fn balance_of<AccountId>(id: AccountId) -> u64;
}
}
}
use test_api::runtime::TestAPI;
struct Runtime;
impl_apis! {
impl Core<Block, AuthorityId> for Runtime {
fn version() -> RuntimeVersion {
version()
}
fn authorities() -> Vec<AuthorityId> {
system::authorities()
}
fn execute_block(block: Block) {
system::execute_block(block)
}
}
impl BlockBuilder<Block, u32, u32> for Runtime {
fn initialise_block(header: <Block as BlockT>::Header) {
system::initialise_block(header)
}
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: u32) -> Vec<u32> {
unimplemented!()
}
fn random_seed() -> <Block as BlockT>::Hash {
unimplemented!()
}
}
impl TestAPI<AccountId> for Runtime {
fn balance_of(id: AccountId) -> u64 {
system::balance_of(id)
}
}
}
+15 -3
View File
@@ -464,6 +464,16 @@ dependencies = [
"unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "sr-api"
version = "0.1.0"
dependencies = [
"parity-codec 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-primitives 0.1.0",
"sr-std 0.1.0",
"sr-version 0.1.0",
]
[[package]]
name = "sr-io"
version = "0.1.0"
@@ -491,6 +501,7 @@ dependencies = [
"serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-io 0.1.0",
"sr-std 0.1.0",
"sr-version 0.1.0",
"substrate-primitives 0.1.0",
]
@@ -564,7 +575,7 @@ dependencies = [
"twox-hash 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"uint 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
"wasmi 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
@@ -591,6 +602,7 @@ dependencies = [
"log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
"parity-codec-derive 2.0.3 (registry+https://github.com/rust-lang/crates.io-index)",
"sr-api 0.1.0",
"sr-io 0.1.0",
"sr-primitives 0.1.0",
"sr-std 0.1.0",
@@ -697,7 +709,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "wasmi"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"byteorder 1.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -801,7 +813,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum untrusted 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f392d7819dbe58833e26872f5f6f0d68b7bbbe90fc3667e98731c4a15ad9a7ae"
"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
"checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d"
"checksum wasmi 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "522fe3fdd44a56f25cd5ddcd8ccdb1cf2e982ceb28fcb00f41d8a018ae5245a8"
"checksum wasmi 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d184c4b7081f30316f74f8d73c197314dcb56ea7af9323522b42a2fa9cb19453"
"checksum winapi 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "92c1eb33641e276cfa214a0522acad57be5c56b10cb348b3c5117db75f3ac4b0"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
"checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
@@ -9,6 +9,7 @@ hex-literal = { version = "0.1.0", optional = true }
parity-codec = { version = "2.0", default-features = false }
parity-codec-derive = { version = "2.0", default-features = false }
substrate-primitives = { path = "../../primitives", default-features = false }
sr-api = { path = "../../sr-api", default-features = false }
sr-std = { path = "../../sr-std", default-features = false }
sr-io = { path = "../../sr-io", default-features = false }
sr-version = { path = "../../sr-version", default-features = false }
@@ -21,6 +22,7 @@ std = [
"log",
"hex-literal",
"parity-codec/std",
"sr-api/std",
"sr-std/std",
"sr-io/std",
"srml-support/std",