Make runtime api generate version and identifier information (#1226)

* Make `decl_runtime_apis!` implement `RuntimeApiInfo` for all runtime
apis

* Make the runtime side generate the info constants as well

* Make `RuntimeApiInfo` implementation use the correct generics

* Adds a test for the runtime api info stuff

* Remove duplicated code by using block from `test-client`

* Adds `compile_fail` tests for `api_version`

* Adds documentation for `api_version`

* Make `impl_runtime_apis!` generate `RUNTIME_API_VERSIONS`

* Update documentation and tests for `RUNTIME_API_VERSIONS`

* Implement `has_api` by using the `RuntimeApiInfo`

* Make `impl_runtime_apis` check that trait identifiers are unique

* Prefix all runtime api function with the corresponding trait

So `execute_block` will be called `Core_execute_block`.
This makes it possible to have traits implement a function with the
same name.

* Rebase master

* Update after master rebase
This commit is contained in:
Bastian Köcher
2018-12-10 11:48:07 +01:00
committed by GitHub
parent a4a67ccbe7
commit 1f6719346f
25 changed files with 661 additions and 396 deletions
+23 -7
View File
@@ -32,7 +32,7 @@ use runtime_primitives::traits::{
ApiRef, ProvideRuntimeApi, Digest, DigestItem,
};
use runtime_primitives::BuildStorage;
use runtime_api::{Core as CoreAPI, CallApiAt, TaggedTransactionQueue, ConstructRuntimeApi};
use runtime_api::{Core as CoreAPI, CallRuntimeAt, TaggedTransactionQueue, ConstructRuntimeApi};
use primitives::{Blake2Hasher, H256, ChangesTrieConfiguration, convert_hash};
use primitives::storage::{StorageKey, StorageData};
use primitives::storage::well_known_keys;
@@ -288,7 +288,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
pub fn authorities_at(&self, id: &BlockId<Block>) -> error::Result<Vec<AuthorityId>> {
match self.backend.blockchain().cache().and_then(|cache| cache.authorities_at(*id)) {
Some(cached_value) => Ok(cached_value),
None => self.executor.call(id, "authorities",&[])
None => self.executor.call(id, "Core_authorities",&[])
.and_then(|r| Vec::<AuthorityId>::decode(&mut &r.return_data[..])
.ok_or(error::ErrorKind::InvalidAuthoritiesSet.into()))
}
@@ -635,7 +635,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
let mut r = self.executor.call_at_state(
transaction_state,
&mut overlay,
"execute_block",
"Core_execute_block",
&<Block as BlockT>::new(import_headers.pre().clone(), body.clone().unwrap_or_default()).encode(),
match (origin, self.block_execution_strategy) {
(BlockOrigin::NetworkInitialSync, _) | (_, ExecutionStrategy::NativeWhenPossible) =>
@@ -1034,7 +1034,7 @@ impl<B, E, Block, RA> ProvideRuntimeApi for Client<B, E, Block, RA> where
}
}
impl<B, E, Block, RA> CallApiAt<Block> for Client<B, E, Block, RA> where
impl<B, E, Block, RA> CallRuntimeAt<Block> for Client<B, E, Block, RA> where
B: backend::Backend<Block, Blake2Hasher>,
E: CallExecutor<Block, Blake2Hasher> + Clone + Send + Sync,
Block: BlockT<Hash=H256>,
@@ -1050,7 +1050,8 @@ impl<B, E, Block, RA> CallApiAt<Block> for Client<B, E, Block, RA> where
initialised_block: &mut Option<BlockId<Block>>,
) -> error::Result<Vec<u8>> {
//TODO: Find a better way to prevent double block initialization
if function != "initialise_block" && initialised_block.map(|id| id != *at).unwrap_or(true) {
if function != "Core_initialise_block"
&& initialised_block.map(|id| id != *at).unwrap_or(true) {
let parent = at;
let header = <<Block as BlockT>::Header as HeaderT>::new(
self.block_number_from_id(parent)?
@@ -1062,12 +1063,16 @@ impl<B, E, Block, RA> CallApiAt<Block> for Client<B, E, Block, RA> where
.ok_or_else(|| error::ErrorKind::UnknownBlock(format!("{:?}", parent)))?,
Default::default()
);
self.call_at_state(at, "initialise_block", header.encode(), changes)?;
self.call_at_state(at, "Core_initialise_block", header.encode(), changes)?;
*initialised_block = Some(*at);
}
self.call_at_state(at, function, args, changes)
}
fn runtime_version_at(&self, at: &BlockId<Block>) -> error::Result<RuntimeVersion> {
self.runtime_version_at(at)
}
}
@@ -1238,7 +1243,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;
use test_client::client::{backend::Backend as TestBackend, runtime_api::ApiExt};
use test_client::BlockBuilderExt;
use test_client::runtime::{self, Block, Transfer, RuntimeApi, test_api::TestAPI};
@@ -1335,6 +1340,17 @@ pub(crate) mod tests {
);
}
#[test]
fn runtime_api_has_test_api() {
let client = test_client::new();
assert!(
client.runtime_api().has_api::<TestAPI<Block>>(
&BlockId::Number(client.info().unwrap().chain.best_number),
).unwrap()
);
}
#[test]
fn authorities_call_works() {
let client = test_client::new();
+6 -6
View File
@@ -91,7 +91,7 @@ mod tests {
Some(&InMemoryChangesTrieStorage::new()),
&mut overlay,
&executor(),
"initialise_block",
"Core_initialise_block",
&header.encode(),
ExecutionStrategy::NativeWhenPossible,
).unwrap();
@@ -102,7 +102,7 @@ mod tests {
Some(&InMemoryChangesTrieStorage::new()),
&mut overlay,
&executor(),
"apply_extrinsic",
"BlockBuilder_apply_extrinsic",
&tx.encode(),
ExecutionStrategy::NativeWhenPossible,
).unwrap();
@@ -113,7 +113,7 @@ mod tests {
Some(&InMemoryChangesTrieStorage::new()),
&mut overlay,
&executor(),
"finalise_block",
"BlockBuilder_finalise_block",
&[],
ExecutionStrategy::NativeWhenPossible,
).unwrap();
@@ -157,7 +157,7 @@ mod tests {
Some(&InMemoryChangesTrieStorage::new()),
&mut overlay,
&executor(),
"execute_block",
"Core_execute_block",
&b1data,
ExecutionStrategy::NativeWhenPossible,
).unwrap();
@@ -182,7 +182,7 @@ mod tests {
Some(&InMemoryChangesTrieStorage::new()),
&mut overlay,
&executor(),
"execute_block",
"Core_execute_block",
&b1data,
ExecutionStrategy::AlwaysWasm,
).unwrap();
@@ -208,7 +208,7 @@ mod tests {
Some(&InMemoryChangesTrieStorage::new()),
&mut overlay,
&Executor::new(),
"execute_block",
"Core_execute_block",
&b1data,
ExecutionStrategy::NativeWhenPossible,
).unwrap();
@@ -166,7 +166,7 @@ mod tests {
.unwrap().storage_root(::std::iter::empty()).0;
// 'fetch' execution proof from remote node
let remote_execution_proof = remote_client.execution_proof(&remote_block_id, "authorities", &[]).unwrap().1;
let remote_execution_proof = remote_client.execution_proof(&remote_block_id, "Core_authorities", &[]).unwrap().1;
// check remote execution proof locally
let local_executor = test_client::LocalExecutor::new();
@@ -179,7 +179,7 @@ mod tests {
extrinsics_root: Default::default(),
digest: Default::default(),
},
method: "authorities".into(),
method: "Core_authorities".into(),
call_data: vec![],
retry_count: None,
}, remote_execution_proof).unwrap();
+21 -47
View File
@@ -21,12 +21,13 @@
pub use state_machine::OverlayedChanges;
#[doc(hidden)]
pub use runtime_primitives::{
traits::{Block as BlockT, GetNodeBlockType, GetRuntimeBlockType, ApiRef}, generic::BlockId,
transaction_validity::TransactionValidity
traits::{Block as BlockT, GetNodeBlockType, GetRuntimeBlockType, ApiRef, RuntimeApiInfo},
generic::BlockId, transaction_validity::TransactionValidity
};
pub use runtime_version::{ApiId, RuntimeVersion};
#[doc(hidden)]
pub use rstd::slice;
pub use runtime_version::{ApiId, RuntimeVersion, ApisVec, create_apis_vec};
#[doc(hidden)]
pub use rstd::{slice, mem};
#[cfg(feature = "std")]
use rstd::result;
pub use codec::{Encode, Decode};
@@ -38,14 +39,16 @@ use primitives::{AuthorityId, OpaqueMetadata};
/// Something that can be constructed to a runtime api.
#[cfg(feature = "std")]
pub trait ConstructRuntimeApi<Block: BlockT>: Sized {
pub trait ConstructRuntimeApi<Block: BlockT> {
/// Construct an instance of the runtime api.
fn construct_runtime_api<'a, T: CallApiAt<Block>>(call: &'a T) -> ApiRef<'a, Self>;
fn construct_runtime_api<'a, T: CallRuntimeAt<Block>>(
call: &'a T
) -> ApiRef<'a, Self> where Self: Sized;
}
/// An extension for the `RuntimeApi`.
#[cfg(feature = "std")]
pub trait ApiExt {
pub trait ApiExt<Block: BlockT> {
/// The given closure will be called with api instance. Inside the closure any api call is
/// allowed. After doing the api call, the closure is allowed to map the `Result` to a
/// different `Result` type. This can be important, as the internal data structure that keeps
@@ -54,12 +57,18 @@ pub trait ApiExt {
fn map_api_result<F: FnOnce(&Self) -> result::Result<R, E>, R, E>(
&self,
map_call: F
) -> result::Result<R, E>;
) -> result::Result<R, E> where Self: Sized;
/// Checks if the given api is implemented and versions match.
fn has_api<A: RuntimeApiInfo + ?Sized>(
&self,
at: &BlockId<Block>
) -> error::Result<bool> where Self: Sized;
}
/// Something that can call the runtime api at a given block.
/// Something that can call into the runtime at a given block.
#[cfg(feature = "std")]
pub trait CallApiAt<Block: BlockT> {
pub trait CallRuntimeAt<Block: BlockT> {
/// Calls the given api function with the given encoded arguments at the given block
/// and returns the encoded result.
fn call_api_at(
@@ -71,43 +80,8 @@ pub trait CallApiAt<Block: BlockT> {
initialised_block: &mut Option<BlockId<Block>>,
) -> error::Result<Vec<u8>>;
/// Call the given api function with strong arguments at the given block
/// and returns the decoded result.
fn call_api_at_strong<In: Encode, Out: Decode>(
&self,
at: &BlockId<Block>,
function: &'static str,
args: &In,
changes: &mut OverlayedChanges,
initialised_block: &mut Option<BlockId<Block>>,
) -> error::Result<Out> where Self: Sized {
let raw = self.call_api_at(
at,
function,
args.encode(),
changes,
initialised_block,
)?;
match Out::decode(&mut &raw[..]) {
Some(out) => Ok(out),
None => bail!(error::ErrorKind::CallResultDecode(function)),
}
}
}
/// The ApiIds for the various standard runtime APIs.
pub mod id {
use super::ApiId;
/// ApiId for the BlockBuilder trait.
pub const BLOCK_BUILDER: ApiId = *b"blkbuild";
/// ApiId for the TaggedTransactionQueue trait.
pub const TAGGED_TRANSACTION_QUEUE: ApiId = *b"validatx";
/// ApiId for the Metadata trait.
pub const METADATA: ApiId = *b"metadata";
/// Returns the runtime version at the given block.
fn runtime_version_at(&self, at: &BlockId<Block>) -> error::Result<RuntimeVersion>;
}
decl_runtime_apis! {