diff --git a/Cargo.lock b/Cargo.lock index fb79c020b84..0ef2913ff16 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1552,7 +1552,7 @@ dependencies = [ [[package]] name = "jsonrpc-core" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b" dependencies = [ "futures 0.1.25 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1564,7 +1564,7 @@ dependencies = [ [[package]] name = "jsonrpc-http-server" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b" dependencies = [ "hyper 0.12.11 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)", @@ -1577,7 +1577,7 @@ dependencies = [ [[package]] name = "jsonrpc-ipc-server" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b" dependencies = [ "jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)", "jsonrpc-server-utils 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)", @@ -1590,7 +1590,7 @@ dependencies = [ [[package]] name = "jsonrpc-macros" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b" dependencies = [ "jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)", "jsonrpc-pubsub 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)", @@ -1600,7 +1600,7 @@ dependencies = [ [[package]] name = "jsonrpc-pubsub" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b" dependencies = [ "jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)", "log 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1610,7 +1610,7 @@ dependencies = [ [[package]] name = "jsonrpc-server-utils" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b" dependencies = [ "bytes 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "globset 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1626,7 +1626,7 @@ dependencies = [ [[package]] name = "jsonrpc-tcp-server" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b" dependencies = [ "jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)", "jsonrpc-server-utils 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)", @@ -1638,7 +1638,7 @@ dependencies = [ [[package]] name = "jsonrpc-ws-server" version = "9.0.0" -source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#207a277b098943864ecaf22dbab7a5e309866d6b" +source = "git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2#f8a54f46f7f1d68b4e7899ca1e929803bf966a5b" dependencies = [ "error-chain 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 9.0.0 (git+https://github.com/paritytech/jsonrpc.git?branch=parity-2.2)", diff --git a/parity/configuration.rs b/parity/configuration.rs index b84db39227d..e12cdf6b658 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -471,6 +471,10 @@ impl Configuration { Ok(name.parse()?) } + fn is_dev_chain(&self) -> Result { + Ok(self.chain()? == SpecType::Dev) + } + fn max_peers(&self) -> u32 { self.args.arg_max_peers .or(cmp::max(self.args.arg_min_peers, Some(DEFAULT_MAX_PEERS))) @@ -528,7 +532,7 @@ impl Configuration { } fn miner_options(&self) -> Result { - let is_dev_chain = self.chain()? == SpecType::Dev; + let is_dev_chain = self.is_dev_chain()?; if is_dev_chain && self.args.flag_force_sealing && self.args.arg_reseal_min_period == 0 { return Err("Force sealing can't be used with reseal_min_period = 0".into()); } @@ -921,6 +925,7 @@ impl Configuration { Ok(NetworkSettings { name: self.args.arg_identity.clone(), chain: format!("{}", self.chain()?), + is_dev_chain: self.is_dev_chain()?, network_port: net_addresses.0.port(), rpc_enabled: http_conf.enabled, rpc_interface: http_conf.interface, @@ -1521,6 +1526,7 @@ mod tests { assert_eq!(conf.network_settings(), Ok(NetworkSettings { name: "testname".to_owned(), chain: "kovan".to_owned(), + is_dev_chain: false, network_port: 30303, rpc_enabled: true, rpc_interface: "127.0.0.1".to_owned(), diff --git a/parity/rpc_apis.rs b/parity/rpc_apis.rs index d07b25a8d28..5eafe325fd8 100644 --- a/parity/rpc_apis.rs +++ b/parity/rpc_apis.rs @@ -338,6 +338,7 @@ impl FullDependencies { self.settings.clone(), signer, self.ws_address.clone(), + self.snapshot.clone().into(), ).to_delegate()); if !for_generic_pubsub { diff --git a/rpc/src/lib.rs b/rpc/src/lib.rs index e1f5423ca1e..d76752a3220 100644 --- a/rpc/src/lib.rs +++ b/rpc/src/lib.rs @@ -148,6 +148,7 @@ pub fn start_http( .threads(threads) .cors(cors_domains.into()) .allowed_hosts(allowed_hosts.into()) + .health_api(("/api/health", "parity_nodeStatus")) .max_request_body_size(max_payload * 1024 * 1024) .start_http(addr)?) } diff --git a/rpc/src/v1/extractors.rs b/rpc/src/v1/extractors.rs index f0433db0297..a7c1c40b1ea 100644 --- a/rpc/src/v1/extractors.rs +++ b/rpc/src/v1/extractors.rs @@ -218,9 +218,10 @@ impl> WsDispatcher { impl> core::Middleware for WsDispatcher { type Future = Either< - core::FutureRpcResult, + core::FutureRpcResult, core::FutureResponse, >; + type CallFuture = core::middleware::NoopCallFuture; fn on_request(&self, request: core::Request, meta: Metadata, process: F) -> Either diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index f177e251568..f64f0efa695 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -52,6 +52,7 @@ mod codes { pub const ENCODING_ERROR: i64 = -32058; pub const FETCH_ERROR: i64 = -32060; pub const NO_LIGHT_PEERS: i64 = -32065; + pub const NO_PEERS: i64 = -32066; pub const DEPRECATED: i64 = -32070; } @@ -500,3 +501,15 @@ pub fn on_demand_others(err: &OnDemandError) -> Error { } } +pub fn status_error(has_peers: bool) -> Error { + if has_peers { + no_work() + } else { + Error { + code: ErrorCode::ServerError(codes::NO_PEERS), + message: "Node is not connected to any peers.".into(), + data: None, + } + } +} + diff --git a/rpc/src/v1/helpers/network_settings.rs b/rpc/src/v1/helpers/network_settings.rs index d011d2394fb..eddb2abe067 100644 --- a/rpc/src/v1/helpers/network_settings.rs +++ b/rpc/src/v1/helpers/network_settings.rs @@ -23,6 +23,8 @@ pub struct NetworkSettings { pub name: String, /// Name of the chain we are connected to pub chain: String, + /// Is development chain + pub is_dev_chain: bool, /// Networking port pub network_port: u16, /// Is JSON-RPC server enabled? @@ -38,6 +40,7 @@ impl Default for NetworkSettings { NetworkSettings { name: "".into(), chain: "foundation".into(), + is_dev_chain: false, network_port: 30303, rpc_enabled: true, rpc_interface: "127.0.0.1".into(), diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 90bf077a299..3d5bf78977f 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -414,4 +414,15 @@ impl Parity for ParityClient { fn submit_work_detail(&self, _nonce: H64, _pow_hash: H256, _mix_hash: H256) -> Result { Err(errors::light_unimplemented(None)) } + + fn status(&self) -> Result<()> { + let has_peers = self.settings.is_dev_chain || self.light_dispatch.sync.peer_numbers().connected > 0; + let is_importing = self.light_dispatch.sync.is_major_importing(); + + if has_peers && !is_importing { + Ok(()) + } else { + Err(errors::status_error(has_peers)) + } + } } diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 8982b73e145..fe3eb320a37 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -30,13 +30,16 @@ use ethcore::account_provider::AccountProvider; use ethcore::client::{BlockChainClient, StateClient, Call}; use ethcore::ids::BlockId; use ethcore::miner::{self, MinerService}; +use ethcore::snapshot::{SnapshotService, RestorationStatus}; use ethcore::state::StateInfo; use ethcore_logger::RotatingLogger; use updater::{Service as UpdateService}; use jsonrpc_core::{BoxFuture, Result}; use jsonrpc_core::futures::future; use jsonrpc_macros::Trailing; + use v1::helpers::{self, errors, fake_sign, ipfs, SigningQueue, SignerService, NetworkSettings}; +use v1::helpers::block_import::is_major_importing; use v1::metadata::Metadata; use v1::traits::Parity; use v1::types::{ @@ -62,6 +65,7 @@ pub struct ParityClient { settings: Arc, signer: Option>, ws_address: Option, + snapshot: Option>, } impl ParityClient where @@ -79,6 +83,7 @@ impl ParityClient where settings: Arc, signer: Option>, ws_address: Option, + snapshot: Option>, ) -> Self { ParityClient { client, @@ -91,6 +96,7 @@ impl ParityClient where settings, signer, ws_address, + snapshot, } } } @@ -481,4 +487,21 @@ impl Parity for ParityClient where fn submit_work_detail(&self, nonce: H64, pow_hash: H256, mix_hash: H256) -> Result { helpers::submit_work_detail(&self.client, &self.miner, nonce, pow_hash, mix_hash) } + + fn status(&self) -> Result<()> { + let has_peers = self.settings.is_dev_chain || self.sync.status().num_peers > 0; + let is_warping = match self.snapshot.as_ref().map(|s| s.status()) { + Some(RestorationStatus::Ongoing { .. }) => true, + _ => false, + }; + let is_not_syncing = + !is_warping && + !is_major_importing(Some(self.sync.status().state), self.client.queue_info()); + + if has_peers && is_not_syncing { + Ok(()) + } else { + Err(errors::status_error(has_peers)) + } + } } diff --git a/rpc/src/v1/informant.rs b/rpc/src/v1/informant.rs index 3dab548437f..dfa20ee4695 100644 --- a/rpc/src/v1/informant.rs +++ b/rpc/src/v1/informant.rs @@ -205,6 +205,7 @@ impl Middleware { impl core::Middleware for Middleware { type Future = core::FutureResponse; + type CallFuture = core::middleware::NoopCallFuture; fn on_request(&self, request: core::Request, meta: M, process: F) -> Either where F: FnOnce(core::Request, M) -> X, diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index 0b59e5beb92..0fa623ed437 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -60,6 +60,7 @@ impl Dependencies { settings: Arc::new(NetworkSettings { name: "mynode".to_owned(), chain: "testchain".to_owned(), + is_dev_chain: false, network_port: 30303, rpc_enabled: true, rpc_interface: "all".to_owned(), @@ -83,6 +84,7 @@ impl Dependencies { self.settings.clone(), signer, self.ws_address.clone(), + None, ) } @@ -552,3 +554,53 @@ fn rpc_parity_block_receipts() { assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } + +#[test] +fn rpc_status_ok() { + let deps = Dependencies::new(); + let io = deps.default_client(); + + let request = r#"{ + "jsonrpc": "2.0", + "method": "parity_nodeStatus", + "params": [], + "id": 1 + }"#; + let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); +} + +#[test] +fn rpc_status_error_peers() { + let deps = Dependencies::new(); + deps.sync.status.write().num_peers = 0; + let io = deps.default_client(); + + let request = r#"{ + "jsonrpc": "2.0", + "method": "parity_nodeStatus", + "params": [], + "id": 1 + }"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32066,"message":"Node is not connected to any peers."},"id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); +} + +#[test] +fn rpc_status_error_sync() { + let deps = Dependencies::new(); + deps.sync.status.write().state = ::sync::SyncState::Blocks; + let io = deps.default_client(); + + let request = r#"{ + "jsonrpc": "2.0", + "method": "parity_nodeStatus", + "params": [], + "id": 1 + }"#; + let response = r#"{"jsonrpc":"2.0","error":{"code":-32001,"message":"Still syncing."},"id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); +} diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index c54489e0796..a81e5008992 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -227,5 +227,15 @@ build_rpc_trait! { /// but returns block hash on success, and returns an explicit error message on failure). #[rpc(name = "parity_submitWorkDetail")] fn submit_work_detail(&self, H64, H256, H256) -> Result; + + /// Returns the status of the node. Used as the health endpoint. + /// + /// The RPC returns successful response if: + /// - The node have a peer (unless running a dev chain) + /// - The node is not syncing. + /// + /// Otherwise the RPC returns error. + #[rpc(name = "parity_nodeStatus")] + fn status(&self) -> Result<()>; } }