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

Commit 71bc094

Browse files
seunlanlegeniklasad1
authored andcommitted
Allow default block parameter to be blockHash (#10932)
* allow default block parameter to be blockHash * requireCanonical * block check takes precedence over canon check
1 parent 2c9bd22 commit 71bc094

File tree

11 files changed

+144
-11
lines changed

11 files changed

+144
-11
lines changed

ethcore/src/client/client.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1793,6 +1793,10 @@ impl BlockChainClient for Client {
17931793
self.config.spec_name.clone()
17941794
}
17951795

1796+
fn chain(&self) -> Arc<BlockProvider> {
1797+
self.chain.read().clone()
1798+
}
1799+
17961800
fn set_spec_name(&self, new_spec_name: String) -> Result<(), ()> {
17971801
trace!(target: "mode", "Client::set_spec_name({:?})", new_spec_name);
17981802
if !self.enabled.load(AtomicOrdering::Relaxed) {

ethcore/src/client/test_client.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ use std::str::FromStr;
2020
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrder};
2121
use std::sync::Arc;
2222
use std::collections::{HashMap, BTreeMap};
23+
use blockchain::BlockProvider;
2324
use std::mem;
2425

2526
use blockchain::{TreeRoute, BlockReceipts};
@@ -703,6 +704,10 @@ impl BlockChainClient for TestBlockChainClient {
703704
}
704705
}
705706

707+
fn chain(&self) -> Arc<BlockProvider> {
708+
unimplemented!()
709+
}
710+
706711
fn list_accounts(&self, _id: BlockId, _after: Option<&Address>, _count: u64) -> Option<Vec<Address>> {
707712
None
708713
}

ethcore/src/client/traits.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use std::collections::BTreeMap;
1818
use std::sync::Arc;
1919

20-
use blockchain::{BlockReceipts, TreeRoute};
20+
use blockchain::{BlockReceipts, TreeRoute, BlockProvider};
2121
use bytes::Bytes;
2222
use call_contract::{CallContract, RegistryInfo};
2323
use ethcore_miner::pool::VerifiedTransaction;
@@ -238,6 +238,8 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
238238
.expect("code will return Some if given BlockId::Latest; qed")
239239
}
240240

241+
fn chain(&self) -> Arc<BlockProvider>;
242+
241243
/// Get block queue information.
242244
fn queue_info(&self) -> BlockQueueInfo;
243245

rpc/src/v1/helpers/errors.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,3 +609,13 @@ pub fn require_experimental(allow_experimental_rpcs: bool, eip: &str) -> Result<
609609
})
610610
}
611611
}
612+
613+
/// returns an error for when require_canonical was specified and
614+
pub fn invalid_input() -> Error {
615+
Error {
616+
// UNSUPPORTED_REQUEST shares the same error code for EIP-1898
617+
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
618+
message: "Invalid input".into(),
619+
data: None
620+
}
621+
}

