Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit a0daabe

Browse files
tomusdrwgavofyork
authored andcommitted
RPC: Block number to block hash (#584)
* Return hash for given block number. * Add some tests. * Fix re-import.
1 parent 5adc601 commit a0daabe

File tree

7 files changed

+125
-31
lines changed

7 files changed

+125
-31
lines changed

substrate/codec/derive/src/lib.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
//! Derives serialization and deserialization codec for complex structs for simple marshalling.
1818
1919
#![cfg_attr(not(feature = "std"), no_std)]
20-
//#![cfg_attr(not(feature = "std"), feature(alloc))]
2120

2221
extern crate proc_macro;
2322
extern crate proc_macro2;

substrate/rpc-servers/src/lib.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ extern crate substrate_runtime_primitives;
3131
extern crate log;
3232

3333
use std::io;
34-
use substrate_runtime_primitives::traits::Block as BlockT;
34+
use substrate_runtime_primitives::traits::{Block as BlockT, NumberFor};
3535

3636
type Metadata = apis::metadata::Metadata;
3737
type RpcHandler = pubsub::PubSubHandler<Metadata>;
@@ -49,7 +49,7 @@ pub fn rpc_handler<Block: BlockT, ExHash, PendingExtrinsics, S, C, A, Y>(
4949
ExHash: Send + Sync + 'static + substrate_runtime_primitives::Serialize + substrate_runtime_primitives::DeserializeOwned,
5050
PendingExtrinsics: serde::Serialize + serde::de::DeserializeOwned + Send + Sync + 'static,
5151
S: apis::state::StateApi<Block::Hash, Metadata=Metadata>,
52-
C: apis::chain::ChainApi<Block::Hash, Block::Header, Block::Extrinsic, Metadata=Metadata>,
52+
C: apis::chain::ChainApi<Block::Hash, Block::Header, NumberFor<Block>, Block::Extrinsic, Metadata=Metadata>,
5353
A: apis::author::AuthorApi<ExHash, Block::Extrinsic, PendingExtrinsics, Metadata=Metadata>,
5454
Y: apis::system::SystemApi,
5555
{

substrate/rpc/src/chain/mod.rs

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,12 @@
1919
use std::sync::Arc;
2020

2121
use client::{self, Client, BlockchainEvents};
22-
use jsonrpc_macros::pubsub;
22+
use jsonrpc_macros::{pubsub, Trailing};
2323
use jsonrpc_pubsub::SubscriptionId;
24-
use jsonrpc_macros::Trailing;
2524
use rpc::Result as RpcResult;
2625
use rpc::futures::{stream, Future, Sink, Stream};
2726
use runtime_primitives::generic::{BlockId, SignedBlock};
28-
use runtime_primitives::traits::Block as BlockT;
27+
use runtime_primitives::traits::{Block as BlockT, Header, NumberFor};
2928
use runtime_version::RuntimeVersion;
3029
use tokio::runtime::TaskExecutor;
3130
use primitives::{KeccakHasher, RlpCodec};
@@ -40,20 +39,22 @@ use self::error::Result;
4039

4140
build_rpc_trait! {
4241
/// Polkadot blockchain API
43-
pub trait ChainApi<Hash, Header, Extrinsic> {
42+
pub trait ChainApi<Hash, Header, Number, Extrinsic> {
4443
type Metadata;
4544

4645
/// Get header of a relay chain block.
4746
#[rpc(name = "chain_getHeader")]
48-
fn header(&self, Hash) -> Result<Option<Header>>;
47+
fn header(&self, Trailing<Hash>) -> Result<Option<Header>>;
4948

5049
/// Get header and body of a relay chain block.
5150
#[rpc(name = "chain_getBlock")]
52-
fn block(&self, Hash) -> Result<Option<SignedBlock<Header, Extrinsic, Hash>>>;
51+
fn block(&self, Trailing<Hash>) -> Result<Option<SignedBlock<Header, Extrinsic, Hash>>>;
5352

54-
/// Get hash of the head.
55-
#[rpc(name = "chain_getHead")]
56-
fn head(&self) -> Result<Hash>;
53+
/// Get hash of the n-th block in the canon chain.
54+
///
55+
/// By default returns latest block hash.
56+
#[rpc(name = "chain_getBlockHash", alias = ["chain_getHead", ])]
57+
fn block_hash(&self, Trailing<Number>) -> Result<Option<Hash>>;
5758

5859
/// Get the runtime version.
5960
#[rpc(name = "chain_getRuntimeVersion")]
@@ -102,23 +103,28 @@ impl<B, E, Block> Chain<B, E, Block> where
102103
}
103104
}
104105

105-
impl<B, E, Block> ChainApi<Block::Hash, Block::Header, Block::Extrinsic> for Chain<B, E, Block> where
106+
impl<B, E, Block> ChainApi<Block::Hash, Block::Header, NumberFor<Block>, Block::Extrinsic> for Chain<B, E, Block> where
106107
Block: BlockT + 'static,
107108
B: client::backend::Backend<Block, KeccakHasher, RlpCodec> + Send + Sync + 'static,
108109
E: client::CallExecutor<Block, KeccakHasher, RlpCodec> + Send + Sync + 'static,
109110
{
110111
type Metadata = ::metadata::Metadata;
111112

112-
fn header(&self, hash: Block::Hash) -> Result<Option<Block::Header>> {
113+
fn header(&self, hash: Trailing<Block::Hash>) -> Result<Option<Block::Header>> {
114+
let hash = self.unwrap_or_best(hash)?;
113115
Ok(self.client.header(&BlockId::Hash(hash))?)
114116
}
115117

116-
fn block(&self, hash: Block::Hash) -> Result<Option<SignedBlock<Block::Header, Block::Extrinsic, Block::Hash>>> {
118+
fn block(&self, hash: Trailing<Block::Hash>) -> Result<Option<SignedBlock<Block::Header, Block::Extrinsic, Block::Hash>>> {
119+
let hash = self.unwrap_or_best(hash)?;
117120
Ok(self.client.block(&BlockId::Hash(hash))?)
118121
}
119122

120-
fn head(&self) -> Result<Block::Hash> {
121-
Ok(self.client.info()?.chain.best_hash)
123+
fn block_hash(&self, number: Trailing<NumberFor<Block>>) -> Result<Option<Block::Hash>> {
124+
Ok(match number.into() {
125+
None => Some(self.client.info()?.chain.best_hash),
126+
Some(number) => self.client.header(&BlockId::number(number))?.map(|h| h.hash()),
127+
})
122128
}
123129

124130
fn runtime_version(&self, at: Trailing<Block::Hash>) -> Result<RuntimeVersion> {
@@ -129,8 +135,8 @@ impl<B, E, Block> ChainApi<Block::Hash, Block::Header, Block::Extrinsic> for Cha
129135
fn subscribe_new_head(&self, _metadata: Self::Metadata, subscriber: pubsub::Subscriber<Block::Header>) {
130136
self.subscriptions.add(subscriber, |sink| {
131137
// send current head right at the start.
132-
let header = self.head()
133-
.and_then(|hash| self.header(hash))
138+
let header = self.block_hash(None.into())
139+
.and_then(|hash| self.header(hash.into()))
134140
.and_then(|header| {
135141
header.ok_or_else(|| self::error::ErrorKind::Unimplemented.into())
136142
})

substrate/rpc/src/chain/tests.rs

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ fn should_return_header() {
2929
client: Arc::new(test_client::new()),
3030
subscriptions: Subscriptions::new(remote),
3131
};
32+
3233
assert_matches!(
33-
client.header(client.client.genesis_hash()),
34+
client.header(Some(client.client.genesis_hash()).into()),
3435
Ok(Some(ref x)) if x == &Header {
3536
parent_hash: 0.into(),
3637
number: 0,
@@ -41,7 +42,18 @@ fn should_return_header() {
4142
);
4243

4344
assert_matches!(
44-
client.header(5.into()),
45+
client.header(None.into()),
46+
Ok(Some(ref x)) if x == &Header {
47+
parent_hash: 0.into(),
48+
number: 0,
49+
state_root: x.state_root.clone(),
50+
extrinsics_root: "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".into(),
51+
digest: Default::default(),
52+
}
53+
);
54+
55+
assert_matches!(
56+
client.header(Some(5.into()).into()),
4557
Ok(None)
4658
);
4759
}
@@ -63,12 +75,26 @@ fn should_return_a_block() {
6375

6476
// Genesis block is not justified, so we can't query it?
6577
assert_matches!(
66-
api.block(api.client.genesis_hash()),
78+
api.block(Some(api.client.genesis_hash()).into()),
6779
Ok(None)
6880
);
6981

7082
assert_matches!(
71-
api.block(block_hash),
83+
api.block(Some(block_hash).into()),
84+
Ok(Some(ref x)) if x.block == Block {
85+
header: Header {
86+
parent_hash: api.client.genesis_hash(),
87+
number: 1,
88+
state_root: x.block.header.state_root.clone(),
89+
extrinsics_root: "56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421".into(),
90+
digest: Default::default(),
91+
},
92+
extrinsics: vec![],
93+
}
94+
);
95+
96+
assert_matches!(
97+
api.block(None.into()),
7298
Ok(Some(ref x)) if x.block == Block {
7399
header: Header {
74100
parent_hash: api.client.genesis_hash(),
@@ -82,9 +108,49 @@ fn should_return_a_block() {
82108
);
83109

84110
assert_matches!(
85-
api.block(5.into()),
111+
api.block(Some(5.into()).into()),
112+
Ok(None)
113+
);
114+
}
115+
116+
#[test]
117+
fn should_return_block_hash() {
118+
let core = ::tokio::runtime::Runtime::new().unwrap();
119+
let remote = core.executor();
120+
121+
let client = Chain {
122+
client: Arc::new(test_client::new()),
123+
subscriptions: Subscriptions::new(remote),
124+
};
125+
126+
assert_matches!(
127+
client.block_hash(None.into()),
128+
Ok(Some(ref x)) if x == &client.client.genesis_hash()
129+
);
130+
131+
132+
assert_matches!(
133+
client.block_hash(Some(0u64).into()),
134+
Ok(Some(ref x)) if x == &client.client.genesis_hash()
135+
);
136+
137+
assert_matches!(
138+
client.block_hash(Some(1u64).into()),
86139
Ok(None)
87140
);
141+
142+
let block = client.client.new_block().unwrap().bake().unwrap();
143+
client.client.justify_and_import(BlockOrigin::Own, block.clone()).unwrap();
144+
145+
assert_matches!(
146+
client.block_hash(Some(0u64).into()),
147+
Ok(Some(ref x)) if x == &client.client.genesis_hash()
148+
);
149+
assert_matches!(
150+
client.block_hash(Some(1u64).into()),
151+
Ok(Some(ref x)) if x == &block.hash()
152+
);
153+
88154
}
89155

90156
#[test]

substrate/rpc/src/helpers.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2018 Parity Technologies (UK) Ltd.
2+
// This file is part of Substrate.
3+
4+
// Substrate is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
9+
// Substrate is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
14+
// You should have received a copy of the GNU General Public License
15+
// along with Substrate. If not, see <http://www.gnu.org/licenses/>.
16+
17+
/// Unwraps the trailing parameter or falls back with the closure result.
18+
pub fn unwrap_or_else<F, H, E>(or_else: F, optional: ::jsonrpc_macros::Trailing<H>) -> Result<H, E> where
19+
F: FnOnce() -> Result<H, E>,
20+
{
21+
match optional.into() {
22+
None => or_else(),
23+
Some(x) => Ok(x),
24+
}
25+
}

substrate/rpc/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ extern crate substrate_test_client as test_client;
4646
extern crate rustc_hex;
4747

4848
mod errors;
49+
mod helpers;
4950
mod subscriptions;
5051

5152
pub mod author;

substrate/rpc/src/state/mod.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,12 @@ impl<B, E, Block: BlockT> State<B, E, Block> {
101101
}
102102

103103
impl<B, E, Block> State<B, E, Block> where
104-
Block: BlockT + 'static,
105-
B: client::backend::Backend<Block, KeccakHasher, RlpCodec> + Send + Sync + 'static,
106-
E: CallExecutor<Block, KeccakHasher, RlpCodec> + Send + Sync + 'static,
104+
Block: BlockT,
105+
B: client::backend::Backend<Block, KeccakHasher, RlpCodec>,
106+
E: CallExecutor<Block, KeccakHasher, RlpCodec>,
107107
{
108108
fn unwrap_or_best(&self, hash: Trailing<Block::Hash>) -> Result<Block::Hash> {
109-
Ok(match hash.into() {
110-
None => self.client.info()?.chain.best_hash,
111-
Some(hash) => hash,
112-
})
109+
::helpers::unwrap_or_else(|| Ok(self.client.info()?.chain.best_hash), hash)
113110
}
114111
}
115112

0 commit comments

Comments
 (0)