From cd429df9e6fa9218e54492f2a79665315bf547ea Mon Sep 17 00:00:00 2001 From: Joonmo Yang Date: Wed, 20 Nov 2019 12:22:58 +0900 Subject: [PATCH 1/4] Refactor rpc startup code --- foundry/rpc.rs | 71 ++++++++++++++++----------------------------- foundry/rpc_apis.rs | 6 ++-- foundry/run_node.rs | 63 ++++++++++++++++++++++------------------ 3 files changed, 64 insertions(+), 76 deletions(-) diff --git a/foundry/rpc.rs b/foundry/rpc.rs index 5a3f696ca4..b3219e25d8 100644 --- a/foundry/rpc.rs +++ b/foundry/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/foundry/rpc_apis.rs b/foundry/rpc_apis.rs index 35c8f1e72c..a4f9f4d218 100644 --- a/foundry/rpc_apis.rs +++ b/foundry/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/foundry/run_node.rs b/foundry/run_node.rs index b47f77abaa..ef420a884a 100644 --- a/foundry/run_node.rs +++ b/foundry/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( @@ -297,36 +297,43 @@ pub fn run_node(matches: &ArgMatches<'_>) -> Result<(), String> { } }; - 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, 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() { + let server = setup_rpc_server(&config, &rpc_apis_deps); + Some(rpc_http_start(server, config.rpc_http_config())?) + } else { + None + } + }; - 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() { + let server = setup_rpc_server(&config, &rpc_apis_deps); + Some(rpc_ipc_start(server, config.rpc_ipc_config())?) + } 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 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 + } + }; - 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 - } + (rpc_server, ipc_server, ws_server) }; let _snapshot_service = { From 3682cff8d5c3e04737e3b97f1461c9545d9e1dec Mon Sep 17 00:00:00 2001 From: SeongChan Lee Date: Fri, 15 Nov 2019 20:41:04 +0900 Subject: [PATCH 2/4] Add RPC devel_snapshot --- core/src/client/client.rs | 10 +++++++++- core/src/client/mod.rs | 4 ++++ core/src/consensus/mod.rs | 2 ++ core/src/consensus/solo/mod.rs | 17 ++++++++++++++++- core/src/lib.rs | 4 ++-- rpc/src/v1/impls/devel.rs | 11 ++++++++--- rpc/src/v1/traits/devel.rs | 4 ++++ spec/JSON-RPC.md | 28 ++++++++++++++++++++++++++++ 8 files changed, 73 insertions(+), 7 deletions(-) diff --git a/core/src/client/client.rs b/core/src/client/client.rs index c5dbf07bdb..8b2e7654c3 100644 --- a/core/src/client/client.rs +++ b/core/src/client/client.rs @@ -43,7 +43,7 @@ use super::{ }; use crate::block::{ClosedBlock, IsBlock, OpenBlock, SealedBlock}; use crate::blockchain::{BlockChain, BlockProvider, BodyProvider, HeaderProvider, InvoiceProvider, TransactionAddress}; -use crate::client::{ConsensusClient, TermInfo}; +use crate::client::{ConsensusClient, SnapshotClient, TermInfo}; use crate::consensus::{CodeChainEngine, EngineError}; use crate::encoded; use crate::error::{BlockImportError, Error, ImportError, SchemeError}; @@ -908,3 +908,11 @@ impl FindActionHandler for Client { self.engine.find_action_handler_for(id) } } + +impl SnapshotClient for Client { + fn notify_snapshot(&self, id: BlockId) { + if let Some(header) = self.block_header(&id) { + self.engine.send_snapshot_notify(header.hash()) + } + } +} diff --git a/core/src/client/mod.rs b/core/src/client/mod.rs index 62b6d11697..1b115489bb 100644 --- a/core/src/client/mod.rs +++ b/core/src/client/mod.rs @@ -336,3 +336,7 @@ pub trait StateInfo { /// is unknown. fn state_at(&self, id: BlockId) -> Option; } + +pub trait SnapshotClient { + fn notify_snapshot(&self, id: BlockId); +} diff --git a/core/src/consensus/mod.rs b/core/src/consensus/mod.rs index faada8d1cb..93f8ac3aa2 100644 --- a/core/src/consensus/mod.rs +++ b/core/src/consensus/mod.rs @@ -241,6 +241,8 @@ pub trait ConsensusEngine: Sync + Send { fn register_snapshot_notify_sender(&self, _sender: SnapshotNotifySender) {} + fn send_snapshot_notify(&self, _block_hash: BlockHash) {} + fn get_best_block_from_best_proposal_header(&self, header: &HeaderView<'_>) -> BlockHash { header.hash() } diff --git a/core/src/consensus/solo/mod.rs b/core/src/consensus/solo/mod.rs index f9f0ad22b1..6be3911b3d 100644 --- a/core/src/consensus/solo/mod.rs +++ b/core/src/consensus/solo/mod.rs @@ -20,13 +20,14 @@ use std::sync::{Arc, Weak}; use ckey::Address; use cstate::{ActionHandler, HitHandler, TopStateView}; -use ctypes::{CommonParams, Header}; +use ctypes::{BlockHash, CommonParams, Header}; use parking_lot::RwLock; use self::params::SoloParams; use super::stake; use super::{ConsensusEngine, Seal}; use crate::block::{ExecutedBlock, IsBlock}; +use crate::client::snapshot_notify::NotifySender; use crate::client::ConsensusClient; use crate::codechain_machine::CodeChainMachine; use crate::consensus::{EngineError, EngineType}; @@ -38,6 +39,7 @@ pub struct Solo { params: SoloParams, machine: CodeChainMachine, action_handlers: Vec>, + snapshot_notify_sender: Arc>>, } impl Solo { @@ -54,6 +56,7 @@ impl Solo { params, machine, action_handlers, + snapshot_notify_sender: Arc::new(RwLock::new(None)), } } @@ -159,6 +162,18 @@ impl ConsensusEngine for Solo { 1 } + fn register_snapshot_notify_sender(&self, sender: NotifySender) { + let mut guard = self.snapshot_notify_sender.write(); + assert!(guard.is_none(), "snapshot_notify_sender is registered twice"); + *guard = Some(sender); + } + + fn send_snapshot_notify(&self, block_hash: BlockHash) { + if let Some(sender) = self.snapshot_notify_sender.read().as_ref() { + sender.notify(block_hash) + } + } + fn action_handlers(&self) -> &[Arc] { &self.action_handlers } diff --git a/core/src/lib.rs b/core/src/lib.rs index 7f8d2e1213..de678c0c76 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -65,8 +65,8 @@ pub use crate::block::Block; pub use crate::client::snapshot_notify; pub use crate::client::{ AccountData, AssetClient, BlockChainClient, BlockChainTrait, ChainNotify, Client, ClientConfig, DatabaseClient, - EngineClient, EngineInfo, ExecuteClient, ImportBlock, MiningBlockChainClient, Shard, StateInfo, TermInfo, - TestBlockChainClient, TextClient, + EngineClient, EngineInfo, ExecuteClient, ImportBlock, MiningBlockChainClient, Shard, SnapshotClient, StateInfo, + TermInfo, TestBlockChainClient, TextClient, }; pub use crate::consensus::{EngineType, TimeGapParams}; pub use crate::db::{COL_STATE, NUM_COLUMNS}; diff --git a/rpc/src/v1/impls/devel.rs b/rpc/src/v1/impls/devel.rs index 588b4f8f83..75af7f3ffe 100644 --- a/rpc/src/v1/impls/devel.rs +++ b/rpc/src/v1/impls/devel.rs @@ -23,7 +23,7 @@ use std::vec::Vec; use ccore::{ BlockId, DatabaseClient, EngineClient, EngineInfo, MinerService, MiningBlockChainClient, SignedTransaction, - TermInfo, COL_STATE, + SnapshotClient, TermInfo, COL_STATE, }; use ccrypto::Blake; use cjson::bytes::Bytes; @@ -33,7 +33,7 @@ use csync::BlockSyncEvent; use ctypes::transaction::{ Action, AssetMintOutput, AssetOutPoint, AssetTransferInput, AssetTransferOutput, Transaction, }; -use ctypes::{Tracker, TxHash}; +use ctypes::{BlockHash, Tracker, TxHash}; use jsonrpc_core::Result; use kvdb::KeyValueDB; use primitives::{H160, H256}; @@ -70,7 +70,7 @@ where impl Devel for DevelClient where - C: DatabaseClient + EngineInfo + EngineClient + MiningBlockChainClient + TermInfo + 'static, + C: DatabaseClient + EngineInfo + EngineClient + MiningBlockChainClient + TermInfo + SnapshotClient + 'static, M: MinerService + 'static, { fn get_state_trie_keys(&self, offset: usize, limit: usize) -> Result> { @@ -108,6 +108,11 @@ where } } + fn snapshot(&self, block_hash: BlockHash) -> Result<()> { + self.client.notify_snapshot(BlockId::Hash(block_hash)); + Ok(()) + } + fn test_tps(&self, setting: TPSTestSetting) -> Result { let common_params = self.client.common_params(BlockId::Latest).unwrap(); let mint_fee = common_params.min_asset_mint_cost(); diff --git a/rpc/src/v1/traits/devel.rs b/rpc/src/v1/traits/devel.rs index e8604e910e..565a331976 100644 --- a/rpc/src/v1/traits/devel.rs +++ b/rpc/src/v1/traits/devel.rs @@ -17,6 +17,7 @@ use std::net::SocketAddr; use cjson::bytes::Bytes; +use ctypes::BlockHash; use jsonrpc_core::Result; use primitives::H256; @@ -39,6 +40,9 @@ pub trait Devel { #[rpc(name = "devel_getBlockSyncPeers")] fn get_block_sync_peers(&self) -> Result>; + #[rpc(name = "devel_snapshot")] + fn snapshot(&self, hash: BlockHash) -> Result<()>; + #[rpc(name = "devel_testTPS")] fn test_tps(&self, setting: TPSTestSetting) -> Result; } diff --git a/spec/JSON-RPC.md b/spec/JSON-RPC.md index cefee266dc..870192c22b 100644 --- a/spec/JSON-RPC.md +++ b/spec/JSON-RPC.md @@ -367,6 +367,7 @@ When `Transaction` is included in any response, there will be an additional fiel *** * [devel_getStateTrieKeys](#devel_getstatetriekeys) * [devel_getStateTrieValue](#devel_getstatetrievalue) + * [devel_snapshot](#devel_snapshot) * [devel_startSealing](#devel_startsealing) * [devel_stopSealing](#devel_stopsealing) * [devel_getBlockSyncPeers](#devel_getblocksyncpeers) @@ -2979,6 +2980,33 @@ Gets the value of the state trie with the given key. [Back to **List of methods**](#list-of-methods) +## devel_snapshot +Snapshot the state of the given block hash. + +### Params + 1. key: `H256` + +### Returns + +### Request Example +``` + curl \ + -H 'Content-Type: application/json' \ + -d '{"jsonrpc": "2.0", "method": "devel_snapshot", "params": ["0xfc196ede542b03b55aee9f106004e7e3d7ea6a9600692e964b4735a260356b50"], "id": null}' \ + localhost:8080 +``` + +### Response Example +``` +{ + "jsonrpc":"2.0", + "result":[], + "id":null +} +``` + +[Back to **List of methods**](#list-of-methods) + ## devel_startSealing Starts and enables sealing blocks by the miner. From 1365d2b03ab701157e2eace19747e4dbf0ecaa22 Mon Sep 17 00:00:00 2001 From: SeongChan Lee Date: Mon, 18 Nov 2019 14:52:26 +0900 Subject: [PATCH 3/4] Add basic e2e test for snapshot --- test/src/e2e/snapshot.test.ts | 66 +++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 test/src/e2e/snapshot.test.ts diff --git a/test/src/e2e/snapshot.test.ts b/test/src/e2e/snapshot.test.ts new file mode 100644 index 0000000000..d0b81f614d --- /dev/null +++ b/test/src/e2e/snapshot.test.ts @@ -0,0 +1,66 @@ +// 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 . + +import { expect } from "chai"; +import * as fs from "fs"; +import "mocha"; +import * as path from "path"; + +import { aliceAddress } from "../helper/constants"; +import CodeChain from "../helper/spawn"; + +const SNAPSHOT_PATH = `${__dirname}/../../../snapshot/`; + +describe("Snapshot", async function() { + let node: CodeChain; + before(async function() { + node = new CodeChain({ + argv: ["--snapshot-path", SNAPSHOT_PATH] + }); + await node.start(); + }); + + it("can make a snapshot when it is requsted with devel rpc", async function() { + const pay = await node.sendPayTx({ + quantity: 100, + recipient: aliceAddress + }); + + const blockHash = (await node.sdk.rpc.chain.getTransaction(pay.hash()))! + .blockHash!; + await node.sdk.rpc.sendRpcRequest("devel_snapshot", [ + blockHash.toJSON() + ]); + // Wait for 1 secs + await new Promise(resolve => setTimeout(resolve, 1000)); + + const stateRoot = (await node.sdk.rpc.chain.getBlock(blockHash))! + .stateRoot; + expect( + path.join(SNAPSHOT_PATH, blockHash.toString(), stateRoot.toString()) + ).to.satisfies(fs.existsSync); + }); + + afterEach(function() { + if (this.currentTest!.state === "failed") { + node.keepLogs(); + } + }); + + after(async function() { + await node.clean(); + }); +}); From c368a9ecdd15ca20093c802bcdf352f8734ba16e Mon Sep 17 00:00:00 2001 From: Joonmo Yang Date: Wed, 20 Nov 2019 12:40:36 +0900 Subject: [PATCH 4/4] Add RPC snapshot_getList --- foundry/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/foundry/rpc_apis.rs b/foundry/rpc_apis.rs index a4f9f4d218..4126906c3a 100644 --- a/foundry/rpc_apis.rs +++ b/foundry/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 6692f66746..729c10cb84 100644 --- a/rpc/src/v1/errors.rs +++ b/rpc/src/v1/errors.rs @@ -273,3 +273,11 @@ pub fn invalid_custom_action(err: String) -> Error { data: None, } } + +pub fn io(error: std::io::Error) -> Error { + Error { + code: ErrorCode::InternalError, + message: format!("{}", error), + data: None, + } +} diff --git a/rpc/src/v1/impls/mod.rs b/rpc/src/v1/impls/mod.rs index eccf961e68..77f6b1873e 100644 --- a/rpc/src/v1/impls/mod.rs +++ b/rpc/src/v1/impls/mod.rs @@ -20,6 +20,7 @@ mod devel; mod engine; mod mempool; mod net; +mod snapshot; pub use self::account::AccountClient; pub use self::chain::ChainClient; @@ -27,3 +28,4 @@ pub use self::devel::DevelClient; pub use self::engine::EngineClient; pub use self::mempool::MempoolClient; 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 bd136c0866..edabb07e57 100644 --- a/rpc/src/v1/traits/mod.rs +++ b/rpc/src/v1/traits/mod.rs @@ -20,6 +20,7 @@ mod devel; mod engine; mod mempool; mod net; +mod snapshot; pub use self::account::Account; pub use self::chain::Chain; @@ -27,3 +28,4 @@ pub use self::devel::Devel; pub use self::engine::Engine; pub use self::mempool::Mempool; 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>; +}