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
+27 -52
View File
@@ -46,6 +46,7 @@ use std::sync::Arc;
use std::time::{self, Duration, Instant};
use client::{Client as SubstrateClient, CallExecutor};
use client::runtime_api::{BlockBuilder as BlockBuilderAPI, Core, Miscellaneous, OldTxQueue};
use codec::{Decode, Encode};
use node_primitives::{AccountId, Timestamp, SessionKey, InherentData};
use node_runtime::Runtime;
@@ -86,31 +87,23 @@ pub trait BlockBuilder<Block: BlockT> {
}
/// Local client abstraction for the consensus.
pub trait AuthoringApi: Send + Sync {
pub trait AuthoringApi:
Send
+ Sync
+ BlockBuilderAPI<<Self as AuthoringApi>::Block, Error=<Self as AuthoringApi>::Error>
+ Core<<Self as AuthoringApi>::Block, AuthorityId, Error=<Self as AuthoringApi>::Error>
+ Miscellaneous<<Self as AuthoringApi>::Block, Error=<Self as AuthoringApi>::Error>
+ OldTxQueue<<Self as AuthoringApi>::Block, Error=<Self as AuthoringApi>::Error>
{
/// The block used for this API type.
type Block: BlockT;
/// The block builder for this API type.
type BlockBuilder: BlockBuilder<Self::Block>;
/// Get the value of the randomness beacon at a given block.
fn random_seed(&self, at: &BlockId<Self::Block>) -> Result<<Self::Block as BlockT>::Hash>;
/// Get validators at a given block.
fn validators(&self, at: &BlockId<Self::Block>) -> Result<Vec<AccountId>>;
/// The error used by this API type.
type Error;
/// Build a block on top of the given, with inherent extrinsics pre-pushed.
fn build_block(&self, at: &BlockId<Self::Block>, inherent_data: InherentData) -> Result<Self::BlockBuilder>;
/// Get the nonce (né index) of an account at a block.
fn index(&self, at: &BlockId<Self::Block>, account: AccountId) -> Result<u64>;
/// Attempt to produce the (encoded) inherent extrinsics for a block being built upon the given.
/// This may vary by runtime and will fail if a runtime doesn't follow the same API.
fn inherent_extrinsics(&self, at: &BlockId<Self::Block>, inherent_data: InherentData) -> Result<Vec<<Self::Block as BlockT>::Extrinsic>>;
/// Evaluate a block. Returns true if the block is good, false if it is known to be bad,
/// and an error if we can't evaluate for some reason.
fn evaluate_block(&self, at: &BlockId<Self::Block>, block: Self::Block) -> Result<bool>;
}
impl<B, E, Block> BlockBuilder<Block> for client::block_builder::BlockBuilder<B, E, Block, Blake2Hasher> where
@@ -134,14 +127,7 @@ impl<B, E, Block> AuthoringApi for SubstrateClient<B, E, Block> where
{
type Block = Block;
type BlockBuilder = client::block_builder::BlockBuilder<B, E, Block, Blake2Hasher>;
fn random_seed(&self, at: &BlockId<Block>) -> Result<<Self::Block as BlockT>::Hash> {
self.call_api_at(at, "random_seed", &()).map_err(Into::into)
}
fn validators(&self, at: &BlockId<Block>) -> Result<Vec<AccountId>> {
self.call_api_at(at, "validators", &()).map_err(Into::into)
}
type Error = client::error::Error;
fn build_block(&self, at: &BlockId<Block>, inherent_data: InherentData) -> Result<Self::BlockBuilder> {
let runtime_version = self.runtime_version_at(at)?;
@@ -152,27 +138,9 @@ impl<B, E, Block> AuthoringApi for SubstrateClient<B, E, Block> where
block_builder.push(inherent)?;
}
}
Ok(block_builder)
}
fn index(&self, at: &BlockId<Block>, account: AccountId) -> Result<u64> {
self.call_api_at(at, "account_nonce", &account).map_err(Into::into)
}
fn inherent_extrinsics(&self, at: &BlockId<Self::Block>, inherent_data: InherentData) -> Result<Vec<<Block as BlockT>::Extrinsic>> {
self.call_api_at(at, "inherent_extrinsics", &inherent_data).map_err(Into::into)
}
fn evaluate_block(&self, at: &BlockId<Self::Block>, block: Self::Block) -> Result<bool> {
let res: client::error::Result<()> = self.call_api_at(at, "execute_block", &block);
match res {
Ok(()) => Ok(true),
Err(err) => match err.kind() {
&client::error::ErrorKind::Execution(_) => Ok(false),
_ => Err(err.into())
}
}
}
}
/// A long-lived network which can create BFT message routing processes on demand.
@@ -217,7 +185,8 @@ impl<N, C> bft::Environment<<C as AuthoringApi>::Block> for ProposerFactory<N, C
N: Network<Block=<C as AuthoringApi>::Block>,
C: AuthoringApi + TPClient<Block=<C as AuthoringApi>::Block>,
<<C as AuthoringApi>::Block as BlockT>::Hash:
Into<<Runtime as SystemT>::Hash> + PartialEq<primitives::H256> + Into<primitives::H256>
Into<<Runtime as SystemT>::Hash> + PartialEq<primitives::H256> + Into<primitives::H256>,
Error: From<<C as AuthoringApi>::Error>
{
type Proposer = Proposer<C>;
type Input = N::Input;
@@ -297,7 +266,8 @@ impl<C: AuthoringApi + TPClient> Proposer<C> {
impl<C> bft::Proposer<<C as AuthoringApi>::Block> for Proposer<C> where
C: AuthoringApi + TPClient<Block=<C as AuthoringApi>::Block>,
<<C as AuthoringApi>::Block as BlockT>::Hash:
Into<<Runtime as SystemT>::Hash> + PartialEq<primitives::H256> + Into<primitives::H256>
Into<<Runtime as SystemT>::Hash> + PartialEq<primitives::H256> + Into<primitives::H256>,
error::Error: From<<C as AuthoringApi>::Error>
{
type Create = Result<<C as AuthoringApi>::Block>;
type Error = Error;
@@ -419,7 +389,7 @@ impl<C> bft::Proposer<<C as AuthoringApi>::Block> for Proposer<C> where
match timestamp_delay {
Some(duration) => future::Either::A(
Delay::new(duration).map_err(|e| Error::from(ErrorKind::Timer(e)))
Delay::new(duration).map_err(|e| ErrorKind::Timer(e).into())
),
None => future::Either::B(future::ok(())),
}
@@ -434,9 +404,13 @@ impl<C> bft::Proposer<<C as AuthoringApi>::Block> for Proposer<C> where
// evaluate whether the block is actually valid.
// TODO: is it better to delay this until the delays are finished?
let evaluated = self.client
.evaluate_block(&self.parent_id, unchecked_proposal.clone())
.map_err(Into::into);
let evaluated = match self.client.execute_block(&self.parent_id, unchecked_proposal.clone()).map_err(Error::from) {
Ok(()) => Ok(true),
Err(err) => match err.kind() {
error::ErrorKind::Client(client::error::ErrorKind::Execution(_)) => Ok(false),
_ => Err(err)
}
};
let future = future::result(evaluated).and_then(move |good| {
let end_result = future::ok(good);
@@ -471,7 +445,8 @@ impl<C> bft::Proposer<<C as AuthoringApi>::Block> for Proposer<C> where
.filter(|tx| tx.verified.sender == local_id)
.last()
.map(|tx| Ok(tx.verified.index()))
.unwrap_or_else(|| AuthoringApi::index(self.client.as_ref(), &self.parent_id, local_id))
.unwrap_or_else(|| self.client.account_nonce(&self.parent_id, local_id))
.map_err(Error::from)
);
match cur_index {
+1
View File
@@ -83,6 +83,7 @@ impl Service {
) -> Service
where
A: AuthoringApi + TPClient<Block = <A as AuthoringApi>::Block> + 'static,
error::Error: From<<A as AuthoringApi>::Error>,
C: BlockchainEvents<<A as AuthoringApi>::Block>
+ ChainHead<<A as AuthoringApi>::Block>
+ BlockBody<<A as AuthoringApi>::Block>,