rpc/src/v1/helpers/light_fetch.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ where
261261
// (they don't have state) we can safely fallback to `Latest`.
262262
let id = match num.unwrap_or_default() {
263263
BlockNumber::Num(n) => BlockId::Number(n),
264+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
264265
BlockNumber::Earliest => BlockId::Earliest,
265266
BlockNumber::Latest => BlockId::Latest,
266267
BlockNumber::Pending => {

rpc/src/v1/impls/eth.rs

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T> EthClient<C, SN, S, M, EM> where
243243

244244
BlockNumberOrId::Number(num) => {
245245
let id = match num {
246+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
246247
BlockNumber::Latest => BlockId::Latest,
247248
BlockNumber::Earliest => BlockId::Earliest,
248249
BlockNumber::Num(n) => BlockId::Number(n),
@@ -439,10 +440,10 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T> EthClient<C, SN, S, M, EM> where
439440
/// if no state found for the best pending block.
440441
fn get_state(&self, number: BlockNumber) -> StateOrBlock {
441442
match number {
443+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash).into(),
442444
BlockNumber::Num(num) => BlockId::Number(num).into(),
443445
BlockNumber::Earliest => BlockId::Earliest.into(),
444446
BlockNumber::Latest => BlockId::Latest.into(),
445-
446447
BlockNumber::Pending => {
447448
let info = self.client.chain_info();
448449

@@ -500,10 +501,22 @@ fn check_known<C>(client: &C, number: BlockNumber) -> Result<()> where C: BlockC
500501

501502
let id = match number {
502503
BlockNumber::Pending => return Ok(()),
503-
504504
BlockNumber::Num(n) => BlockId::Number(n),
505505
BlockNumber::Latest => BlockId::Latest,
506506
BlockNumber::Earliest => BlockId::Earliest,
507+
BlockNumber::Hash { hash, require_canonical } => {
508+
// block check takes precedence over canon check.
509+
match client.block_status(BlockId::Hash(hash.clone())) {
510+
BlockStatus::InChain => {},
511+
_ => return Err(errors::unknown_block()),
512+
};
513+
514+
if require_canonical && !client.chain().is_canon(&hash) {
515+
return Err(errors::invalid_input())
516+
}
517+
518+
return Ok(())
519+
}
507520
};
508521

509522
match client.block_status(id) {
@@ -615,6 +628,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
615628

616629
let num = num.unwrap_or_default();
617630
let id = match num {
631+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
618632
BlockNumber::Num(n) => BlockId::Number(n),
619633
BlockNumber::Earliest => BlockId::Earliest,
620634
BlockNumber::Latest => BlockId::Latest,
@@ -789,6 +803,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
789803

790804
fn transaction_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture<Option<Transaction>> {
791805
let block_id = match num {
806+
BlockNumber::Hash { hash, .. } => PendingOrBlock::Block(BlockId::Hash(hash)),
792807
BlockNumber::Latest => PendingOrBlock::Block(BlockId::Latest),
793808
BlockNumber::Earliest => PendingOrBlock::Block(BlockId::Earliest),
794809
BlockNumber::Num(num) => PendingOrBlock::Block(BlockId::Number(num)),
@@ -825,6 +840,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
825840

826841
fn uncle_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture<Option<RichBlock>> {
827842
let id = match num {
843+
BlockNumber::Hash { hash, .. } => PendingUncleId { id: PendingOrBlock::Block(BlockId::Hash(hash)), position: index.value() },
828844
BlockNumber::Latest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Latest), position: index.value() },
829845
BlockNumber::Earliest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Earliest), position: index.value() },
830846
BlockNumber::Num(num) => PendingUncleId { id: PendingOrBlock::Block(BlockId::Number(num)), position: index.value() },
@@ -941,12 +957,14 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
941957
let signed = try_bf!(fake_sign::sign_call(request));
942958

943959
let num = num.unwrap_or_default();
960+
try_bf!(check_known(&*self.client, num.clone()));
944961

945962
let (mut state, header) =
946963
if num == BlockNumber::Pending {
947964
self.pending_state_and_header_with_fallback()
948965
} else {
949966
let id = match num {
967+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
950968
BlockNumber::Num(num) => BlockId::Number(num),
951969
BlockNumber::Earliest => BlockId::Earliest,
952970
BlockNumber::Latest => BlockId::Latest,
@@ -985,6 +1003,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
9851003
self.pending_state_and_header_with_fallback()
9861004
} else {
9871005
let id = match num {
1006+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
9881007
BlockNumber::Num(num) => BlockId::Number(num),
9891008
BlockNumber::Earliest => BlockId::Earliest,
9901009
BlockNumber::Latest => BlockId::Latest,

rpc/src/v1/impls/parity.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,7 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
350350
(header.encoded(), None)
351351
} else {
352352
let id = match number {
353+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
353354
BlockNumber::Num(num) => BlockId::Number(num),
354355
BlockNumber::Earliest => BlockId::Earliest,
355356
BlockNumber::Latest => BlockId::Latest,
@@ -381,6 +382,7 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
381382
.collect()
382383
))
383384
},
385+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
384386
BlockNumber::Num(num) => BlockId::Number(num),
385387
BlockNumber::Earliest => BlockId::Earliest,
386388
BlockNumber::Latest => BlockId::Latest,
@@ -412,6 +414,7 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
412414
(state, header)
413415
} else {
414416
let id = match num {
417+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
415418
BlockNumber::Num(num) => BlockId::Number(num),
416419
BlockNumber::Earliest => BlockId::Earliest,
417420
BlockNumber::Latest => BlockId::Latest,

rpc/src/v1/impls/traces.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ impl<C, S> Traces for TracesClient<C> where
9595
let signed = fake_sign::sign_call(request)?;
9696

9797
let id = match block {
98+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
9899
BlockNumber::Num(num) => BlockId::Number(num),
99100
BlockNumber::Earliest => BlockId::Earliest,
100101
BlockNumber::Latest => BlockId::Latest,
@@ -122,6 +123,7 @@ impl<C, S> Traces for TracesClient<C> where
122123
.collect::<Result<Vec<_>>>()?;
123124

124125
let id = match block {
126+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
125127
BlockNumber::Num(num) => BlockId::Number(num),
126128
BlockNumber::Earliest => BlockId::Earliest,
127129
BlockNumber::Latest => BlockId::Latest,
@@ -144,6 +146,7 @@ impl<C, S> Traces for TracesClient<C> where
144146
let signed = SignedTransaction::new(tx).map_err(errors::transaction)?;
145147

146148
let id = match block {
149+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
147150
BlockNumber::Num(num) => BlockId::Number(num),
148151
BlockNumber::Earliest => BlockId::Earliest,
149152
BlockNumber::Latest => BlockId::Latest,
@@ -167,6 +170,7 @@ impl<C, S> Traces for TracesClient<C> where
167170

168171
fn replay_block_transactions(&self, block_number: BlockNumber, flags: TraceOptions) -> Result<Vec<TraceResultsWithTransactionHash>> {
169172
let id = match block_number {
173+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
170174
BlockNumber::Num(num) => BlockId::Number(num),
171175
BlockNumber::Earliest => BlockId::Earliest,
172176
BlockNumber::Latest => BlockId::Latest,

rpc/src/v1/types/block_number.rs

Lines changed: 91 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,20 @@
1616

1717
use std::fmt;
1818
use serde::{Deserialize, Deserializer, Serialize, Serializer};
19-
use serde::de::{Error, Visitor};
19+
use serde::de::{Error, Visitor, MapAccess};
2020
use ethcore::client::BlockId;
21+
use ethereum_types::H256;
2122

2223
/// Represents rpc api block number param.
2324
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
2425
pub enum BlockNumber {
26+
/// Hash
27+
Hash {
28+
/// block hash
29+
hash: H256,
30+
/// only return blocks part of the canon chain
31+
require_canonical: bool,
32+
},
2533
/// Number
2634
Num(u64),
2735
/// Latest block
@@ -68,6 +76,7 @@ impl LightBlockNumber for BlockNumber {
6876
// Since light clients don't produce pending blocks
6977
// (they don't have state) we can safely fallback to `Latest`.
7078
match self {
79+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
7180
BlockNumber::Num(n) => BlockId::Number(n),
7281
BlockNumber::Earliest => BlockId::Earliest,
7382
BlockNumber::Latest => BlockId::Latest,
@@ -82,6 +91,9 @@ impl LightBlockNumber for BlockNumber {
8291
impl Serialize for BlockNumber {
8392
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
8493
match *self {
94+
BlockNumber::Hash{ hash, require_canonical } => serializer.serialize_str(
95+
&format!("{{ 'hash': '{}', 'requireCanonical': '{}' }}", hash, require_canonical)
96+
),
8597
BlockNumber::Num(ref x) => serializer.serialize_str(&format!("0x{:x}", x)),
8698
BlockNumber::Latest => serializer.serialize_str("latest"),
8799
BlockNumber::Earliest => serializer.serialize_str("earliest"),
@@ -99,6 +111,54 @@ impl<'a> Visitor<'a> for BlockNumberVisitor {
99111
write!(formatter, "a block number or 'latest', 'earliest' or 'pending'")
100112
}
101113

114+
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error> where V: MapAccess<'a> {
115+
let (mut require_canonical, mut block_number, mut block_hash) = (false, None::<u64>, None::<H256>);
116+
117+
loop {
118+
let key_str: Option<String> = visitor.next_key()?;
119+
120+
match key_str {
121+
Some(key) => match key.as_str() {
122+
"blockNumber" => {
123+
let value: String = visitor.next_value()?;
124+
if value.starts_with("0x") {
125+
let number = u64::from_str_radix(&value[2..], 16).map_err(|e| {
126+
Error::custom(format!("Invalid block number: {}", e))
127+
})?;
128+
129+
block_number = Some(number);
130+
break;
131+
} else {
132+
return Err(Error::custom("Invalid block number: missing 0x prefix".to_string()))
133+
}
134+
}
135+
"blockHash" => {
136+
block_hash = Some(visitor.next_value()?);
137+
}
138+
"requireCanonical" => {
139+
require_canonical = visitor.next_value()?;
140+
}
141+
key => {
142+
return Err(Error::custom(format!("Unknown key: {}", key)))
143+
}
144+
}
145+
None => {
146+
break
147+
}
148+
};
149+
}
150+
151+
if let Some(number) = block_number {
152+
return Ok(BlockNumber::Num(number))
153+
}
154+
155+
if let Some(hash) = block_hash {
156+
return Ok(BlockNumber::Hash { hash, require_canonical })
157+
}
158+
159+
return Err(Error::custom("Invalid input"))
160+
}
161+
102162
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: Error {
103163
match value {
104164
"latest" => Ok(BlockNumber::Latest),
@@ -107,7 +167,9 @@ impl<'a> Visitor<'a> for BlockNumberVisitor {
107167
_ if value.starts_with("0x") => u64::from_str_radix(&value[2..], 16).map(BlockNumber::Num).map_err(|e| {
108168
Error::custom(format!("Invalid block number: {}", e))
109169
}),
110-
_ => Err(Error::custom("Invalid block number: missing 0x prefix".to_string())),
170+
_ => {
171+
Err(Error::custom("Invalid block number: missing 0x prefix".to_string()))
172+
},
111173
}
112174
}
113175

@@ -119,10 +181,10 @@ impl<'a> Visitor<'a> for BlockNumberVisitor {
119181
/// Converts `BlockNumber` to `BlockId`, panics on `BlockNumber::Pending`
120182
pub fn block_number_to_id(number: BlockNumber) -> BlockId {
121183
match number {
184+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
122185
BlockNumber::Num(num) => BlockId::Number(num),
123186
BlockNumber::Earliest => BlockId::Earliest,
124187
BlockNumber::Latest => BlockId::Latest,
125-
126188
BlockNumber::Pending => panic!("`BlockNumber::Pending` should be handled manually")
127189
}
128190
}
@@ -131,19 +193,40 @@ pub fn block_number_to_id(number: BlockNumber) -> BlockId {
131193
mod tests {
132194
use ethcore::client::BlockId;
133195
use super::*;
196+
use std::str::FromStr;
134197
use serde_json;
135198

136199
#[test]
137200
fn block_number_deserialization() {
138-
let s = r#"["0xa", "latest", "earliest", "pending"]"#;
201+
let s = r#"[
202+
"0xa",
203+
"latest",
204+
"earliest",
205+
"pending",
206+
{"blockNumber": "0xa"},
207+
{"blockHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"},
208+
{"blockHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "requireCanonical": true}
209+
]"#;
139210
let deserialized: Vec<BlockNumber> = serde_json::from_str(s).unwrap();
140-
assert_eq!(deserialized, vec![BlockNumber::Num(10), BlockNumber::Latest, BlockNumber::Earliest, BlockNumber::Pending])
211+
212+
assert_eq!(
213+
deserialized,
214+
vec![
215+
BlockNumber::Num(10),
216+
BlockNumber::Latest,
217+
BlockNumber::Earliest,
218+
BlockNumber::Pending,
219+
BlockNumber::Num(10),
220+
BlockNumber::Hash { hash: H256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(), require_canonical: false },
221+
BlockNumber::Hash { hash: H256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(), require_canonical: true }
222+
]
223+
)
141224
}
142225

143226
#[test]
144-
fn should_not_deserialize_decimal() {
145-
let s = r#""10""#;
146-
assert!(serde_json::from_str::<BlockNumber>(s).is_err());
227+
fn should_not_deserialize() {
228+
let s = r#"[{}, "10"]"#;
229+
assert!(serde_json::from_str::<Vec<BlockNumber>>(s).is_err());
147230
}
148231

149232
#[test]

rpc/src/v1/types/filter.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ impl Filter {
8282
}
8383

8484
let num_to_id = |num| match num {
85+
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
8586
BlockNumber::Num(n) => BlockId::Number(n),
8687
BlockNumber::Earliest => BlockId::Earliest,
8788
BlockNumber::Latest | BlockNumber::Pending => BlockId::Latest,

0 commit comments

Comments
 (0)