diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index cc7e1d4ce..6168d9432 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -463,6 +463,10 @@ usage! { ["API and Console Options – HTTP JSON-RPC"] + FLAG flag_jsonrpc_allow_missing_blocks: (bool) = false, or |c: &Config| c.rpc.as_ref()?.allow_missing_blocks.clone(), + "--jsonrpc-allow-missing-blocks", + "RPC calls will return 'null' instead of an error if ancient block sync is still in progress and the block information requested could not be found", + FLAG flag_no_jsonrpc: (bool) = false, or |c: &Config| c.rpc.as_ref()?.disable.clone(), "--no-jsonrpc", "Disable the HTTP JSON-RPC API server.", @@ -1230,6 +1234,7 @@ struct Rpc { keep_alive: Option, experimental_rpcs: Option, poll_lifetime: Option, + allow_missing_blocks: Option, } #[derive(Default, Debug, PartialEq, Deserialize)] @@ -1697,6 +1702,7 @@ mod tests { arg_jsonrpc_threads: 4, arg_jsonrpc_max_payload: None, arg_poll_lifetime: 60u32, + flag_jsonrpc_allow_missing_blocks: false, // WS flag_no_ws: false, @@ -1973,6 +1979,7 @@ mod tests { keep_alive: None, experimental_rpcs: None, poll_lifetime: None, + allow_missing_blocks: None }), ipc: Some(Ipc { disable: None, diff --git a/parity/cli/tests/config.full.toml b/parity/cli/tests/config.full.toml index 8bf97cac6..7d3184a77 100644 --- a/parity/cli/tests/config.full.toml +++ b/parity/cli/tests/config.full.toml @@ -64,6 +64,7 @@ interface = "local" cors = ["null"] apis = ["web3", "eth", "net", "parity", "traces", "rpc", "secretstore"] hosts = ["none"] +allow_missing_blocks = false [websockets] disable = false diff --git a/parity/configuration.rs b/parity/configuration.rs index 6bc90ec56..93f207c15 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -370,6 +370,7 @@ impl Configuration { miner_extras: self.miner_extras()?, stratum: self.stratum_options()?, update_policy: update_policy, + allow_missing_blocks: self.args.flag_jsonrpc_allow_missing_blocks, mode: mode, tracing: tracing, fat_db: fat_db, @@ -1390,6 +1391,7 @@ mod tests { let args = vec!["parity"]; let conf = parse(&args); let mut expected = RunCmd { + allow_missing_blocks: false, cache_config: Default::default(), dirs: Default::default(), spec: Default::default(), diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index 8acbae7c3..660ec6628 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -21,24 +21,24 @@ use std::sync::{Arc, Weak}; pub use parity_rpc::signer::SignerService; -use ethcore_service::PrivateTxService; use ethcore::account_provider::AccountProvider; use ethcore::client::Client; use ethcore::miner::Miner; use ethcore::snapshot::SnapshotService; use ethcore_logger::RotatingLogger; -use sync::{ManageNetwork, SyncProvider, LightSync}; +use ethcore_private_tx::Provider as PrivateTransactionManager; +use ethcore_service::PrivateTxService; use hash_fetch::fetch::Client as FetchClient; use jsonrpc_core::{self as core, MetaIoHandler}; use light::client::LightChainClient; -use light::{TransactionQueue as LightTransactionQueue, Cache as LightDataCache}; +use light::{Cache as LightDataCache, TransactionQueue as LightTransactionQueue}; use miner::external::ExternalMiner; -use parity_runtime::Executor; use parity_rpc::dispatch::{FullDispatcher, LightDispatcher}; use parity_rpc::informant::{ActivityNotifier, ClientNotifier}; -use parity_rpc::{Metadata, NetworkSettings, Host}; +use parity_rpc::{Host, Metadata, NetworkSettings}; +use parity_runtime::Executor; use parking_lot::{Mutex, RwLock}; -use ethcore_private_tx::Provider as PrivateTransactionManager; +use sync::{LightSync, ManageNetwork, SyncProvider}; use updater::Updater; #[derive(Debug, PartialEq, Clone, Eq, Hash)] @@ -106,7 +106,7 @@ impl FromStr for Api { "signer" => Ok(Signer), "traces" => Ok(Traces), "web3" => Ok(Web3), - api => Err(format!("Unknown api: {}", api)) + api => Err(format!("Unknown api: {}", api)), } } } @@ -149,20 +149,20 @@ impl FromStr for ApiSet { match api { "all" => { apis.extend(ApiSet::All.list_apis()); - }, + } "safe" => { // Safe APIs are those that are safe even in UnsafeContext. apis.extend(ApiSet::UnsafeContext.list_apis()); - }, + } // Remove the API api if api.starts_with("-") => { let api = api[1..].parse()?; apis.remove(&api); - }, + } api => { let api = api.parse()?; apis.insert(api); - }, + } } } @@ -205,11 +205,9 @@ pub trait Dependencies { fn activity_notifier(&self) -> Self::Notifier; /// Extend the given I/O handler with endpoints for each API. - fn extend_with_set( - &self, - handler: &mut MetaIoHandler, - apis: &HashSet, - ) where S: core::Middleware; + fn extend_with_set(&self, handler: &mut MetaIoHandler, apis: &HashSet) + where + S: core::Middleware; } /// RPC dependencies for a full node. @@ -235,6 +233,7 @@ pub struct FullDependencies { pub whisper_rpc: Option<::whisper::RpcFactory>, pub gas_price_percentile: usize, pub poll_lifetime: u32, + pub allow_missing_blocks: bool, } impl FullDependencies { @@ -243,24 +242,39 @@ impl FullDependencies { handler: &mut MetaIoHandler, apis: &HashSet, for_generic_pubsub: bool, - ) where S: core::Middleware { + ) where + S: core::Middleware, + { use parity_rpc::v1::*; macro_rules! add_signing_methods { - ($namespace:ident, $handler:expr, $deps:expr, $nonces:expr) => { - { - let deps = &$deps; - let dispatcher = FullDispatcher::new(deps.client.clone(), deps.miner.clone(), $nonces, deps.gas_price_percentile); - if deps.signer_service.is_enabled() { - $handler.extend_with($namespace::to_delegate(SigningQueueClient::new(&deps.signer_service, dispatcher, deps.executor.clone(), &deps.secret_store))) - } else { - $handler.extend_with($namespace::to_delegate(SigningUnsafeClient::new(&deps.secret_store, dispatcher))) - } + ($namespace:ident, $handler:expr, $deps:expr, $nonces:expr) => {{ + let deps = &$deps; + let dispatcher = FullDispatcher::new( + deps.client.clone(), + deps.miner.clone(), + $nonces, + deps.gas_price_percentile, + ); + if deps.signer_service.is_enabled() { + $handler.extend_with($namespace::to_delegate(SigningQueueClient::new( + &deps.signer_service, + dispatcher, + deps.executor.clone(), + &deps.secret_store, + ))) + } else { + $handler.extend_with($namespace::to_delegate(SigningUnsafeClient::new( + &deps.secret_store, + dispatcher, + ))) } - } + }}; } - let nonces = Arc::new(Mutex::new(dispatch::Reservations::new(self.executor.clone()))); + let nonces = Arc::new(Mutex::new(dispatch::Reservations::new( + self.executor.clone(), + ))); let dispatcher = FullDispatcher::new( self.client.clone(), self.miner.clone(), @@ -271,13 +285,13 @@ impl FullDependencies { match *api { Api::Debug => { handler.extend_with(DebugClient::new(self.client.clone()).to_delegate()); - }, + } Api::Web3 => { handler.extend_with(Web3Client::new().to_delegate()); - }, + } Api::Net => { handler.extend_with(NetClient::new(&self.sync).to_delegate()); - }, + } Api::Eth => { let client = EthClient::new( &self.client, @@ -291,91 +305,119 @@ impl FullDependencies { allow_pending_receipt_query: !self.geth_compatibility, send_block_number_in_get_work: !self.geth_compatibility, gas_price_percentile: self.gas_price_percentile, + allow_missing_blocks: self.allow_missing_blocks, allow_experimental_rpcs: self.experimental_rpcs, } ); handler.extend_with(client.to_delegate()); if !for_generic_pubsub { - let filter_client = EthFilterClient::new(self.client.clone(), self.miner.clone(), self.poll_lifetime); + let filter_client = EthFilterClient::new( + self.client.clone(), + self.miner.clone(), + self.poll_lifetime, + ); handler.extend_with(filter_client.to_delegate()); add_signing_methods!(EthSigning, handler, self, nonces.clone()); } - }, + } Api::EthPubSub => { if !for_generic_pubsub { - let client = EthPubSubClient::new(self.client.clone(), self.executor.clone()); + let client = + EthPubSubClient::new(self.client.clone(), self.executor.clone()); let h = client.handler(); - self.miner.add_transactions_listener(Box::new(move |hashes| if let Some(h) = h.upgrade() { - h.notify_new_transactions(hashes); - })); + self.miner + .add_transactions_listener(Box::new(move |hashes| { + if let Some(h) = h.upgrade() { + h.notify_new_transactions(hashes); + } + })); if let Some(h) = client.handler().upgrade() { self.client.add_notify(h); } handler.extend_with(client.to_delegate()); } - }, + } Api::Personal => { - handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility, self.experimental_rpcs).to_delegate()); - }, + handler.extend_with( + PersonalClient::new( + &self.secret_store, + dispatcher.clone(), + self.geth_compatibility, + self.experimental_rpcs, + ).to_delegate(), + ); + } Api::Signer => { - handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.executor.clone()).to_delegate()); - }, + handler.extend_with( + SignerClient::new( + &self.secret_store, + dispatcher.clone(), + &self.signer_service, + self.executor.clone(), + ).to_delegate(), + ); + } Api::Parity => { let signer = match self.signer_service.is_enabled() { true => Some(self.signer_service.clone()), false => None, }; - handler.extend_with(ParityClient::new( - self.client.clone(), - self.miner.clone(), - self.sync.clone(), - self.updater.clone(), - self.net_service.clone(), - self.secret_store.clone(), - self.logger.clone(), - self.settings.clone(), - signer, - self.ws_address.clone(), - self.snapshot.clone().into(), - ).to_delegate()); + handler.extend_with( + ParityClient::new( + self.client.clone(), + self.miner.clone(), + self.sync.clone(), + self.updater.clone(), + self.net_service.clone(), + self.secret_store.clone(), + self.logger.clone(), + self.settings.clone(), + signer, + self.ws_address.clone(), + self.snapshot.clone().into(), + ).to_delegate(), + ); if !for_generic_pubsub { add_signing_methods!(ParitySigning, handler, self, nonces.clone()); } - }, + } Api::ParityPubSub => { if !for_generic_pubsub { let mut rpc = MetaIoHandler::default(); - let apis = ApiSet::List(apis.clone()).retain(ApiSet::PubSub).list_apis(); + let apis = ApiSet::List(apis.clone()) + .retain(ApiSet::PubSub) + .list_apis(); self.extend_api(&mut rpc, &apis, true); - handler.extend_with(PubSubClient::new(rpc, self.executor.clone()).to_delegate()); + handler.extend_with( + PubSubClient::new(rpc, self.executor.clone()).to_delegate(), + ); } - }, + } Api::ParityAccounts => { - handler.extend_with(ParityAccountsClient::new(&self.secret_store).to_delegate()); - }, - Api::ParitySet => { - handler.extend_with(ParitySetClient::new( + handler + .extend_with(ParityAccountsClient::new(&self.secret_store).to_delegate()); + } + Api::ParitySet => handler.extend_with( + ParitySetClient::new( &self.client, &self.miner, &self.updater, &self.net_service, self.fetch.clone(), - ).to_delegate()) - }, - Api::Traces => { - handler.extend_with(TracesClient::new(&self.client).to_delegate()) - }, + ).to_delegate(), + ), + Api::Traces => handler.extend_with(TracesClient::new(&self.client).to_delegate()), Api::Rpc => { let modules = to_modules(&apis); handler.extend_with(RpcClient::new(modules).to_delegate()); - }, + } Api::SecretStore => { handler.extend_with(SecretStoreClient::new(&self.secret_store).to_delegate()); - }, + } Api::Whisper => { if let Some(ref whisper_rpc) = self.whisper_rpc { let whisper = whisper_rpc.make_handler(self.net.clone()); @@ -386,15 +428,18 @@ impl FullDependencies { if !for_generic_pubsub { if let Some(ref whisper_rpc) = self.whisper_rpc { let whisper = whisper_rpc.make_handler(self.net.clone()); - handler.extend_with( - ::parity_whisper::rpc::WhisperPubSub::to_delegate(whisper) - ); + handler.extend_with(::parity_whisper::rpc::WhisperPubSub::to_delegate( + whisper, + )); } } - }, + } Api::Private => { - handler.extend_with(PrivateClient::new(self.private_tx_service.as_ref().map(|p| p.provider())).to_delegate()); - }, + handler.extend_with( + PrivateClient::new(self.private_tx_service.as_ref().map(|p| p.provider())) + .to_delegate(), + ); + } } } } @@ -409,11 +454,10 @@ impl Dependencies for FullDependencies { } } - fn extend_with_set( - &self, - handler: &mut MetaIoHandler, - apis: &HashSet, - ) where S: core::Middleware { + fn extend_with_set(&self, handler: &mut MetaIoHandler, apis: &HashSet) + where + S: core::Middleware, + { self.extend_api(handler, apis, false) } } @@ -463,40 +507,44 @@ impl LightDependencies { self.on_demand.clone(), self.cache.clone(), self.transaction_queue.clone(), - Arc::new(Mutex::new(dispatch::Reservations::new(self.executor.clone()))), + Arc::new(Mutex::new(dispatch::Reservations::new( + self.executor.clone(), + ))), self.gas_price_percentile, ); macro_rules! add_signing_methods { - ($namespace:ident, $handler:expr, $deps:expr) => { - { - let deps = &$deps; - let dispatcher = dispatcher.clone(); - let secret_store = deps.secret_store.clone(); - if deps.signer_service.is_enabled() { - $handler.extend_with($namespace::to_delegate( - SigningQueueClient::new(&deps.signer_service, dispatcher, deps.executor.clone(), &secret_store) - )) - } else { - $handler.extend_with( - $namespace::to_delegate(SigningUnsafeClient::new(&secret_store, dispatcher)) - ) + ($namespace:ident, $handler:expr, $deps:expr) => {{ + let deps = &$deps; + let dispatcher = dispatcher.clone(); + let secret_store = deps.secret_store.clone(); + if deps.signer_service.is_enabled() { + $handler.extend_with($namespace::to_delegate(SigningQueueClient::new( + &deps.signer_service, + dispatcher, + deps.executor.clone(), + &secret_store, + ))) + } else { + $handler.extend_with($namespace::to_delegate(SigningUnsafeClient::new( + &secret_store, + dispatcher, + ))) } - } - } + }}; } 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()); - }, + } Api::Net => { handler.extend_with(light::NetClient::new(self.sync.clone()).to_delegate()); - }, + } Api::Eth => { let client = light::EthClient::new( self.sync.clone(), @@ -514,7 +562,7 @@ impl LightDependencies { handler.extend_with(EthFilter::to_delegate(client)); add_signing_methods!(EthSigning, handler, self); } - }, + } Api::EthPubSub => { let client = EthPubSubClient::light( self.client.clone(), @@ -526,77 +574,98 @@ impl LightDependencies { ); self.client.add_listener(client.handler() as Weak<_>); let h = client.handler(); - self.transaction_queue.write().add_listener(Box::new(move |transactions| { - if let Some(h) = h.upgrade() { - h.notify_new_transactions(transactions); - } - })); + self.transaction_queue + .write() + .add_listener(Box::new(move |transactions| { + if let Some(h) = h.upgrade() { + h.notify_new_transactions(transactions); + } + })); handler.extend_with(EthPubSub::to_delegate(client)); - }, + } Api::Personal => { - handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility, self.experimental_rpcs).to_delegate()); - }, + handler.extend_with( + PersonalClient::new( + &self.secret_store, + dispatcher.clone(), + self.geth_compatibility, + self.experimental_rpcs, + ).to_delegate(), + ); + } Api::Signer => { - handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.executor.clone()).to_delegate()); - }, + handler.extend_with( + SignerClient::new( + &self.secret_store, + dispatcher.clone(), + &self.signer_service, + self.executor.clone(), + ).to_delegate(), + ); + } Api::Parity => { let signer = match self.signer_service.is_enabled() { true => Some(self.signer_service.clone()), false => None, }; - handler.extend_with(light::ParityClient::new( - Arc::new(dispatcher.clone()), - self.secret_store.clone(), - self.logger.clone(), - self.settings.clone(), - signer, - self.ws_address.clone(), - self.gas_price_percentile, - ).to_delegate()); + handler.extend_with( + light::ParityClient::new( + Arc::new(dispatcher.clone()), + self.secret_store.clone(), + self.logger.clone(), + self.settings.clone(), + signer, + self.ws_address.clone(), + self.gas_price_percentile, + ).to_delegate(), + ); if !for_generic_pubsub { add_signing_methods!(ParitySigning, handler, self); } - }, + } Api::ParityPubSub => { if !for_generic_pubsub { let mut rpc = MetaIoHandler::default(); - let apis = ApiSet::List(apis.clone()).retain(ApiSet::PubSub).list_apis(); + let apis = ApiSet::List(apis.clone()) + .retain(ApiSet::PubSub) + .list_apis(); self.extend_api(&mut rpc, &apis, true); - handler.extend_with(PubSubClient::new(rpc, self.executor.clone()).to_delegate()); + handler.extend_with( + PubSubClient::new(rpc, self.executor.clone()).to_delegate(), + ); } - }, + } Api::ParityAccounts => { - handler.extend_with(ParityAccountsClient::new(&self.secret_store).to_delegate()); - }, - Api::ParitySet => { - handler.extend_with(light::ParitySetClient::new( - self.sync.clone(), - self.fetch.clone(), - ).to_delegate()) - }, - Api::Traces => { - handler.extend_with(light::TracesClient.to_delegate()) - }, + handler + .extend_with(ParityAccountsClient::new(&self.secret_store).to_delegate()); + } + Api::ParitySet => handler.extend_with( + light::ParitySetClient::new(self.sync.clone(), self.fetch.clone()) + .to_delegate(), + ), + Api::Traces => handler.extend_with(light::TracesClient.to_delegate()), Api::Rpc => { let modules = to_modules(&apis); handler.extend_with(RpcClient::new(modules).to_delegate()); - }, + } Api::SecretStore => { handler.extend_with(SecretStoreClient::new(&self.secret_store).to_delegate()); - }, + } Api::Whisper => { if let Some(ref whisper_rpc) = self.whisper_rpc { let whisper = whisper_rpc.make_handler(self.net.clone()); handler.extend_with(::parity_whisper::rpc::Whisper::to_delegate(whisper)); } - }, + } Api::WhisperPubSub => { if let Some(ref whisper_rpc) = self.whisper_rpc { let whisper = whisper_rpc.make_handler(self.net.clone()); - handler.extend_with(::parity_whisper::rpc::WhisperPubSub::to_delegate(whisper)); + handler.extend_with(::parity_whisper::rpc::WhisperPubSub::to_delegate( + whisper, + )); } - }, + } Api::Private => { if let Some(ref tx_manager) = self.private_tx_service { let private_tx_service = Some(tx_manager.clone()); @@ -611,13 +680,14 @@ impl LightDependencies { impl Dependencies for LightDependencies { type Notifier = LightClientNotifier; - fn activity_notifier(&self) -> Self::Notifier { LightClientNotifier } + fn activity_notifier(&self) -> Self::Notifier { + LightClientNotifier + } - fn extend_with_set( - &self, - handler: &mut MetaIoHandler, - apis: &HashSet, - ) where S: core::Middleware { + fn extend_with_set(&self, handler: &mut MetaIoHandler, apis: &HashSet) + where + S: core::Middleware, + { self.extend_api(handler, apis, false) } } @@ -639,7 +709,10 @@ impl ApiSet { Api::Whisper, Api::WhisperPubSub, Api::Private, - ].into_iter().cloned().collect(); + ] + .into_iter() + .cloned() + .collect(); match *self { ApiSet::List(ref apis) => apis.clone(), @@ -647,13 +720,13 @@ impl ApiSet { public_list.insert(Api::Traces); public_list.insert(Api::ParityPubSub); public_list - }, + } ApiSet::IpcContext => { public_list.insert(Api::Traces); public_list.insert(Api::ParityPubSub); public_list.insert(Api::ParityAccounts); public_list - }, + } ApiSet::SafeContext => { public_list.insert(Api::Debug); public_list.insert(Api::Traces); @@ -663,7 +736,7 @@ impl ApiSet { public_list.insert(Api::Signer); public_list.insert(Api::SecretStore); public_list - }, + } ApiSet::All => { public_list.insert(Api::Debug); public_list.insert(Api::Traces); @@ -674,14 +747,17 @@ impl ApiSet { public_list.insert(Api::Personal); public_list.insert(Api::SecretStore); public_list - }, + } ApiSet::PubSub => [ Api::Eth, Api::Parity, Api::ParityAccounts, Api::ParitySet, Api::Traces, - ].into_iter().cloned().collect() + ] + .into_iter() + .cloned() + .collect(), } } } @@ -718,15 +794,29 @@ mod test { #[test] fn test_api_set_parsing() { - assert_eq!(ApiSet::List(vec![Api::Web3, Api::Eth].into_iter().collect()), "web3,eth".parse().unwrap()); + assert_eq!( + ApiSet::List(vec![Api::Web3, Api::Eth].into_iter().collect()), + "web3,eth".parse().unwrap() + ); } #[test] fn test_api_set_unsafe_context() { let expected = vec![ // make sure this list contains only SAFE methods - Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub, Api::Private, - ].into_iter().collect(); + Api::Web3, + Api::Net, + Api::Eth, + Api::EthPubSub, + Api::Parity, + Api::ParityPubSub, + Api::Traces, + Api::Rpc, + Api::Whisper, + Api::WhisperPubSub, + Api::Private, + ].into_iter() + .collect(); assert_eq!(ApiSet::UnsafeContext.list_apis(), expected); } @@ -734,10 +824,21 @@ mod test { fn test_api_set_ipc_context() { let expected = vec![ // safe - Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub, Api::Private, + Api::Web3, + Api::Net, + Api::Eth, + Api::EthPubSub, + Api::Parity, + Api::ParityPubSub, + Api::Traces, + Api::Rpc, + Api::Whisper, + Api::WhisperPubSub, + Api::Private, // semi-safe - Api::ParityAccounts - ].into_iter().collect(); + Api::ParityAccounts, + ].into_iter() + .collect(); assert_eq!(ApiSet::IpcContext.list_apis(), expected); } @@ -745,41 +846,106 @@ mod test { fn test_api_set_safe_context() { let expected = vec![ // safe - Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::SecretStore, Api::Whisper, Api::WhisperPubSub, Api::Private, + Api::Web3, + Api::Net, + Api::Eth, + Api::EthPubSub, + Api::Parity, + Api::ParityPubSub, + Api::Traces, + Api::Rpc, + Api::SecretStore, + Api::Whisper, + Api::WhisperPubSub, + Api::Private, // semi-safe Api::ParityAccounts, // Unsafe - Api::ParitySet, Api::Signer, Api::Debug - ].into_iter().collect(); + Api::ParitySet, + Api::Signer, + Api::Debug, + ].into_iter() + .collect(); assert_eq!(ApiSet::SafeContext.list_apis(), expected); } #[test] fn test_all_apis() { - assert_eq!("all".parse::().unwrap(), ApiSet::List(vec![ - 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::Personal, - Api::Private, - Api::Debug, - ].into_iter().collect())); + assert_eq!( + "all".parse::().unwrap(), + ApiSet::List( + vec![ + 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::Personal, + Api::Private, + Api::Debug, + ].into_iter() + .collect() + ) + ); } #[test] fn test_all_without_personal_apis() { - assert_eq!("personal,all,-personal".parse::().unwrap(), ApiSet::List(vec![ - 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::Debug, - ].into_iter().collect())); + assert_eq!( + "personal,all,-personal".parse::().unwrap(), + ApiSet::List( + vec![ + 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::Debug, + ].into_iter() + .collect() + ) + ); } #[test] fn test_safe_parsing() { - assert_eq!("safe".parse::().unwrap(), ApiSet::List(vec![ - Api::Web3, Api::Net, Api::Eth, Api::EthPubSub, Api::Parity, Api::ParityPubSub, Api::Traces, Api::Rpc, Api::Whisper, Api::WhisperPubSub, Api::Private, - ].into_iter().collect())); + assert_eq!( + "safe".parse::().unwrap(), + ApiSet::List( + vec![ + Api::Web3, + Api::Net, + Api::Eth, + Api::EthPubSub, + Api::Parity, + Api::ParityPubSub, + Api::Traces, + Api::Rpc, + Api::Whisper, + Api::WhisperPubSub, + Api::Private, + ].into_iter() + .collect() + ) + ); } } diff --git a/parity/run.rs b/parity/run.rs index 3a7299df3..4d5d371b4 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -127,6 +127,7 @@ pub struct RunCmd { pub stratum: Option, pub snapshot_conf: SnapshotConfiguration, pub check_seal: bool, + pub allow_missing_blocks: bool, pub download_old_blocks: bool, pub verifier_settings: VerifierSettings, pub serve_light: bool, @@ -717,6 +718,7 @@ fn execute_impl(cmd: RunCmd, logger: Arc, on_client_rq: private_tx_service: Some(private_tx_service.clone()), gas_price_percentile: cmd.gas_price_percentile, poll_lifetime: cmd.poll_lifetime, + allow_missing_blocks: cmd.allow_missing_blocks, }); let dependencies = rpc::Dependencies { diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index b1e11f580..d4bfff742 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -18,15 +18,18 @@ use std::fmt; -use ethcore::account_provider::{SignError as AccountError}; +use ethcore::account_provider::SignError as AccountError; use ethcore::error::{Error as EthcoreError, ErrorKind, CallError}; use ethcore::client::BlockId; -use jsonrpc_core::{futures, Error, ErrorCode, Value}; +use jsonrpc_core::{futures, Result as RpcResult, Error, ErrorCode, Value}; use rlp::DecoderError; use transaction::Error as TransactionError; use ethcore_private_tx::Error as PrivateTransactionError; use vm::Error as VMError; use light::on_demand::error::{Error as OnDemandError, ErrorKind as OnDemandErrorKind}; +use ethcore::client::BlockChainClient; +use ethcore::blockchain_info::BlockChainInfo; +use v1::types::BlockNumber; mod codes { // NOTE [ToDr] Codes from [-32099, -32000] @@ -208,6 +211,60 @@ pub fn cannot_submit_work(err: EthcoreError) -> Error { } } +pub fn unavailable_block() -> Error { + Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "Ancient block sync is still in progress".into(), + data: None, + } +} + +pub fn check_block_number_existence<'a, T, C>( + client: &'a C, + num: BlockNumber, + allow_missing_blocks: bool, +) -> + impl Fn(Option) -> RpcResult> + 'a + where C: BlockChainClient, +{ + move |response| { + if response.is_none() { + if let BlockNumber::Num(block_number) = num { + // tried to fetch block number and got nothing even though the block number is + // less than the latest block number + if block_number < client.chain_info().best_block_number && !allow_missing_blocks { + return Err(unavailable_block()); + } + } + } + Ok(response) + } +} + +pub fn check_block_gap<'a, T, C>( + client: &'a C, + allow_missing_blocks: bool, +) -> impl Fn(Option) -> RpcResult> + 'a + where C: BlockChainClient, +{ + move |response| { + if response.is_none() && !allow_missing_blocks { + let BlockChainInfo { ancient_block_hash, .. } = client.chain_info(); + // block information was requested, but unfortunately we couldn't find it and there + // are gaps in the database ethcore/src/blockchain/blockchain.rs + if ancient_block_hash.is_some() { + return Err(Error { + code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), + message: "Block information is incomplete while ancient block sync is still in progress, before \ + it's finished we can't determine the existence of requested item.".into(), + data: None, + }) + } + } + Ok(response) + } +} + pub fn not_enough_data() -> Error { Error { code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST), @@ -328,22 +385,22 @@ pub fn transaction_message(error: &TransactionError) -> String { Old => "Transaction nonce is too low. Try incrementing the nonce.".into(), TooCheapToReplace => { "Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce.".into() - }, + } LimitReached => { "There are too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into() - }, + } InsufficientGas { minimal, got } => { format!("Transaction gas is too low. There is not enough gas to cover minimal cost of the transaction (minimal: {}, got: {}). Try increasing supplied gas.", minimal, got) - }, + } InsufficientGasPrice { minimal, got } => { format!("Transaction gas price is too low. It does not satisfy your node's minimal gas price (minimal: {}, got: {}). Try increasing the gas price.", minimal, got) - }, + } InsufficientBalance { balance, cost } => { format!("Insufficient funds. The account you tried to send transaction from does not have enough funds. Required {} and got: {}.", cost, balance) - }, + } GasLimitExceeded { limit, got } => { format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got) - }, + } InvalidSignature(ref sig) => format!("Invalid signature: {}", sig), InvalidChainId => "Invalid chain id.".into(), InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(), @@ -382,7 +439,6 @@ pub fn decode>(error: T) -> Error { message: "decoding error".into(), data: None, } - } } diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 821745600..3a2e58040 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -65,6 +65,9 @@ pub struct EthClientOptions { pub send_block_number_in_get_work: bool, /// Gas Price Percentile used as default gas price. pub gas_price_percentile: usize, + /// Return 'null' instead of an error if ancient block sync is still in + /// progress and the block information requested could not be found. + pub allow_missing_blocks: bool, /// Enable Experimental RPC-Calls pub allow_experimental_rpcs: bool, } @@ -86,6 +89,7 @@ impl Default for EthClientOptions { allow_pending_receipt_query: true, send_block_number_in_get_work: true, gas_price_percentile: 50, + allow_missing_blocks: false, allow_experimental_rpcs: false, } } @@ -645,30 +649,51 @@ impl Eth for EthClient< } fn block_transaction_count_by_hash(&self, hash: RpcH256) -> BoxFuture> { - Box::new(future::ok(self.client.block(BlockId::Hash(hash.into())) - .map(|block| block.transactions_count().into()))) + let trx_count = self.client.block(BlockId::Hash(hash.into())) + .map(|block| block.transactions_count().into()); + let result = Ok(trx_count) + .and_then(errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); + Box::new(future::done(result)) } fn block_transaction_count_by_number(&self, num: BlockNumber) -> BoxFuture> { - Box::new(future::ok(match num { + Box::new(future::done(match num { BlockNumber::Pending => - Some(self.miner.pending_transaction_hashes(&*self.client).len().into()), - _ => - self.client.block(block_number_to_id(num)).map(|block| block.transactions_count().into()) + Ok(Some(self.miner.pending_transaction_hashes(&*self.client).len().into())), + _ => { + let trx_count = self.client.block(block_number_to_id(num.clone())) + .map(|block| block.transactions_count().into()); + Ok(trx_count) + .and_then(errors::check_block_number_existence( + &*self.client, + num, + self.options.allow_missing_blocks + )) + } })) } fn block_uncles_count_by_hash(&self, hash: RpcH256) -> BoxFuture> { - Box::new(future::ok(self.client.block(BlockId::Hash(hash.into())) - .map(|block| block.uncles_count().into()))) + let uncle_count = self.client.block(BlockId::Hash(hash.into())) + .map(|block| block.uncles_count().into()); + let result = Ok(uncle_count) + .and_then(errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); + Box::new(future::done(result)) } fn block_uncles_count_by_number(&self, num: BlockNumber) -> BoxFuture> { - Box::new(future::ok(match num { - BlockNumber::Pending => Some(0.into()), - _ => self.client.block(block_number_to_id(num)) - .map(|block| block.uncles_count().into() - ), + Box::new(future::done(match num { + BlockNumber::Pending => Ok(Some(0.into())), + _ => { + let uncles_count = self.client.block(block_number_to_id(num.clone())) + .map(|block| block.uncles_count().into()); + Ok(uncles_count) + .and_then(errors::check_block_number_existence( + &*self.client, + num, + self.options.allow_missing_blocks + )) + } })) } @@ -687,11 +712,15 @@ impl Eth for EthClient< } fn block_by_hash(&self, hash: RpcH256, include_txs: bool) -> BoxFuture> { - Box::new(future::done(self.rich_block(BlockId::Hash(hash.into()).into(), include_txs))) + let result = self.rich_block(BlockId::Hash(hash.into()).into(), include_txs) + .and_then(errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); + Box::new(future::done(result)) } fn block_by_number(&self, num: BlockNumber, include_txs: bool) -> BoxFuture> { - Box::new(future::done(self.rich_block(num.into(), include_txs))) + let result = self.rich_block(num.clone().into(), include_txs).and_then( + errors::check_block_number_existence(&*self.client, num, self.options.allow_missing_blocks)); + Box::new(future::done(result)) } fn transaction_by_hash(&self, hash: RpcH256) -> BoxFuture> { @@ -700,13 +729,16 @@ impl Eth for EthClient< self.miner.transaction(&hash) .map(|t| Transaction::from_pending(t.pending().clone())) }); - - Box::new(future::ok(tx)) + let result = Ok(tx).and_then( + errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); + Box::new(future::done(result)) } fn transaction_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> BoxFuture> { let id = PendingTransactionId::Location(PendingOrBlock::Block(BlockId::Hash(hash.into())), index.value()); - Box::new(future::done(self.transaction(id))) + let result = self.transaction(id).and_then( + errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); + Box::new(future::done(result)) } fn transaction_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture> { @@ -718,7 +750,9 @@ impl Eth for EthClient< }; let transaction_id = PendingTransactionId::Location(block_id, index.value()); - Box::new(future::done(self.transaction(transaction_id))) + let result = self.transaction(transaction_id).and_then( + errors::check_block_number_existence(&*self.client, num, self.options.allow_missing_blocks)); + Box::new(future::done(result)) } fn transaction_receipt(&self, hash: RpcH256) -> BoxFuture> { @@ -732,14 +766,17 @@ impl Eth for EthClient< } let receipt = self.client.transaction_receipt(TransactionId::Hash(hash)); - Box::new(future::ok(receipt.map(Into::into))) + let result = Ok(receipt.map(Into::into)) + .and_then(errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); + Box::new(future::done(result)) } fn uncle_by_block_hash_and_index(&self, hash: RpcH256, index: Index) -> BoxFuture> { - Box::new(future::done(self.uncle(PendingUncleId { + let result = self.uncle(PendingUncleId { id: PendingOrBlock::Block(BlockId::Hash(hash.into())), position: index.value() - }))) + }).and_then(errors::check_block_gap(&*self.client, self.options.allow_missing_blocks)); + Box::new(future::done(result)) } fn uncle_by_block_number_and_index(&self, num: BlockNumber, index: Index) -> BoxFuture> { @@ -751,7 +788,14 @@ impl Eth for EthClient< BlockNumber::Pending => PendingUncleId { id: PendingOrBlock::Pending, position: index.value() }, }; - Box::new(future::done(self.uncle(id))) + let result = self.uncle(id) + .and_then(errors::check_block_number_existence( + &*self.client, + num, + self.options.allow_missing_blocks + )); + + Box::new(future::done(result)) } fn compilers(&self) -> Result> { diff --git a/rpc/src/v1/tests/eth.rs b/rpc/src/v1/tests/eth.rs index b0c599aa8..9a7bb19b1 100644 --- a/rpc/src/v1/tests/eth.rs +++ b/rpc/src/v1/tests/eth.rs @@ -146,6 +146,7 @@ impl EthTester { send_block_number_in_get_work: true, gas_price_percentile: 50, allow_experimental_rpcs: true, + allow_missing_blocks: false }, );