From 62455a4094414eba1c7fa3adb69436a7a8b0a6dc Mon Sep 17 00:00:00 2001 From: debris Date: Mon, 18 Apr 2016 18:15:03 +0200 Subject: [PATCH 01/16] separated from blockchain and made reusable db reader and batch writer --- ethcore/src/blockchain/blockchain.rs | 65 ++++++++-------------------- ethcore/src/{ => db}/db.rs | 0 ethcore/src/db/mod.rs | 23 ++++++++++ ethcore/src/db/reader.rs | 65 ++++++++++++++++++++++++++++ ethcore/src/db/writer.rs | 60 +++++++++++++++++++++++++ 5 files changed, 165 insertions(+), 48 deletions(-) rename ethcore/src/{ => db}/db.rs (100%) create mode 100644 ethcore/src/db/mod.rs create mode 100644 ethcore/src/db/reader.rs create mode 100644 ethcore/src/db/writer.rs diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 60585d19f..4d1bec884 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -30,7 +30,7 @@ use blockchain::bloom_indexer::BloomIndexer; use blockchain::tree_route::TreeRoute; use blockchain::update::ExtrasUpdate; use blockchain::{CacheSize, ImportRoute}; -use db::{Writable, Readable, Key}; +use db::{Writable, Readable, Key, DatabaseReader, BatchWriter, CacheUpdatePolicy}; const BLOOM_INDEX_SIZE: usize = 16; const BLOOM_LEVELS: u8 = 3; @@ -183,7 +183,7 @@ impl BlockProvider for BlockChain { /// Returns true if the given block is known /// (though not necessarily a part of the canon chain). fn is_known(&self, hash: &H256) -> bool { - self.query_extras_exist(hash, &self.block_details) + DatabaseReader::new(&self.extras_db, &self.block_details).exists(hash) } // We do not store tracing information. @@ -466,28 +466,25 @@ impl BlockChain { batch.put(b"best", &update.info.hash).unwrap(); { - let mut write_details = self.block_details.write().unwrap(); - for (hash, details) in update.block_details.into_iter() { - batch.write(&hash, &details); - self.note_used(CacheID::Extras(ExtrasIndex::BlockDetails, hash.clone())); - write_details.insert(hash, details); + for hash in update.block_details.keys().cloned() { + self.note_used(CacheID::Extras(ExtrasIndex::BlockDetails, hash)); } + + let mut write_details = self.block_details.write().unwrap(); + BatchWriter::new(&batch, &mut write_details) + .extend(update.block_details, CacheUpdatePolicy::Overwrite); } { let mut write_receipts = self.block_receipts.write().unwrap(); - for (hash, receipt) in &update.block_receipts { - batch.write(hash, receipt); - write_receipts.remove(hash); - } + BatchWriter::new(&batch, &mut write_receipts) + .extend(update.block_receipts, CacheUpdatePolicy::Remove); } { let mut write_blocks_blooms = self.blocks_blooms.write().unwrap(); - for (bloom_hash, blocks_bloom) in &update.blocks_blooms { - batch.write(bloom_hash, blocks_bloom); - write_blocks_blooms.remove(bloom_hash); - } + BatchWriter::new(&batch, &mut write_blocks_blooms) + .extend(update.blocks_blooms, CacheUpdatePolicy::Remove); } // These cached values must be updated last and togeterh @@ -508,15 +505,11 @@ impl BlockChain { } } - for (number, hash) in &update.block_hashes { - batch.write(number, hash); - write_hashes.remove(number); - } + BatchWriter::new(&batch, &mut write_hashes) + .extend(update.block_hashes, CacheUpdatePolicy::Remove); - for (hash, tx_address) in &update.transactions_addresses { - batch.write(hash, tx_address); - write_txs.remove(hash); - } + BatchWriter::new(&batch, &mut write_txs) + .extend(update.transactions_addresses, CacheUpdatePolicy::Remove); // update extras database self.extras_db.write(batch).unwrap(); @@ -751,32 +744,8 @@ impl BlockChain { T: ExtrasIndexable + Clone + Decodable, K: Key + Eq + Hash + Clone, H256: From { - { - let read = cache.read().unwrap(); - if let Some(v) = read.get(hash) { - return Some(v.clone()); - } - } - self.note_used(CacheID::Extras(T::index(), H256::from(hash.clone()))); - - self.extras_db.read(hash).map(|t: T| { - let mut write = cache.write().unwrap(); - write.insert(hash.clone(), t.clone()); - t - }) - } - - fn query_extras_exist(&self, hash: &K, cache: &RwLock>) -> bool where - K: Key + Eq + Hash + Clone { - { - let read = cache.read().unwrap(); - if let Some(_) = read.get(hash) { - return true; - } - } - - self.extras_db.exists::(hash) + DatabaseReader::new(&self.extras_db, cache).read(hash) } /// Get current cache size. diff --git a/ethcore/src/db.rs b/ethcore/src/db/db.rs similarity index 100% rename from ethcore/src/db.rs rename to ethcore/src/db/db.rs diff --git a/ethcore/src/db/mod.rs b/ethcore/src/db/mod.rs new file mode 100644 index 000000000..199614b79 --- /dev/null +++ b/ethcore/src/db/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2015, 2016 Ethcore (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 . + +pub mod db; +mod reader; +mod writer; + +pub use self::db::{Key, Writable, Readable}; +pub use self::reader::DatabaseReader; +pub use self::writer::{BatchWriter, CacheUpdatePolicy}; diff --git a/ethcore/src/db/reader.rs b/ethcore/src/db/reader.rs new file mode 100644 index 000000000..4e4755b11 --- /dev/null +++ b/ethcore/src/db/reader.rs @@ -0,0 +1,65 @@ +// Copyright 2015, 2016 Ethcore (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 . + +use std::hash::Hash; +use std::sync::RwLock; +use std::collections::HashMap; +use util::Database; +use util::rlp::Decodable; +use super::{Readable, Key}; + +pub struct DatabaseReader<'a, K, V> where K: 'a, V: 'a { + db: &'a Database, + cache: &'a RwLock>, +} + +impl<'a, K, V> DatabaseReader<'a, K, V> { + pub fn new(db: &'a Database, cache: &'a RwLock>) -> Self { + DatabaseReader { + db: db, + cache: cache, + } + } + + pub fn read(&self, key: &K) -> Option where + K: Eq + Hash + Clone + Key, + V: Clone + Decodable { + { + let read = self.cache.read().unwrap(); + if let Some(v) = read.get(key) { + return Some(v.clone()); + } + } + + self.db.read(key).map(|value: V|{ + let mut write = self.cache.write().unwrap(); + write.insert(key.clone(), value.clone()); + value + }) + } + + pub fn exists(&self, key: &K) -> bool where + K: Eq + Hash + Key { + { + let read = self.cache.read().unwrap(); + if read.get(key).is_some() { + return true; + } + } + + self.db.exists::(key) + } +} diff --git a/ethcore/src/db/writer.rs b/ethcore/src/db/writer.rs new file mode 100644 index 000000000..35658eadc --- /dev/null +++ b/ethcore/src/db/writer.rs @@ -0,0 +1,60 @@ +// Copyright 2015, 2016 Ethcore (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 . + +use std::collections::HashMap; +use std::hash::Hash; +use util::DBTransaction; +use util::rlp::Encodable; +use super::{Key, Writable}; + +#[derive(Clone, Copy)] +pub enum CacheUpdatePolicy { + Overwrite, + Remove, +} + +pub struct BatchWriter<'a, K, V> where K: 'a, V: 'a { + batch: &'a DBTransaction, + cache: &'a mut HashMap, +} + +impl<'a, K, V> BatchWriter<'a, K, V> { + pub fn new(batch: &'a DBTransaction, cache: &'a mut HashMap) -> Self { + BatchWriter { + batch: batch, + cache: cache, + } + } + + pub fn extend(&mut self, map: HashMap, policy: CacheUpdatePolicy) where + K: Key + Hash + Eq, + V: Encodable { + match policy { + CacheUpdatePolicy::Overwrite => { + for (key, value) in map.into_iter() { + self.batch.write(&key, &value); + self.cache.insert(key, value); + } + }, + CacheUpdatePolicy::Remove => { + for (key, value) in &map { + self.batch.write(key, value); + self.cache.remove(key); + } + }, + } + } +} From 4b82fc1d1f5a1f3031581a8ec66929108c0038e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 20 Apr 2016 10:01:53 +0200 Subject: [PATCH 02/16] Bumping status page --- Cargo.lock | 6 +++--- webapp/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 39aed230f..576920fab 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -356,7 +356,7 @@ dependencies = [ "jsonrpc-core 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-http-server 5.0.1 (git+https://github.com/debris/jsonrpc-http-server.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-status 0.1.7 (git+https://github.com/tomusdrw/parity-status.git)", + "parity-status 0.2.1 (git+https://github.com/tomusdrw/parity-status.git)", "parity-wallet 0.1.1 (git+https://github.com/tomusdrw/parity-wallet.git)", "parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)", "url 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -746,8 +746,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "parity-status" -version = "0.1.7" -source = "git+https://github.com/tomusdrw/parity-status.git#5b7010eb7ecc38e80ab506902e083dc0dd48c43f" +version = "0.2.1" +source = "git+https://github.com/tomusdrw/parity-status.git#3ed519c39d625fe8dcdb83eae34590f1e53e589e" dependencies = [ "parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)", ] diff --git a/webapp/Cargo.toml b/webapp/Cargo.toml index f52150a9b..db15e5786 100644 --- a/webapp/Cargo.toml +++ b/webapp/Cargo.toml @@ -17,7 +17,7 @@ ethcore-rpc = { path = "../rpc" } ethcore-util = { path = "../util" } parity-webapp = { git = "https://github.com/tomusdrw/parity-webapp.git" } # List of apps -parity-status = { git = "https://github.com/tomusdrw/parity-status.git", version = "0.1.7" } +parity-status = { git = "https://github.com/tomusdrw/parity-status.git", version = "0.2.1" } parity-wallet = { git = "https://github.com/tomusdrw/parity-wallet.git", optional = true } clippy = { version = "0.0.63", optional = true} From bffa1e1ec9d336f6855f7dd756e9ad4fb1ea51cb Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 20 Apr 2016 15:45:42 +0200 Subject: [PATCH 03/16] simplified writing and reading from database with cache --- ethcore/src/blockchain/blockchain.rs | 22 +++----- ethcore/src/db/db.rs | 77 +++++++++++++++++++++++++++- ethcore/src/db/mod.rs | 7 +-- ethcore/src/db/reader.rs | 65 ----------------------- ethcore/src/db/writer.rs | 60 ---------------------- 5 files changed, 85 insertions(+), 146 deletions(-) delete mode 100644 ethcore/src/db/reader.rs delete mode 100644 ethcore/src/db/writer.rs diff --git a/ethcore/src/blockchain/blockchain.rs b/ethcore/src/blockchain/blockchain.rs index 4d1bec884..a35b14124 100644 --- a/ethcore/src/blockchain/blockchain.rs +++ b/ethcore/src/blockchain/blockchain.rs @@ -30,7 +30,7 @@ use blockchain::bloom_indexer::BloomIndexer; use blockchain::tree_route::TreeRoute; use blockchain::update::ExtrasUpdate; use blockchain::{CacheSize, ImportRoute}; -use db::{Writable, Readable, Key, DatabaseReader, BatchWriter, CacheUpdatePolicy}; +use db::{Writable, Readable, Key, CacheUpdatePolicy}; const BLOOM_INDEX_SIZE: usize = 16; const BLOOM_LEVELS: u8 = 3; @@ -183,7 +183,7 @@ impl BlockProvider for BlockChain { /// Returns true if the given block is known /// (though not necessarily a part of the canon chain). fn is_known(&self, hash: &H256) -> bool { - DatabaseReader::new(&self.extras_db, &self.block_details).exists(hash) + self.extras_db.exists_with_cache(&self.block_details, hash) } // We do not store tracing information. @@ -471,20 +471,17 @@ impl BlockChain { } let mut write_details = self.block_details.write().unwrap(); - BatchWriter::new(&batch, &mut write_details) - .extend(update.block_details, CacheUpdatePolicy::Overwrite); + batch.extend_with_cache(&mut write_details, update.block_details, CacheUpdatePolicy::Overwrite); } { let mut write_receipts = self.block_receipts.write().unwrap(); - BatchWriter::new(&batch, &mut write_receipts) - .extend(update.block_receipts, CacheUpdatePolicy::Remove); + batch.extend_with_cache(&mut write_receipts, update.block_receipts, CacheUpdatePolicy::Remove); } { let mut write_blocks_blooms = self.blocks_blooms.write().unwrap(); - BatchWriter::new(&batch, &mut write_blocks_blooms) - .extend(update.blocks_blooms, CacheUpdatePolicy::Remove); + batch.extend_with_cache(&mut write_blocks_blooms, update.blocks_blooms, CacheUpdatePolicy::Remove); } // These cached values must be updated last and togeterh @@ -505,11 +502,8 @@ impl BlockChain { } } - BatchWriter::new(&batch, &mut write_hashes) - .extend(update.block_hashes, CacheUpdatePolicy::Remove); - - BatchWriter::new(&batch, &mut write_txs) - .extend(update.transactions_addresses, CacheUpdatePolicy::Remove); + batch.extend_with_cache(&mut write_hashes, update.block_hashes, CacheUpdatePolicy::Remove); + batch.extend_with_cache(&mut write_txs, update.transactions_addresses, CacheUpdatePolicy::Remove); // update extras database self.extras_db.write(batch).unwrap(); @@ -745,7 +739,7 @@ impl BlockChain { K: Key + Eq + Hash + Clone, H256: From { self.note_used(CacheID::Extras(T::index(), H256::from(hash.clone()))); - DatabaseReader::new(&self.extras_db, cache).read(hash) + self.extras_db.read_with_cache(cache, hash) } /// Get current cache size. diff --git a/ethcore/src/db/db.rs b/ethcore/src/db/db.rs index 23e2d74d1..3dca6ca19 100644 --- a/ethcore/src/db/db.rs +++ b/ethcore/src/db/db.rs @@ -16,9 +16,18 @@ //! Extras db utils. +use std::hash::Hash; +use std::sync::RwLock; +use std::collections::HashMap; use util::{H264, DBTransaction, Database}; use util::rlp::{encode, Encodable, decode, Decodable}; +#[derive(Clone, Copy)] +pub enum CacheUpdatePolicy { + Overwrite, + Remove, +} + /// Should be used to get database key associated with given value. pub trait Key { /// Returns db key. @@ -27,16 +36,82 @@ pub trait Key { /// Should be used to write value into database. pub trait Writable { - /// Writes key into database. + /// Writes the value into the database. fn write(&self, key: &Key, value: &T) where T: Encodable; + + /// Writes the value into the database and updates the cache. + fn write_with_cache(&self, cache: &mut HashMap, key: K, value: T, policy: CacheUpdatePolicy) where + K: Key + Hash + Eq, + T: Encodable { + self.write(&key, &value); + match policy { + CacheUpdatePolicy::Overwrite => { + cache.insert(key, value); + }, + CacheUpdatePolicy::Remove => { + cache.remove(&key); + } + } + } + + /// Writes the values into the database and updates the cache. + fn extend_with_cache(&self, cache: &mut HashMap, values: HashMap, policy: CacheUpdatePolicy) + where K: Key + Hash + Eq, T: Encodable { + match policy { + CacheUpdatePolicy::Overwrite => { + for (key, value) in values.into_iter() { + self.write(&key, &value); + cache.insert(key, value); + } + }, + CacheUpdatePolicy::Remove => { + for (key, value) in &values { + self.write(key, value); + cache.remove(key); + } + }, + } + } } /// Should be used to read values from database. pub trait Readable { /// Returns value for given key. fn read(&self, key: &Key) -> Option where T: Decodable; + + /// Returns value for given key either in cache or in database. + fn read_with_cache(&self, cache: &RwLock>, key: &K) -> Option where + K: Key + Eq + Hash + Clone, + T: Clone + Decodable { + { + let read = cache.read().unwrap(); + if let Some(v) = read.get(key) { + return Some(v.clone()); + } + } + + self.read(key).map(|value: T|{ + let mut write = cache.write().unwrap(); + write.insert(key.clone(), value.clone()); + value + }) + } + /// Returns true if given value exists. fn exists(&self, key: &Key) -> bool; + + /// Returns true if given value exists either in cache or in database. + fn exists_with_cache(&self, cache: &RwLock>, key: &K) -> bool where + K: Eq + Hash + Key { + { + let read = cache.read().unwrap(); + if read.get(key).is_some() { + return true; + } + } + + self.exists::(key) + } } impl Writable for DBTransaction { diff --git a/ethcore/src/db/mod.rs b/ethcore/src/db/mod.rs index 199614b79..f56ff3cfc 100644 --- a/ethcore/src/db/mod.rs +++ b/ethcore/src/db/mod.rs @@ -15,9 +15,4 @@ // along with Parity. If not, see . pub mod db; -mod reader; -mod writer; - -pub use self::db::{Key, Writable, Readable}; -pub use self::reader::DatabaseReader; -pub use self::writer::{BatchWriter, CacheUpdatePolicy}; +pub use self::db::{Key, Writable, Readable, CacheUpdatePolicy}; diff --git a/ethcore/src/db/reader.rs b/ethcore/src/db/reader.rs deleted file mode 100644 index 4e4755b11..000000000 --- a/ethcore/src/db/reader.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2015, 2016 Ethcore (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 . - -use std::hash::Hash; -use std::sync::RwLock; -use std::collections::HashMap; -use util::Database; -use util::rlp::Decodable; -use super::{Readable, Key}; - -pub struct DatabaseReader<'a, K, V> where K: 'a, V: 'a { - db: &'a Database, - cache: &'a RwLock>, -} - -impl<'a, K, V> DatabaseReader<'a, K, V> { - pub fn new(db: &'a Database, cache: &'a RwLock>) -> Self { - DatabaseReader { - db: db, - cache: cache, - } - } - - pub fn read(&self, key: &K) -> Option where - K: Eq + Hash + Clone + Key, - V: Clone + Decodable { - { - let read = self.cache.read().unwrap(); - if let Some(v) = read.get(key) { - return Some(v.clone()); - } - } - - self.db.read(key).map(|value: V|{ - let mut write = self.cache.write().unwrap(); - write.insert(key.clone(), value.clone()); - value - }) - } - - pub fn exists(&self, key: &K) -> bool where - K: Eq + Hash + Key { - { - let read = self.cache.read().unwrap(); - if read.get(key).is_some() { - return true; - } - } - - self.db.exists::(key) - } -} diff --git a/ethcore/src/db/writer.rs b/ethcore/src/db/writer.rs deleted file mode 100644 index 35658eadc..000000000 --- a/ethcore/src/db/writer.rs +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2015, 2016 Ethcore (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 . - -use std::collections::HashMap; -use std::hash::Hash; -use util::DBTransaction; -use util::rlp::Encodable; -use super::{Key, Writable}; - -#[derive(Clone, Copy)] -pub enum CacheUpdatePolicy { - Overwrite, - Remove, -} - -pub struct BatchWriter<'a, K, V> where K: 'a, V: 'a { - batch: &'a DBTransaction, - cache: &'a mut HashMap, -} - -impl<'a, K, V> BatchWriter<'a, K, V> { - pub fn new(batch: &'a DBTransaction, cache: &'a mut HashMap) -> Self { - BatchWriter { - batch: batch, - cache: cache, - } - } - - pub fn extend(&mut self, map: HashMap, policy: CacheUpdatePolicy) where - K: Key + Hash + Eq, - V: Encodable { - match policy { - CacheUpdatePolicy::Overwrite => { - for (key, value) in map.into_iter() { - self.batch.write(&key, &value); - self.cache.insert(key, value); - } - }, - CacheUpdatePolicy::Remove => { - for (key, value) in &map { - self.batch.write(key, value); - self.cache.remove(key); - } - }, - } - } -} From 273e4d6f32c2741f973ebf1de2dbb4410c57fca4 Mon Sep 17 00:00:00 2001 From: debris Date: Wed, 20 Apr 2016 15:53:01 +0200 Subject: [PATCH 04/16] removed db/module, a single file is enoguh --- ethcore/src/{db => }/db.rs | 0 ethcore/src/db/mod.rs | 18 ------------------ 2 files changed, 18 deletions(-) rename ethcore/src/{db => }/db.rs (100%) delete mode 100644 ethcore/src/db/mod.rs diff --git a/ethcore/src/db/db.rs b/ethcore/src/db.rs similarity index 100% rename from ethcore/src/db/db.rs rename to ethcore/src/db.rs diff --git a/ethcore/src/db/mod.rs b/ethcore/src/db/mod.rs deleted file mode 100644 index f56ff3cfc..000000000 --- a/ethcore/src/db/mod.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2015, 2016 Ethcore (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 . - -pub mod db; -pub use self::db::{Key, Writable, Readable, CacheUpdatePolicy}; From 26ed38ecc293d9922be3fc28a8fb564b2d4051d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 09:43:42 +0200 Subject: [PATCH 05/16] Version 0.2.2 of status page --- Cargo.lock | 6 +++--- webapp/Cargo.toml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 576920fab..3e3520acd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -356,7 +356,7 @@ dependencies = [ "jsonrpc-core 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-http-server 5.0.1 (git+https://github.com/debris/jsonrpc-http-server.git)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "parity-status 0.2.1 (git+https://github.com/tomusdrw/parity-status.git)", + "parity-status 0.2.2 (git+https://github.com/tomusdrw/parity-status.git)", "parity-wallet 0.1.1 (git+https://github.com/tomusdrw/parity-wallet.git)", "parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)", "url 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -746,8 +746,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "parity-status" -version = "0.2.1" -source = "git+https://github.com/tomusdrw/parity-status.git#3ed519c39d625fe8dcdb83eae34590f1e53e589e" +version = "0.2.2" +source = "git+https://github.com/tomusdrw/parity-status.git#fb9cb1ebe33e97fde5ae6ef8f3a94685d3f6f2aa" dependencies = [ "parity-webapp 0.1.0 (git+https://github.com/tomusdrw/parity-webapp.git)", ] diff --git a/webapp/Cargo.toml b/webapp/Cargo.toml index db15e5786..18a534857 100644 --- a/webapp/Cargo.toml +++ b/webapp/Cargo.toml @@ -17,7 +17,7 @@ ethcore-rpc = { path = "../rpc" } ethcore-util = { path = "../util" } parity-webapp = { git = "https://github.com/tomusdrw/parity-webapp.git" } # List of apps -parity-status = { git = "https://github.com/tomusdrw/parity-status.git", version = "0.2.1" } +parity-status = { git = "https://github.com/tomusdrw/parity-status.git", version = "0.2.2" } parity-wallet = { git = "https://github.com/tomusdrw/parity-wallet.git", optional = true } clippy = { version = "0.0.63", optional = true} From 09b2d7b3a6726faf09424c41949009320ff42daa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 13:12:43 +0200 Subject: [PATCH 06/16] Separating RPC --- parity/die.rs | 54 +++++++++++++++ parity/hypervisor/mod.rs | 1 + parity/main.rs | 142 ++++----------------------------------- parity/rpc.rs | 85 +++++++++++++++++++++++ parity/setup_log.rs | 60 +++++++++++++++++ 5 files changed, 213 insertions(+), 129 deletions(-) create mode 100644 parity/die.rs create mode 100644 parity/rpc.rs create mode 100644 parity/setup_log.rs diff --git a/parity/die.rs b/parity/die.rs new file mode 100644 index 000000000..602e2cdee --- /dev/null +++ b/parity/die.rs @@ -0,0 +1,54 @@ +// Copyright 2015, 2016 Ethcore (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 . + +use std; +use ethcore; +use util::UtilError; +use std::process::exit; + +#[macro_export] +macro_rules! die { + ($($arg:tt)*) => (die_with_message(&format!("{}", format_args!($($arg)*)))); +} + +pub fn die_with_error(e: ethcore::error::Error) -> ! { + use ethcore::error::Error; + + match e { + Error::Util(UtilError::StdIo(e)) => die_with_io_error(e), + _ => die!("{:?}", e), + } +} + +pub fn die_with_io_error(e: std::io::Error) -> ! { + match e.kind() { + std::io::ErrorKind::PermissionDenied => { + die!("No permissions to bind to specified port.") + }, + std::io::ErrorKind::AddrInUse => { + die!("Specified address is already in use. Please make sure that nothing is listening on the same port or try using a different one.") + }, + std::io::ErrorKind::AddrNotAvailable => { + die!("Could not use specified interface or given address is invalid.") + }, + _ => die!("{:?}", e), + } +} + +pub fn die_with_message(msg: &str) -> ! { + println!("ERROR: {}", msg); + exit(1); +} diff --git a/parity/hypervisor/mod.rs b/parity/hypervisor/mod.rs index bbc95b150..1776aa023 100644 --- a/parity/hypervisor/mod.rs +++ b/parity/hypervisor/mod.rs @@ -116,6 +116,7 @@ impl Hypervisor { } } +#[cfg(test)] mod tests { use super::*; use std::sync::atomic::{AtomicBool,Ordering}; diff --git a/parity/main.rs b/parity/main.rs index dff7ceaeb..612719ce2 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -20,6 +20,7 @@ #![cfg_attr(feature="dev", feature(plugin))] #![cfg_attr(feature="dev", plugin(clippy))] #![cfg_attr(feature="dev", allow(useless_format))] + extern crate docopt; extern crate num_cpus; extern crate rustc_serialize; @@ -46,7 +47,8 @@ extern crate bincode; #[macro_use] extern crate hyper; #[cfg(feature = "rpc")] -extern crate ethcore_rpc as rpc; +extern crate ethcore_rpc; + #[cfg(feature = "webapp")] extern crate ethcore_webapp as webapp; @@ -54,9 +56,7 @@ use std::io::{BufRead, BufReader}; use std::fs::File; use std::net::{SocketAddr, IpAddr}; use std::env; -use std::process::exit; use std::path::PathBuf; -use env_logger::LogBuilder; use ctrlc::CtrlC; use util::*; use util::panics::{MayPanic, ForwardPanic, PanicHandler}; @@ -70,24 +70,20 @@ use ethminer::{Miner, MinerService}; use docopt::Docopt; use daemonize::Daemonize; use number_prefix::{binary_prefix, Standalone, Prefixed}; -#[cfg(feature = "rpc")] -use rpc::Server as RpcServer; + #[cfg(feature = "webapp")] use webapp::Server as WebappServer; +#[macro_use] +mod die; mod price_info; mod upgrade; mod hypervisor; +mod setup_log; +mod rpc; -fn die_with_message(msg: &str) -> ! { - println!("ERROR: {}", msg); - exit(1); -} - -#[macro_export] -macro_rules! die { - ($($arg:tt)*) => (die_with_message(&format!("{}", format_args!($($arg)*)))); -} +use die::*; +use rpc::RpcServer; const USAGE: &'static str = r#" Parity. Ethereum Client. @@ -281,78 +277,6 @@ struct Args { flag_networkid: Option, } -fn setup_log(init: &Option) -> Arc { - use rlog::*; - - let mut levels = String::new(); - let mut builder = LogBuilder::new(); - builder.filter(None, LogLevelFilter::Info); - - if env::var("RUST_LOG").is_ok() { - let lvl = &env::var("RUST_LOG").unwrap(); - levels.push_str(&lvl); - levels.push_str(","); - builder.parse(lvl); - } - - if let Some(ref s) = *init { - levels.push_str(s); - builder.parse(s); - } - - let logs = Arc::new(RotatingLogger::new(levels)); - let log2 = logs.clone(); - let format = move |record: &LogRecord| { - let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap(); - let format = if max_log_level() <= LogLevelFilter::Info { - format!("{}{}", timestamp, record.args()) - } else { - format!("{}{}:{}: {}", timestamp, record.level(), record.target(), record.args()) - }; - log2.append(format.clone()); - format - }; - builder.format(format); - builder.init().unwrap(); - logs -} - -#[cfg(feature = "rpc")] -fn setup_rpc_server( - client: Arc, - sync: Arc, - secret_store: Arc, - miner: Arc, - url: &SocketAddr, - cors_domain: Option, - apis: Vec<&str>, - logger: Arc, -) -> RpcServer { - use rpc::v1::*; - - let server = rpc::RpcServer::new(); - for api in apis.into_iter() { - match api { - "web3" => server.add_delegate(Web3Client::new().to_delegate()), - "net" => server.add_delegate(NetClient::new(&sync).to_delegate()), - "eth" => { - server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate()); - server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate()); - }, - "personal" => server.add_delegate(PersonalClient::new(&secret_store).to_delegate()), - "ethcore" => server.add_delegate(EthcoreClient::new(&miner, logger.clone()).to_delegate()), - _ => { - die!("{}: Invalid API name to be enabled.", api); - }, - } - } - let start_result = server.start_http(url, cors_domain); - match start_result { - Err(rpc::RpcServerError::IoError(err)) => die_with_io_error(err), - Err(e) => die!("{:?}", e), - Ok(server) => server, - } -} #[cfg(feature = "webapp")] fn setup_webapp_server( @@ -364,7 +288,7 @@ fn setup_webapp_server( auth: Option<(String, String)>, logger: Arc, ) -> WebappServer { - use rpc::v1::*; + use ethcore_rpc::v1::*; let server = webapp::ServerBuilder::new(); server.add_delegate(Web3Client::new().to_delegate()); @@ -389,22 +313,6 @@ fn setup_webapp_server( } -#[cfg(not(feature = "rpc"))] -struct RpcServer; - -#[cfg(not(feature = "rpc"))] -fn setup_rpc_server( - _client: Arc, - _sync: Arc, - _secret_store: Arc, - _miner: Arc, - _url: &SocketAddr, - _cors_domain: Option, - _apis: Vec<&str>, - _logger: Arc, -) -> ! { - die!("Your Parity version has been compiled without JSON-RPC support.") -} #[cfg(not(feature = "webapp"))] struct WebappServer; @@ -704,7 +612,7 @@ impl Configuration { let panic_handler = PanicHandler::new_in_arc(); // Setup logging - let logger = setup_log(&self.args.flag_logging); + let logger = setup_log::setup_log(&self.args.flag_logging); // Raise fdlimit unsafe { ::fdlimit::raise_fd_limit(); } @@ -749,7 +657,7 @@ impl Configuration { let addr = SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid JSONRPC listen host/port given.", url)); let cors_domain = self.args.flag_jsonrpc_cors.clone().or(self.args.flag_rpccorsdomain.clone()); - Some(setup_rpc_server( + Some(rpc::setup_rpc_server( service.client(), sync.clone(), account_service.clone(), @@ -828,31 +736,7 @@ fn wait_for_exit(panic_handler: Arc, _rpc_server: Option ! { - use ethcore::error::Error; - - match e { - Error::Util(UtilError::StdIo(e)) => die_with_io_error(e), - _ => die!("{:?}", e), - } -} -fn die_with_io_error(e: std::io::Error) -> ! { - match e.kind() { - std::io::ErrorKind::PermissionDenied => { - die!("No permissions to bind to specified port.") - }, - std::io::ErrorKind::AddrInUse => { - die!("Specified address is already in use. Please make sure that nothing is listening on the same port or try using a different one.") - }, - std::io::ErrorKind::AddrNotAvailable => { - die!("Could not use specified interface or given address is invalid.") - }, - _ => die!("{:?}", e), - } -} - fn main() { - Configuration::parse().execute(); } diff --git a/parity/rpc.rs b/parity/rpc.rs new file mode 100644 index 000000000..b5a2eadb5 --- /dev/null +++ b/parity/rpc.rs @@ -0,0 +1,85 @@ +// Copyright 2015, 2016 Ethcore (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 . + + +use std::sync::Arc; +use std::net::SocketAddr; +use ethcore::client::Client; +use ethsync::EthSync; +use ethminer::Miner; +use util::RotatingLogger; +use util::keys::store::{AccountService}; +use die::*; + +#[cfg(feature = "rpc")] +pub use ethcore_rpc::Server as RpcServer; +#[cfg(feature = "rpc")] +use ethcore_rpc::{RpcServerError, RpcServer as Server}; + +#[cfg(not(feature = "rpc"))] +pub struct RpcServer; + +#[cfg(not(feature = "rpc"))] +pub fn setup_rpc_server( + _client: Arc, + _sync: Arc, + _secret_store: Arc, + _miner: Arc, + _url: &SocketAddr, + _cors_domain: Option, + _apis: Vec<&str>, + _logger: Arc, +) -> ! { + die!("Your Parity version has been compiled without JSON-RPC support.") +} + +#[cfg(feature = "rpc")] +pub fn setup_rpc_server( + client: Arc, + sync: Arc, + secret_store: Arc, + miner: Arc, + url: &SocketAddr, + cors_domain: Option, + apis: Vec<&str>, + logger: Arc, +) -> RpcServer { + use ethcore_rpc::v1::*; + + let server = Server::new(); + for api in apis.into_iter() { + match api { + "web3" => server.add_delegate(Web3Client::new().to_delegate()), + "net" => server.add_delegate(NetClient::new(&sync).to_delegate()), + "eth" => { + server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate()); + server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate()); + }, + "personal" => server.add_delegate(PersonalClient::new(&secret_store).to_delegate()), + "ethcore" => server.add_delegate(EthcoreClient::new(&miner, logger.clone()).to_delegate()), + _ => { + die!("{}: Invalid API name to be enabled.", api); + }, + } + } + let start_result = server.start_http(url, cors_domain); + match start_result { + Err(RpcServerError::IoError(err)) => die_with_io_error(err), + Err(e) => die!("{:?}", e), + Ok(server) => server, + } +} + diff --git a/parity/setup_log.rs b/parity/setup_log.rs new file mode 100644 index 000000000..13ae41c40 --- /dev/null +++ b/parity/setup_log.rs @@ -0,0 +1,60 @@ +// Copyright 2015, 2016 Ethcore (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 . + + +use std::env; +use std::sync::Arc; +use time; +use env_logger::LogBuilder; +use util::{RotatingLogger}; + +/// Sets up the logger +pub fn setup_log(init: &Option) -> Arc { + use rlog::*; + + let mut levels = String::new(); + let mut builder = LogBuilder::new(); + builder.filter(None, LogLevelFilter::Info); + + if env::var("RUST_LOG").is_ok() { + let lvl = &env::var("RUST_LOG").unwrap(); + levels.push_str(&lvl); + levels.push_str(","); + builder.parse(lvl); + } + + if let Some(ref s) = *init { + levels.push_str(s); + builder.parse(s); + } + + let logs = Arc::new(RotatingLogger::new(levels)); + let log2 = logs.clone(); + let format = move |record: &LogRecord| { + let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap(); + let format = if max_log_level() <= LogLevelFilter::Info { + format!("{}{}", timestamp, record.args()) + } else { + format!("{}{}:{}: {}", timestamp, record.level(), record.target(), record.args()) + }; + log2.append(format.clone()); + format + }; + builder.format(format); + builder.init().unwrap(); + logs +} + From 3e4adcb3b6e5443449b397c9265d0cb5fd2e2994 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 13:57:27 +0200 Subject: [PATCH 07/16] Splitting informant,io_handler and webapps --- parity/informant.rs | 89 ++++++++++++++++ parity/io_handler.rs | 54 ++++++++++ parity/main.rs | 248 ++++++------------------------------------- parity/rpc.rs | 57 +++++++--- parity/webapp.rs | 117 ++++++++++++++++++++ 5 files changed, 335 insertions(+), 230 deletions(-) create mode 100644 parity/informant.rs create mode 100644 parity/io_handler.rs create mode 100644 parity/webapp.rs diff --git a/parity/informant.rs b/parity/informant.rs new file mode 100644 index 000000000..866e92e40 --- /dev/null +++ b/parity/informant.rs @@ -0,0 +1,89 @@ +// Copyright 2015, 2016 Ethcore (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 . + +use std::sync::RwLock; +use std::ops::{Deref, DerefMut}; +use ethsync::{EthSync, SyncProvider}; +use ethcore::client::*; +use number_prefix::{binary_prefix, Standalone, Prefixed}; + +pub struct Informant { + chain_info: RwLock>, + cache_info: RwLock>, + report: RwLock>, +} + +impl Default for Informant { + fn default() -> Self { + Informant { + chain_info: RwLock::new(None), + cache_info: RwLock::new(None), + report: RwLock::new(None), + } + } +} + +impl Informant { + fn format_bytes(b: usize) -> String { + match binary_prefix(b as f64) { + Standalone(bytes) => format!("{} bytes", bytes), + Prefixed(prefix, n) => format!("{:.0} {}B", n, prefix), + } + } + + pub fn tick(&self, client: &Client, sync: &EthSync) { + // 5 seconds betwen calls. TODO: calculate this properly. + let dur = 5usize; + + let chain_info = client.chain_info(); + let queue_info = client.queue_info(); + let cache_info = client.blockchain_cache_info(); + let sync_info = sync.status(); + + let mut write_report = self.report.write().unwrap(); + let report = client.report(); + + if let (_, _, &Some(ref last_report)) = ( + self.chain_info.read().unwrap().deref(), + self.cache_info.read().unwrap().deref(), + write_report.deref() + ) { + println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} db, {} chain, {} queue, {} sync ]", + chain_info.best_block_number, + chain_info.best_block_hash, + (report.blocks_imported - last_report.blocks_imported) / dur, + (report.transactions_applied - last_report.transactions_applied) / dur, + (report.gas_processed - last_report.gas_processed) / From::from(dur), + + sync_info.num_active_peers, + sync_info.num_peers, + sync_info.last_imported_block_number.unwrap_or(chain_info.best_block_number), + queue_info.unverified_queue_size, + queue_info.verified_queue_size, + + Informant::format_bytes(report.state_db_mem), + Informant::format_bytes(cache_info.total()), + Informant::format_bytes(queue_info.mem_used), + Informant::format_bytes(sync_info.mem_used), + ); + } + + *self.chain_info.write().unwrap().deref_mut() = Some(chain_info); + *self.cache_info.write().unwrap().deref_mut() = Some(cache_info); + *write_report.deref_mut() = Some(report); + } +} + diff --git a/parity/io_handler.rs b/parity/io_handler.rs new file mode 100644 index 000000000..07cebb5b7 --- /dev/null +++ b/parity/io_handler.rs @@ -0,0 +1,54 @@ +// Copyright 2015, 2016 Ethcore (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 . + +use std::sync::Arc; +use ethcore::client::Client; +use ethcore::service::NetSyncMessage; +use ethsync::EthSync; +use util::keys::store::AccountService; +use util::{TimerToken, IoHandler, IoContext}; + +use informant::Informant; + +const INFO_TIMER: TimerToken = 0; + +const ACCOUNT_TICK_TIMER: TimerToken = 10; +const ACCOUNT_TICK_MS: u64 = 60000; + +pub struct ClientIoHandler { + pub client: Arc, + pub sync: Arc, + pub accounts: Arc, + pub info: Informant, +} + +impl IoHandler for ClientIoHandler { + fn initialize(&self, io: &IoContext) { + io.register_timer(INFO_TIMER, 5000).expect("Error registering timer"); + io.register_timer(ACCOUNT_TICK_TIMER, ACCOUNT_TICK_MS).expect("Error registering account timer"); + + } + + fn timeout(&self, _io: &IoContext, timer: TimerToken) { + match timer { + INFO_TIMER => { self.info.tick(&self.client, &self.sync); } + ACCOUNT_TICK_TIMER => { self.accounts.tick(); }, + _ => {} + } + } +} + + diff --git a/parity/main.rs b/parity/main.rs index 612719ce2..ecd93d02b 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -42,7 +42,6 @@ extern crate ethcore_ipc as ipc; extern crate ethcore_ipc_nano as nanoipc; extern crate serde; extern crate bincode; - // for price_info.rs #[macro_use] extern crate hyper; @@ -50,7 +49,7 @@ extern crate bincode; extern crate ethcore_rpc; #[cfg(feature = "webapp")] -extern crate ethcore_webapp as webapp; +extern crate ethcore_webapp; use std::io::{BufRead, BufReader}; use std::fs::File; @@ -63,16 +62,12 @@ use util::panics::{MayPanic, ForwardPanic, PanicHandler}; use util::keys::store::*; use ethcore::spec::*; use ethcore::client::*; -use ethcore::service::{ClientService, NetSyncMessage}; use ethcore::ethereum; -use ethsync::{EthSync, SyncConfig, SyncProvider}; +use ethcore::service::ClientService; +use ethsync::{EthSync, SyncConfig}; use ethminer::{Miner, MinerService}; use docopt::Docopt; use daemonize::Daemonize; -use number_prefix::{binary_prefix, Standalone, Prefixed}; - -#[cfg(feature = "webapp")] -use webapp::Server as WebappServer; #[macro_use] mod die; @@ -81,9 +76,14 @@ mod upgrade; mod hypervisor; mod setup_log; mod rpc; +mod webapp; +mod informant; +mod io_handler; use die::*; use rpc::RpcServer; +use webapp::WebappServer; +use io_handler::ClientIoHandler; const USAGE: &'static str = r#" Parity. Ethereum Client. @@ -278,58 +278,6 @@ struct Args { } -#[cfg(feature = "webapp")] -fn setup_webapp_server( - client: Arc, - sync: Arc, - secret_store: Arc, - miner: Arc, - url: &SocketAddr, - auth: Option<(String, String)>, - logger: Arc, -) -> WebappServer { - use ethcore_rpc::v1::*; - - let server = webapp::ServerBuilder::new(); - server.add_delegate(Web3Client::new().to_delegate()); - server.add_delegate(NetClient::new(&sync).to_delegate()); - server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate()); - server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate()); - server.add_delegate(PersonalClient::new(&secret_store).to_delegate()); - server.add_delegate(EthcoreClient::new(&miner, logger).to_delegate()); - let start_result = match auth { - None => { - server.start_unsecure_http(url) - }, - Some((username, password)) => { - server.start_basic_auth_http(url, &username, &password) - }, - }; - match start_result { - Err(webapp::ServerError::IoError(err)) => die_with_io_error(err), - Err(e) => die!("{:?}", e), - Ok(handle) => handle, - } - -} - - -#[cfg(not(feature = "webapp"))] -struct WebappServer; - -#[cfg(not(feature = "webapp"))] -fn setup_webapp_server( - _client: Arc, - _sync: Arc, - _secret_store: Arc, - _miner: Arc, - _url: &SocketAddr, - _auth: Option<(String, String)>, - _logger: Arc, -) -> ! { - die!("Your Parity version has been compiled without WebApps support.") -} - fn print_version() { println!("\ Parity @@ -644,66 +592,33 @@ impl Configuration { let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone()); // Setup rpc - let rpc_server = if self.args.flag_jsonrpc || self.args.flag_rpc { - let apis = self.args.flag_rpcapi.as_ref().unwrap_or(&self.args.flag_jsonrpc_apis); - let url = format!("{}:{}", - match self.args.flag_rpcaddr.as_ref().unwrap_or(&self.args.flag_jsonrpc_interface).as_str() { - "all" => "0.0.0.0", - "local" => "127.0.0.1", - x => x, - }, - self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port) - ); - let addr = SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid JSONRPC listen host/port given.", url)); - let cors_domain = self.args.flag_jsonrpc_cors.clone().or(self.args.flag_rpccorsdomain.clone()); + let rpc_server = rpc::new(rpc::Configuration { + enabled: self.args.flag_jsonrpc || self.args.flag_rpc, + interface: self.args.flag_rpcaddr.clone().unwrap_or(self.args.flag_jsonrpc_interface.clone()), + port: self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port), + apis: self.args.flag_rpcapi.clone().unwrap_or(self.args.flag_jsonrpc_apis.clone()), + cors: self.args.flag_jsonrpc_cors.clone().or(self.args.flag_rpccorsdomain.clone()), + }, rpc::Dependencies { + client: client.clone(), + sync: sync.clone(), + secret_store: account_service.clone(), + miner: miner.clone(), + logger: logger.clone() + }); - Some(rpc::setup_rpc_server( - service.client(), - sync.clone(), - account_service.clone(), - miner.clone(), - &addr, - cors_domain, - apis.split(',').collect(), - logger.clone(), - )) - } else { - None - }; - - let webapp_server = if self.args.flag_webapp { - let url = format!("{}:{}", - match self.args.flag_webapp_interface.as_str() { - "all" => "0.0.0.0", - "local" => "127.0.0.1", - x => x, - }, - self.args.flag_webapp_port - ); - let addr = SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid Webapps listen host/port given.", url)); - let auth = self.args.flag_webapp_user.as_ref().map(|username| { - let password = self.args.flag_webapp_pass.as_ref().map_or_else(|| { - use rpassword::read_password; - println!("Type password for WebApps server (user: {}): ", username); - let pass = read_password().unwrap(); - println!("OK, got it. Starting server..."); - pass - }, |pass| pass.to_owned()); - (username.to_owned(), password) - }); - - Some(setup_webapp_server( - service.client(), - sync.clone(), - account_service.clone(), - miner.clone(), - &addr, - auth, - logger.clone(), - )) - } else { - None - }; + let webapp_server = webapp::new(webapp::Configuration { + enabled: self.args.flag_webapp, + interface: self.args.flag_webapp_interface.clone(), + port: self.args.flag_webapp_port, + user: self.args.flag_webapp_user.clone(), + pass: self.args.flag_webapp_pass.clone(), + }, webapp::Dependencies { + client: client.clone(), + sync: sync.clone(), + secret_store: account_service.clone(), + miner: miner.clone(), + logger: logger.clone() + }); // Register IO handler let io_handler = Arc::new(ClientIoHandler { @@ -740,101 +655,6 @@ fn main() { Configuration::parse().execute(); } -struct Informant { - chain_info: RwLock>, - cache_info: RwLock>, - report: RwLock>, -} - -impl Default for Informant { - fn default() -> Self { - Informant { - chain_info: RwLock::new(None), - cache_info: RwLock::new(None), - report: RwLock::new(None), - } - } -} - -impl Informant { - fn format_bytes(b: usize) -> String { - match binary_prefix(b as f64) { - Standalone(bytes) => format!("{} bytes", bytes), - Prefixed(prefix, n) => format!("{:.0} {}B", n, prefix), - } - } - - pub fn tick(&self, client: &Client, sync: &EthSync) { - // 5 seconds betwen calls. TODO: calculate this properly. - let dur = 5usize; - - let chain_info = client.chain_info(); - let queue_info = client.queue_info(); - let cache_info = client.blockchain_cache_info(); - let sync_info = sync.status(); - - let mut write_report = self.report.write().unwrap(); - let report = client.report(); - - if let (_, _, &Some(ref last_report)) = ( - self.chain_info.read().unwrap().deref(), - self.cache_info.read().unwrap().deref(), - write_report.deref() - ) { - println!("[ #{} {} ]---[ {} blk/s | {} tx/s | {} gas/s //··· {}/{} peers, #{}, {}+{} queued ···// mem: {} db, {} chain, {} queue, {} sync ]", - chain_info.best_block_number, - chain_info.best_block_hash, - (report.blocks_imported - last_report.blocks_imported) / dur, - (report.transactions_applied - last_report.transactions_applied) / dur, - (report.gas_processed - last_report.gas_processed) / From::from(dur), - - sync_info.num_active_peers, - sync_info.num_peers, - sync_info.last_imported_block_number.unwrap_or(chain_info.best_block_number), - queue_info.unverified_queue_size, - queue_info.verified_queue_size, - - Informant::format_bytes(report.state_db_mem), - Informant::format_bytes(cache_info.total()), - Informant::format_bytes(queue_info.mem_used), - Informant::format_bytes(sync_info.mem_used), - ); - } - - *self.chain_info.write().unwrap().deref_mut() = Some(chain_info); - *self.cache_info.write().unwrap().deref_mut() = Some(cache_info); - *write_report.deref_mut() = Some(report); - } -} - -const INFO_TIMER: TimerToken = 0; - -const ACCOUNT_TICK_TIMER: TimerToken = 10; -const ACCOUNT_TICK_MS: u64 = 60000; - -struct ClientIoHandler { - client: Arc, - sync: Arc, - accounts: Arc, - info: Informant, -} - -impl IoHandler for ClientIoHandler { - fn initialize(&self, io: &IoContext) { - io.register_timer(INFO_TIMER, 5000).expect("Error registering timer"); - io.register_timer(ACCOUNT_TICK_TIMER, ACCOUNT_TICK_MS).expect("Error registering account timer"); - - } - - fn timeout(&self, _io: &IoContext, timer: TimerToken) { - match timer { - INFO_TIMER => { self.info.tick(&self.client, &self.sync); } - ACCOUNT_TICK_TIMER => { self.accounts.tick(); }, - _ => {} - } - } -} - /// Parity needs at least 1 test to generate coverage reports correctly. #[test] fn if_works() { diff --git a/parity/rpc.rs b/parity/rpc.rs index b5a2eadb5..b2e6391d8 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -15,6 +15,7 @@ // along with Parity. If not, see . +use std::str::FromStr; use std::sync::Arc; use std::net::SocketAddr; use ethcore::client::Client; @@ -28,34 +29,58 @@ use die::*; pub use ethcore_rpc::Server as RpcServer; #[cfg(feature = "rpc")] use ethcore_rpc::{RpcServerError, RpcServer as Server}; - #[cfg(not(feature = "rpc"))] pub struct RpcServer; +pub struct Configuration { + pub enabled: bool, + pub interface: String, + pub port: u16, + pub apis: String, + pub cors: Option, +} + +pub struct Dependencies { + pub client: Arc, + pub sync: Arc, + pub secret_store: Arc, + pub miner: Arc, + pub logger: Arc, +} + +pub fn new(conf: Configuration, deps: Dependencies) -> Option { + if !conf.enabled { + return None; + } + + let interface = match conf.interface.as_str() { + "all" => "0.0.0.0", + "local" => "127.0.0.1", + x => x, + }; + let apis = conf.apis.split(',').collect(); + let url = format!("{}:{}", interface, conf.port); + let addr = SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid JSONRPC listen host/port given.", url)); + + Some(setup_rpc_server(deps, &addr, conf.cors, apis)) +} + #[cfg(not(feature = "rpc"))] pub fn setup_rpc_server( - _client: Arc, - _sync: Arc, - _secret_store: Arc, - _miner: Arc, + _deps: Dependencies, _url: &SocketAddr, _cors_domain: Option, _apis: Vec<&str>, - _logger: Arc, ) -> ! { die!("Your Parity version has been compiled without JSON-RPC support.") } #[cfg(feature = "rpc")] pub fn setup_rpc_server( - client: Arc, - sync: Arc, - secret_store: Arc, - miner: Arc, + deps: Dependencies, url: &SocketAddr, cors_domain: Option, apis: Vec<&str>, - logger: Arc, ) -> RpcServer { use ethcore_rpc::v1::*; @@ -63,13 +88,13 @@ pub fn setup_rpc_server( for api in apis.into_iter() { match api { "web3" => server.add_delegate(Web3Client::new().to_delegate()), - "net" => server.add_delegate(NetClient::new(&sync).to_delegate()), + "net" => server.add_delegate(NetClient::new(&deps.sync).to_delegate()), "eth" => { - server.add_delegate(EthClient::new(&client, &sync, &secret_store, &miner).to_delegate()); - server.add_delegate(EthFilterClient::new(&client, &miner).to_delegate()); + server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner).to_delegate()); + server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); }, - "personal" => server.add_delegate(PersonalClient::new(&secret_store).to_delegate()), - "ethcore" => server.add_delegate(EthcoreClient::new(&miner, logger.clone()).to_delegate()), + "personal" => server.add_delegate(PersonalClient::new(&deps.secret_store).to_delegate()), + "ethcore" => server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone()).to_delegate()), _ => { die!("{}: Invalid API name to be enabled.", api); }, diff --git a/parity/webapp.rs b/parity/webapp.rs new file mode 100644 index 000000000..74e8c2e51 --- /dev/null +++ b/parity/webapp.rs @@ -0,0 +1,117 @@ +// Copyright 2015, 2016 Ethcore (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 . + +use std::sync::Arc; +use std::str::FromStr; +use std::net::SocketAddr; +use ethcore::client::Client; +use ethsync::EthSync; +use ethminer::Miner; +use util::RotatingLogger; +use util::keys::store::{AccountService}; +use die::*; + +#[cfg(feature = "webapp")] +pub use ethcore_webapp::Server as WebappServer; +#[cfg(not(feature = "webapp"))] +pub struct WebappServer; + +pub struct Configuration { + pub enabled: bool, + pub interface: String, + pub port: u16, + pub user: Option, + pub pass: Option, +} + +pub struct Dependencies { + pub client: Arc, + pub sync: Arc, + pub secret_store: Arc, + pub miner: Arc, + pub logger: Arc, +} + +pub fn new(configuration: Configuration, deps: Dependencies) -> Option { + if !configuration.enabled { + return None; + } + + let interface = match configuration.interface.as_str() { + "all" => "0.0.0.0", + "local" => "127.0.0.1", + x => x, + }; + let url = format!("{}:{}", interface, configuration.port); + let addr = SocketAddr::from_str(&url).unwrap_or_else(|_| die!("{}: Invalid Webapps listen host/port given.", url)); + + let auth = configuration.user.as_ref().map(|username| { + let password = configuration.pass.as_ref().map_or_else(|| { + use rpassword::read_password; + println!("Type password for WebApps server (user: {}): ", username); + let pass = read_password().unwrap(); + println!("OK, got it. Starting server..."); + pass + }, |pass| pass.to_owned()); + (username.to_owned(), password) + }); + + Some(setup_webapp_server(deps, &addr, auth)) +} + +#[cfg(not(feature = "webapp"))] +pub fn setup_webapp_server( + _deps: Dependencies, + _url: &SocketAddr, + _auth: Option<(String, String)>, +) -> ! { + die!("Your Parity version has been compiled without WebApps support.") +} + +#[cfg(feature = "webapp")] +pub fn setup_webapp_server( + deps: Dependencies, + url: &SocketAddr, + auth: Option<(String, String)> +) -> WebappServer { + use ethcore_rpc::v1::*; + use ethcore_webapp as webapp; + + let server = webapp::ServerBuilder::new(); + server.add_delegate(Web3Client::new().to_delegate()); + server.add_delegate(NetClient::new(&deps.sync).to_delegate()); + server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner).to_delegate()); + server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); + server.add_delegate(PersonalClient::new(&deps.secret_store).to_delegate()); + server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger).to_delegate()); + + let start_result = match auth { + None => { + server.start_unsecure_http(url) + }, + Some((username, password)) => { + server.start_basic_auth_http(url, &username, &password) + }, + }; + + match start_result { + Err(webapp::ServerError::IoError(err)) => die_with_io_error(err), + Err(e) => die!("{:?}", e), + Ok(handle) => handle, + } + +} + From bad735a8e6320744f8f398c905f007092ed616bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 15:41:25 +0200 Subject: [PATCH 08/16] Separating --- parity/cli.rs | 223 ++++++++++++++++++ parity/main.rs | 534 ++++++++++++++----------------------------- parity/price_info.rs | 16 ++ 3 files changed, 407 insertions(+), 366 deletions(-) create mode 100644 parity/cli.rs diff --git a/parity/cli.rs b/parity/cli.rs new file mode 100644 index 000000000..45c621665 --- /dev/null +++ b/parity/cli.rs @@ -0,0 +1,223 @@ +// Copyright 2015, 2016 Ethcore (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 . + +use util::version; + +pub const USAGE: &'static str = r#" +Parity. Ethereum Client. + By Wood/Paronyan/Kotewicz/Drwięga/Volf. + Copyright 2015, 2016 Ethcore (UK) Limited + +Usage: + parity daemon [options] + parity account (new | list) [options] + parity [options] + +Protocol Options: + --chain CHAIN Specify the blockchain type. CHAIN may be either a + JSON chain specification file or olympic, frontier, + homestead, mainnet, morden, or testnet + [default: homestead]. + -d --db-path PATH Specify the database & configuration directory path + [default: $HOME/.parity]. + --keys-path PATH Specify the path for JSON key files to be found + [default: $HOME/.parity/keys]. + --identity NAME Specify your node's name. + +Account Options: + --unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution. + ACCOUNTS is a comma-delimited list of addresses. + --password FILE Provide a file containing a password for unlocking + an account. + +Networking Options: + --port PORT Override the port on which the node should listen + [default: 30303]. + --peers NUM Try to maintain that many peers [default: 25]. + --nat METHOD Specify method to use for determining public + address. Must be one of: any, none, upnp, + extip: [default: any]. + --network-id INDEX Override the network identifier from the chain we + are on. + --bootnodes NODES Override the bootnodes from our chain. NODES should + be comma-delimited enodes. + --no-discovery Disable new peer discovery. + --node-key KEY Specify node secret key, either as 64-character hex + string or input to SHA3 operation. + +API and Console Options: + -j --jsonrpc Enable the JSON-RPC API server. + --jsonrpc-port PORT Specify the port portion of the JSONRPC API server + [default: 8545]. + --jsonrpc-interface IP Specify the hostname portion of the JSONRPC API + server, IP should be an interface's IP address, or + all (all interfaces) or local [default: local]. + --jsonrpc-cors URL Specify CORS header for JSON-RPC API responses. + --jsonrpc-apis APIS Specify the APIs available through the JSONRPC + interface. APIS is a comma-delimited list of API + name. Possible name are web3, eth and net. + [default: web3,eth,net,personal,ethcore]. + -w --webapp Enable the web applications server (e.g. + status page). + --webapp-port PORT Specify the port portion of the WebApps server + [default: 8080]. + --webapp-interface IP Specify the hostname portion of the WebApps + server, IP should be an interface's IP address, or + all (all interfaces) or local [default: local]. + --webapp-user USERNAME Specify username for WebApps server. It will be + used in HTTP Basic Authentication Scheme. + If --webapp-pass is not specified you will be + asked for password on startup. + --webapp-pass PASSWORD Specify password for WebApps server. Use only in + conjunction with --webapp-user. + +Sealing/Mining Options: + --force-sealing Force the node to author new blocks as if it were + always sealing/mining. + --usd-per-tx USD Amount of USD to be paid for a basic transaction + [default: 0.005]. The minimum gas price is set + accordingly. + --usd-per-eth SOURCE USD value of a single ETH. SOURCE may be either an + amount in USD or a web service [default: etherscan]. + --gas-floor-target GAS Amount of gas per block to target when sealing a new + block [default: 4712388]. + --author ADDRESS Specify the block author (aka "coinbase") address + for sending block rewards from sealed blocks + [default: 0037a6b811ffeb6e072da21179d11b1406371c63]. + --extra-data STRING Specify a custom extra-data for authored blocks, no + more than 32 characters. + --tx-limit LIMIT Limit of transactions kept in the queue (waiting to + be included in next block) [default: 1024]. + +Footprint Options: + --pruning METHOD Configure pruning of the state/storage trie. METHOD + may be one of auto, archive, basic, fast, light: + archive - keep all state trie data. No pruning. + basic - reference count in disk DB. Slow but light. + fast - maintain journal overlay. Fast but 50MB used. + light - early merges with partial tracking. Fast + and light. Experimental! + auto - use the method most recently synced or + default to archive if none synced [default: auto]. + --cache-pref-size BYTES Specify the prefered size of the blockchain cache in + bytes [default: 16384]. + --cache-max-size BYTES Specify the maximum size of the blockchain cache in + bytes [default: 262144]. + --queue-max-size BYTES Specify the maximum size of memory to use for block + queue [default: 52428800]. + --cache MEGABYTES Set total amount of discretionary memory to use for + the entire system, overrides other cache and queue + options. + +Geth-compatibility Options: + --datadir PATH Equivalent to --db-path PATH. + --testnet Equivalent to --chain testnet. + --networkid INDEX Equivalent to --network-id INDEX. + --maxpeers COUNT Equivalent to --peers COUNT. + --nodekey KEY Equivalent to --node-key KEY. + --nodiscover Equivalent to --no-discovery. + --rpc Equivalent to --jsonrpc. + --rpcaddr IP Equivalent to --jsonrpc-interface IP. + --rpcport PORT Equivalent to --jsonrpc-port PORT. + --rpcapi APIS Equivalent to --jsonrpc-apis APIS. + --rpccorsdomain URL Equivalent to --jsonrpc-cors URL. + --gasprice WEI Minimum amount of Wei per GAS to be paid for a + transaction to be accepted for mining. Overrides + --basic-tx-usd. + --etherbase ADDRESS Equivalent to --author ADDRESS. + --extradata STRING Equivalent to --extra-data STRING. + +Miscellaneous Options: + -l --logging LOGGING Specify the logging level. Must conform to the same + format as RUST_LOG. + -v --version Show information about version. + -h --help Show this screen. +"#; + +#[derive(Debug, RustcDecodable)] +pub struct Args { + pub cmd_daemon: bool, + pub cmd_account: bool, + pub cmd_new: bool, + pub cmd_list: bool, + pub arg_pid_file: String, + pub flag_chain: String, + pub flag_db_path: String, + pub flag_identity: String, + pub flag_unlock: Option, + pub flag_password: Vec, + pub flag_cache: Option, + pub flag_keys_path: String, + pub flag_bootnodes: Option, + pub flag_network_id: Option, + pub flag_pruning: String, + pub flag_port: u16, + pub flag_peers: usize, + pub flag_no_discovery: bool, + pub flag_nat: String, + pub flag_node_key: Option, + pub flag_cache_pref_size: usize, + pub flag_cache_max_size: usize, + pub flag_queue_max_size: usize, + pub flag_jsonrpc: bool, + pub flag_jsonrpc_interface: String, + pub flag_jsonrpc_port: u16, + pub flag_jsonrpc_cors: Option, + pub flag_jsonrpc_apis: String, + pub flag_webapp: bool, + pub flag_webapp_port: u16, + pub flag_webapp_interface: String, + pub flag_webapp_user: Option, + pub flag_webapp_pass: Option, + pub flag_force_sealing: bool, + pub flag_author: String, + pub flag_usd_per_tx: String, + pub flag_usd_per_eth: String, + pub flag_gas_floor_target: String, + pub flag_extra_data: Option, + pub flag_tx_limit: usize, + pub flag_logging: Option, + pub flag_version: bool, + // geth-compatibility... + pub flag_nodekey: Option, + pub flag_nodiscover: bool, + pub flag_maxpeers: Option, + pub flag_datadir: Option, + pub flag_extradata: Option, + pub flag_etherbase: Option, + pub flag_gasprice: Option, + pub flag_rpc: bool, + pub flag_rpcaddr: Option, + pub flag_rpcport: Option, + pub flag_rpccorsdomain: Option, + pub flag_rpcapi: Option, + pub flag_testnet: bool, + pub flag_networkid: Option, +} + +pub fn print_version() { + println!("\ +Parity + version {} +Copyright 2015, 2016 Ethcore (UK) Limited +License GPLv3+: GNU GPL version 3 or later . +This is free software: you are free to change and redistribute it. +There is NO WARRANTY, to the extent permitted by law. + +By Wood/Paronyan/Kotewicz/Drwięga/Volf.\ +", version()); +} + diff --git a/parity/main.rs b/parity/main.rs index ecd93d02b..e8aa24839 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -59,10 +59,10 @@ use std::path::PathBuf; use ctrlc::CtrlC; use util::*; use util::panics::{MayPanic, ForwardPanic, PanicHandler}; -use util::keys::store::*; -use ethcore::spec::*; -use ethcore::client::*; +use util::keys::store::AccountService; use ethcore::ethereum; +use ethcore::client::{append_path, get_db_path, ClientConfig}; +use ethcore::spec::Spec; use ethcore::service::ClientService; use ethsync::{EthSync, SyncConfig}; use ethminer::{Miner, MinerService}; @@ -79,222 +79,181 @@ mod rpc; mod webapp; mod informant; mod io_handler; +mod cli; use die::*; +use cli::{USAGE, print_version, Args}; use rpc::RpcServer; use webapp::WebappServer; use io_handler::ClientIoHandler; -const USAGE: &'static str = r#" -Parity. Ethereum Client. - By Wood/Paronyan/Kotewicz/Drwięga/Volf. - Copyright 2015, 2016 Ethcore (UK) Limited - -Usage: - parity daemon [options] - parity account (new | list) [options] - parity [options] - -Protocol Options: - --chain CHAIN Specify the blockchain type. CHAIN may be either a - JSON chain specification file or olympic, frontier, - homestead, mainnet, morden, or testnet - [default: homestead]. - -d --db-path PATH Specify the database & configuration directory path - [default: $HOME/.parity]. - --keys-path PATH Specify the path for JSON key files to be found - [default: $HOME/.parity/keys]. - --identity NAME Specify your node's name. - -Account Options: - --unlock ACCOUNTS Unlock ACCOUNTS for the duration of the execution. - ACCOUNTS is a comma-delimited list of addresses. - --password FILE Provide a file containing a password for unlocking - an account. - -Networking Options: - --port PORT Override the port on which the node should listen - [default: 30303]. - --peers NUM Try to maintain that many peers [default: 25]. - --nat METHOD Specify method to use for determining public - address. Must be one of: any, none, upnp, - extip: [default: any]. - --network-id INDEX Override the network identifier from the chain we - are on. - --bootnodes NODES Override the bootnodes from our chain. NODES should - be comma-delimited enodes. - --no-discovery Disable new peer discovery. - --node-key KEY Specify node secret key, either as 64-character hex - string or input to SHA3 operation. - -API and Console Options: - -j --jsonrpc Enable the JSON-RPC API server. - --jsonrpc-port PORT Specify the port portion of the JSONRPC API server - [default: 8545]. - --jsonrpc-interface IP Specify the hostname portion of the JSONRPC API - server, IP should be an interface's IP address, or - all (all interfaces) or local [default: local]. - --jsonrpc-cors URL Specify CORS header for JSON-RPC API responses. - --jsonrpc-apis APIS Specify the APIs available through the JSONRPC - interface. APIS is a comma-delimited list of API - name. Possible name are web3, eth and net. - [default: web3,eth,net,personal,ethcore]. - -w --webapp Enable the web applications server (e.g. - status page). - --webapp-port PORT Specify the port portion of the WebApps server - [default: 8080]. - --webapp-interface IP Specify the hostname portion of the WebApps - server, IP should be an interface's IP address, or - all (all interfaces) or local [default: local]. - --webapp-user USERNAME Specify username for WebApps server. It will be - used in HTTP Basic Authentication Scheme. - If --webapp-pass is not specified you will be - asked for password on startup. - --webapp-pass PASSWORD Specify password for WebApps server. Use only in - conjunction with --webapp-user. - -Sealing/Mining Options: - --force-sealing Force the node to author new blocks as if it were - always sealing/mining. - --usd-per-tx USD Amount of USD to be paid for a basic transaction - [default: 0.005]. The minimum gas price is set - accordingly. - --usd-per-eth SOURCE USD value of a single ETH. SOURCE may be either an - amount in USD or a web service [default: etherscan]. - --gas-floor-target GAS Amount of gas per block to target when sealing a new - block [default: 4712388]. - --author ADDRESS Specify the block author (aka "coinbase") address - for sending block rewards from sealed blocks - [default: 0037a6b811ffeb6e072da21179d11b1406371c63]. - --extra-data STRING Specify a custom extra-data for authored blocks, no - more than 32 characters. - --tx-limit LIMIT Limit of transactions kept in the queue (waiting to - be included in next block) [default: 1024]. - -Footprint Options: - --pruning METHOD Configure pruning of the state/storage trie. METHOD - may be one of auto, archive, basic, fast, light: - archive - keep all state trie data. No pruning. - basic - reference count in disk DB. Slow but light. - fast - maintain journal overlay. Fast but 50MB used. - light - early merges with partial tracking. Fast - and light. Experimental! - auto - use the method most recently synced or - default to archive if none synced [default: auto]. - --cache-pref-size BYTES Specify the prefered size of the blockchain cache in - bytes [default: 16384]. - --cache-max-size BYTES Specify the maximum size of the blockchain cache in - bytes [default: 262144]. - --queue-max-size BYTES Specify the maximum size of memory to use for block - queue [default: 52428800]. - --cache MEGABYTES Set total amount of discretionary memory to use for - the entire system, overrides other cache and queue - options. - -Geth-compatibility Options: - --datadir PATH Equivalent to --db-path PATH. - --testnet Equivalent to --chain testnet. - --networkid INDEX Equivalent to --network-id INDEX. - --maxpeers COUNT Equivalent to --peers COUNT. - --nodekey KEY Equivalent to --node-key KEY. - --nodiscover Equivalent to --no-discovery. - --rpc Equivalent to --jsonrpc. - --rpcaddr IP Equivalent to --jsonrpc-interface IP. - --rpcport PORT Equivalent to --jsonrpc-port PORT. - --rpcapi APIS Equivalent to --jsonrpc-apis APIS. - --rpccorsdomain URL Equivalent to --jsonrpc-cors URL. - --gasprice WEI Minimum amount of Wei per GAS to be paid for a - transaction to be accepted for mining. Overrides - --basic-tx-usd. - --etherbase ADDRESS Equivalent to --author ADDRESS. - --extradata STRING Equivalent to --extra-data STRING. - -Miscellaneous Options: - -l --logging LOGGING Specify the logging level. Must conform to the same - format as RUST_LOG. - -v --version Show information about version. - -h --help Show this screen. -"#; - -#[derive(Debug, RustcDecodable)] -struct Args { - cmd_daemon: bool, - cmd_account: bool, - cmd_new: bool, - cmd_list: bool, - arg_pid_file: String, - flag_chain: String, - flag_db_path: String, - flag_identity: String, - flag_unlock: Option, - flag_password: Vec, - flag_cache: Option, - flag_keys_path: String, - flag_bootnodes: Option, - flag_network_id: Option, - flag_pruning: String, - flag_port: u16, - flag_peers: usize, - flag_no_discovery: bool, - flag_nat: String, - flag_node_key: Option, - flag_cache_pref_size: usize, - flag_cache_max_size: usize, - flag_queue_max_size: usize, - flag_jsonrpc: bool, - flag_jsonrpc_interface: String, - flag_jsonrpc_port: u16, - flag_jsonrpc_cors: Option, - flag_jsonrpc_apis: String, - flag_webapp: bool, - flag_webapp_port: u16, - flag_webapp_interface: String, - flag_webapp_user: Option, - flag_webapp_pass: Option, - flag_force_sealing: bool, - flag_author: String, - flag_usd_per_tx: String, - flag_usd_per_eth: String, - flag_gas_floor_target: String, - flag_extra_data: Option, - flag_tx_limit: usize, - flag_logging: Option, - flag_version: bool, - // geth-compatibility... - flag_nodekey: Option, - flag_nodiscover: bool, - flag_maxpeers: Option, - flag_datadir: Option, - flag_extradata: Option, - flag_etherbase: Option, - flag_gasprice: Option, - flag_rpc: bool, - flag_rpcaddr: Option, - flag_rpcport: Option, - flag_rpccorsdomain: Option, - flag_rpcapi: Option, - flag_testnet: bool, - flag_networkid: Option, -} - - -fn print_version() { - println!("\ -Parity - version {} -Copyright 2015, 2016 Ethcore (UK) Limited -License GPLv3+: GNU GPL version 3 or later . -This is free software: you are free to change and redistribute it. -There is NO WARRANTY, to the extent permitted by law. - -By Wood/Paronyan/Kotewicz/Drwięga/Volf.\ -", version()); -} - struct Configuration { args: Args } +fn main() { + let conf = Configuration::parse(); + execute(conf); +} + +fn execute(conf: Configuration) { + if conf.args.flag_version { + print_version(); + return; + } + + execute_upgrades(&conf); + + if conf.args.cmd_daemon { + Daemonize::new() + .pid_file(conf.args.arg_pid_file.clone()) + .chown_pid_file(true) + .start() + .unwrap_or_else(|e| die!("Couldn't daemonize; {}", e)); + } + + if conf.args.cmd_account { + execute_account_cli(conf); + return; + } + + execute_client(conf); +} + +fn execute_upgrades(conf: &Configuration) { + match ::upgrade::upgrade(Some(&conf.path())) { + Ok(upgrades_applied) if upgrades_applied > 0 => { + println!("Executed {} upgrade scripts - ok", upgrades_applied); + }, + Err(e) => { + die!("Error upgrading parity data: {:?}", e); + }, + _ => {}, + } +} + +fn execute_client(conf: Configuration) { + // Setup panic handler + let panic_handler = PanicHandler::new_in_arc(); + + // Setup logging + let logger = setup_log::setup_log(&conf.args.flag_logging); + // Raise fdlimit + unsafe { ::fdlimit::raise_fd_limit(); } + + let spec = conf.spec(); + let net_settings = conf.net_settings(&spec); + let sync_config = conf.sync_config(&spec); + let client_config = conf.client_config(&spec); + + // Secret Store + let account_service = Arc::new(conf.account_service()); + + // Build client + let mut service = ClientService::start( + client_config, spec, net_settings, &Path::new(&conf.path()) + ).unwrap_or_else(|e| die_with_error(e)); + + panic_handler.forward_from(&service); + let client = service.client(); + + // Miner + let miner = Miner::new(conf.args.flag_force_sealing); + miner.set_author(conf.author()); + miner.set_gas_floor_target(conf.gas_floor_target()); + miner.set_extra_data(conf.extra_data()); + miner.set_minimal_gas_price(conf.gas_price()); + miner.set_transactions_limit(conf.args.flag_tx_limit); + + // Sync + let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone()); + + // Setup rpc + let rpc_server = rpc::new(rpc::Configuration { + enabled: conf.args.flag_jsonrpc || conf.args.flag_rpc, + interface: conf.args.flag_rpcaddr.clone().unwrap_or(conf.args.flag_jsonrpc_interface.clone()), + port: conf.args.flag_rpcport.unwrap_or(conf.args.flag_jsonrpc_port), + apis: conf.args.flag_rpcapi.clone().unwrap_or(conf.args.flag_jsonrpc_apis.clone()), + cors: conf.args.flag_jsonrpc_cors.clone().or(conf.args.flag_rpccorsdomain.clone()), + }, rpc::Dependencies { + client: client.clone(), + sync: sync.clone(), + secret_store: account_service.clone(), + miner: miner.clone(), + logger: logger.clone() + }); + + let webapp_server = webapp::new(webapp::Configuration { + enabled: conf.args.flag_webapp, + interface: conf.args.flag_webapp_interface.clone(), + port: conf.args.flag_webapp_port, + user: conf.args.flag_webapp_user.clone(), + pass: conf.args.flag_webapp_pass.clone(), + }, webapp::Dependencies { + client: client.clone(), + sync: sync.clone(), + secret_store: account_service.clone(), + miner: miner.clone(), + logger: logger.clone() + }); + + // Register IO handler + let io_handler = Arc::new(ClientIoHandler { + client: service.client(), + info: Default::default(), + sync: sync.clone(), + accounts: account_service.clone(), + }); + service.io().register_handler(io_handler).expect("Error registering IO handler"); + + // Handle exit + wait_for_exit(panic_handler, rpc_server, webapp_server); +} + +fn execute_account_cli(conf: Configuration) { + use util::keys::store::SecretStore; + use rpassword::read_password; + let mut secret_store = SecretStore::new_in(Path::new(&conf.keys_path())); + if conf.args.cmd_new { + println!("Please note that password is NOT RECOVERABLE."); + print!("Type password: "); + let password = read_password().unwrap(); + print!("Repeat password: "); + let password_repeat = read_password().unwrap(); + if password != password_repeat { + println!("Passwords do not match!"); + return; + } + println!("New account address:"); + let new_address = secret_store.new_account(&password).unwrap(); + println!("{:?}", new_address); + return; + } + if conf.args.cmd_list { + println!("Known addresses:"); + for &(addr, _) in &secret_store.accounts().unwrap() { + println!("{:?}", addr); + } + } +} + +fn wait_for_exit(panic_handler: Arc, _rpc_server: Option, _webapp_server: Option) { + let exit = Arc::new(Condvar::new()); + + // Handle possible exits + let e = exit.clone(); + CtrlC::set_handler(move || { e.notify_all(); }); + + // Handle panics + let e = exit.clone(); + panic_handler.on_panic(move |_reason| { e.notify_all(); }); + + // Wait for signal + let mutex = Mutex::new(()); + let _ = exit.wait(mutex.lock().unwrap()).unwrap(); + info!("Finishing work, please wait..."); +} + impl Configuration { fn parse() -> Self { Configuration { @@ -474,64 +433,6 @@ impl Configuration { sync_config } - fn execute(&self) { - if self.args.flag_version { - print_version(); - return; - } - - match ::upgrade::upgrade(Some(&self.path())) { - Ok(upgrades_applied) => { - if upgrades_applied > 0 { - println!("Executed {} upgrade scripts - ok", upgrades_applied); - } - }, - Err(e) => { - die!("Error upgrading parity data: {:?}", e); - } - } - - if self.args.cmd_daemon { - Daemonize::new() - .pid_file(self.args.arg_pid_file.clone()) - .chown_pid_file(true) - .start() - .unwrap_or_else(|e| die!("Couldn't daemonize; {}", e)); - } - if self.args.cmd_account { - self.execute_account_cli(); - return; - } - self.execute_client(); - } - - fn execute_account_cli(&self) { - use util::keys::store::SecretStore; - use rpassword::read_password; - let mut secret_store = SecretStore::new_in(Path::new(&self.keys_path())); - if self.args.cmd_new { - println!("Please note that password is NOT RECOVERABLE."); - print!("Type password: "); - let password = read_password().unwrap(); - print!("Repeat password: "); - let password_repeat = read_password().unwrap(); - if password != password_repeat { - println!("Passwords do not match!"); - return; - } - println!("New account address:"); - let new_address = secret_store.new_account(&password).unwrap(); - println!("{:?}", new_address); - return; - } - if self.args.cmd_list { - println!("Known addresses:"); - for &(addr, _) in &secret_store.accounts().unwrap() { - println!("{:?}", addr); - } - } - } - fn account_service(&self) -> AccountService { // Secret Store let passwords = self.args.flag_password.iter().flat_map(|filename| { @@ -554,105 +455,6 @@ impl Configuration { } account_service } - - fn execute_client(&self) { - // Setup panic handler - let panic_handler = PanicHandler::new_in_arc(); - - // Setup logging - let logger = setup_log::setup_log(&self.args.flag_logging); - // Raise fdlimit - unsafe { ::fdlimit::raise_fd_limit(); } - - let spec = self.spec(); - let net_settings = self.net_settings(&spec); - let sync_config = self.sync_config(&spec); - let client_config = self.client_config(&spec); - - // Secret Store - let account_service = Arc::new(self.account_service()); - - // Build client - let mut service = ClientService::start( - client_config, spec, net_settings, &Path::new(&self.path()) - ).unwrap_or_else(|e| die_with_error(e)); - - panic_handler.forward_from(&service); - let client = service.client(); - - // Miner - let miner = Miner::new(self.args.flag_force_sealing); - miner.set_author(self.author()); - miner.set_gas_floor_target(self.gas_floor_target()); - miner.set_extra_data(self.extra_data()); - miner.set_minimal_gas_price(self.gas_price()); - miner.set_transactions_limit(self.args.flag_tx_limit); - - // Sync - let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone()); - - // Setup rpc - let rpc_server = rpc::new(rpc::Configuration { - enabled: self.args.flag_jsonrpc || self.args.flag_rpc, - interface: self.args.flag_rpcaddr.clone().unwrap_or(self.args.flag_jsonrpc_interface.clone()), - port: self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port), - apis: self.args.flag_rpcapi.clone().unwrap_or(self.args.flag_jsonrpc_apis.clone()), - cors: self.args.flag_jsonrpc_cors.clone().or(self.args.flag_rpccorsdomain.clone()), - }, rpc::Dependencies { - client: client.clone(), - sync: sync.clone(), - secret_store: account_service.clone(), - miner: miner.clone(), - logger: logger.clone() - }); - - let webapp_server = webapp::new(webapp::Configuration { - enabled: self.args.flag_webapp, - interface: self.args.flag_webapp_interface.clone(), - port: self.args.flag_webapp_port, - user: self.args.flag_webapp_user.clone(), - pass: self.args.flag_webapp_pass.clone(), - }, webapp::Dependencies { - client: client.clone(), - sync: sync.clone(), - secret_store: account_service.clone(), - miner: miner.clone(), - logger: logger.clone() - }); - - // Register IO handler - let io_handler = Arc::new(ClientIoHandler { - client: service.client(), - info: Default::default(), - sync: sync.clone(), - accounts: account_service.clone(), - }); - service.io().register_handler(io_handler).expect("Error registering IO handler"); - - // Handle exit - wait_for_exit(panic_handler, rpc_server, webapp_server); - } -} - -fn wait_for_exit(panic_handler: Arc, _rpc_server: Option, _webapp_server: Option) { - let exit = Arc::new(Condvar::new()); - - // Handle possible exits - let e = exit.clone(); - CtrlC::set_handler(move || { e.notify_all(); }); - - // Handle panics - let e = exit.clone(); - panic_handler.on_panic(move |_reason| { e.notify_all(); }); - - // Wait for signal - let mutex = Mutex::new(()); - let _ = exit.wait(mutex.lock().unwrap()).unwrap(); - info!("Finishing work, please wait..."); -} - -fn main() { - Configuration::parse().execute(); } /// Parity needs at least 1 test to generate coverage reports correctly. diff --git a/parity/price_info.rs b/parity/price_info.rs index 405424b3d..05b89d848 100644 --- a/parity/price_info.rs +++ b/parity/price_info.rs @@ -1,3 +1,19 @@ +// Copyright 2015, 2016 Ethcore (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 . + use rustc_serialize::json::Json; use std::io::Read; use hyper::Client; From 69af2de3bad98f223e6711146b26426315919190 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 16:02:11 +0200 Subject: [PATCH 09/16] Fixing clippy warning --- parity/setup_log.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parity/setup_log.rs b/parity/setup_log.rs index 13ae41c40..1fdc625f5 100644 --- a/parity/setup_log.rs +++ b/parity/setup_log.rs @@ -42,7 +42,7 @@ pub fn setup_log(init: &Option) -> Arc { } let logs = Arc::new(RotatingLogger::new(levels)); - let log2 = logs.clone(); + let loggger = logs.clone(); let format = move |record: &LogRecord| { let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap(); let format = if max_log_level() <= LogLevelFilter::Info { @@ -50,7 +50,7 @@ pub fn setup_log(init: &Option) -> Arc { } else { format!("{}{}:{}: {}", timestamp, record.level(), record.target(), record.args()) }; - log2.append(format.clone()); + logger.append(format.clone()); format }; builder.format(format); From c47d08e3084000f2fe98721bb4f050a021c48e00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 15:56:35 +0200 Subject: [PATCH 10/16] Fixing warnings --- ipc/codegen/src/codegen.rs | 6 +++--- ipc/codegen/src/typegen.rs | 4 ++-- json/src/spec/engine.rs | 2 +- miner/src/transaction_queue.rs | 2 +- parity/hypervisor/mod.rs | 7 +++++++ parity/main.rs | 4 ++-- parity/upgrade.rs | 3 ++- rpc/src/v1/tests/ethcore.rs | 1 + util/src/bytes.rs | 4 ++-- 9 files changed, 21 insertions(+), 12 deletions(-) diff --git a/ipc/codegen/src/codegen.rs b/ipc/codegen/src/codegen.rs index 1744f9a9c..71083fb78 100644 --- a/ipc/codegen/src/codegen.rs +++ b/ipc/codegen/src/codegen.rs @@ -77,7 +77,7 @@ fn push_handshake_struct(cx: &ExtCtxt, push: &mut FnMut(Annotatable)) { pub struct BinHandshake { api_version: String, protocol_version: String, - _reserved: Vec, + reserved: Vec, } ).unwrap(); @@ -306,7 +306,7 @@ fn strip_ptr(ty: &P) -> P { } fn has_ptr(ty: &P) -> bool { - if let ast::TyKind::Rptr(_, ref ptr_mut) = ty.node { + if let ast::TyKind::Rptr(_, ref _ptr_mut) = ty.node { true } else { false } @@ -625,7 +625,7 @@ fn push_client_implementation( let payload = BinHandshake { protocol_version: $item_ident::protocol_version().to_string(), api_version: $item_ident::api_version().to_string(), - _reserved: vec![0u8; 64], + reserved: vec![0u8; 64], }; let mut socket_ref = self.socket.borrow_mut(); diff --git a/ipc/codegen/src/typegen.rs b/ipc/codegen/src/typegen.rs index e6e828ee7..1f4ef5b63 100644 --- a/ipc/codegen/src/typegen.rs +++ b/ipc/codegen/src/typegen.rs @@ -138,11 +138,11 @@ pub fn push_bin_box( let serialize_impl = quote_item!(cx, impl ::serde::ser::Serialize for $ident { - fn serialize<__S>(&self, _serializer: &mut __S) -> ::std::result::Result<(), __S::Error> + fn serialize<__S>(&self, serializer: &mut __S) -> ::std::result::Result<(), __S::Error> where __S: ::serde::ser::Serializer { let &$ident(ref val) = self; - _serializer.serialize_bytes(val.as_slice()) + serializer.serialize_bytes(val.as_slice()) } }).unwrap(); diff --git a/json/src/spec/engine.rs b/json/src/spec/engine.rs index 6b56d7b71..e5f5dc718 100644 --- a/json/src/spec/engine.rs +++ b/json/src/spec/engine.rs @@ -16,7 +16,7 @@ //! Engine deserialization. -use serde::{Deserializer, Error}; +use serde::Deserializer; use serde::de::Visitor; use spec::Ethash; diff --git a/miner/src/transaction_queue.rs b/miner/src/transaction_queue.rs index e9f2570e3..63b49f9f4 100644 --- a/miner/src/transaction_queue.rs +++ b/miner/src/transaction_queue.rs @@ -230,7 +230,7 @@ impl TransactionSet { by_hash.remove(&order.hash) .expect("Hash found in `by_priorty` matches the one dropped; so it is included in `by_hash`"); - let max = removed.get(&sender).map(|val| cmp::max(*val, nonce)).unwrap_or(nonce); + let max = removed.get(&sender).map_or(nonce, |val| cmp::max(*val, nonce)); removed.insert(sender, max); removed })) diff --git a/parity/hypervisor/mod.rs b/parity/hypervisor/mod.rs index bbc95b150..4b46a77c9 100644 --- a/parity/hypervisor/mod.rs +++ b/parity/hypervisor/mod.rs @@ -41,6 +41,12 @@ pub struct Hypervisor { processes: RwLock>, } +impl Default for Hypervisor { + fn default() -> Self { + Hypervisor::new() + } +} + impl Hypervisor { /// initializes the Hypervisor service with the open ipc socket for incoming clients pub fn new() -> Hypervisor { @@ -116,6 +122,7 @@ impl Hypervisor { } } +#[cfg(test)] mod tests { use super::*; use std::sync::atomic::{AtomicBool,Ordering}; diff --git a/parity/main.rs b/parity/main.rs index dff7ceaeb..8311e82ca 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -301,7 +301,7 @@ fn setup_log(init: &Option) -> Arc { } let logs = Arc::new(RotatingLogger::new(levels)); - let log2 = logs.clone(); + let logger = logs.clone(); let format = move |record: &LogRecord| { let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap(); let format = if max_log_level() <= LogLevelFilter::Info { @@ -309,7 +309,7 @@ fn setup_log(init: &Option) -> Arc { } else { format!("{}{}:{}: {}", timestamp, record.level(), record.target(), record.args()) }; - log2.append(format.clone()); + logger.append(format.clone()); format }; builder.format(format); diff --git a/parity/upgrade.rs b/parity/upgrade.rs index b74c17f64..30c977df4 100644 --- a/parity/upgrade.rs +++ b/parity/upgrade.rs @@ -21,6 +21,7 @@ use std::collections::*; use std::fs::{File, create_dir_all}; use std::env; use std::io::{Read, Write}; +use std::path::PathBuf; #[cfg_attr(feature="dev", allow(enum_variant_names))] #[derive(Debug)] @@ -99,7 +100,7 @@ fn with_locked_version(db_path: Option<&str>, script: F) -> Result, u16); + let tup = (vec![1u16, 1u16, 1u16, 1u16], 10u16); let bytes = vec![ // map @@ -507,8 +509,6 @@ fn raw_bytes_from_tuple() { // 10u16 10u8, 0u8]; - type Tup = (Vec, u16); - let tup_from = Tup::from_bytes(&bytes).unwrap(); assert_eq!(tup, tup_from); From ee474b425b12acf8aa6e41c7fa93299f66aa31d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 16:19:47 +0200 Subject: [PATCH 11/16] Fixing hook. --- hook.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hook.sh b/hook.sh index d98297835..7fac4a834 100755 --- a/hook.sh +++ b/hook.sh @@ -4,7 +4,7 @@ echo "#!/bin/sh\n" > $FILE # Exit on any error echo "set -e" >> $FILE # Run release build -echo "cargo build --release --features dev" >> $FILE +echo "cargo build --features dev" >> $FILE # Build tests echo "cargo test --no-run --features dev \\" >> $FILE echo " -p ethash -p ethcore-util -p ethcore -p ethsync -p ethcore-rpc -p parity -p ethminer -p ethcore-webapp" >> $FILE From f5fcada5bacc3d1a99235974bdcc5c9e3f6289d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 16:32:44 +0200 Subject: [PATCH 12/16] Getting rid of generated code warnings --- parity/hypervisor/mod.rs | 1 + parity/main.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/parity/hypervisor/mod.rs b/parity/hypervisor/mod.rs index 4b46a77c9..fbd807d94 100644 --- a/parity/hypervisor/mod.rs +++ b/parity/hypervisor/mod.rs @@ -18,6 +18,7 @@ // while not included in binary #![allow(dead_code)] +#![cfg_attr(feature="dev", allow(used_underscore_binding))] pub mod service; diff --git a/parity/main.rs b/parity/main.rs index 8311e82ca..b48017336 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -20,6 +20,7 @@ #![cfg_attr(feature="dev", feature(plugin))] #![cfg_attr(feature="dev", plugin(clippy))] #![cfg_attr(feature="dev", allow(useless_format))] + extern crate docopt; extern crate num_cpus; extern crate rustc_serialize; From 3e280a3386ec5f7c4ddd9017e576e974d5e848a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 16:45:04 +0200 Subject: [PATCH 13/16] Splitting Configuration to separate module --- parity/configuration.rs | 240 +++++++++++++++++++++++++++++++++++++++ parity/main.rs | 241 +++------------------------------------- parity/setup_log.rs | 2 +- 3 files changed, 254 insertions(+), 229 deletions(-) create mode 100644 parity/configuration.rs diff --git a/parity/configuration.rs b/parity/configuration.rs new file mode 100644 index 000000000..d6a50d398 --- /dev/null +++ b/parity/configuration.rs @@ -0,0 +1,240 @@ +// Copyright 2015, 2016 Ethcore (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 . + +use std::env; +use std::fs::File; +use std::io::{BufRead, BufReader}; +use std::net::{SocketAddr, IpAddr}; +use std::path::PathBuf; +use cli::{USAGE, Args}; +use docopt::Docopt; + +use die::*; +use util::*; +use util::keys::store::AccountService; +use ethcore::client::{append_path, get_db_path, ClientConfig}; +use ethcore::ethereum; +use ethcore::spec::Spec; +use ethsync::SyncConfig; +use price_info::PriceInfo; + +pub struct Configuration { + pub args: Args +} + +impl Configuration { + pub fn parse() -> Self { + Configuration { + args: Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()), + } + } + + pub fn path(&self) -> String { + let d = self.args.flag_datadir.as_ref().unwrap_or(&self.args.flag_db_path); + d.replace("$HOME", env::home_dir().unwrap().to_str().unwrap()) + } + + pub fn author(&self) -> Address { + let d = self.args.flag_etherbase.as_ref().unwrap_or(&self.args.flag_author); + Address::from_str(clean_0x(d)).unwrap_or_else(|_| { + die!("{}: Invalid address for --author. Must be 40 hex characters, with or without the 0x at the beginning.", d) + }) + } + + pub fn gas_floor_target(&self) -> U256 { + let d = &self.args.flag_gas_floor_target; + U256::from_dec_str(d).unwrap_or_else(|_| { + die!("{}: Invalid target gas floor given. Must be a decimal unsigned 256-bit number.", d) + }) + } + + pub fn gas_price(&self) -> U256 { + match self.args.flag_gasprice.as_ref() { + Some(d) => { + U256::from_dec_str(d).unwrap_or_else(|_| { + die!("{}: Invalid gas price given. Must be a decimal unsigned 256-bit number.", d) + }) + } + _ => { + let usd_per_tx: f32 = FromStr::from_str(&self.args.flag_usd_per_tx).unwrap_or_else(|_| { + die!("{}: Invalid basic transaction price given in USD. Must be a decimal number.", self.args.flag_usd_per_tx) + }); + let usd_per_eth = match self.args.flag_usd_per_eth.as_str() { + "etherscan" => PriceInfo::get().map_or_else(|| { + die!("Unable to retrieve USD value of ETH from etherscan. Rerun with a different value for --usd-per-eth.") + }, |x| x.ethusd), + x => FromStr::from_str(x).unwrap_or_else(|_| die!("{}: Invalid ether price given in USD. Must be a decimal number.", x)) + }; + let wei_per_usd: f32 = 1.0e18 / usd_per_eth; + let gas_per_tx: f32 = 21000.0; + let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx; + info!("Using a conversion rate of Ξ1 = US${} ({} wei/gas)", usd_per_eth, wei_per_gas); + U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap() + } + } + } + + pub fn extra_data(&self) -> Bytes { + match self.args.flag_extradata.as_ref().or(self.args.flag_extra_data.as_ref()) { + Some(ref x) if x.len() <= 32 => x.as_bytes().to_owned(), + None => version_data(), + Some(ref x) => { die!("{}: Extra data must be at most 32 characters.", x); } + } + } + + pub fn keys_path(&self) -> String { + self.args.flag_keys_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap()) + } + + pub fn spec(&self) -> Spec { + if self.args.flag_testnet { + return ethereum::new_morden(); + } + match self.args.flag_chain.as_ref() { + "frontier" | "homestead" | "mainnet" => ethereum::new_frontier(), + "morden" | "testnet" => ethereum::new_morden(), + "olympic" => ethereum::new_olympic(), + f => Spec::load(contents(f).unwrap_or_else(|_| { + die!("{}: Couldn't read chain specification file. Sure it exists?", f) + }).as_ref()), + } + } + + pub fn normalize_enode(e: &str) -> Option { + if is_valid_node_url(e) { + Some(e.to_owned()) + } else { + None + } + } + + pub fn init_nodes(&self, spec: &Spec) -> Vec { + match self.args.flag_bootnodes { + Some(ref x) if !x.is_empty() => x.split(',').map(|s| { + Self::normalize_enode(s).unwrap_or_else(|| { + die!("{}: Invalid node address format given for a boot node.", s) + }) + }).collect(), + Some(_) => Vec::new(), + None => spec.nodes().clone(), + } + } + + pub fn net_addresses(&self) -> (Option, Option) { + let listen_address = Some(SocketAddr::new(IpAddr::from_str("0.0.0.0").unwrap(), self.args.flag_port)); + let public_address = if self.args.flag_nat.starts_with("extip:") { + let host = &self.args.flag_nat[6..]; + let host = IpAddr::from_str(host).unwrap_or_else(|_| die!("Invalid host given with `--nat extip:{}`", host)); + Some(SocketAddr::new(host, self.args.flag_port)) + } else { + listen_address + }; + (listen_address, public_address) + } + + pub fn net_settings(&self, spec: &Spec) -> NetworkConfiguration { + let mut ret = NetworkConfiguration::new(); + ret.nat_enabled = self.args.flag_nat == "any" || self.args.flag_nat == "upnp"; + ret.boot_nodes = self.init_nodes(spec); + let (listen, public) = self.net_addresses(); + ret.listen_address = listen; + ret.public_address = public; + ret.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).unwrap_or_else(|_| s.sha3())); + ret.discovery_enabled = !self.args.flag_no_discovery && !self.args.flag_nodiscover; + ret.ideal_peers = self.args.flag_maxpeers.unwrap_or(self.args.flag_peers) as u32; + let mut net_path = PathBuf::from(&self.path()); + net_path.push("network"); + ret.config_path = Some(net_path.to_str().unwrap().to_owned()); + ret + } + + pub fn find_best_db(&self, spec: &Spec) -> Option { + let mut ret = None; + let mut latest_era = None; + let jdb_types = [journaldb::Algorithm::Archive, journaldb::Algorithm::EarlyMerge, journaldb::Algorithm::OverlayRecent, journaldb::Algorithm::RefCounted]; + for i in jdb_types.into_iter() { + let db = journaldb::new(&append_path(&get_db_path(&Path::new(&self.path()), *i, spec.genesis_header().hash()), "state"), *i); + trace!(target: "parity", "Looking for best DB: {} at {:?}", i, db.latest_era()); + match (latest_era, db.latest_era()) { + (Some(best), Some(this)) if best >= this => {} + (_, None) => {} + (_, Some(this)) => { + latest_era = Some(this); + ret = Some(*i); + } + } + } + ret + } + + pub fn client_config(&self, spec: &Spec) -> ClientConfig { + let mut client_config = ClientConfig::default(); + match self.args.flag_cache { + Some(mb) => { + client_config.blockchain.max_cache_size = mb * 1024 * 1024; + client_config.blockchain.pref_cache_size = client_config.blockchain.max_cache_size * 3 / 4; + } + None => { + client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size; + client_config.blockchain.max_cache_size = self.args.flag_cache_max_size; + } + } + client_config.pruning = match self.args.flag_pruning.as_str() { + "archive" => journaldb::Algorithm::Archive, + "light" => journaldb::Algorithm::EarlyMerge, + "fast" => journaldb::Algorithm::OverlayRecent, + "basic" => journaldb::Algorithm::RefCounted, + "auto" => self.find_best_db(spec).unwrap_or(journaldb::Algorithm::OverlayRecent), + _ => { die!("Invalid pruning method given."); } + }; + trace!(target: "parity", "Using pruning strategy of {}", client_config.pruning); + client_config.name = self.args.flag_identity.clone(); + client_config.queue.max_mem_use = self.args.flag_queue_max_size; + client_config + } + + pub fn sync_config(&self, spec: &Spec) -> SyncConfig { + let mut sync_config = SyncConfig::default(); + sync_config.network_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref()).map_or(spec.network_id(), |id| { + U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --network-id/--networkid", id)) + }); + sync_config + } + + pub fn account_service(&self) -> AccountService { + // Secret Store + let passwords = self.args.flag_password.iter().flat_map(|filename| { + BufReader::new(&File::open(filename).unwrap_or_else(|_| die!("{} Unable to read password file. Ensure it exists and permissions are correct.", filename))) + .lines() + .map(|l| l.unwrap()) + .collect::>() + .into_iter() + }).collect::>(); + let account_service = AccountService::new_in(Path::new(&self.keys_path())); + if let Some(ref unlocks) = self.args.flag_unlock { + for d in unlocks.split(',') { + let a = Address::from_str(clean_0x(&d)).unwrap_or_else(|_| { + die!("{}: Invalid address for --unlock. Must be 40 hex characters, without the 0x at the beginning.", d) + }); + if passwords.iter().find(|p| account_service.unlock_account_no_expire(&a, p).is_ok()).is_none() { + die!("No password given to unlock account {}. Pass the password using `--password`.", a); + } + } + } + account_service + } +} + diff --git a/parity/main.rs b/parity/main.rs index e8aa24839..267ae3d54 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -42,8 +42,8 @@ extern crate ethcore_ipc as ipc; extern crate ethcore_ipc_nano as nanoipc; extern crate serde; extern crate bincode; -// for price_info.rs -#[macro_use] extern crate hyper; +#[macro_use] +extern crate hyper; // for price_info.rs #[cfg(feature = "rpc")] extern crate ethcore_rpc; @@ -51,24 +51,6 @@ extern crate ethcore_rpc; #[cfg(feature = "webapp")] extern crate ethcore_webapp; -use std::io::{BufRead, BufReader}; -use std::fs::File; -use std::net::{SocketAddr, IpAddr}; -use std::env; -use std::path::PathBuf; -use ctrlc::CtrlC; -use util::*; -use util::panics::{MayPanic, ForwardPanic, PanicHandler}; -use util::keys::store::AccountService; -use ethcore::ethereum; -use ethcore::client::{append_path, get_db_path, ClientConfig}; -use ethcore::spec::Spec; -use ethcore::service::ClientService; -use ethsync::{EthSync, SyncConfig}; -use ethminer::{Miner, MinerService}; -use docopt::Docopt; -use daemonize::Daemonize; - #[macro_use] mod die; mod price_info; @@ -80,16 +62,22 @@ mod webapp; mod informant; mod io_handler; mod cli; +mod configuration; + +use ctrlc::CtrlC; +use util::*; +use util::panics::{MayPanic, ForwardPanic, PanicHandler}; +use ethcore::service::ClientService; +use ethsync::EthSync; +use ethminer::{Miner, MinerService}; +use daemonize::Daemonize; use die::*; -use cli::{USAGE, print_version, Args}; +use cli::print_version; use rpc::RpcServer; use webapp::WebappServer; use io_handler::ClientIoHandler; - -struct Configuration { - args: Args -} +use configuration::Configuration; fn main() { let conf = Configuration::parse(); @@ -254,209 +242,6 @@ fn wait_for_exit(panic_handler: Arc, _rpc_server: Option Self { - Configuration { - args: Docopt::new(USAGE).and_then(|d| d.decode()).unwrap_or_else(|e| e.exit()), - } - } - - fn path(&self) -> String { - let d = self.args.flag_datadir.as_ref().unwrap_or(&self.args.flag_db_path); - d.replace("$HOME", env::home_dir().unwrap().to_str().unwrap()) - } - - fn author(&self) -> Address { - let d = self.args.flag_etherbase.as_ref().unwrap_or(&self.args.flag_author); - Address::from_str(clean_0x(d)).unwrap_or_else(|_| { - die!("{}: Invalid address for --author. Must be 40 hex characters, with or without the 0x at the beginning.", d) - }) - } - - fn gas_floor_target(&self) -> U256 { - let d = &self.args.flag_gas_floor_target; - U256::from_dec_str(d).unwrap_or_else(|_| { - die!("{}: Invalid target gas floor given. Must be a decimal unsigned 256-bit number.", d) - }) - } - - fn gas_price(&self) -> U256 { - match self.args.flag_gasprice.as_ref() { - Some(d) => { - U256::from_dec_str(d).unwrap_or_else(|_| { - die!("{}: Invalid gas price given. Must be a decimal unsigned 256-bit number.", d) - }) - } - _ => { - let usd_per_tx: f32 = FromStr::from_str(&self.args.flag_usd_per_tx).unwrap_or_else(|_| { - die!("{}: Invalid basic transaction price given in USD. Must be a decimal number.", self.args.flag_usd_per_tx) - }); - let usd_per_eth = match self.args.flag_usd_per_eth.as_str() { - "etherscan" => price_info::PriceInfo::get().map_or_else(|| { - die!("Unable to retrieve USD value of ETH from etherscan. Rerun with a different value for --usd-per-eth.") - }, |x| x.ethusd), - x => FromStr::from_str(x).unwrap_or_else(|_| die!("{}: Invalid ether price given in USD. Must be a decimal number.", x)) - }; - let wei_per_usd: f32 = 1.0e18 / usd_per_eth; - let gas_per_tx: f32 = 21000.0; - let wei_per_gas: f32 = wei_per_usd * usd_per_tx / gas_per_tx; - info!("Using a conversion rate of Ξ1 = US${} ({} wei/gas)", usd_per_eth, wei_per_gas); - U256::from_dec_str(&format!("{:.0}", wei_per_gas)).unwrap() - } - } - } - - fn extra_data(&self) -> Bytes { - match self.args.flag_extradata.as_ref().or(self.args.flag_extra_data.as_ref()) { - Some(ref x) if x.len() <= 32 => x.as_bytes().to_owned(), - None => version_data(), - Some(ref x) => { die!("{}: Extra data must be at most 32 characters.", x); } - } - } - - fn keys_path(&self) -> String { - self.args.flag_keys_path.replace("$HOME", env::home_dir().unwrap().to_str().unwrap()) - } - - fn spec(&self) -> Spec { - if self.args.flag_testnet { - return ethereum::new_morden(); - } - match self.args.flag_chain.as_ref() { - "frontier" | "homestead" | "mainnet" => ethereum::new_frontier(), - "morden" | "testnet" => ethereum::new_morden(), - "olympic" => ethereum::new_olympic(), - f => Spec::load(contents(f).unwrap_or_else(|_| { - die!("{}: Couldn't read chain specification file. Sure it exists?", f) - }).as_ref()), - } - } - - fn normalize_enode(e: &str) -> Option { - if is_valid_node_url(e) { - Some(e.to_owned()) - } else { - None - } - } - - fn init_nodes(&self, spec: &Spec) -> Vec { - match self.args.flag_bootnodes { - Some(ref x) if !x.is_empty() => x.split(',').map(|s| { - Self::normalize_enode(s).unwrap_or_else(|| { - die!("{}: Invalid node address format given for a boot node.", s) - }) - }).collect(), - Some(_) => Vec::new(), - None => spec.nodes().clone(), - } - } - - fn net_addresses(&self) -> (Option, Option) { - let listen_address = Some(SocketAddr::new(IpAddr::from_str("0.0.0.0").unwrap(), self.args.flag_port)); - let public_address = if self.args.flag_nat.starts_with("extip:") { - let host = &self.args.flag_nat[6..]; - let host = IpAddr::from_str(host).unwrap_or_else(|_| die!("Invalid host given with `--nat extip:{}`", host)); - Some(SocketAddr::new(host, self.args.flag_port)) - } else { - listen_address - }; - (listen_address, public_address) - } - - fn net_settings(&self, spec: &Spec) -> NetworkConfiguration { - let mut ret = NetworkConfiguration::new(); - ret.nat_enabled = self.args.flag_nat == "any" || self.args.flag_nat == "upnp"; - ret.boot_nodes = self.init_nodes(spec); - let (listen, public) = self.net_addresses(); - ret.listen_address = listen; - ret.public_address = public; - ret.use_secret = self.args.flag_node_key.as_ref().map(|s| Secret::from_str(&s).unwrap_or_else(|_| s.sha3())); - ret.discovery_enabled = !self.args.flag_no_discovery && !self.args.flag_nodiscover; - ret.ideal_peers = self.args.flag_maxpeers.unwrap_or(self.args.flag_peers) as u32; - let mut net_path = PathBuf::from(&self.path()); - net_path.push("network"); - ret.config_path = Some(net_path.to_str().unwrap().to_owned()); - ret - } - - fn find_best_db(&self, spec: &Spec) -> Option { - let mut ret = None; - let mut latest_era = None; - let jdb_types = [journaldb::Algorithm::Archive, journaldb::Algorithm::EarlyMerge, journaldb::Algorithm::OverlayRecent, journaldb::Algorithm::RefCounted]; - for i in jdb_types.into_iter() { - let db = journaldb::new(&append_path(&get_db_path(&Path::new(&self.path()), *i, spec.genesis_header().hash()), "state"), *i); - trace!(target: "parity", "Looking for best DB: {} at {:?}", i, db.latest_era()); - match (latest_era, db.latest_era()) { - (Some(best), Some(this)) if best >= this => {} - (_, None) => {} - (_, Some(this)) => { - latest_era = Some(this); - ret = Some(*i); - } - } - } - ret - } - - fn client_config(&self, spec: &Spec) -> ClientConfig { - let mut client_config = ClientConfig::default(); - match self.args.flag_cache { - Some(mb) => { - client_config.blockchain.max_cache_size = mb * 1024 * 1024; - client_config.blockchain.pref_cache_size = client_config.blockchain.max_cache_size * 3 / 4; - } - None => { - client_config.blockchain.pref_cache_size = self.args.flag_cache_pref_size; - client_config.blockchain.max_cache_size = self.args.flag_cache_max_size; - } - } - client_config.pruning = match self.args.flag_pruning.as_str() { - "archive" => journaldb::Algorithm::Archive, - "light" => journaldb::Algorithm::EarlyMerge, - "fast" => journaldb::Algorithm::OverlayRecent, - "basic" => journaldb::Algorithm::RefCounted, - "auto" => self.find_best_db(spec).unwrap_or(journaldb::Algorithm::OverlayRecent), - _ => { die!("Invalid pruning method given."); } - }; - trace!(target: "parity", "Using pruning strategy of {}", client_config.pruning); - client_config.name = self.args.flag_identity.clone(); - client_config.queue.max_mem_use = self.args.flag_queue_max_size; - client_config - } - - fn sync_config(&self, spec: &Spec) -> SyncConfig { - let mut sync_config = SyncConfig::default(); - sync_config.network_id = self.args.flag_network_id.as_ref().or(self.args.flag_networkid.as_ref()).map_or(spec.network_id(), |id| { - U256::from_str(id).unwrap_or_else(|_| die!("{}: Invalid index given with --network-id/--networkid", id)) - }); - sync_config - } - - fn account_service(&self) -> AccountService { - // Secret Store - let passwords = self.args.flag_password.iter().flat_map(|filename| { - BufReader::new(&File::open(filename).unwrap_or_else(|_| die!("{} Unable to read password file. Ensure it exists and permissions are correct.", filename))) - .lines() - .map(|l| l.unwrap()) - .collect::>() - .into_iter() - }).collect::>(); - let account_service = AccountService::new_in(Path::new(&self.keys_path())); - if let Some(ref unlocks) = self.args.flag_unlock { - for d in unlocks.split(',') { - let a = Address::from_str(clean_0x(&d)).unwrap_or_else(|_| { - die!("{}: Invalid address for --unlock. Must be 40 hex characters, without the 0x at the beginning.", d) - }); - if passwords.iter().find(|p| account_service.unlock_account_no_expire(&a, p).is_ok()).is_none() { - die!("No password given to unlock account {}. Pass the password using `--password`.", a); - } - } - } - account_service - } -} - /// Parity needs at least 1 test to generate coverage reports correctly. #[test] fn if_works() { diff --git a/parity/setup_log.rs b/parity/setup_log.rs index 1fdc625f5..75cd0f574 100644 --- a/parity/setup_log.rs +++ b/parity/setup_log.rs @@ -42,7 +42,7 @@ pub fn setup_log(init: &Option) -> Arc { } let logs = Arc::new(RotatingLogger::new(levels)); - let loggger = logs.clone(); + let logger = logs.clone(); let format = move |record: &LogRecord| { let timestamp = time::strftime("%Y-%m-%d %H:%M:%S %Z", &time::now()).unwrap(); let format = if max_log_level() <= LogLevelFilter::Info { From 0458a3378c93e217f704dcda448130930f482686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 19:19:42 +0200 Subject: [PATCH 14/16] Additional RPC methods for settings --- parity/configuration.rs | 13 +++++ parity/main.rs | 8 ++- parity/rpc.rs | 4 +- parity/webapp.rs | 4 +- rpc/src/v1/impls/ethcore.rs | 29 +++++++++- rpc/src/v1/tests/ethcore.rs | 105 +++++++++++++++++++++++++++++++---- rpc/src/v1/traits/ethcore.rs | 20 +++++++ util/src/lib.rs | 1 + util/src/network_settings.rs | 35 ++++++++++++ 9 files changed, 202 insertions(+), 17 deletions(-) create mode 100644 util/src/network_settings.rs diff --git a/parity/configuration.rs b/parity/configuration.rs index d6a50d398..8f7e93805 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -25,6 +25,7 @@ use docopt::Docopt; use die::*; use util::*; use util::keys::store::AccountService; +use util::network_settings::NetworkSettings; use ethcore::client::{append_path, get_db_path, ClientConfig}; use ethcore::ethereum; use ethcore::spec::Spec; @@ -236,5 +237,17 @@ impl Configuration { } account_service } + + pub fn network_settings(&self) -> NetworkSettings { + NetworkSettings { + name: self.args.flag_identity.clone(), + chain: self.args.flag_chain.clone(), + max_peers: self.args.flag_maxpeers.unwrap_or(self.args.flag_peers), + network_port: self.args.flag_port, + rpc_enabled: self.args.flag_rpc || self.args.flag_jsonrpc, + rpc_interface: self.args.flag_rpcaddr.clone().unwrap_or(self.args.flag_jsonrpc_interface.clone()), + rpc_port: self.args.flag_rpcport.unwrap_or(self.args.flag_jsonrpc_port), + } + } } diff --git a/parity/main.rs b/parity/main.rs index 267ae3d54..1cc9fffd0 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -153,6 +153,8 @@ fn execute_client(conf: Configuration) { miner.set_minimal_gas_price(conf.gas_price()); miner.set_transactions_limit(conf.args.flag_tx_limit); + let network_settings = Arc::new(conf.network_settings()); + // Sync let sync = EthSync::register(service.network(), sync_config, client.clone(), miner.clone()); @@ -168,7 +170,8 @@ fn execute_client(conf: Configuration) { sync: sync.clone(), secret_store: account_service.clone(), miner: miner.clone(), - logger: logger.clone() + logger: logger.clone(), + settings: network_settings.clone(), }); let webapp_server = webapp::new(webapp::Configuration { @@ -182,7 +185,8 @@ fn execute_client(conf: Configuration) { sync: sync.clone(), secret_store: account_service.clone(), miner: miner.clone(), - logger: logger.clone() + logger: logger.clone(), + settings: network_settings.clone(), }); // Register IO handler diff --git a/parity/rpc.rs b/parity/rpc.rs index b2e6391d8..8cb3b4d17 100644 --- a/parity/rpc.rs +++ b/parity/rpc.rs @@ -23,6 +23,7 @@ use ethsync::EthSync; use ethminer::Miner; use util::RotatingLogger; use util::keys::store::{AccountService}; +use util::network_settings::NetworkSettings; use die::*; #[cfg(feature = "rpc")] @@ -46,6 +47,7 @@ pub struct Dependencies { pub secret_store: Arc, pub miner: Arc, pub logger: Arc, + pub settings: Arc, } pub fn new(conf: Configuration, deps: Dependencies) -> Option { @@ -94,7 +96,7 @@ pub fn setup_rpc_server( server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); }, "personal" => server.add_delegate(PersonalClient::new(&deps.secret_store).to_delegate()), - "ethcore" => server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone()).to_delegate()), + "ethcore" => server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger.clone(), deps.settings.clone()).to_delegate()), _ => { die!("{}: Invalid API name to be enabled.", api); }, diff --git a/parity/webapp.rs b/parity/webapp.rs index 74e8c2e51..a4598fb4a 100644 --- a/parity/webapp.rs +++ b/parity/webapp.rs @@ -22,6 +22,7 @@ use ethsync::EthSync; use ethminer::Miner; use util::RotatingLogger; use util::keys::store::{AccountService}; +use util::network_settings::NetworkSettings; use die::*; #[cfg(feature = "webapp")] @@ -43,6 +44,7 @@ pub struct Dependencies { pub secret_store: Arc, pub miner: Arc, pub logger: Arc, + pub settings: Arc, } pub fn new(configuration: Configuration, deps: Dependencies) -> Option { @@ -96,7 +98,7 @@ pub fn setup_webapp_server( server.add_delegate(EthClient::new(&deps.client, &deps.sync, &deps.secret_store, &deps.miner).to_delegate()); server.add_delegate(EthFilterClient::new(&deps.client, &deps.miner).to_delegate()); server.add_delegate(PersonalClient::new(&deps.secret_store).to_delegate()); - server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger).to_delegate()); + server.add_delegate(EthcoreClient::new(&deps.miner, deps.logger, deps.settings).to_delegate()); let start_result = match auth { None => { diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index 533aad80e..492f5810c 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -16,8 +16,10 @@ //! Ethcore-specific rpc implementation. use util::{U256, Address, RotatingLogger}; +use util::network_settings::NetworkSettings; use std::sync::{Arc, Weak}; use std::ops::Deref; +use std::collections::BTreeMap; use jsonrpc_core::*; use ethminer::{MinerService}; use v1::traits::Ethcore; @@ -28,14 +30,16 @@ pub struct EthcoreClient where M: MinerService { miner: Weak, logger: Arc, + settings: Arc, } impl EthcoreClient where M: MinerService { /// Creates new `EthcoreClient`. - pub fn new(miner: &Arc, logger: Arc) -> Self { + pub fn new(miner: &Arc, logger: Arc, settings: Arc) -> Self { EthcoreClient { miner: Arc::downgrade(miner), logger: logger, + settings: settings, } } } @@ -102,4 +106,27 @@ impl Ethcore for EthcoreClient where M: MinerService + 'static { to_value(&self.logger.levels()) } + fn net_chain(&self, _params: Params) -> Result { + to_value(&self.settings.chain) + } + + fn net_max_peers(&self, _params: Params) -> Result { + to_value(&self.settings.max_peers) + } + + fn net_port(&self, _params: Params) -> Result { + to_value(&self.settings.network_port) + } + + fn node_name(&self, _params: Params) -> Result { + to_value(&self.settings.name) + } + + fn rpc_settings(&self, _params: Params) -> Result { + let mut map = BTreeMap::new(); + map.insert("enabled".to_owned(), Value::Bool(self.settings.rpc_enabled)); + map.insert("interface".to_owned(), Value::String(self.settings.rpc_interface.clone())); + map.insert("port".to_owned(), Value::U64(self.settings.rpc_port as u64)); + Ok(Value::Object(map)) + } } diff --git a/rpc/src/v1/tests/ethcore.rs b/rpc/src/v1/tests/ethcore.rs index 4544541dd..a2e06acc2 100644 --- a/rpc/src/v1/tests/ethcore.rs +++ b/rpc/src/v1/tests/ethcore.rs @@ -23,6 +23,7 @@ use v1::tests::helpers::TestMinerService; use util::numbers::*; use rustc_serialize::hex::FromHex; use util::log::RotatingLogger; +use util::network_settings::NetworkSettings; fn miner_service() -> Arc { @@ -32,10 +33,26 @@ fn logger() -> Arc { Arc::new(RotatingLogger::new("rpc=trace".to_owned())) } +fn settings() -> Arc { + Arc::new(NetworkSettings { + name: "mynode".to_owned(), + chain: "testchain".to_owned(), + max_peers: 25, + network_port: 30303, + rpc_enabled: true, + rpc_interface: "all".to_owned(), + rpc_port: 8545, + }) +} + +fn ethcore_client(miner: &Arc) -> EthcoreClient { + EthcoreClient::new(&miner, logger(), settings()) +} + #[test] fn rpc_ethcore_extra_data() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); + let ethcore = ethcore_client(&miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -49,7 +66,7 @@ fn rpc_ethcore_extra_data() { #[test] fn rpc_ethcore_gas_floor_target() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); + let ethcore = ethcore_client(&miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -62,7 +79,7 @@ fn rpc_ethcore_gas_floor_target() { #[test] fn rpc_ethcore_min_gas_price() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); + let ethcore = ethcore_client(&miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -75,7 +92,7 @@ fn rpc_ethcore_min_gas_price() { #[test] fn rpc_ethcore_set_min_gas_price() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); + let ethcore = ethcore_client(&miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -89,7 +106,7 @@ fn rpc_ethcore_set_min_gas_price() { #[test] fn rpc_ethcore_set_gas_floor_target() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); + let ethcore = ethcore_client(&miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -103,7 +120,7 @@ fn rpc_ethcore_set_gas_floor_target() { #[test] fn rpc_ethcore_set_extra_data() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); + let ethcore = ethcore_client(&miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -117,7 +134,7 @@ fn rpc_ethcore_set_extra_data() { #[test] fn rpc_ethcore_set_author() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); + let ethcore = ethcore_client(&miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -134,7 +151,7 @@ fn rpc_ethcore_dev_logs() { let logger = logger(); logger.append("a".to_owned()); logger.append("b".to_owned()); - let ethcore = EthcoreClient::new(&miner, logger.clone()).to_delegate(); + let ethcore = EthcoreClient::new(&miner, logger.clone(), settings()).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -147,7 +164,7 @@ fn rpc_ethcore_dev_logs() { #[test] fn rpc_ethcore_dev_logs_levels() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); + let ethcore = ethcore_client(&miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -159,7 +176,7 @@ fn rpc_ethcore_dev_logs_levels() { #[test] fn rpc_ethcore_set_transactions_limit() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); + let ethcore = ethcore_client(&miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -170,11 +187,10 @@ fn rpc_ethcore_set_transactions_limit() { assert_eq!(miner.transactions_limit(), 10_240_240); } - #[test] fn rpc_ethcore_transactions_limit() { let miner = miner_service(); - let ethcore = EthcoreClient::new(&miner, logger()).to_delegate(); + let ethcore = ethcore_client(&miner).to_delegate(); let io = IoHandler::new(); io.add_delegate(ethcore); @@ -183,3 +199,68 @@ fn rpc_ethcore_transactions_limit() { assert_eq!(io.handle_request(request), Some(response.to_owned())); } + +#[test] +fn rpc_ethcore_net_chain() { + let miner = miner_service(); + let ethcore = ethcore_client(&miner).to_delegate(); + let io = IoHandler::new(); + io.add_delegate(ethcore); + + let request = r#"{"jsonrpc": "2.0", "method": "ethcore_netChain", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"testchain","id":1}"#; + + assert_eq!(io.handle_request(request), Some(response.to_owned())); +} + +#[test] +fn rpc_ethcore_net_max_peers() { + let miner = miner_service(); + let ethcore = ethcore_client(&miner).to_delegate(); + let io = IoHandler::new(); + io.add_delegate(ethcore); + + let request = r#"{"jsonrpc": "2.0", "method": "ethcore_netMaxPeers", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":25,"id":1}"#; + + assert_eq!(io.handle_request(request), Some(response.to_owned())); +} + +#[test] +fn rpc_ethcore_net_port() { + let miner = miner_service(); + let ethcore = ethcore_client(&miner).to_delegate(); + let io = IoHandler::new(); + io.add_delegate(ethcore); + + let request = r#"{"jsonrpc": "2.0", "method": "ethcore_netPort", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":30303,"id":1}"#; + + assert_eq!(io.handle_request(request), Some(response.to_owned())); +} + +#[test] +fn rpc_ethcore_rpc_settings() { + let miner = miner_service(); + let ethcore = ethcore_client(&miner).to_delegate(); + let io = IoHandler::new(); + io.add_delegate(ethcore); + + let request = r#"{"jsonrpc": "2.0", "method": "ethcore_rpcSettings", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"enabled":true,"interface":"all","port":8545},"id":1}"#; + + assert_eq!(io.handle_request(request), Some(response.to_owned())); +} + +#[test] +fn rpc_ethcore_node_name() { + let miner = miner_service(); + let ethcore = ethcore_client(&miner).to_delegate(); + let io = IoHandler::new(); + io.add_delegate(ethcore); + + let request = r#"{"jsonrpc": "2.0", "method": "ethcore_nodeName", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"mynode","id":1}"#; + + assert_eq!(io.handle_request(request), Some(response.to_owned())); +} diff --git a/rpc/src/v1/traits/ethcore.rs b/rpc/src/v1/traits/ethcore.rs index f00d7bf9a..9aab17b4c 100644 --- a/rpc/src/v1/traits/ethcore.rs +++ b/rpc/src/v1/traits/ethcore.rs @@ -54,6 +54,21 @@ pub trait Ethcore: Sized + Send + Sync + 'static { /// Returns logs levels fn dev_logs_levels(&self, _: Params) -> Result { rpc_unimplemented!() } + /// Returns chain name + fn net_chain(&self, _: Params) -> Result { rpc_unimplemented!() } + + /// Returns max peers + fn net_max_peers(&self, _: Params) -> Result { rpc_unimplemented!() } + + /// Returns network port + fn net_port(&self, _: Params) -> Result { rpc_unimplemented!() } + + /// Returns rpc settings + fn rpc_settings(&self, _: Params) -> Result { rpc_unimplemented!() } + + /// Returns node name + fn node_name(&self, _: Params) -> Result { rpc_unimplemented!() } + /// Should be used to convert object to io delegate. fn to_delegate(self) -> IoDelegate { let mut delegate = IoDelegate::new(Arc::new(self)); @@ -69,6 +84,11 @@ pub trait Ethcore: Sized + Send + Sync + 'static { delegate.add_method("ethcore_transactionsLimit", Ethcore::transactions_limit); delegate.add_method("ethcore_devLogs", Ethcore::dev_logs); delegate.add_method("ethcore_devLogsLevels", Ethcore::dev_logs_levels); + delegate.add_method("ethcore_netChain", Ethcore::net_chain); + delegate.add_method("ethcore_netMaxPeers", Ethcore::net_max_peers); + delegate.add_method("ethcore_netPort", Ethcore::net_port); + delegate.add_method("ethcore_rpcSettings", Ethcore::rpc_settings); + delegate.add_method("ethcore_nodeName", Ethcore::node_name); delegate } diff --git a/util/src/lib.rs b/util/src/lib.rs index dfdb300f6..530f2b4c5 100644 --- a/util/src/lib.rs +++ b/util/src/lib.rs @@ -147,6 +147,7 @@ pub mod log; pub mod panics; pub mod keys; pub mod table; +pub mod network_settings; pub use common::*; pub use misc::*; diff --git a/util/src/network_settings.rs b/util/src/network_settings.rs new file mode 100644 index 000000000..723266ade --- /dev/null +++ b/util/src/network_settings.rs @@ -0,0 +1,35 @@ +// Copyright 2015, 2016 Ethcore (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 . +//! Structure to hold network settings configured from CLI + +/// Networking & RPC settings +pub struct NetworkSettings { + /// Node name + pub name: String, + /// Name of the chain we are connected to + pub chain: String, + /// Ideal number of peers + pub max_peers: usize, + /// Networking port + pub network_port: u16, + /// Is JSON-RPC server enabled? + pub rpc_enabled: bool, + /// Interface that JSON-RPC listens on + pub rpc_interface: String, + /// Port for JSON-RPC server + pub rpc_port: u16, +} + From bacac7d0afd6f384bf82826df5d2ab3960ec5aba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 20:42:36 +0200 Subject: [PATCH 15/16] Fixing transaction_queue deadlock --- miner/src/miner.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/miner/src/miner.rs b/miner/src/miner.rs index a45b34f85..1ee339f64 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -247,11 +247,11 @@ impl MinerService for Miner { match import { Ok(ref res) => { trace!(target: "own_tx", "Imported transaction to {:?} (hash: {:?})", res, hash); - trace!(target: "own_tx", "Status: {:?}", self.status()); + trace!(target: "own_tx", "Status: {:?}", self.transaction_queue.status()); }, Err(ref e) => { trace!(target: "own_tx", "Failed to import transaction {:?} (hash: {:?})", e, hash); - trace!(target: "own_tx", "Status: {:?}", self.status()); + trace!(target: "own_tx", "Status: {:?}", self.transaction_queue.status()); }, } import From 08a0d42ed3d8eb463ddded3eddcc62d2b34a0666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 21 Apr 2016 20:52:27 +0200 Subject: [PATCH 16/16] Compilation fix --- miner/src/miner.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/miner/src/miner.rs b/miner/src/miner.rs index 1ee339f64..3088ebc30 100644 --- a/miner/src/miner.rs +++ b/miner/src/miner.rs @@ -247,11 +247,11 @@ impl MinerService for Miner { match import { Ok(ref res) => { trace!(target: "own_tx", "Imported transaction to {:?} (hash: {:?})", res, hash); - trace!(target: "own_tx", "Status: {:?}", self.transaction_queue.status()); + trace!(target: "own_tx", "Status: {:?}", transaction_queue.status()); }, Err(ref e) => { trace!(target: "own_tx", "Failed to import transaction {:?} (hash: {:?})", e, hash); - trace!(target: "own_tx", "Status: {:?}", self.transaction_queue.status()); + trace!(target: "own_tx", "Status: {:?}", transaction_queue.status()); }, } import