Merge pull request #5002 from paritytech/lightcli

Quick'n'dirty CLI for the light client
This commit is contained in:
Robert Habermeier
2017-04-05 13:50:17 +02:00
committed by GitHub
42 changed files with 1518 additions and 382 deletions

View File

@@ -39,7 +39,7 @@ warp = true
allow_ips = "all"
snapshot_peers = 0
max_pending_peers = 64
serve_light = true
no_serve_light = false
reserved_only = false
reserved_peers = "./path_to_file"

View File

@@ -94,6 +94,7 @@ usage! {
flag_chain: String = "foundation", or |c: &Config| otry!(c.parity).chain.clone(),
flag_keys_path: String = "$BASE/keys", or |c: &Config| otry!(c.parity).keys_path.clone(),
flag_identity: String = "", or |c: &Config| otry!(c.parity).identity.clone(),
flag_light: bool = false, or |c: &Config| otry!(c.parity).light,
// -- Account Options
flag_unlock: Option<String> = None,
@@ -149,6 +150,8 @@ usage! {
flag_reserved_only: bool = false,
or |c: &Config| otry!(c.network).reserved_only.clone(),
flag_no_ancient_blocks: bool = false, or |_| None,
flag_no_serve_light: bool = false,
or |c: &Config| otry!(c.network).no_serve_light.clone(),
// -- API and Console Options
// RPC
@@ -379,6 +382,7 @@ struct Operating {
db_path: Option<String>,
keys_path: Option<String>,
identity: Option<String>,
light: Option<bool>,
}
#[derive(Default, Debug, PartialEq, RustcDecodable)]
@@ -414,6 +418,7 @@ struct Network {
node_key: Option<String>,
reserved_peers: Option<String>,
reserved_only: Option<bool>,
no_serve_light: Option<bool>,
}
#[derive(Default, Debug, PartialEq, RustcDecodable)]
@@ -639,6 +644,7 @@ mod tests {
flag_db_path: Some("$HOME/.parity/chains".into()),
flag_keys_path: "$HOME/.parity/keys".into(),
flag_identity: "".into(),
flag_light: false,
// -- Account Options
flag_unlock: Some("0xdeadbeefcafe0000000000000000000000000000".into()),
@@ -669,6 +675,7 @@ mod tests {
flag_reserved_peers: Some("./path_to_file".into()),
flag_reserved_only: false,
flag_no_ancient_blocks: false,
flag_no_serve_light: false,
// -- API and Console Options
// RPC
@@ -844,6 +851,7 @@ mod tests {
db_path: None,
keys_path: None,
identity: None,
light: None,
}),
account: Some(Account {
unlock: Some(vec!["0x1".into(), "0x2".into(), "0x3".into()]),
@@ -873,6 +881,7 @@ mod tests {
node_key: None,
reserved_peers: Some("./path/to/reserved_peers".into()),
reserved_only: Some(true),
no_serve_light: None,
}),
rpc: Some(Rpc {
disable: Some(true),

View File

@@ -70,6 +70,11 @@ Operating Options:
--keys-path PATH Specify the path for JSON key files to be found
(default: {flag_keys_path}).
--identity NAME Specify your node's name. (default: {flag_identity})
--light Experimental: run in light client mode. Light clients
synchronize a bare minimum of data and fetch necessary
data on-demand from the network. Much lower in storage,
potentially higher in bandwidth. Has no effect with
subcommands (default: {flag_light}).
Account Options:
--unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution.
@@ -129,6 +134,7 @@ Networking Options:
--max-pending-peers NUM Allow up to NUM pending connections. (default: {flag_max_pending_peers})
--no-ancient-blocks Disable downloading old blocks after snapshot restoration
or warp sync. (default: {flag_no_ancient_blocks})
--no-serve-light Disable serving of light peers. (default: {flag_no_serve_light})
API and Console Options:
--no-jsonrpc Disable the JSON-RPC API server. (default: {flag_no_jsonrpc})

View File

@@ -382,6 +382,8 @@ impl Configuration {
check_seal: !self.args.flag_no_seal_check,
download_old_blocks: !self.args.flag_no_ancient_blocks,
verifier_settings: verifier_settings,
serve_light: !self.args.flag_no_serve_light,
light: self.args.flag_light,
};
Cmd::Run(run_cmd)
};
@@ -1201,6 +1203,8 @@ mod tests {
check_seal: true,
download_old_blocks: true,
verifier_settings: Default::default(),
serve_light: true,
light: false,
};
expected.secretstore_conf.enabled = cfg!(feature = "secretstore");
assert_eq!(conf.into_command().unwrap().cmd, Cmd::Run(expected));

View File

@@ -18,12 +18,14 @@ use std::path::PathBuf;
use std::sync::Arc;
use dir::default_data_path;
use ethcore::client::Client;
use ethsync::SyncProvider;
use ethcore::client::{Client, BlockChainClient, BlockId};
use ethcore::transaction::{Transaction, Action};
use hash_fetch::fetch::Client as FetchClient;
use hash_fetch::urlhint::ContractClient;
use helpers::replace_home;
use rpc_apis::SignerService;
use parity_reactor;
use util::{Bytes, Address, U256};
#[derive(Debug, PartialEq, Clone)]
pub struct Configuration {
@@ -43,15 +45,53 @@ impl Default for Configuration {
}
}
pub struct Dependencies {
/// Registrar implementation of the full client.
pub struct FullRegistrar {
/// Handle to the full client.
pub client: Arc<Client>,
pub sync: Arc<SyncProvider>,
}
impl ContractClient for FullRegistrar {
fn registrar(&self) -> Result<Address, String> {
self.client.additional_params().get("registrar")
.ok_or_else(|| "Registrar not defined.".into())
.and_then(|registrar| {
registrar.parse().map_err(|e| format!("Invalid registrar address: {:?}", e))
})
}
fn call(&self, address: Address, data: Bytes) -> Result<Bytes, String> {
let from = Address::default();
let transaction = Transaction {
nonce: self.client.latest_nonce(&from),
action: Action::Call(address),
gas: U256::from(50_000_000),
gas_price: U256::default(),
value: U256::default(),
data: data,
}.fake_sign(from);
self.client.call(&transaction, BlockId::Latest, Default::default())
.map_err(|e| format!("{:?}", e))
.map(|executed| {
executed.output
})
}
}
// TODO: light client implementation forwarding to OnDemand and waiting for future
// to resolve.
pub struct Dependencies {
pub sync_status: Arc<::parity_dapps::SyncStatus>,
pub contract_client: Arc<ContractClient>,
pub remote: parity_reactor::TokioRemote,
pub fetch: FetchClient,
pub signer: Arc<SignerService>,
}
pub fn new(configuration: Configuration, deps: Dependencies) -> Result<Option<Middleware>, String> {
pub fn new(configuration: Configuration, deps: Dependencies)
-> Result<Option<Middleware>, String>
{
if !configuration.enabled {
return Ok(None);
}
@@ -96,13 +136,8 @@ mod server {
use super::Dependencies;
use std::path::PathBuf;
use std::sync::Arc;
use util::{Bytes, Address, U256};
use ethcore::transaction::{Transaction, Action};
use ethcore::client::{Client, BlockChainClient, BlockId};
use ethcore_rpc::is_major_importing;
use hash_fetch::fetch::Client as FetchClient;
use hash_fetch::urlhint::ContractClient;
use parity_dapps;
use parity_reactor;
@@ -113,12 +148,8 @@ mod server {
dapps_path: PathBuf,
extra_dapps: Vec<PathBuf>,
) -> Result<Middleware, String> {
let sync = deps.sync;
let signer = deps.signer.clone();
let client = deps.client;
let parity_remote = parity_reactor::Remote::new(deps.remote.clone());
let registrar = Arc::new(Registrar { client: client.clone() });
let sync_status = Arc::new(move || is_major_importing(Some(sync.status().state), client.queue_info()));
let web_proxy_tokens = Arc::new(move |token| signer.is_valid_web_proxy_access_token(&token));
Ok(parity_dapps::Middleware::new(
@@ -126,42 +157,10 @@ mod server {
deps.signer.address(),
dapps_path,
extra_dapps,
registrar,
sync_status,
deps.contract_client,
deps.sync_status,
web_proxy_tokens,
deps.fetch.clone(),
))
}
struct Registrar {
client: Arc<Client>,
}
impl ContractClient for Registrar {
fn registrar(&self) -> Result<Address, String> {
self.client.additional_params().get("registrar")
.ok_or_else(|| "Registrar not defined.".into())
.and_then(|registrar| {
registrar.parse().map_err(|e| format!("Invalid registrar address: {:?}", e))
})
}
fn call(&self, address: Address, data: Bytes) -> Result<Bytes, String> {
let from = Address::default();
let transaction = Transaction {
nonce: self.client.latest_nonce(&from),
action: Action::Call(address),
gas: U256::from(50_000_000),
gas_price: U256::default(),
value: U256::default(),
data: data,
}.fake_sign(from);
self.client.call(&transaction, BlockId::Latest, Default::default())
.map_err(|e| format!("{:?}", e))
.map(|executed| {
executed.output
})
}
}
}

View File

@@ -0,0 +1,21 @@
// Copyright 2015-2017 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 <http://www.gnu.org/licenses/>.
//! Utilities and helpers for the light client.
mod queue_cull;
pub use self::queue_cull::QueueCull;

View File

@@ -0,0 +1,99 @@
// Copyright 2015-2017 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 <http://www.gnu.org/licenses/>.
//! Service for culling the light client's transaction queue.
use std::sync::Arc;
use std::time::Duration;
use ethcore::service::ClientIoMessage;
use ethsync::LightSync;
use io::{IoContext, IoHandler, TimerToken};
use light::client::Client;
use light::on_demand::{request, OnDemand};
use light::TransactionQueue;
use futures::{future, stream, Future, Stream};
use parity_reactor::Remote;
use util::RwLock;
// Attepmt to cull once every 10 minutes.
const TOKEN: TimerToken = 1;
const TIMEOUT_MS: u64 = 1000 * 60 * 10;
// But make each attempt last only 9 minutes
const PURGE_TIMEOUT_MS: u64 = 1000 * 60 * 9;
/// Periodically culls the transaction queue of mined transactions.
pub struct QueueCull {
/// A handle to the client, for getting the latest block header.
pub client: Arc<Client>,
/// A handle to the sync service.
pub sync: Arc<LightSync>,
/// The on-demand request service.
pub on_demand: Arc<OnDemand>,
/// The transaction queue.
pub txq: Arc<RwLock<TransactionQueue>>,
/// Event loop remote.
pub remote: Remote,
}
impl IoHandler<ClientIoMessage> for QueueCull {
fn initialize(&self, io: &IoContext<ClientIoMessage>) {
io.register_timer(TOKEN, TIMEOUT_MS).expect("Error registering timer");
}
fn timeout(&self, _io: &IoContext<ClientIoMessage>, timer: TimerToken) {
if timer != TOKEN { return }
let senders = self.txq.read().queued_senders();
if senders.is_empty() { return }
let (sync, on_demand, txq) = (self.sync.clone(), self.on_demand.clone(), self.txq.clone());
let best_header = self.client.best_block_header();
let start_nonce = self.client.engine().account_start_nonce();
info!(target: "cull", "Attempting to cull queued transactions from {} senders.", senders.len());
self.remote.spawn_with_timeout(move || {
let maybe_fetching = sync.with_context(move |ctx| {
// fetch the nonce of each sender in the queue.
let nonce_futures = senders.iter()
.map(|&address| request::Account { header: best_header.clone(), address: address })
.map(|request| on_demand.account(ctx, request))
.map(move |fut| fut.map(move |x| x.map(|acc| acc.nonce).unwrap_or(start_nonce)))
.zip(senders.iter())
.map(|(fut, &addr)| fut.map(move |nonce| (addr, nonce)));
// as they come in, update each sender to the new nonce.
stream::futures_unordered(nonce_futures)
.fold(txq, |txq, (address, nonce)| {
txq.write().cull(address, nonce);
future::ok(txq)
})
.map(|_| ()) // finally, discard the txq handle and log errors.
.map_err(|_| debug!(target: "cull", "OnDemand prematurely closed channel."))
});
match maybe_fetching {
Some(fut) => fut.boxed(),
None => future::ok(()).boxed(),
}
}, Duration::from_millis(PURGE_TIMEOUT_MS), || {})
}
}

View File

@@ -28,6 +28,7 @@ extern crate ctrlc;
extern crate docopt;
extern crate env_logger;
extern crate fdlimit;
extern crate futures;
extern crate isatty;
extern crate jsonrpc_core;
extern crate num_cpus;
@@ -105,6 +106,7 @@ mod deprecated;
mod dir;
mod helpers;
mod informant;
mod light_helpers;
mod migration;
mod modules;
mod params;

View File

@@ -30,7 +30,7 @@ use ethcore::migrations::Extract;
/// Database is assumed to be at default version, when no version file is found.
const DEFAULT_VERSION: u32 = 5;
/// Current version of database models.
const CURRENT_VERSION: u32 = 11;
const CURRENT_VERSION: u32 = 12;
/// First version of the consolidated database.
const CONSOLIDATION_VERSION: u32 = 9;
/// Defines how many items are migrated to the new version of database at once.
@@ -147,6 +147,7 @@ fn consolidated_database_migrations(compaction_profile: &CompactionProfile) -> R
let mut manager = MigrationManager::new(default_migration_settings(compaction_profile));
manager.add_migration(migrations::ToV10::new()).map_err(|_| Error::MigrationImpossible)?;
manager.add_migration(migrations::TO_V11).map_err(|_| Error::MigrationImpossible)?;
manager.add_migration(migrations::TO_V12).map_err(|_| Error::MigrationImpossible)?;
Ok(manager)
}

View File

@@ -81,8 +81,8 @@ impl fmt::Display for IpcConfiguration {
}
}
pub struct Dependencies {
pub apis: Arc<rpc_apis::Dependencies>,
pub struct Dependencies<D: rpc_apis::Dependencies> {
pub apis: Arc<D>,
pub remote: TokioRemote,
pub stats: Arc<RpcStats>,
}
@@ -112,11 +112,17 @@ impl rpc::IpcMetaExtractor<Metadata> for RpcExtractor {
}
}
fn setup_apis(apis: ApiSet, deps: &Dependencies) -> MetaIoHandler<Metadata, Middleware> {
rpc_apis::setup_rpc(deps.stats.clone(), deps.apis.clone(), apis)
fn setup_apis<D>(apis: ApiSet, deps: &Dependencies<D>) -> MetaIoHandler<Metadata, Middleware<D::Notifier>>
where D: rpc_apis::Dependencies
{
rpc_apis::setup_rpc(deps.stats.clone(), &*deps.apis, apis)
}
pub fn new_http(conf: HttpConfiguration, deps: &Dependencies, middleware: Option<dapps::Middleware>) -> Result<Option<HttpServer>, String> {
pub fn new_http<D: rpc_apis::Dependencies>(
conf: HttpConfiguration,
deps: &Dependencies<D>,
middleware: Option<dapps::Middleware>
) -> Result<Option<HttpServer>, String> {
if !conf.enabled {
return Ok(None);
}
@@ -157,7 +163,10 @@ pub fn new_http(conf: HttpConfiguration, deps: &Dependencies, middleware: Option
}
}
pub fn new_ipc(conf: IpcConfiguration, dependencies: &Dependencies) -> Result<Option<IpcServer>, String> {
pub fn new_ipc<D: rpc_apis::Dependencies>(
conf: IpcConfiguration,
dependencies: &Dependencies<D>
) -> Result<Option<IpcServer>, String> {
if !conf.enabled {
return Ok(None);
}

View File

@@ -27,12 +27,14 @@ use ethcore::client::Client;
use ethcore::miner::{Miner, ExternalMiner};
use ethcore::snapshot::SnapshotService;
use ethcore_rpc::{Metadata, NetworkSettings};
use ethcore_rpc::informant::{Middleware, RpcStats, ClientNotifier};
use ethcore_rpc::dispatch::FullDispatcher;
use ethsync::{ManageNetwork, SyncProvider};
use ethcore_rpc::informant::{ActivityNotifier, Middleware, RpcStats, ClientNotifier};
use ethcore_rpc::dispatch::{FullDispatcher, LightDispatcher};
use ethsync::{ManageNetwork, SyncProvider, LightSync};
use hash_fetch::fetch::Client as FetchClient;
use jsonrpc_core::{MetaIoHandler};
use light::{TransactionQueue as LightTransactionQueue, Cache as LightDataCache};
use updater::Updater;
use util::{Mutex, RwLock};
use ethcore_logger::RotatingLogger;
#[derive(Debug, PartialEq, Clone, Eq, Hash)]
@@ -112,25 +114,6 @@ impl FromStr for ApiSet {
}
}
pub struct Dependencies {
pub signer_service: Arc<SignerService>,
pub client: Arc<Client>,
pub snapshot: Arc<SnapshotService>,
pub sync: Arc<SyncProvider>,
pub net: Arc<ManageNetwork>,
pub secret_store: Option<Arc<AccountProvider>>,
pub miner: Arc<Miner>,
pub external_miner: Arc<ExternalMiner>,
pub logger: Arc<RotatingLogger>,
pub settings: Arc<NetworkSettings>,
pub net_service: Arc<ManageNetwork>,
pub updater: Arc<Updater>,
pub geth_compatibility: bool,
pub dapps_interface: Option<String>,
pub dapps_port: Option<u16>,
pub fetch: FetchClient,
}
fn to_modules(apis: &[Api]) -> BTreeMap<String, String> {
let mut modules = BTreeMap::new();
for api in apis {
@@ -151,6 +134,274 @@ fn to_modules(apis: &[Api]) -> BTreeMap<String, String> {
modules
}
/// RPC dependencies can be used to initialize RPC endpoints from APIs.
pub trait Dependencies {
type Notifier: ActivityNotifier;
/// Create the activity notifier.
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<Metadata, Middleware<Self::Notifier>>, apis: &[Api]);
}
/// RPC dependencies for a full node.
pub struct FullDependencies {
pub signer_service: Arc<SignerService>,
pub client: Arc<Client>,
pub snapshot: Arc<SnapshotService>,
pub sync: Arc<SyncProvider>,
pub net: Arc<ManageNetwork>,
pub secret_store: Option<Arc<AccountProvider>>,
pub miner: Arc<Miner>,
pub external_miner: Arc<ExternalMiner>,
pub logger: Arc<RotatingLogger>,
pub settings: Arc<NetworkSettings>,
pub net_service: Arc<ManageNetwork>,
pub updater: Arc<Updater>,
pub geth_compatibility: bool,
pub dapps_interface: Option<String>,
pub dapps_port: Option<u16>,
pub fetch: FetchClient,
}
impl Dependencies for FullDependencies {
type Notifier = ClientNotifier;
fn activity_notifier(&self) -> ClientNotifier {
ClientNotifier {
client: self.client.clone(),
}
}
fn extend_with_set(&self, handler: &mut MetaIoHandler<Metadata, Middleware>, apis: &[Api]) {
use ethcore_rpc::v1::*;
macro_rules! add_signing_methods {
($namespace:ident, $handler:expr, $deps:expr) => {
{
let deps = &$deps;
let dispatcher = FullDispatcher::new(Arc::downgrade(&deps.client), Arc::downgrade(&deps.miner));
if deps.signer_service.is_enabled() {
$handler.extend_with($namespace::to_delegate(SigningQueueClient::new(&deps.signer_service, dispatcher, &deps.secret_store)))
} else {
$handler.extend_with($namespace::to_delegate(SigningUnsafeClient::new(&deps.secret_store, dispatcher)))
}
}
}
}
let dispatcher = FullDispatcher::new(Arc::downgrade(&self.client), Arc::downgrade(&self.miner));
for api in apis {
match *api {
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,
&self.snapshot,
&self.sync,
&self.secret_store,
&self.miner,
&self.external_miner,
EthClientOptions {
pending_nonce_from_queue: self.geth_compatibility,
allow_pending_receipt_query: !self.geth_compatibility,
send_block_number_in_get_work: !self.geth_compatibility,
}
);
handler.extend_with(client.to_delegate());
let filter_client = EthFilterClient::new(&self.client, &self.miner);
handler.extend_with(filter_client.to_delegate());
add_signing_methods!(EthSigning, handler, self);
},
Api::Personal => {
handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility).to_delegate());
},
Api::Signer => {
handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service).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,
&self.miner,
&self.sync,
&self.updater,
&self.net_service,
&self.secret_store,
self.logger.clone(),
self.settings.clone(),
signer,
self.dapps_interface.clone(),
self.dapps_port,
).to_delegate());
add_signing_methods!(EthSigning, handler, self);
add_signing_methods!(ParitySigning, handler, self);
},
Api::ParityAccounts => {
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, &self.miner).to_delegate())
},
Api::Rpc => {
let modules = to_modules(&apis);
handler.extend_with(RpcClient::new(modules).to_delegate());
}
}
}
}
}
/// Light client notifier. Doesn't do anything yet, but might in the future.
pub struct LightClientNotifier;
impl ActivityNotifier for LightClientNotifier {
fn active(&self) {}
}
/// RPC dependencies for a light client.
pub struct LightDependencies {
pub signer_service: Arc<SignerService>,
pub client: Arc<::light::client::Client>,
pub sync: Arc<LightSync>,
pub net: Arc<ManageNetwork>,
pub secret_store: Arc<AccountProvider>,
pub logger: Arc<RotatingLogger>,
pub settings: Arc<NetworkSettings>,
pub on_demand: Arc<::light::on_demand::OnDemand>,
pub cache: Arc<Mutex<LightDataCache>>,
pub transaction_queue: Arc<RwLock<LightTransactionQueue>>,
pub dapps_interface: Option<String>,
pub dapps_port: Option<u16>,
pub fetch: FetchClient,
pub geth_compatibility: bool,
}
impl Dependencies for LightDependencies {
type Notifier = LightClientNotifier;
fn activity_notifier(&self) -> Self::Notifier { LightClientNotifier }
fn extend_with_set(&self, handler: &mut MetaIoHandler<Metadata, Middleware<Self::Notifier>>, apis: &[Api]) {
use ethcore_rpc::v1::*;
let dispatcher = LightDispatcher::new(
self.sync.clone(),
self.client.clone(),
self.on_demand.clone(),
self.cache.clone(),
self.transaction_queue.clone(),
);
macro_rules! add_signing_methods {
($namespace:ident, $handler:expr, $deps:expr) => {
{
let deps = &$deps;
let dispatcher = dispatcher.clone();
let secret_store = Some(deps.secret_store.clone());
if deps.signer_service.is_enabled() {
$handler.extend_with($namespace::to_delegate(
SigningQueueClient::new(&deps.signer_service, dispatcher, &secret_store)
))
} else {
$handler.extend_with(
$namespace::to_delegate(SigningUnsafeClient::new(&secret_store, dispatcher))
)
}
}
}
}
for api in apis {
match *api {
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(),
self.client.clone(),
self.on_demand.clone(),
self.transaction_queue.clone(),
self.secret_store.clone(),
self.cache.clone(),
);
handler.extend_with(client.to_delegate());
// TODO: filters.
add_signing_methods!(EthSigning, handler, self);
},
Api::Personal => {
let secret_store = Some(self.secret_store.clone());
handler.extend_with(PersonalClient::new(&secret_store, dispatcher.clone(), self.geth_compatibility).to_delegate());
},
Api::Signer => {
let secret_store = Some(self.secret_store.clone());
handler.extend_with(SignerClient::new(&secret_store, dispatcher.clone(), &self.signer_service).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.dapps_interface.clone(),
self.dapps_port,
).to_delegate());
add_signing_methods!(EthSigning, handler, self);
add_signing_methods!(ParitySigning, handler, self);
},
Api::ParityAccounts => {
let secret_store = Some(self.secret_store.clone());
handler.extend_with(ParityAccountsClient::new(&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());
}
}
}
}
}
impl ApiSet {
pub fn list_apis(&self) -> HashSet<Api> {
let mut safe_list = vec![Api::Web3, Api::Net, Api::Eth, Api::Parity, Api::Traces, Api::Rpc]
@@ -172,110 +423,12 @@ impl ApiSet {
}
}
macro_rules! add_signing_methods {
($namespace:ident, $handler:expr, $deps:expr) => {
{
let handler = &mut $handler;
let deps = &$deps;
let dispatcher = FullDispatcher::new(Arc::downgrade(&deps.client), Arc::downgrade(&deps.miner));
if deps.signer_service.is_enabled() {
handler.extend_with($namespace::to_delegate(SigningQueueClient::new(&deps.signer_service, dispatcher, &deps.secret_store)))
} else {
handler.extend_with($namespace::to_delegate(SigningUnsafeClient::new(&deps.secret_store, dispatcher)))
}
}
}
}
pub fn setup_rpc(stats: Arc<RpcStats>, deps: Arc<Dependencies>, apis: ApiSet) -> MetaIoHandler<Metadata, Middleware> {
use ethcore_rpc::v1::*;
let mut handler = MetaIoHandler::with_middleware(Middleware::new(stats, ClientNotifier {
client: deps.client.clone(),
}));
pub fn setup_rpc<D: Dependencies>(stats: Arc<RpcStats>, deps: &D, apis: ApiSet) -> MetaIoHandler<Metadata, Middleware<D::Notifier>> {
let mut handler = MetaIoHandler::with_middleware(Middleware::new(stats, deps.activity_notifier()));
// it's turned into vector, cause ont of the cases requires &[]
let apis = apis.list_apis().into_iter().collect::<Vec<_>>();
let dispatcher = FullDispatcher::new(Arc::downgrade(&deps.client), Arc::downgrade(&deps.miner));
deps.extend_with_set(&mut handler, &apis[..]);
for api in &apis {
match *api {
Api::Web3 => {
handler.extend_with(Web3Client::new().to_delegate());
},
Api::Net => {
handler.extend_with(NetClient::new(&deps.sync).to_delegate());
},
Api::Eth => {
let client = EthClient::new(
&deps.client,
&deps.snapshot,
&deps.sync,
&deps.secret_store,
&deps.miner,
&deps.external_miner,
EthClientOptions {
pending_nonce_from_queue: deps.geth_compatibility,
allow_pending_receipt_query: !deps.geth_compatibility,
send_block_number_in_get_work: !deps.geth_compatibility,
}
);
handler.extend_with(client.to_delegate());
let filter_client = EthFilterClient::new(&deps.client, &deps.miner);
handler.extend_with(filter_client.to_delegate());
add_signing_methods!(EthSigning, handler, deps);
},
Api::Personal => {
handler.extend_with(PersonalClient::new(&deps.secret_store, dispatcher.clone(), deps.geth_compatibility).to_delegate());
},
Api::Signer => {
handler.extend_with(SignerClient::new(&deps.secret_store, dispatcher.clone(), &deps.signer_service).to_delegate());
},
Api::Parity => {
let signer = match deps.signer_service.is_enabled() {
true => Some(deps.signer_service.clone()),
false => None,
};
handler.extend_with(ParityClient::new(
&deps.client,
&deps.miner,
&deps.sync,
&deps.updater,
&deps.net_service,
&deps.secret_store,
deps.logger.clone(),
deps.settings.clone(),
signer,
deps.dapps_interface.clone(),
deps.dapps_port,
).to_delegate());
add_signing_methods!(EthSigning, handler, deps);
add_signing_methods!(ParitySigning, handler, deps);
},
Api::ParityAccounts => {
handler.extend_with(ParityAccountsClient::new(&deps.secret_store).to_delegate());
},
Api::ParitySet => {
handler.extend_with(ParitySetClient::new(
&deps.client,
&deps.miner,
&deps.updater,
&deps.net_service,
deps.fetch.clone(),
).to_delegate())
},
Api::Traces => {
handler.extend_with(TracesClient::new(&deps.client, &deps.miner).to_delegate())
},
Api::Rpc => {
let modules = to_modules(&apis);
handler.extend_with(RpcClient::new(modules).to_delegate());
}
}
}
handler
}

View File

@@ -30,6 +30,7 @@ use ethcore::account_provider::{AccountProvider, AccountProviderSettings};
use ethcore::miner::{Miner, MinerService, ExternalMiner, MinerOptions};
use ethcore::snapshot;
use ethcore::verification::queue::VerifierSettings;
use light::Cache as LightDataCache;
use ethsync::SyncConfig;
use informant::Informant;
use updater::{UpdatePolicy, Updater};
@@ -60,6 +61,10 @@ const SNAPSHOT_PERIOD: u64 = 10000;
// how many blocks to wait before starting a periodic snapshot.
const SNAPSHOT_HISTORY: u64 = 100;
// Number of minutes before a given gas price corpus should expire.
// Light client only.
const GAS_CORPUS_EXPIRATION_MINUTES: i64 = 60 * 6;
// Pops along with error messages when a password is missing or invalid.
const VERIFY_PASSWORD_HINT: &'static str = "Make sure valid password is present in files passed using `--password` or in the configuration file.";
@@ -107,6 +112,8 @@ pub struct RunCmd {
pub check_seal: bool,
pub download_old_blocks: bool,
pub verifier_settings: VerifierSettings,
pub serve_light: bool,
pub light: bool,
}
pub fn open_ui(signer_conf: &signer::Configuration) -> Result<(), String> {
@@ -148,6 +155,171 @@ impl ::local_store::NodeInfo for FullNodeInfo {
}
}
// helper for light execution.
fn execute_light(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> Result<(bool, Option<String>), String> {
use light::client as light_client;
use ethsync::{LightSyncParams, LightSync, ManageNetwork};
use util::RwLock;
let panic_handler = PanicHandler::new_in_arc();
// load spec
let spec = cmd.spec.spec()?;
// load genesis hash
let genesis_hash = spec.genesis_header().hash();
// database paths
let db_dirs = cmd.dirs.database(genesis_hash, cmd.spec.legacy_fork_name(), spec.data_dir.clone());
// user defaults path
let user_defaults_path = db_dirs.user_defaults_path();
// load user defaults
let user_defaults = UserDefaults::load(&user_defaults_path)?;
// select pruning algorithm
let algorithm = cmd.pruning.to_algorithm(&user_defaults);
let compaction = cmd.compaction.compaction_profile(db_dirs.db_root_path().as_path());
// execute upgrades
execute_upgrades(&cmd.dirs.base, &db_dirs, algorithm, compaction.clone())?;
// create dirs used by parity
cmd.dirs.create_dirs(cmd.dapps_conf.enabled, cmd.signer_conf.enabled, cmd.secretstore_conf.enabled)?;
info!("Starting {}", Colour::White.bold().paint(version()));
info!("Running in experimental {} mode.", Colour::Blue.bold().paint("Light Client"));
// start client and create transaction queue.
let mut config = light_client::Config {
queue: Default::default(),
chain_column: ::ethcore::db::COL_LIGHT_CHAIN,
db_cache_size: Some(cmd.cache_config.blockchain() as usize * 1024 * 1024),
db_compaction: compaction,
db_wal: cmd.wal,
};
config.queue.max_mem_use = cmd.cache_config.queue() as usize * 1024 * 1024;
config.queue.verifier_settings = cmd.verifier_settings;
let service = light_client::Service::start(config, &spec, &db_dirs.client_path(algorithm))
.map_err(|e| format!("Error starting light client: {}", e))?;
let txq = Arc::new(RwLock::new(::light::transaction_queue::TransactionQueue::default()));
let provider = ::light::provider::LightProvider::new(service.client().clone(), txq.clone());
// start network.
// set up bootnodes
let mut net_conf = cmd.net_conf;
if !cmd.custom_bootnodes {
net_conf.boot_nodes = spec.nodes.clone();
}
// TODO: configurable cache size.
let cache = LightDataCache::new(Default::default(), ::time::Duration::minutes(GAS_CORPUS_EXPIRATION_MINUTES));
let cache = Arc::new(::util::Mutex::new(cache));
// start on_demand service.
let on_demand = Arc::new(::light::on_demand::OnDemand::new(cache.clone()));
// set network path.
net_conf.net_config_path = Some(db_dirs.network_path().to_string_lossy().into_owned());
let sync_params = LightSyncParams {
network_config: net_conf.into_basic().map_err(|e| format!("Failed to produce network config: {}", e))?,
client: Arc::new(provider),
network_id: cmd.network_id.unwrap_or(spec.network_id()),
subprotocol_name: ::ethsync::LIGHT_PROTOCOL,
handlers: vec![on_demand.clone()],
};
let light_sync = LightSync::new(sync_params).map_err(|e| format!("Error starting network: {}", e))?;
let light_sync = Arc::new(light_sync);
// spin up event loop
let event_loop = EventLoop::spawn();
// queue cull service.
let queue_cull = Arc::new(::light_helpers::QueueCull {
client: service.client().clone(),
sync: light_sync.clone(),
on_demand: on_demand.clone(),
txq: txq.clone(),
remote: event_loop.remote(),
});
service.register_handler(queue_cull).map_err(|e| format!("Error attaching service: {:?}", e))?;
// start the network.
light_sync.start_network();
// fetch service
let fetch = FetchClient::new().map_err(|e| format!("Error starting fetch client: {:?}", e))?;
let passwords = passwords_from_files(&cmd.acc_conf.password_files)?;
// prepare account provider
let account_provider = Arc::new(prepare_account_provider(&cmd.spec, &cmd.dirs, &spec.data_dir, cmd.acc_conf, &passwords)?);
let rpc_stats = Arc::new(informant::RpcStats::default());
let signer_path = cmd.signer_conf.signer_path.clone();
// start RPCs
let deps_for_rpc_apis = Arc::new(rpc_apis::LightDependencies {
signer_service: Arc::new(rpc_apis::SignerService::new(move || {
signer::generate_new_token(signer_path.clone()).map_err(|e| format!("{:?}", e))
}, cmd.ui_address)),
client: service.client().clone(),
sync: light_sync.clone(),
net: light_sync.clone(),
secret_store: account_provider,
logger: logger,
settings: Arc::new(cmd.net_settings),
on_demand: on_demand,
cache: cache,
transaction_queue: txq,
dapps_interface: match cmd.dapps_conf.enabled {
true => Some(cmd.http_conf.interface.clone()),
false => None,
},
dapps_port: match cmd.dapps_conf.enabled {
true => Some(cmd.http_conf.port),
false => None,
},
fetch: fetch,
geth_compatibility: cmd.geth_compatibility,
});
let dependencies = rpc::Dependencies {
apis: deps_for_rpc_apis.clone(),
remote: event_loop.raw_remote(),
stats: rpc_stats.clone(),
};
// start rpc servers
let _http_server = rpc::new_http(cmd.http_conf, &dependencies, None)?;
let _ipc_server = rpc::new_ipc(cmd.ipc_conf, &dependencies)?;
// the signer server
let signer_deps = signer::Dependencies {
apis: deps_for_rpc_apis.clone(),
remote: event_loop.raw_remote(),
rpc_stats: rpc_stats.clone(),
};
let signing_queue = deps_for_rpc_apis.signer_service.queue();
let _signer_server = signer::start(cmd.signer_conf.clone(), signing_queue, signer_deps)?;
// TODO: Dapps
// minimal informant thread. Just prints block number every 5 seconds.
// TODO: integrate with informant.rs
let informant_client = service.client().clone();
::std::thread::spawn(move || loop {
info!("#{}", informant_client.best_block_header().number());
::std::thread::sleep(::std::time::Duration::from_secs(5));
});
// wait for ctrl-c.
Ok(wait_for_exit(panic_handler, None, None, can_restart))
}
pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> Result<(bool, Option<String>), String> {
if cmd.ui && cmd.dapps_conf.enabled {
// Check if Parity is already running
@@ -157,12 +329,17 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
}
}
// set up panic handler
let panic_handler = PanicHandler::new_in_arc();
// increase max number of open files
raise_fd_limit();
// run as light client.
if cmd.light {
return execute_light(cmd, can_restart, logger);
}
// set up panic handler
let panic_handler = PanicHandler::new_in_arc();
// load spec
let spec = cmd.spec.spec()?;
@@ -244,6 +421,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
sync_config.fork_block = spec.fork_block();
sync_config.warp_sync = cmd.warp_sync;
sync_config.download_old_blocks = cmd.download_old_blocks;
sync_config.serve_light = cmd.serve_light;
let passwords = passwords_from_files(&cmd.acc_conf.password_files)?;
@@ -407,7 +585,8 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
true => None,
false => Some(account_provider.clone())
};
let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies {
let deps_for_rpc_apis = Arc::new(rpc_apis::FullDependencies {
signer_service: Arc::new(rpc_apis::SignerService::new(move || {
signer::generate_new_token(signer_path.clone()).map_err(|e| format!("{:?}", e))
}, cmd.ui_address)),
@@ -440,13 +619,18 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
stats: rpc_stats.clone(),
};
// the dapps middleware
let dapps_deps = dapps::Dependencies {
client: client.clone(),
sync: sync_provider.clone(),
remote: event_loop.raw_remote(),
fetch: fetch.clone(),
signer: deps_for_rpc_apis.signer_service.clone(),
// the dapps server
let dapps_deps = {
let (sync, client) = (sync_provider.clone(), client.clone());
let contract_client = Arc::new(::dapps::FullRegistrar { client: client.clone() });
dapps::Dependencies {
sync_status: Arc::new(move || is_major_importing(Some(sync.status().state), client.queue_info())),
contract_client: contract_client,
remote: event_loop.raw_remote(),
fetch: fetch.clone(),
signer: deps_for_rpc_apis.signer_service.clone(),
}
};
let dapps_middleware = dapps::new(cmd.dapps_conf.clone(), dapps_deps)?;
@@ -460,7 +644,8 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
remote: event_loop.raw_remote(),
rpc_stats: rpc_stats.clone(),
};
let signer_server = signer::start(cmd.signer_conf.clone(), signer_deps)?;
let signing_queue = deps_for_rpc_apis.signer_service.queue();
let signer_server = signer::start(cmd.signer_conf.clone(), signing_queue, signer_deps)?;
// secret store key server
let secretstore_deps = secretstore::Dependencies {

View File

@@ -23,7 +23,7 @@ pub use ethcore_signer::Server as SignerServer;
use ansi_term::Colour;
use dir::default_data_path;
use ethcore_rpc::informant::RpcStats;
use ethcore_rpc;
use ethcore_rpc::{self, ConfirmationsQueue};
use ethcore_signer as signer;
use helpers::replace_home;
use parity_reactor::TokioRemote;
@@ -55,8 +55,8 @@ impl Default for Configuration {
}
}
pub struct Dependencies {
pub apis: Arc<rpc_apis::Dependencies>,
pub struct Dependencies<D: rpc_apis::Dependencies> {
pub apis: Arc<D>,
pub remote: TokioRemote,
pub rpc_stats: Arc<RpcStats>,
}
@@ -77,11 +77,15 @@ impl signer::MetaExtractor<ethcore_rpc::Metadata> for StandardExtractor {
}
}
pub fn start(conf: Configuration, deps: Dependencies) -> Result<Option<SignerServer>, String> {
pub fn start<D: rpc_apis::Dependencies>(
conf: Configuration,
queue: Arc<ConfirmationsQueue>,
deps: Dependencies<D>,
) -> Result<Option<SignerServer>, String> {
if !conf.enabled {
Ok(None)
} else {
Ok(Some(do_start(conf, deps)?))
Ok(Some(do_start(conf, queue, deps)?))
}
}
@@ -125,14 +129,18 @@ pub fn generate_new_token(path: String) -> io::Result<String> {
Ok(code)
}
fn do_start(conf: Configuration, deps: Dependencies) -> Result<SignerServer, String> {
fn do_start<D: rpc_apis::Dependencies>(
conf: Configuration,
queue: Arc<ConfirmationsQueue>,
deps: Dependencies<D>
) -> Result<SignerServer, String> {
let addr = format!("{}:{}", conf.interface, conf.port)
.parse()
.map_err(|_| format!("Invalid port specified: {}", conf.port))?;
let start_result = {
let server = signer::ServerBuilder::new(
deps.apis.signer_service.queue(),
queue,
codes_path(conf.signer_path),
);
if conf.skip_origin_validation {
@@ -141,7 +149,7 @@ fn do_start(conf: Configuration, deps: Dependencies) -> Result<SignerServer, Str
}
let server = server.skip_origin_validation(conf.skip_origin_validation);
let server = server.stats(deps.rpc_stats.clone());
let handler = rpc_apis::setup_rpc(deps.rpc_stats, deps.apis, rpc_apis::ApiSet::SafeContext);
let handler = rpc_apis::setup_rpc(deps.rpc_stats, &*deps.apis, rpc_apis::ApiSet::SafeContext);
let remote = deps.remote.clone();
server.start_with_extractor(addr, handler, remote, StandardExtractor)
};