Allow default block parameter to be blockHash (#10932)
* allow default block parameter to be blockHash * requireCanonical * block check takes precedence over canon check
This commit is contained in:
parent
13ccb9f827
commit
72279856cd
@ -1759,6 +1759,10 @@ impl BlockChainClient for Client {
|
||||
self.config.spec_name.clone()
|
||||
}
|
||||
|
||||
fn chain(&self) -> Arc<BlockProvider> {
|
||||
self.chain.read().clone()
|
||||
}
|
||||
|
||||
fn set_spec_name(&self, new_spec_name: String) -> Result<(), ()> {
|
||||
trace!(target: "mode", "Client::set_spec_name({:?})", new_spec_name);
|
||||
if !self.enabled.load(AtomicOrdering::Relaxed) {
|
||||
|
@ -20,6 +20,7 @@ use std::str::FromStr;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering as AtomicOrder};
|
||||
use std::sync::Arc;
|
||||
use std::collections::{HashMap, BTreeMap};
|
||||
use blockchain::BlockProvider;
|
||||
use std::mem;
|
||||
|
||||
use blockchain::{TreeRoute, BlockReceipts};
|
||||
@ -703,6 +704,10 @@ impl BlockChainClient for TestBlockChainClient {
|
||||
}
|
||||
}
|
||||
|
||||
fn chain(&self) -> Arc<BlockProvider> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn list_accounts(&self, _id: BlockId, _after: Option<&Address>, _count: u64) -> Option<Vec<Address>> {
|
||||
None
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::sync::Arc;
|
||||
|
||||
use blockchain::{BlockReceipts, TreeRoute};
|
||||
use blockchain::{BlockReceipts, TreeRoute, BlockProvider};
|
||||
use bytes::Bytes;
|
||||
use call_contract::{CallContract, RegistryInfo};
|
||||
use ethcore_miner::pool::VerifiedTransaction;
|
||||
@ -233,6 +233,8 @@ pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContra
|
||||
.expect("code will return Some if given BlockId::Latest; qed")
|
||||
}
|
||||
|
||||
fn chain(&self) -> Arc<BlockProvider>;
|
||||
|
||||
/// Get block queue information.
|
||||
fn queue_info(&self) -> BlockQueueInfo;
|
||||
|
||||
|
@ -609,3 +609,13 @@ pub fn require_experimental(allow_experimental_rpcs: bool, eip: &str) -> Result<
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// returns an error for when require_canonical was specified and
|
||||
pub fn invalid_input() -> Error {
|
||||
Error {
|
||||
// UNSUPPORTED_REQUEST shares the same error code for EIP-1898
|
||||
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
|
||||
message: "Invalid input".into(),
|
||||
data: None
|
||||
}
|
||||
}
|
||||
|
@ -263,6 +263,7 @@ where
|
||||
// (they don't have state) we can safely fallback to `Latest`.
|
||||
let id = match num.unwrap_or_default() {
|
||||
BlockNumber::Num(n) => BlockId::Number(n),
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
BlockNumber::Pending => {
|
||||
|
@ -241,6 +241,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> EthClient<C, SN, S
|
||||
|
||||
BlockNumberOrId::Number(num) => {
|
||||
let id = match num {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Num(n) => BlockId::Number(n),
|
||||
@ -433,10 +434,10 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> EthClient<C, SN, S
|
||||
|
||||
fn get_state(&self, number: BlockNumber) -> StateOrBlock {
|
||||
match number {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash).into(),
|
||||
BlockNumber::Num(num) => BlockId::Number(num).into(),
|
||||
BlockNumber::Earliest => BlockId::Earliest.into(),
|
||||
BlockNumber::Latest => BlockId::Latest.into(),
|
||||
|
||||
BlockNumber::Pending => {
|
||||
let info = self.client.chain_info();
|
||||
|
||||
@ -472,10 +473,22 @@ fn check_known<C>(client: &C, number: BlockNumber) -> Result<()> where C: BlockC
|
||||
|
||||
let id = match number {
|
||||
BlockNumber::Pending => return Ok(()),
|
||||
|
||||
BlockNumber::Num(n) => BlockId::Number(n),
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Hash { hash, require_canonical } => {
|
||||
// block check takes precedence over canon check.
|
||||
match client.block_status(BlockId::Hash(hash.clone())) {
|
||||
BlockStatus::InChain => {},
|
||||
_ => return Err(errors::unknown_block()),
|
||||
};
|
||||
|
||||
if require_canonical && !client.chain().is_canon(&hash) {
|
||||
return Err(errors::invalid_input())
|
||||
}
|
||||
|
||||
return Ok(())
|
||||
}
|
||||
};
|
||||
|
||||
match client.block_status(id) {
|
||||
@ -589,6 +602,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
|
||||
|
||||
let num = num.unwrap_or_default();
|
||||
let id = match num {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(n) => BlockId::Number(n),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
@ -762,6 +776,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
|
||||
|
||||
fn transaction_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture<Option<Transaction>> {
|
||||
let block_id = match num {
|
||||
BlockNumber::Hash { hash, .. } => PendingOrBlock::Block(BlockId::Hash(hash)),
|
||||
BlockNumber::Latest => PendingOrBlock::Block(BlockId::Latest),
|
||||
BlockNumber::Earliest => PendingOrBlock::Block(BlockId::Earliest),
|
||||
BlockNumber::Num(num) => PendingOrBlock::Block(BlockId::Number(num)),
|
||||
@ -798,6 +813,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
|
||||
|
||||
fn uncle_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture<Option<RichBlock>> {
|
||||
let id = match num {
|
||||
BlockNumber::Hash { hash, .. } => PendingUncleId { id: PendingOrBlock::Block(BlockId::Hash(hash)), position: index.value() },
|
||||
BlockNumber::Latest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Latest), position: index.value() },
|
||||
BlockNumber::Earliest => PendingUncleId { id: PendingOrBlock::Block(BlockId::Earliest), position: index.value() },
|
||||
BlockNumber::Num(num) => PendingUncleId { id: PendingOrBlock::Block(BlockId::Number(num)), position: index.value() },
|
||||
@ -914,6 +930,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
|
||||
let signed = try_bf!(fake_sign::sign_call(request));
|
||||
|
||||
let num = num.unwrap_or_default();
|
||||
try_bf!(check_known(&*self.client, num.clone()));
|
||||
|
||||
let (mut state, header) = if num == BlockNumber::Pending {
|
||||
let info = self.client.chain_info();
|
||||
@ -923,6 +940,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
|
||||
(state, header)
|
||||
} else {
|
||||
let id = match num {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(num) => BlockId::Number(num),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
@ -964,6 +982,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM, T: StateInfo + 'static> Eth for EthClient<
|
||||
(state, header)
|
||||
} else {
|
||||
let id = match num {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(num) => BlockId::Number(num),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
|
@ -350,6 +350,7 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
||||
(header.encoded(), None)
|
||||
} else {
|
||||
let id = match number {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(num) => BlockId::Number(num),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
@ -381,6 +382,7 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
||||
.collect()
|
||||
))
|
||||
},
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(num) => BlockId::Number(num),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
@ -412,6 +414,7 @@ impl<C, M, U, S> Parity for ParityClient<C, M, U> where
|
||||
(state, header)
|
||||
} else {
|
||||
let id = match num {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(num) => BlockId::Number(num),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
|
@ -95,6 +95,7 @@ impl<C, S> Traces for TracesClient<C> where
|
||||
let signed = fake_sign::sign_call(request)?;
|
||||
|
||||
let id = match block {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(num) => BlockId::Number(num),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
@ -122,6 +123,7 @@ impl<C, S> Traces for TracesClient<C> where
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
|
||||
let id = match block {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(num) => BlockId::Number(num),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
@ -144,6 +146,7 @@ impl<C, S> Traces for TracesClient<C> where
|
||||
let signed = SignedTransaction::new(tx).map_err(errors::transaction)?;
|
||||
|
||||
let id = match block {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(num) => BlockId::Number(num),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
@ -167,6 +170,7 @@ impl<C, S> Traces for TracesClient<C> where
|
||||
|
||||
fn replay_block_transactions(&self, block_number: BlockNumber, flags: TraceOptions) -> Result<Vec<TraceResultsWithTransactionHash>> {
|
||||
let id = match block_number {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(num) => BlockId::Number(num),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
|
@ -16,12 +16,20 @@
|
||||
|
||||
use std::fmt;
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
use serde::de::{Error, Visitor};
|
||||
use serde::de::{Error, Visitor, MapAccess};
|
||||
use ethcore::client::BlockId;
|
||||
use ethereum_types::H256;
|
||||
|
||||
/// Represents rpc api block number param.
|
||||
#[derive(Debug, PartialEq, Clone, Hash, Eq)]
|
||||
pub enum BlockNumber {
|
||||
/// Hash
|
||||
Hash {
|
||||
/// block hash
|
||||
hash: H256,
|
||||
/// only return blocks part of the canon chain
|
||||
require_canonical: bool,
|
||||
},
|
||||
/// Number
|
||||
Num(u64),
|
||||
/// Latest block
|
||||
@ -68,6 +76,7 @@ impl LightBlockNumber for BlockNumber {
|
||||
// Since light clients don't produce pending blocks
|
||||
// (they don't have state) we can safely fallback to `Latest`.
|
||||
match self {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(n) => BlockId::Number(n),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
@ -82,6 +91,9 @@ impl LightBlockNumber for BlockNumber {
|
||||
impl Serialize for BlockNumber {
|
||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer {
|
||||
match *self {
|
||||
BlockNumber::Hash{ hash, require_canonical } => serializer.serialize_str(
|
||||
&format!("{{ 'hash': '{}', 'requireCanonical': '{}' }}", hash, require_canonical)
|
||||
),
|
||||
BlockNumber::Num(ref x) => serializer.serialize_str(&format!("0x{:x}", x)),
|
||||
BlockNumber::Latest => serializer.serialize_str("latest"),
|
||||
BlockNumber::Earliest => serializer.serialize_str("earliest"),
|
||||
@ -99,6 +111,54 @@ impl<'a> Visitor<'a> for BlockNumberVisitor {
|
||||
write!(formatter, "a block number or 'latest', 'earliest' or 'pending'")
|
||||
}
|
||||
|
||||
fn visit_map<V>(self, mut visitor: V) -> Result<Self::Value, V::Error> where V: MapAccess<'a> {
|
||||
let (mut require_canonical, mut block_number, mut block_hash) = (false, None::<u64>, None::<H256>);
|
||||
|
||||
loop {
|
||||
let key_str: Option<String> = visitor.next_key()?;
|
||||
|
||||
match key_str {
|
||||
Some(key) => match key.as_str() {
|
||||
"blockNumber" => {
|
||||
let value: String = visitor.next_value()?;
|
||||
if value.starts_with("0x") {
|
||||
let number = u64::from_str_radix(&value[2..], 16).map_err(|e| {
|
||||
Error::custom(format!("Invalid block number: {}", e))
|
||||
})?;
|
||||
|
||||
block_number = Some(number);
|
||||
break;
|
||||
} else {
|
||||
return Err(Error::custom("Invalid block number: missing 0x prefix".to_string()))
|
||||
}
|
||||
}
|
||||
"blockHash" => {
|
||||
block_hash = Some(visitor.next_value()?);
|
||||
}
|
||||
"requireCanonical" => {
|
||||
require_canonical = visitor.next_value()?;
|
||||
}
|
||||
key => {
|
||||
return Err(Error::custom(format!("Unknown key: {}", key)))
|
||||
}
|
||||
}
|
||||
None => {
|
||||
break
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if let Some(number) = block_number {
|
||||
return Ok(BlockNumber::Num(number))
|
||||
}
|
||||
|
||||
if let Some(hash) = block_hash {
|
||||
return Ok(BlockNumber::Hash { hash, require_canonical })
|
||||
}
|
||||
|
||||
return Err(Error::custom("Invalid input"))
|
||||
}
|
||||
|
||||
fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: Error {
|
||||
match value {
|
||||
"latest" => Ok(BlockNumber::Latest),
|
||||
@ -107,7 +167,9 @@ impl<'a> Visitor<'a> for BlockNumberVisitor {
|
||||
_ if value.starts_with("0x") => u64::from_str_radix(&value[2..], 16).map(BlockNumber::Num).map_err(|e| {
|
||||
Error::custom(format!("Invalid block number: {}", e))
|
||||
}),
|
||||
_ => Err(Error::custom("Invalid block number: missing 0x prefix".to_string())),
|
||||
_ => {
|
||||
Err(Error::custom("Invalid block number: missing 0x prefix".to_string()))
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -119,10 +181,10 @@ impl<'a> Visitor<'a> for BlockNumberVisitor {
|
||||
/// Converts `BlockNumber` to `BlockId`, panics on `BlockNumber::Pending`
|
||||
pub fn block_number_to_id(number: BlockNumber) -> BlockId {
|
||||
match number {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(num) => BlockId::Number(num),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
|
||||
BlockNumber::Pending => panic!("`BlockNumber::Pending` should be handled manually")
|
||||
}
|
||||
}
|
||||
@ -131,19 +193,40 @@ pub fn block_number_to_id(number: BlockNumber) -> BlockId {
|
||||
mod tests {
|
||||
use ethcore::client::BlockId;
|
||||
use super::*;
|
||||
use std::str::FromStr;
|
||||
use serde_json;
|
||||
|
||||
#[test]
|
||||
fn block_number_deserialization() {
|
||||
let s = r#"["0xa", "latest", "earliest", "pending"]"#;
|
||||
let s = r#"[
|
||||
"0xa",
|
||||
"latest",
|
||||
"earliest",
|
||||
"pending",
|
||||
{"blockNumber": "0xa"},
|
||||
{"blockHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"},
|
||||
{"blockHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "requireCanonical": true}
|
||||
]"#;
|
||||
let deserialized: Vec<BlockNumber> = serde_json::from_str(s).unwrap();
|
||||
assert_eq!(deserialized, vec![BlockNumber::Num(10), BlockNumber::Latest, BlockNumber::Earliest, BlockNumber::Pending])
|
||||
|
||||
assert_eq!(
|
||||
deserialized,
|
||||
vec![
|
||||
BlockNumber::Num(10),
|
||||
BlockNumber::Latest,
|
||||
BlockNumber::Earliest,
|
||||
BlockNumber::Pending,
|
||||
BlockNumber::Num(10),
|
||||
BlockNumber::Hash { hash: H256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(), require_canonical: false },
|
||||
BlockNumber::Hash { hash: H256::from_str("1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347").unwrap(), require_canonical: true }
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_not_deserialize_decimal() {
|
||||
let s = r#""10""#;
|
||||
assert!(serde_json::from_str::<BlockNumber>(s).is_err());
|
||||
fn should_not_deserialize() {
|
||||
let s = r#"[{}, "10"]"#;
|
||||
assert!(serde_json::from_str::<Vec<BlockNumber>>(s).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
@ -82,6 +82,7 @@ impl Filter {
|
||||
}
|
||||
|
||||
let num_to_id = |num| match num {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(n) => BlockId::Number(n),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest | BlockNumber::Pending => BlockId::Latest,
|
||||
|
@ -43,6 +43,7 @@ pub struct TraceFilter {
|
||||
impl Into<client::TraceFilter> for TraceFilter {
|
||||
fn into(self) -> client::TraceFilter {
|
||||
let num_to_id = |num| match num {
|
||||
BlockNumber::Hash { hash, .. } => BlockId::Hash(hash),
|
||||
BlockNumber::Num(n) => BlockId::Number(n),
|
||||
BlockNumber::Earliest => BlockId::Earliest,
|
||||
BlockNumber::Latest => BlockId::Latest,
|
||||
|
Loading…
Reference in New Issue
Block a user