From 437fc26c2f183fef7db43ecef5c97b9830e4a8f5 Mon Sep 17 00:00:00 2001 From: Joonmo Yang Date: Wed, 20 Nov 2019 12:22:58 +0900 Subject: [PATCH 1/2] Refactor rpc startup code --- codechain/rpc.rs | 71 +++++++++++++++---------------------------- codechain/rpc_apis.rs | 6 ++-- codechain/run_node.rs | 64 +++++++++++++++++++++----------------- 3 files changed, 65 insertions(+), 76 deletions(-) diff --git a/codechain/rpc.rs b/codechain/rpc.rs index f642c42005..a9daa7d7a4 100644 --- a/codechain/rpc.rs +++ b/codechain/rpc.rs @@ -15,8 +15,8 @@ // along with this program. If not, see . use std::io; -use std::net::SocketAddr; +use crate::config::Config; use crate::rpc_apis; use crpc::{ jsonrpc_core, start_http, start_ipc, start_ws, HttpServer, IpcServer, MetaIoHandler, Middleware, WsError, WsServer, @@ -33,38 +33,27 @@ pub struct RpcHttpConfig { } pub fn rpc_http_start( - cfg: RpcHttpConfig, - enable_devel_api: bool, - deps: &rpc_apis::ApiDependencies, + server: MetaIoHandler<(), impl Middleware<()>>, + config: RpcHttpConfig, ) -> Result { - let url = format!("{}:{}", cfg.interface, cfg.port); + let url = format!("{}:{}", config.interface, config.port); let addr = url.parse().map_err(|_| format!("Invalid JSONRPC listen host/port given: {}", url))?; - let server = setup_http_rpc_server(&addr, cfg.cors.clone(), cfg.hosts.clone(), enable_devel_api, deps)?; - cinfo!(RPC, "RPC Listening on {}", url); - if let Some(hosts) = cfg.hosts { - cinfo!(RPC, "Allowed hosts are {:?}", hosts); - } - if let Some(cors) = cfg.cors { - cinfo!(RPC, "CORS domains are {:?}", cors); - } - Ok(server) -} - -fn setup_http_rpc_server( - url: &SocketAddr, - cors_domains: Option>, - allowed_hosts: Option>, - enable_devel_api: bool, - deps: &rpc_apis::ApiDependencies, -) -> Result { - let server = setup_rpc_server(enable_devel_api, deps); - let start_result = start_http(url, cors_domains, allowed_hosts, server); + let start_result = start_http(&addr, config.cors.clone(), config.hosts.clone(), server); match start_result { Err(ref err) if err.kind() == io::ErrorKind::AddrInUse => { Err(format!("RPC address {} is already in use, make sure that another instance of a CodeChain node is not running or change the address using the --jsonrpc-port option.", url)) }, Err(e) => Err(format!("RPC error: {:?}", e)), - Ok(server) => Ok(server), + Ok(server) => { + cinfo!(RPC, "RPC Listening on {}", url); + if let Some(hosts) = config.hosts { + cinfo!(RPC, "Allowed hosts are {:?}", hosts); + } + if let Some(cors) = config.cors { + cinfo!(RPC, "CORS domains are {:?}", cors); + } + Ok(server) + }, } } @@ -74,19 +63,17 @@ pub struct RpcIpcConfig { } pub fn rpc_ipc_start( - cfg: &RpcIpcConfig, - enable_devel_api: bool, - deps: &rpc_apis::ApiDependencies, + server: MetaIoHandler<(), impl Middleware<()>>, + config: RpcIpcConfig, ) -> Result { - let server = setup_rpc_server(enable_devel_api, deps); - let start_result = start_ipc(&cfg.socket_addr, server); + let start_result = start_ipc(&config.socket_addr, server); match start_result { Err(ref err) if err.kind() == io::ErrorKind::AddrInUse => { - Err(format!("IPC address {} is already in use, make sure that another instance of a Codechain node is not running or change the address using the --ipc-path options.", cfg.socket_addr)) + Err(format!("IPC address {} is already in use, make sure that another instance of a Codechain node is not running or change the address using the --ipc-path options.", config.socket_addr)) }, Err(e) => Err(format!("IPC error: {:?}", e)), Ok(server) => { - cinfo!(RPC, "IPC Listening on {}", cfg.socket_addr); + cinfo!(RPC, "IPC Listening on {}", config.socket_addr); Ok(server) }, } @@ -99,15 +86,10 @@ pub struct RpcWsConfig { pub max_connections: usize, } -pub fn rpc_ws_start( - cfg: &RpcWsConfig, - enable_devel_api: bool, - deps: &rpc_apis::ApiDependencies, -) -> Result { - let server = setup_rpc_server(enable_devel_api, deps); - let url = format!("{}:{}", cfg.interface, cfg.port); +pub fn rpc_ws_start(server: MetaIoHandler<(), impl Middleware<()>>, config: RpcWsConfig) -> Result { + let url = format!("{}:{}", config.interface, config.port); let addr = url.parse().map_err(|_| format!("Invalid WebSockets listen host/port given: {}", url))?; - let start_result = start_ws(&addr, server, cfg.max_connections); + let start_result = start_ws(&addr, server, config.max_connections); match start_result { Err(WsError::Io(ref err)) if err.kind() == io::ErrorKind::AddrInUse => { Err(format!("WebSockets address {} is already in use, make sure that another instance of a Codechain node is not running or change the address using the --ws-port options.", addr)) @@ -120,12 +102,9 @@ pub fn rpc_ws_start( } } -fn setup_rpc_server( - enable_devel_api: bool, - deps: &rpc_apis::ApiDependencies, -) -> MetaIoHandler<(), impl Middleware<()>> { +pub fn setup_rpc_server(config: &Config, deps: &rpc_apis::ApiDependencies) -> MetaIoHandler<(), impl Middleware<()>> { let mut handler = MetaIoHandler::with_middleware(LogMiddleware::new()); - deps.extend_api(enable_devel_api, &mut handler); + deps.extend_api(config, &mut handler); rpc_apis::setup_rpc(handler) } diff --git a/codechain/rpc_apis.rs b/codechain/rpc_apis.rs index 3f4d3c016a..a447b43b42 100644 --- a/codechain/rpc_apis.rs +++ b/codechain/rpc_apis.rs @@ -22,6 +22,8 @@ use cnetwork::{EventSender, NetworkControl}; use crpc::{MetaIoHandler, Middleware, Params, Value}; use csync::BlockSyncEvent; +use crate::config::Config; + pub struct ApiDependencies { pub client: Arc, pub miner: Arc, @@ -31,11 +33,11 @@ pub struct ApiDependencies { } impl ApiDependencies { - pub fn extend_api(&self, enable_devel_api: bool, handler: &mut MetaIoHandler<(), impl Middleware<()>>) { + pub fn extend_api(&self, config: &Config, handler: &mut MetaIoHandler<(), impl Middleware<()>>) { use crpc::v1::*; handler.extend_with(ChainClient::new(Arc::clone(&self.client)).to_delegate()); handler.extend_with(MempoolClient::new(Arc::clone(&self.client)).to_delegate()); - if enable_devel_api { + if config.rpc.enable_devel_api { handler.extend_with( DevelClient::new(Arc::clone(&self.client), Arc::clone(&self.miner), self.block_sync.clone()) .to_delegate(), diff --git a/codechain/run_node.rs b/codechain/run_node.rs index a2fe31670f..837589f070 100644 --- a/codechain/run_node.rs +++ b/codechain/run_node.rs @@ -44,7 +44,7 @@ use crate::config::{self, load_config}; use crate::constants::{DEFAULT_DB_PATH, DEFAULT_KEYS_PATH}; use crate::dummy_network_service::DummyNetworkService; use crate::json::PasswordFile; -use crate::rpc::{rpc_http_start, rpc_ipc_start, rpc_ws_start}; +use crate::rpc::{rpc_http_start, rpc_ipc_start, rpc_ws_start, setup_rpc_server}; use crate::rpc_apis::ApiDependencies; fn network_start( @@ -326,36 +326,44 @@ pub fn run_node(matches: &ArgMatches) -> Result<(), String> { } }; - let rpc_apis_deps = Arc::new(ApiDependencies { - client: client.client(), - miner: Arc::clone(&miner), - network_control: Arc::clone(&network_service), - account_provider: ap, - block_sync: maybe_sync_sender, - }); + let (rpc_server, ipc_server, ws_server) = { + let rpc_apis_deps = ApiDependencies { + client: client.client(), + miner: Arc::clone(&miner), + network_control: Arc::clone(&network_service), + account_provider: ap, + block_sync: maybe_sync_sender, + }; - let rpc_server = { - if !config.rpc.disable.unwrap() { - Some(rpc_http_start(config.rpc_http_config(), config.rpc.enable_devel_api, &*rpc_apis_deps)?) - } else { - None - } - }; - let ipc_server = { - if !config.ipc.disable.unwrap() { - Some(rpc_ipc_start(&config.rpc_ipc_config(), config.rpc.enable_devel_api, &*rpc_apis_deps)?) - } else { - None - } - }; + let rpc_server = { + if !config.rpc.disable.unwrap() { + let server = setup_rpc_server(&config, &rpc_apis_deps); + Some(rpc_http_start(server, config.rpc_http_config())?) + } else { + None + } + }; - let ws_server = { - if !config.ws.disable.unwrap() { - Some(rpc_ws_start(&config.rpc_ws_config(), config.rpc.enable_devel_api, &*rpc_apis_deps)?) - } else { - None - } + let ipc_server = { + if !config.ipc.disable.unwrap() { + let server = setup_rpc_server(&config, &rpc_apis_deps); + Some(rpc_ipc_start(server, config.rpc_ipc_config())?) + } else { + None + } + }; + + let ws_server = { + if !config.ws.disable.unwrap() { + let server = setup_rpc_server(&config, &rpc_apis_deps); + Some(rpc_ws_start(server, config.rpc_ws_config())?) + } else { + None + } + }; + + (rpc_server, ipc_server, ws_server) }; if (!config.stratum.disable.unwrap()) && (miner.engine_type() == EngineType::PoW) { From 3b4c3297ac5488e96967ea9f8e7c332413f100fd Mon Sep 17 00:00:00 2001 From: Joonmo Yang Date: Wed, 20 Nov 2019 12:40:36 +0900 Subject: [PATCH 2/2] Add RPC snapshot_getList --- codechain/rpc_apis.rs | 1 + rpc/src/v1/errors.rs | 8 ++++ rpc/src/v1/impls/mod.rs | 2 + rpc/src/v1/impls/snapshot.rs | 88 +++++++++++++++++++++++++++++++++++ rpc/src/v1/traits/mod.rs | 2 + rpc/src/v1/traits/snapshot.rs | 26 +++++++++++ 6 files changed, 127 insertions(+) create mode 100644 rpc/src/v1/impls/snapshot.rs create mode 100644 rpc/src/v1/traits/snapshot.rs diff --git a/codechain/rpc_apis.rs b/codechain/rpc_apis.rs index a447b43b42..bc701275df 100644 --- a/codechain/rpc_apis.rs +++ b/codechain/rpc_apis.rs @@ -37,6 +37,7 @@ impl ApiDependencies { use crpc::v1::*; handler.extend_with(ChainClient::new(Arc::clone(&self.client)).to_delegate()); handler.extend_with(MempoolClient::new(Arc::clone(&self.client)).to_delegate()); + handler.extend_with(SnapshotClient::new(Arc::clone(&self.client), config.snapshot.path.clone()).to_delegate()); if config.rpc.enable_devel_api { handler.extend_with( DevelClient::new(Arc::clone(&self.client), Arc::clone(&self.miner), self.block_sync.clone()) diff --git a/rpc/src/v1/errors.rs b/rpc/src/v1/errors.rs index 6af198ee29..8f15a45cb7 100644 --- a/rpc/src/v1/errors.rs +++ b/rpc/src/v1/errors.rs @@ -304,6 +304,14 @@ pub fn invalid_custom_action(err: String) -> Error { } } +pub fn io(error: std::io::Error) -> Error { + Error { + code: ErrorCode::InternalError, + message: format!("{}", error), + data: None, + } +} + /// Internal error signifying a logic error in code. /// Should not be used when function can just fail /// because of invalid parameters or incomplete node state. diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index 45e7678459..3360f5682e 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -21,6 +21,7 @@ mod engine; mod mempool; mod miner; mod net; +mod snapshot; pub use self::account::AccountClient; pub use self::chain::ChainClient; @@ -29,3 +30,4 @@ pub use self::engine::EngineClient; pub use self::mempool::MempoolClient; pub use self::miner::MinerClient; pub use self::net::NetClient; +pub use self::snapshot::SnapshotClient; diff --git a/rpc/src/v1/impls/snapshot.rs b/rpc/src/v1/impls/snapshot.rs new file mode 100644 index 0000000000..f030c9bbd3 --- /dev/null +++ b/rpc/src/v1/impls/snapshot.rs @@ -0,0 +1,88 @@ +// Copyright 2018-2019 Kodebox, Inc. +// This file is part of CodeChain. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use std::fs; +use std::str::FromStr; +use std::sync::Arc; + +use ccore::{BlockChainClient, BlockId}; +use ctypes::BlockHash; +use primitives::H256; + +use jsonrpc_core::Result; + +use super::super::errors; +use super::super::traits::Snapshot; +use super::super::types::BlockNumberAndHash; + +pub struct SnapshotClient +where + C: BlockChainClient, { + client: Arc, + snapshot_path: Option, +} + +impl SnapshotClient +where + C: BlockChainClient, +{ + pub fn new(client: Arc, snapshot_path: Option) -> Self { + SnapshotClient { + client, + snapshot_path, + } + } +} + +impl Snapshot for SnapshotClient +where + C: BlockChainClient + 'static, +{ + fn get_snapshot_list(&self) -> Result> { + if let Some(snapshot_path) = &self.snapshot_path { + let mut result = Vec::new(); + for entry in fs::read_dir(snapshot_path).map_err(errors::io)? { + let entry = entry.map_err(errors::io)?; + + // Check if the entry is a directory + let file_type = entry.file_type().map_err(errors::io)?; + if !file_type.is_dir() { + continue + } + + let path = entry.path(); + let name = match path.file_name().expect("Directories always have file name").to_str() { + Some(n) => n, + None => continue, + }; + let hash = match H256::from_str(name) { + Ok(h) => BlockHash::from(h), + Err(_) => continue, + }; + if let Some(number) = self.client.block_number(&BlockId::Hash(hash)) { + result.push(BlockNumberAndHash { + number, + hash, + }); + } + } + result.sort_unstable_by(|a, b| b.number.cmp(&a.number)); + Ok(result) + } else { + Ok(Vec::new()) + } + } +} diff --git a/rpc/src/v1/traits/mod.rs b/rpc/src/v1/traits/mod.rs index 719f186e49..7f2bd04599 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -21,6 +21,7 @@ mod engine; mod mempool; mod miner; mod net; +mod snapshot; pub use self::account::Account; pub use self::chain::Chain; @@ -29,3 +30,4 @@ pub use self::engine::Engine; pub use self::mempool::Mempool; pub use self::miner::Miner; pub use self::net::Net; +pub use self::snapshot::Snapshot; diff --git a/rpc/src/v1/traits/snapshot.rs b/rpc/src/v1/traits/snapshot.rs new file mode 100644 index 0000000000..0fd9c18366 --- /dev/null +++ b/rpc/src/v1/traits/snapshot.rs @@ -0,0 +1,26 @@ +// Copyright 2018-2019 Kodebox, Inc. +// This file is part of CodeChain. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . + +use jsonrpc_core::Result; + +use super::super::types::BlockNumberAndHash; + +#[rpc(server)] +pub trait Snapshot { + /// Gets list of block numbers and block hashes of the snapshots. + #[rpc(name = "snapshot_getList")] + fn get_snapshot_list(&self) -> Result>; +}