From 61bd47ccc1769eea13498fcd4e4c96a7ec9cb1da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Sat, 8 Sep 2018 04:04:28 +0200 Subject: [PATCH] Bad blocks RPC + reporting (#9433) * Bad blocks RPC. * Return bad blocks via RPC. * Fix test. * More verbose bad block message. * Expose via CLI. * Remove stray whitespace. * Remove stray newline. * Fix tests. --- ethcore/src/client/bad_blocks.rs | 81 ++++++++++++++++++++++++++ ethcore/src/client/client.rs | 58 +++++++++++++------ ethcore/src/client/mod.rs | 3 +- ethcore/src/client/test_client.rs | 16 +++++- ethcore/src/client/traits.rs | 8 ++- parity/cli/mod.rs | 2 +- parity/rpc_apis.rs | 63 ++++++++++++-------- rpc/src/v1/impls/debug.rs | 96 +++++++++++++++++++++++++++++++ rpc/src/v1/impls/mod.rs | 14 +++-- rpc/src/v1/mod.rs | 2 +- rpc/src/v1/tests/mocked/debug.rs | 37 ++++++++++++ rpc/src/v1/tests/mocked/mod.rs | 1 + rpc/src/v1/traits/debug.rs | 30 ++++++++++ rpc/src/v1/traits/mod.rs | 18 +++--- util/memory_cache/src/lib.rs | 5 ++ 15 files changed, 375 insertions(+), 59 deletions(-) create mode 100644 ethcore/src/client/bad_blocks.rs create mode 100644 rpc/src/v1/impls/debug.rs create mode 100644 rpc/src/v1/tests/mocked/debug.rs create mode 100644 rpc/src/v1/traits/debug.rs diff --git a/ethcore/src/client/bad_blocks.rs b/ethcore/src/client/bad_blocks.rs new file mode 100644 index 000000000..e00306672 --- /dev/null +++ b/ethcore/src/client/bad_blocks.rs @@ -0,0 +1,81 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity 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. + +// Parity 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 Parity. If not, see . + +//! Stores recently seen bad blocks. + +use bytes::{Bytes, ToPretty}; +use ethereum_types::H256; +use itertools::Itertools; +use memory_cache::MemoryLruCache; +use parking_lot::RwLock; +use verification::queue::kind::blocks::Unverified; + +/// Recently seen bad blocks. +pub struct BadBlocks { + last_blocks: RwLock>, +} + +impl Default for BadBlocks { + fn default() -> Self { + BadBlocks { + last_blocks: RwLock::new(MemoryLruCache::new(8 * 1024 * 1024)), + } + } +} + +impl BadBlocks { + /// Reports given RLP as invalid block. + pub fn report(&self, raw: Bytes, message: String) { + match Unverified::from_rlp(raw) { + Ok(unverified) => { + error!( + target: "client", + "\nBad block detected: {}\nRLP: {}\nHeader: {:?}\nUncles: {}\nTransactions:{}\n", + message, + unverified.bytes.to_hex(), + unverified.header, + unverified.uncles + .iter() + .enumerate() + .map(|(index, uncle)| format!("[Uncle {}] {:?}", index, uncle)) + .join("\n"), + unverified.transactions + .iter() + .enumerate() + .map(|(index, tx)| format!("[Tx {}] {:?}", index, tx)) + .join("\n"), + ); + self.last_blocks.write().insert(unverified.header.hash(), (unverified, message)); + }, + Err(err) => { + error!(target: "client", "Bad undecodable block detected: {}\n{:?}", message, err); + }, + } + } + + /// Returns a list of recently detected bad blocks with error descriptions. + pub fn bad_blocks(&self) -> Vec<(Unverified, String)> { + self.last_blocks.read() + .backstore() + .iter() + .map(|(_k, (unverified, message))| ( + Unverified::from_rlp(unverified.bytes.clone()) + .expect("Bytes coming from UnverifiedBlock so decodable; qed"), + message.clone(), + )) + .collect() + } +} diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index b67ff16fc..5588c9067 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -39,14 +39,15 @@ use client::{ RegistryInfo, ReopenBlock, PrepareOpenBlock, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock, StateInfo, StateClient, Call, AccountData, BlockChain as BlockChainTrait, BlockProducer, SealedBlockImporter, - ClientIoMessage + ClientIoMessage, }; use client::{ BlockId, TransactionId, UncleId, TraceId, ClientConfig, BlockChainClient, TraceFilter, CallAnalytics, BlockImportError, Mode, ChainNotify, ChainRoute, PruningInfo, ProvingBlockChainClient, EngineInfo, ChainMessageType, - IoClient, + IoClient, BadBlocks, }; +use client::bad_blocks; use encoded; use engines::{EthEngine, EpochTransition, ForkChoice}; use error::{ @@ -163,6 +164,9 @@ struct Importer { /// Ethereum engine to be used during import pub engine: Arc, + + /// A lru cache of recently detected bad blocks + pub bad_blocks: bad_blocks::BadBlocks, } /// Blockchain database client backed by a persistent database. Owns and manages a blockchain and a block queue. @@ -254,6 +258,7 @@ impl Importer { miner, ancient_verifier: AncientVerifier::new(engine.clone()), engine, + bad_blocks: Default::default(), }) } @@ -291,22 +296,27 @@ impl Importer { continue; } - if let Ok(closed_block) = self.check_and_lock_block(block, client) { - if self.engine.is_proposal(&header) { - self.block_queue.mark_as_good(&[hash]); - proposed_blocks.push(bytes); - } else { - imported_blocks.push(hash); + let raw = block.bytes.clone(); + match self.check_and_lock_block(block, client) { + Ok(closed_block) => { + if self.engine.is_proposal(&header) { + self.block_queue.mark_as_good(&[hash]); + proposed_blocks.push(bytes); + } else { + imported_blocks.push(hash); - let transactions_len = closed_block.transactions().len(); + let transactions_len = closed_block.transactions().len(); - let route = self.commit_block(closed_block, &header, encoded::Block::new(bytes), client); - import_results.push(route); + let route = self.commit_block(closed_block, &header, encoded::Block::new(bytes), client); + import_results.push(route); - client.report.write().accrue_block(&header, transactions_len); - } - } else { - invalid_blocks.insert(header.hash()); + client.report.write().accrue_block(&header, transactions_len); + } + }, + Err(err) => { + self.bad_blocks.report(raw, format!("{:?}", err)); + invalid_blocks.insert(header.hash()); + }, } } @@ -1390,12 +1400,22 @@ impl ImportBlock for Client { if self.chain.read().is_known(&unverified.hash()) { bail!(BlockImportErrorKind::Import(ImportErrorKind::AlreadyInChain)); } + let status = self.block_status(BlockId::Hash(unverified.parent_hash())); if status == BlockStatus::Unknown { bail!(BlockImportErrorKind::Block(BlockError::UnknownParent(unverified.parent_hash()))); } - Ok(self.importer.block_queue.import(unverified)?) + let raw = unverified.bytes.clone(); + match self.importer.block_queue.import(unverified).map_err(Into::into) { + Ok(res) => Ok(res), + // we only care about block errors (not import errors) + Err(BlockImportError(BlockImportErrorKind::Block(err), _))=> { + self.importer.bad_blocks.report(raw, format!("{:?}", err)); + bail!(BlockImportErrorKind::Block(err)) + }, + Err(e) => Err(e), + } } } @@ -1532,6 +1552,12 @@ impl EngineInfo for Client { } } +impl BadBlocks for Client { + fn bad_blocks(&self) -> Vec<(Unverified, String)> { + self.importer.bad_blocks.bad_blocks() + } +} + impl BlockChainClient for Client { fn replay(&self, id: TransactionId, analytics: CallAnalytics) -> Result { let address = self.transaction_address(id).ok_or(CallError::TransactionNotFound)?; diff --git a/ethcore/src/client/mod.rs b/ethcore/src/client/mod.rs index ffd303c12..964a56f53 100644 --- a/ethcore/src/client/mod.rs +++ b/ethcore/src/client/mod.rs @@ -17,6 +17,7 @@ //! Blockchain database client. mod ancient_import; +mod bad_blocks; mod client; mod config; #[cfg(any(test, feature = "test-helpers"))] @@ -36,7 +37,7 @@ pub use self::test_client::{TestBlockChainClient, EachBlockWith}; pub use self::chain_notify::{ChainNotify, ChainRoute, ChainRouteType, ChainMessageType}; pub use self::traits::{ Nonce, Balance, ChainInfo, BlockInfo, ReopenBlock, PrepareOpenBlock, CallContract, TransactionInfo, RegistryInfo, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, - StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter + StateOrBlock, StateClient, Call, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, BadBlocks, }; pub use state::StateInfo; pub use self::traits::{BlockChainClient, EngineClient, ProvingBlockChainClient, IoClient}; diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 642b56718..81fbc4e6f 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -39,7 +39,8 @@ use client::{ PrepareOpenBlock, BlockChainClient, BlockChainInfo, BlockStatus, BlockId, Mode, TransactionId, UncleId, TraceId, TraceFilter, LastHashes, CallAnalytics, BlockImportError, ProvingBlockChainClient, ScheduleInfo, ImportSealedBlock, BroadcastProposalBlock, ImportBlock, StateOrBlock, - Call, StateClient, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, IoClient + Call, StateClient, EngineInfo, AccountData, BlockChain, BlockProducer, SealedBlockImporter, IoClient, + BadBlocks, }; use db::{NUM_COLUMNS, COL_STATE}; use header::{Header as BlockHeader, BlockNumber}; @@ -615,6 +616,19 @@ impl EngineInfo for TestBlockChainClient { } } +impl BadBlocks for TestBlockChainClient { + fn bad_blocks(&self) -> Vec<(Unverified, String)> { + vec![ + (Unverified { + header: Default::default(), + transactions: vec![], + uncles: vec![], + bytes: vec![1, 2, 3], + }, "Invalid block".into()) + ] + } +} + impl BlockChainClient for TestBlockChainClient { fn replay(&self, _id: TransactionId, _analytics: CallAnalytics) -> Result { self.execution_result.read().clone().unwrap() diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index fb18ba284..719fce916 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -211,9 +211,15 @@ pub trait IoClient: Sync + Send { fn queue_consensus_message(&self, message: Bytes); } +/// Provides recently seen bad blocks. +pub trait BadBlocks { + /// Returns a list of blocks that were recently not imported because they were invalid. + fn bad_blocks(&self) -> Vec<(Unverified, String)>; +} + /// Blockchain database client. Owns and manages a blockchain and a block queue. pub trait BlockChainClient : Sync + Send + AccountData + BlockChain + CallContract + RegistryInfo + ImportBlock -+ IoClient { ++ IoClient + BadBlocks { /// Look up the block number for the given block ID. fn block_number(&self, id: BlockId) -> Option; diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index 594bd9a9b..899b3932d 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -477,7 +477,7 @@ usage! { ARG arg_jsonrpc_apis: (String) = "web3,eth,pubsub,net,parity,private,parity_pubsub,traces,rpc,shh,shh_pubsub", or |c: &Config| c.rpc.as_ref()?.apis.as_ref().map(|vec| vec.join(",")), "--jsonrpc-apis=[APIS]", - "Specify the APIs available through the HTTP JSON-RPC interface using a comma-delimited list of API names. Possible names are: all, safe, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, rpc, secretstore, shh, shh_pubsub. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub", + "Specify the APIs available through the HTTP JSON-RPC interface using a comma-delimited list of API names. Possible names are: all, safe, debug, web3, net, eth, pubsub, personal, signer, parity, parity_pubsub, parity_accounts, parity_set, traces, rpc, secretstore, shh, shh_pubsub. You can also disable a specific API by putting '-' in the front, example: all,-personal. 'safe' enables the following APIs: web3, net, eth, pubsub, parity, parity_pubsub, traces, rpc, shh, shh_pubsub", ARG arg_jsonrpc_hosts: (String) = "none", or |c: &Config| c.rpc.as_ref()?.hosts.as_ref().map(|vec| vec.join(",")), "--jsonrpc-hosts=[HOSTS]", diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 7ecbaee5f..2d7b2476a 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -58,18 +58,10 @@ pub enum Api { Signer, /// Parity - Custom extensions (Safe) Parity, - /// Parity PubSub - Generic Publish-Subscriber (Safety depends on other APIs exposed). - ParityPubSub, - /// Parity Accounts extensions (UNSAFE: Passwords, Side Effects (new account)) - ParityAccounts, - /// Parity - Set methods (UNSAFE: Side Effects affecting node operation) - ParitySet, /// Traces (Safe) Traces, /// Rpc (Safe) Rpc, - /// SecretStore (UNSAFE: arbitrary hash signing) - SecretStore, /// Private transaction manager (Safe) Private, /// Whisper (Safe) @@ -78,6 +70,17 @@ pub enum Api { Whisper, /// Whisper Pub-Sub (Safe but same concerns as above). WhisperPubSub, + /// Parity PubSub - Generic Publish-Subscriber (Safety depends on other APIs exposed). + ParityPubSub, + /// Parity Accounts extensions (UNSAFE: Passwords, Side Effects (new account)) + ParityAccounts, + /// Parity - Set methods (UNSAFE: Side Effects affecting node operation) + ParitySet, + /// SecretStore (UNSAFE: arbitrary hash signing) + SecretStore, + /// Geth-compatible (best-effort) debug API (Potentially UNSAFE) + /// NOTE We don't aim to support all methods, only the ones that are useful. + Debug, } impl FromStr for Api { @@ -87,22 +90,23 @@ impl FromStr for Api { use self::Api::*; match s { - "web3" => Ok(Web3), - "net" => Ok(Net), + "debug" => Ok(Debug), "eth" => Ok(Eth), - "pubsub" => Ok(EthPubSub), - "personal" => Ok(Personal), - "signer" => Ok(Signer), + "net" => Ok(Net), "parity" => Ok(Parity), - "parity_pubsub" => Ok(ParityPubSub), "parity_accounts" => Ok(ParityAccounts), + "parity_pubsub" => Ok(ParityPubSub), "parity_set" => Ok(ParitySet), - "traces" => Ok(Traces), + "personal" => Ok(Personal), + "private" => Ok(Private), + "pubsub" => Ok(EthPubSub), "rpc" => Ok(Rpc), "secretstore" => Ok(SecretStore), - "private" => Ok(Private), "shh" => Ok(Whisper), "shh_pubsub" => Ok(WhisperPubSub), + "signer" => Ok(Signer), + "traces" => Ok(Traces), + "web3" => Ok(Web3), api => Err(format!("Unknown api: {}", api)) } } @@ -171,20 +175,21 @@ fn to_modules(apis: &HashSet) -> BTreeMap { let mut modules = BTreeMap::new(); for api in apis { let (name, version) = match *api { - Api::Web3 => ("web3", "1.0"), - Api::Net => ("net", "1.0"), + Api::Debug => ("debug", "1.0"), Api::Eth => ("eth", "1.0"), Api::EthPubSub => ("pubsub", "1.0"), - Api::Personal => ("personal", "1.0"), - Api::Signer => ("signer", "1.0"), + Api::Net => ("net", "1.0"), Api::Parity => ("parity", "1.0"), Api::ParityAccounts => ("parity_accounts", "1.0"), Api::ParityPubSub => ("parity_pubsub", "1.0"), Api::ParitySet => ("parity_set", "1.0"), - Api::Traces => ("traces", "1.0"), + Api::Personal => ("personal", "1.0"), + Api::Private => ("private", "1.0"), Api::Rpc => ("rpc", "1.0"), Api::SecretStore => ("secretstore", "1.0"), - Api::Private => ("private", "1.0"), + Api::Signer => ("signer", "1.0"), + Api::Traces => ("traces", "1.0"), + Api::Web3 => ("web3", "1.0"), Api::Whisper => ("shh", "1.0"), Api::WhisperPubSub => ("shh_pubsub", "1.0"), }; @@ -265,6 +270,9 @@ impl FullDependencies { ); for api in apis { match *api { + Api::Debug => { + handler.extend_with(DebugClient::new(self.client.clone()).to_delegate()); + }, Api::Web3 => { handler.extend_with(Web3Client::new().to_delegate()); }, @@ -481,6 +489,9 @@ impl LightDependencies { for api in apis { match *api { + Api::Debug => { + warn!(target: "rpc", "Debug API is not available in light client mode.") + }, Api::Web3 => { handler.extend_with(Web3Client::new().to_delegate()); }, @@ -647,6 +658,7 @@ impl ApiSet { public_list }, ApiSet::SafeContext => { + public_list.insert(Api::Debug); public_list.insert(Api::Traces); public_list.insert(Api::ParityPubSub); public_list.insert(Api::ParityAccounts); @@ -656,6 +668,7 @@ impl ApiSet { public_list }, ApiSet::All => { + public_list.insert(Api::Debug); public_list.insert(Api::Traces); public_list.insert(Api::ParityPubSub); public_list.insert(Api::ParityAccounts); @@ -682,6 +695,7 @@ mod test { #[test] fn test_api_parsing() { + assert_eq!(Api::Debug, "debug".parse().unwrap()); assert_eq!(Api::Web3, "web3".parse().unwrap()); assert_eq!(Api::Net, "net".parse().unwrap()); assert_eq!(Api::Eth, "eth".parse().unwrap()); @@ -738,7 +752,7 @@ mod test { // semi-safe Api::ParityAccounts, // Unsafe - Api::ParitySet, Api::Signer, + Api::ParitySet, Api::Signer, Api::Debug ].into_iter().collect(); assert_eq!(ApiSet::SafeContext.list_apis(), expected); } @@ -751,6 +765,7 @@ mod test { Api::ParitySet, Api::Signer, Api::Personal, Api::Private, + Api::Debug, ].into_iter().collect())); } @@ -760,7 +775,7 @@ mod test { Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub, Api::ParityAccounts, Api::ParitySet, Api::Signer, - Api::Private + Api::Private, Api::Debug, ].into_iter().collect())); } diff --git a/rpc/src/v1/impls/debug.rs b/rpc/src/v1/impls/debug.rs new file mode 100644 index 000000000..60d9dcdc8 --- /dev/null +++ b/rpc/src/v1/impls/debug.rs @@ -0,0 +1,96 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity 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. + +// Parity 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 Parity. If not, see . + +//! Debug APIs RPC implementation + +use std::sync::Arc; + +use ethcore::client::BlockChainClient; +use transaction::LocalizedTransaction; + +use jsonrpc_core::Result; +use v1::traits::Debug; +use v1::types::{Block, Bytes, RichBlock, BlockTransactions, Transaction}; + +/// Debug rpc implementation. +pub struct DebugClient { + client: Arc, +} + +impl DebugClient { + /// Creates new debug client. + pub fn new(client: Arc) -> Self { + Self { + client, + } + } +} + +impl Debug for DebugClient { + fn bad_blocks(&self) -> Result> { + fn cast>(t: &T) -> O { + (*t).into() + } + + Ok(self.client.bad_blocks().into_iter().map(|(block, reason)| { + let number = block.header.number(); + let hash = block.header.hash(); + RichBlock { + inner: Block { + hash: Some(hash.into()), + size: Some(block.bytes.len().into()), + parent_hash: cast(block.header.parent_hash()), + uncles_hash: cast(block.header.uncles_hash()), + author: cast(block.header.author()), + miner: cast(block.header.author()), + state_root: cast(block.header.state_root()), + receipts_root: cast(block.header.receipts_root()), + number: Some(number.into()), + gas_used: cast(block.header.gas_used()), + gas_limit: cast(block.header.gas_limit()), + logs_bloom: Some(cast(block.header.log_bloom())), + timestamp: block.header.timestamp().into(), + difficulty: cast(block.header.difficulty()), + total_difficulty: None, + seal_fields: block.header.seal().into_iter().cloned().map(Into::into).collect(), + uncles: block.uncles.into_iter().map(|u| u.hash().into()).collect(), + transactions: BlockTransactions::Full(block.transactions + .into_iter() + .enumerate() + .map(|(transaction_index, signed)| Transaction::from_localized(LocalizedTransaction { + block_number: number.into(), + block_hash: hash.into(), + transaction_index, + signed, + cached_sender: None, + })).collect() + ), + transactions_root: cast(block.header.transactions_root()), + extra_data: block.header.extra_data().clone().into(), + }, + extra_info: vec![ + ("reason".to_owned(), reason), + ("rlp".to_owned(), serialize(&Bytes(block.bytes))), + ("hash".to_owned(), format!("{:#x}", hash)), + ].into_iter().collect(), + } + }).collect()) + } +} + +fn serialize(t: &T) -> String { + ::serde_json::to_string(t).expect("RPC types serialization is non-fallible.") +} diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 134914720..44bc62883 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -16,6 +16,7 @@ //! Ethereum rpc interface implementation. +mod debug; mod eth; mod eth_filter; mod eth_pubsub; @@ -24,18 +25,19 @@ mod parity; mod parity_accounts; mod parity_set; mod personal; +mod private; mod pubsub; +mod rpc; +mod secretstore; mod signer; mod signing; mod signing_unsafe; -mod rpc; -mod secretstore; mod traces; mod web3; -mod private; pub mod light; +pub use self::debug::DebugClient; pub use self::eth::{EthClient, EthClientOptions}; pub use self::eth_filter::EthFilterClient; pub use self::eth_pubsub::EthPubSubClient; @@ -44,12 +46,12 @@ pub use self::parity::ParityClient; pub use self::parity_accounts::ParityAccountsClient; pub use self::parity_set::ParitySetClient; pub use self::personal::PersonalClient; +pub use self::private::PrivateClient; pub use self::pubsub::PubSubClient; +pub use self::rpc::RpcClient; +pub use self::secretstore::SecretStoreClient; pub use self::signer::SignerClient; pub use self::signing::SigningQueueClient; pub use self::signing_unsafe::SigningUnsafeClient; pub use self::traces::TracesClient; pub use self::web3::Web3Client; -pub use self::rpc::RpcClient; -pub use self::secretstore::SecretStoreClient; -pub use self::private::PrivateClient; diff --git a/rpc/src/v1/mod.rs b/rpc/src/v1/mod.rs index a0b74aa5a..c9700ec4c 100644 --- a/rpc/src/v1/mod.rs +++ b/rpc/src/v1/mod.rs @@ -41,7 +41,7 @@ pub mod informant; pub mod metadata; pub mod traits; -pub use self::traits::{Web3, Eth, EthFilter, EthPubSub, EthSigning, Net, Parity, ParityAccounts, ParitySet, ParitySigning, PubSub, Signer, Personal, Traces, Rpc, SecretStore, Private}; +pub use self::traits::{Debug, Eth, EthFilter, EthPubSub, EthSigning, Net, Parity, ParityAccounts, ParitySet, ParitySigning, Personal, PubSub, Private, Rpc, SecretStore, Signer, Traces, Web3}; pub use self::impls::*; pub use self::helpers::{NetworkSettings, block_import, dispatch}; pub use self::metadata::Metadata; diff --git a/rpc/src/v1/tests/mocked/debug.rs b/rpc/src/v1/tests/mocked/debug.rs new file mode 100644 index 000000000..987cb5e15 --- /dev/null +++ b/rpc/src/v1/tests/mocked/debug.rs @@ -0,0 +1,37 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity 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. + +// Parity 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 Parity. If not, see . + +use std::sync::Arc; + +use ethcore::client::TestBlockChainClient; + +use jsonrpc_core::IoHandler; +use v1::{Debug, DebugClient}; + +fn io() -> IoHandler { + let client = Arc::new(TestBlockChainClient::new()); + + let mut io = IoHandler::new(); + io.extend_with(DebugClient::new(client).to_delegate()); + io +} + +#[test] +fn rpc_debug_get_bad_blocks() { + let request = r#"{"jsonrpc": "2.0", "method": "debug_getBadBlocks", "params": [], "id": 1}"#; + let response = "{\"jsonrpc\":\"2.0\",\"result\":[{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0x0\",\"extraData\":\"0x\",\"gasLimit\":\"0x0\",\"gasUsed\":\"0x0\",\"hash\":\"27bfb37e507ce90da141307204b1c6ba24194380613590ac50ca4b1d7198ff65\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"number\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"reason\":\"Invalid block\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"rlp\":\"\\\"0x010203\\\"\",\"sealFields\":[],\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x3\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"timestamp\":\"0x0\",\"totalDifficulty\":null,\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]}],\"id\":1}"; + assert_eq!(io().handle_request_sync(request), Some(response.to_owned())); +} diff --git a/rpc/src/v1/tests/mocked/mod.rs b/rpc/src/v1/tests/mocked/mod.rs index a3de3b3b7..9eabe2aa2 100644 --- a/rpc/src/v1/tests/mocked/mod.rs +++ b/rpc/src/v1/tests/mocked/mod.rs @@ -17,6 +17,7 @@ //! RPC mocked tests. Most of these test that the RPC server is serializing and forwarding //! method calls properly. +mod debug; mod eth; mod eth_pubsub; mod manage_network; diff --git a/rpc/src/v1/traits/debug.rs b/rpc/src/v1/traits/debug.rs new file mode 100644 index 000000000..0fedf0a5a --- /dev/null +++ b/rpc/src/v1/traits/debug.rs @@ -0,0 +1,30 @@ +// Copyright 2015-2018 Parity Technologies (UK) Ltd. +// This file is part of Parity. + +// Parity 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. + +// Parity 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 Parity. If not, see . + +//! Debug RPC interface. + +use jsonrpc_core::Result; + +use v1::types::RichBlock; + +build_rpc_trait! { + /// Debug RPC interface. + pub trait Debug { + /// Returns recently seen bad blocks. + #[rpc(name = "debug_getBadBlocks")] + fn bad_blocks(&self) -> Result>; + } +} diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index 62edac8ed..7c4f617e1 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -16,7 +16,7 @@ //! Ethereum rpc interfaces. -pub mod web3; +pub mod debug; pub mod eth; pub mod eth_pubsub; pub mod eth_signing; @@ -26,14 +26,15 @@ pub mod parity_accounts; pub mod parity_set; pub mod parity_signing; pub mod personal; +pub mod private; pub mod pubsub; -pub mod signer; -pub mod traces; pub mod rpc; pub mod secretstore; -pub mod private; +pub mod signer; +pub mod traces; +pub mod web3; -pub use self::web3::Web3; +pub use self::debug::Debug; pub use self::eth::{Eth, EthFilter}; pub use self::eth_pubsub::EthPubSub; pub use self::eth_signing::EthSigning; @@ -43,9 +44,10 @@ pub use self::parity_accounts::ParityAccounts; pub use self::parity_set::ParitySet; pub use self::parity_signing::ParitySigning; pub use self::personal::Personal; +pub use self::private::Private; pub use self::pubsub::PubSub; -pub use self::signer::Signer; -pub use self::traces::Traces; pub use self::rpc::Rpc; pub use self::secretstore::SecretStore; -pub use self::private::Private; +pub use self::signer::Signer; +pub use self::traces::Traces; +pub use self::web3::Web3; diff --git a/util/memory_cache/src/lib.rs b/util/memory_cache/src/lib.rs index ff996142b..ab3feafbf 100644 --- a/util/memory_cache/src/lib.rs +++ b/util/memory_cache/src/lib.rs @@ -86,6 +86,11 @@ impl MemoryLruCache { pub fn current_size(&self) -> usize { self.cur_size } + + /// Get backing LRU cache instance (read only) + pub fn backstore(&self) -> &LruCache { + &self.inner + } } #[cfg(test)]