diff --git a/substrate/codec/derive/src/lib.rs b/substrate/codec/derive/src/lib.rs index 5f8b67901e2a2..1bf3aebe7522a 100644 --- a/substrate/codec/derive/src/lib.rs +++ b/substrate/codec/derive/src/lib.rs @@ -17,7 +17,6 @@ //! Derives serialization and deserialization codec for complex structs for simple marshalling. #![cfg_attr(not(feature = "std"), no_std)] -//#![cfg_attr(not(feature = "std"), feature(alloc))] extern crate proc_macro; extern crate proc_macro2; diff --git a/substrate/rpc-servers/src/lib.rs b/substrate/rpc-servers/src/lib.rs index 8fcdea851f527..530d637f096d0 100644 --- a/substrate/rpc-servers/src/lib.rs +++ b/substrate/rpc-servers/src/lib.rs @@ -31,7 +31,7 @@ extern crate substrate_runtime_primitives; extern crate log; use std::io; -use substrate_runtime_primitives::traits::Block as BlockT; +use substrate_runtime_primitives::traits::{Block as BlockT, NumberFor}; type Metadata = apis::metadata::Metadata; type RpcHandler = pubsub::PubSubHandler; @@ -49,7 +49,7 @@ pub fn rpc_handler( ExHash: Send + Sync + 'static + substrate_runtime_primitives::Serialize + substrate_runtime_primitives::DeserializeOwned, PendingExtrinsics: serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static, S: apis::state::StateApi, - C: apis::chain::ChainApi, + C: apis::chain::ChainApi, Block::Extrinsic, Metadata=Metadata>, A: apis::author::AuthorApi, Y: apis::system::SystemApi, { diff --git a/substrate/rpc/src/chain/mod.rs b/substrate/rpc/src/chain/mod.rs index bae10a50c0409..7866ae3ef2cd7 100644 --- a/substrate/rpc/src/chain/mod.rs +++ b/substrate/rpc/src/chain/mod.rs @@ -19,13 +19,12 @@ use std::sync::Arc; use client::{self, Client, BlockchainEvents}; -use jsonrpc_macros::pubsub; +use jsonrpc_macros::{pubsub, Trailing}; use jsonrpc_pubsub::SubscriptionId; -use jsonrpc_macros::Trailing; use rpc::Result as RpcResult; use rpc::futures::{stream, Future, Sink, Stream}; use runtime_primitives::generic::{BlockId, SignedBlock}; -use runtime_primitives::traits::Block as BlockT; +use runtime_primitives::traits::{Block as BlockT, Header, NumberFor}; use runtime_version::RuntimeVersion; use tokio::runtime::TaskExecutor; use primitives::{KeccakHasher, RlpCodec}; @@ -40,20 +39,22 @@ use self::error::Result; build_rpc_trait! { /// Polkadot blockchain API - pub trait ChainApi { + pub trait ChainApi { type Metadata; /// Get header of a relay chain block. #[rpc(name = "chain_getHeader")] - fn header(&self, Hash) -> Result>; + fn header(&self, Trailing) -> Result>; /// Get header and body of a relay chain block. #[rpc(name = "chain_getBlock")] - fn block(&self, Hash) -> Result>>; + fn block(&self, Trailing) -> Result>>; - /// Get hash of the head. - #[rpc(name = "chain_getHead")] - fn head(&self) -> Result; + /// Get hash of the n-th block in the canon chain. + /// + /// By default returns latest block hash. + #[rpc(name = "chain_getBlockHash", alias = ["chain_getHead", ])] + fn block_hash(&self, Trailing) -> Result>; /// Get the runtime version. #[rpc(name = "chain_getRuntimeVersion")] @@ -102,23 +103,28 @@ impl Chain where } } -impl ChainApi for Chain where +impl ChainApi, Block::Extrinsic> for Chain where Block: BlockT + 'static, B: client::backend::Backend + Send + Sync + 'static, E: client::CallExecutor + Send + Sync + 'static, { type Metadata = ::metadata::Metadata; - fn header(&self, hash: Block::Hash) -> Result> { + fn header(&self, hash: Trailing) -> Result> { + let hash = self.unwrap_or_best(hash)?; Ok(self.client.header(&BlockId::Hash(hash))?) } - fn block(&self, hash: Block::Hash) -> Result>> { + fn block(&self, hash: Trailing) -> Result>> { + let hash = self.unwrap_or_best(hash)?; Ok(self.client.block(&BlockId::Hash(hash))?) } - fn head(&self) -> Result { - Ok(self.client.info()?.chain.best_hash) + fn block_hash(&self, number: Trailing>) -> Result> { + Ok(match number.into() { + None => Some(self.client.info()?.chain.best_hash), + Some(number) => self.client.header(&BlockId::number(number))?.map(|h| h.hash()), + }) } fn runtime_version(&self, at: Trailing) -> Result { @@ -129,8 +135,8 @@ impl ChainApi for Cha fn subscribe_new_head(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber) { self.subscriptions.add(subscriber, |sink| { // send current head right at the start. - let header = self.head() - .and_then(|hash| self.header(hash)) + let header = self.block_hash(None.into()) + .and_then(|hash| self.header(hash.into())) .and_then(|header| { header.ok_or_else(|| self::error::ErrorKind::Unimplemented.into()) }) diff --git a/substrate/rpc/src/chain/tests.rs b/substrate/rpc/src/chain/tests.rs index 1f32705f8037b..0be58b2b4be8b 100644 --- a/substrate/rpc/src/chain/tests.rs +++ b/substrate/rpc/src/chain/tests.rs @@ -29,8 +29,9 @@ fn should_return_header() { client: Arc::new(test_client::new()), subscriptions: Subscriptions::new(remote), }; + assert_matches!( - client.header(client.client.genesis_hash()), + client.header(Some(client.client.genesis_hash()).into()), Ok(Some(ref x)) if x == &Header { parent_hash: 0.into(), number: 0, @@ -41,7 +42,18 @@ fn should_return_header() { ); assert_matches!( - client.header(5.into()), + client.header(None.into()), + Ok(Some(ref x)) if x == &Header { + parent_hash: 0.into(), + number: 0, + state_root: x.state_root.clone(), + extrinsics_root: "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".into(), + digest: Default::default(), + } + ); + + assert_matches!( + client.header(Some(5.into()).into()), Ok(None) ); } @@ -63,12 +75,26 @@ fn should_return_a_block() { // Genesis block is not justified, so we can't query it? assert_matches!( - api.block(api.client.genesis_hash()), + api.block(Some(api.client.genesis_hash()).into()), Ok(None) ); assert_matches!( - api.block(block_hash), + api.block(Some(block_hash).into()), + Ok(Some(ref x)) if x.block == Block { + header: Header { + parent_hash: api.client.genesis_hash(), + number: 1, + state_root: x.block.header.state_root.clone(), + extrinsics_root: "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".into(), + digest: Default::default(), + }, + extrinsics: vec![], + } + ); + + assert_matches!( + api.block(None.into()), Ok(Some(ref x)) if x.block == Block { header: Header { parent_hash: api.client.genesis_hash(), @@ -82,9 +108,49 @@ fn should_return_a_block() { ); assert_matches!( - api.block(5.into()), + api.block(Some(5.into()).into()), + Ok(None) + ); +} + +#[test] +fn should_return_block_hash() { + let core = ::tokio::runtime::Runtime::new().unwrap(); + let remote = core.executor(); + + let client = Chain { + client: Arc::new(test_client::new()), + subscriptions: Subscriptions::new(remote), + }; + + assert_matches!( + client.block_hash(None.into()), + Ok(Some(ref x)) if x == &client.client.genesis_hash() + ); + + + assert_matches!( + client.block_hash(Some(0u64).into()), + Ok(Some(ref x)) if x == &client.client.genesis_hash() + ); + + assert_matches!( + client.block_hash(Some(1u64).into()), Ok(None) ); + + let block = client.client.new_block().unwrap().bake().unwrap(); + client.client.justify_and_import(BlockOrigin::Own, block.clone()).unwrap(); + + assert_matches!( + client.block_hash(Some(0u64).into()), + Ok(Some(ref x)) if x == &client.client.genesis_hash() + ); + assert_matches!( + client.block_hash(Some(1u64).into()), + Ok(Some(ref x)) if x == &block.hash() + ); + } #[test] diff --git a/substrate/rpc/src/helpers.rs b/substrate/rpc/src/helpers.rs new file mode 100644 index 0000000000000..dca1a45db563d --- /dev/null +++ b/substrate/rpc/src/helpers.rs @@ -0,0 +1,25 @@ +// 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 . + +/// Unwraps the trailing parameter or falls back with the closure result. +pub fn unwrap_or_else(or_else: F, optional: ::jsonrpc_macros::Trailing) -> Result where + F: FnOnce() -> Result, +{ + match optional.into() { + None => or_else(), + Some(x) => Ok(x), + } +} diff --git a/substrate/rpc/src/lib.rs b/substrate/rpc/src/lib.rs index 3f76c1b5ddba1..760b1ca567263 100644 --- a/substrate/rpc/src/lib.rs +++ b/substrate/rpc/src/lib.rs @@ -46,6 +46,7 @@ extern crate substrate_test_client as test_client; extern crate rustc_hex; mod errors; +mod helpers; mod subscriptions; pub mod author; diff --git a/substrate/rpc/src/state/mod.rs b/substrate/rpc/src/state/mod.rs index 28aa01710955c..80369326de047 100644 --- a/substrate/rpc/src/state/mod.rs +++ b/substrate/rpc/src/state/mod.rs @@ -101,15 +101,12 @@ impl State { } impl State where - Block: BlockT + 'static, - B: client::backend::Backend + Send + Sync + 'static, - E: CallExecutor + Send + Sync + 'static, + Block: BlockT, + B: client::backend::Backend, + E: CallExecutor, { fn unwrap_or_best(&self, hash: Trailing) -> Result { - Ok(match hash.into() { - None => self.client.info()?.chain.best_hash, - Some(hash) => hash, - }) + ::helpers::unwrap_or_else(|| Ok(self.client.info()?.chain.best_hash), hash) } }