From cf8f27ce0f21eaa86f27f2e9ada0ab3fd515e3ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Wed, 2 Nov 2016 19:42:21 +0100 Subject: [PATCH 01/29] Initializing logger for each command (#3090) * Initializing logger for each command * Single logger setup * Whitespace [ci:skip] --- logger/src/lib.rs | 2 +- parity/blockchain.rs | 12 ++------- parity/configuration.rs | 59 +++++++++++++++++++++++------------------ parity/main.rs | 15 ++++++----- parity/run.rs | 9 +++---- parity/signer.rs | 9 +++++-- parity/snapshot.rs | 5 ---- 7 files changed, 55 insertions(+), 56 deletions(-) diff --git a/logger/src/lib.rs b/logger/src/lib.rs index a79f6fc43..7e13f406c 100644 --- a/logger/src/lib.rs +++ b/logger/src/lib.rs @@ -36,7 +36,7 @@ use regex::Regex; use util::RotatingLogger; use util::log::Colour; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub struct Config { pub mode: Option, pub color: bool, diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 2e0fb4233..8e84c488c 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -21,7 +21,6 @@ use std::time::{Instant, Duration}; use std::thread::sleep; use std::sync::Arc; use rustc_serialize::hex::FromHex; -use ethcore_logger::{setup_log, Config as LogConfig}; use io::{PanicHandler, ForwardPanic}; use util::{ToPretty, Uint}; use rlp::PayloadInfo; @@ -71,7 +70,6 @@ pub enum BlockchainCmd { #[derive(Debug, PartialEq)] pub struct ImportBlockchain { pub spec: SpecType, - pub logger_config: LogConfig, pub cache_config: CacheConfig, pub dirs: Directories, pub file_path: Option, @@ -85,12 +83,12 @@ pub struct ImportBlockchain { pub fat_db: Switch, pub vm_type: VMType, pub check_seal: bool, + pub with_color: bool, } #[derive(Debug, PartialEq)] pub struct ExportBlockchain { pub spec: SpecType, - pub logger_config: LogConfig, pub cache_config: CacheConfig, pub dirs: Directories, pub file_path: Option, @@ -120,9 +118,6 @@ fn execute_import(cmd: ImportBlockchain) -> Result { // Setup panic handler let panic_handler = PanicHandler::new_in_arc(); - // Setup logging - let _logger = setup_log(&cmd.logger_config); - // create dirs used by parity try!(cmd.dirs.create_dirs()); @@ -196,7 +191,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result { } }; - let informant = Informant::new(client.clone(), None, None, None, cmd.logger_config.color); + let informant = Informant::new(client.clone(), None, None, None, cmd.with_color); try!(service.register_io_handler(Arc::new(ImportIoHandler { info: Arc::new(informant), @@ -269,9 +264,6 @@ fn execute_export(cmd: ExportBlockchain) -> Result { // Setup panic handler let panic_handler = PanicHandler::new_in_arc(); - // Setup logging - let _logger = setup_log(&cmd.logger_config); - // create dirs used by parity try!(cmd.dirs.create_dirs()); diff --git a/parity/configuration.rs b/parity/configuration.rs index 1972b9f1e..8c9a4d0bd 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -35,7 +35,7 @@ use params::{ResealPolicy, AccountsConfig, GasPricerConfig, MinerExtras}; use ethcore_logger::Config as LogConfig; use dir::Directories; use dapps::Configuration as DappsConfiguration; -use signer::Configuration as SignerConfiguration; +use signer::{Configuration as SignerConfiguration, SignerCommand}; use run::RunCmd; use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat}; use presale::ImportWallet; @@ -49,11 +49,16 @@ pub enum Cmd { Account(AccountCmd), ImportPresaleWallet(ImportWallet), Blockchain(BlockchainCmd), - SignerToken(String), + SignerToken(SignerCommand), Snapshot(SnapshotCommand), Hash(Option), } +pub struct Execute { + pub logger: LogConfig, + pub cmd: Cmd, +} + #[derive(Debug, PartialEq)] pub struct Configuration { pub args: Args, @@ -70,7 +75,7 @@ impl Configuration { Ok(config) } - pub fn into_command(self) -> Result { + pub fn into_command(self) -> Result { let dirs = self.directories(); let pruning = try!(self.args.flag_pruning.parse()); let pruning_history = self.args.flag_pruning_history; @@ -99,7 +104,9 @@ impl Configuration { let cmd = if self.args.flag_version { Cmd::Version } else if self.args.cmd_signer && self.args.cmd_new_token { - Cmd::SignerToken(dirs.signer) + Cmd::SignerToken(SignerCommand { + path: dirs.signer + }) } else if self.args.cmd_tools && self.args.cmd_hash { Cmd::Hash(self.args.arg_file) } else if self.args.cmd_account { @@ -141,7 +148,6 @@ impl Configuration { } else if self.args.cmd_import { let import_cmd = ImportBlockchain { spec: spec, - logger_config: logger_config, cache_config: cache_config, dirs: dirs, file_path: self.args.arg_file.clone(), @@ -155,12 +161,12 @@ impl Configuration { fat_db: fat_db, vm_type: vm_type, check_seal: !self.args.flag_no_seal_check, + with_color: logger_config.color, }; Cmd::Blockchain(BlockchainCmd::Import(import_cmd)) } else if self.args.cmd_export { let export_cmd = ExportBlockchain { spec: spec, - logger_config: logger_config, cache_config: cache_config, dirs: dirs, file_path: self.args.arg_file.clone(), @@ -184,7 +190,6 @@ impl Configuration { spec: spec, pruning: pruning, pruning_history: pruning_history, - logger_config: logger_config, mode: mode, tracing: tracing, fat_db: fat_db, @@ -202,7 +207,6 @@ impl Configuration { spec: spec, pruning: pruning, pruning_history: pruning_history, - logger_config: logger_config, mode: mode, tracing: tracing, fat_db: fat_db, @@ -227,7 +231,7 @@ impl Configuration { pruning: pruning, pruning_history: pruning_history, daemon: daemon, - logger_config: logger_config, + logger_config: logger_config.clone(), miner_options: miner_options, http_conf: http_conf, ipc_conf: ipc_conf, @@ -258,7 +262,10 @@ impl Configuration { Cmd::Run(run_cmd) }; - Ok(cmd) + Ok(Execute { + logger: logger_config, + cmd: cmd, + }) } fn enable_network(&self, mode: &Mode) -> bool { @@ -684,7 +691,7 @@ mod tests { use ethcore::miner::{MinerOptions, PrioritizationStrategy}; use helpers::{replace_home, default_network_config}; use run::RunCmd; - use signer::Configuration as SignerConfiguration; + use signer::{Configuration as SignerConfiguration, SignerCommand}; use blockchain::{BlockchainCmd, ImportBlockchain, ExportBlockchain, DataFormat}; use presale::ImportWallet; use account::{AccountCmd, NewAccount, ImportAccounts}; @@ -705,14 +712,14 @@ mod tests { fn test_command_version() { let args = vec!["parity", "--version"]; let conf = parse(&args); - assert_eq!(conf.into_command().unwrap(), Cmd::Version); + assert_eq!(conf.into_command().unwrap().cmd, Cmd::Version); } #[test] fn test_command_account_new() { let args = vec!["parity", "account", "new"]; let conf = parse(&args); - assert_eq!(conf.into_command().unwrap(), Cmd::Account(AccountCmd::New(NewAccount { + assert_eq!(conf.into_command().unwrap().cmd, Cmd::Account(AccountCmd::New(NewAccount { iterations: 10240, path: replace_home("$HOME/.parity/keys"), password_file: None, @@ -723,16 +730,16 @@ mod tests { fn test_command_account_list() { let args = vec!["parity", "account", "list"]; let conf = parse(&args); - assert_eq!(conf.into_command().unwrap(), Cmd::Account( - AccountCmd::List(replace_home("$HOME/.parity/keys"))) - ); + assert_eq!(conf.into_command().unwrap().cmd, Cmd::Account( + AccountCmd::List(replace_home("$HOME/.parity/keys")), + )); } #[test] fn test_command_account_import() { let args = vec!["parity", "account", "import", "my_dir", "another_dir"]; let conf = parse(&args); - assert_eq!(conf.into_command().unwrap(), Cmd::Account(AccountCmd::Import(ImportAccounts { + assert_eq!(conf.into_command().unwrap().cmd, Cmd::Account(AccountCmd::Import(ImportAccounts { from: vec!["my_dir".into(), "another_dir".into()], to: replace_home("$HOME/.parity/keys"), }))); @@ -742,7 +749,7 @@ mod tests { fn test_command_wallet_import() { let args = vec!["parity", "wallet", "import", "my_wallet.json", "--password", "pwd"]; let conf = parse(&args); - assert_eq!(conf.into_command().unwrap(), Cmd::ImportPresaleWallet(ImportWallet { + assert_eq!(conf.into_command().unwrap().cmd, Cmd::ImportPresaleWallet(ImportWallet { iterations: 10240, path: replace_home("$HOME/.parity/keys"), wallet_path: "my_wallet.json".into(), @@ -754,9 +761,8 @@ mod tests { fn test_command_blockchain_import() { let args = vec!["parity", "import", "blockchain.json"]; let conf = parse(&args); - assert_eq!(conf.into_command().unwrap(), Cmd::Blockchain(BlockchainCmd::Import(ImportBlockchain { + assert_eq!(conf.into_command().unwrap().cmd, Cmd::Blockchain(BlockchainCmd::Import(ImportBlockchain { spec: Default::default(), - logger_config: Default::default(), cache_config: Default::default(), dirs: Default::default(), file_path: Some("blockchain.json".into()), @@ -770,6 +776,7 @@ mod tests { fat_db: Default::default(), vm_type: VMType::Interpreter, check_seal: true, + with_color: true, }))); } @@ -777,9 +784,8 @@ mod tests { fn test_command_blockchain_export() { let args = vec!["parity", "export", "blockchain.json"]; let conf = parse(&args); - assert_eq!(conf.into_command().unwrap(), Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { + assert_eq!(conf.into_command().unwrap().cmd, Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { spec: Default::default(), - logger_config: Default::default(), cache_config: Default::default(), dirs: Default::default(), file_path: Some("blockchain.json".into()), @@ -801,9 +807,8 @@ mod tests { fn test_command_blockchain_export_with_custom_format() { let args = vec!["parity", "export", "--format", "hex", "blockchain.json"]; let conf = parse(&args); - assert_eq!(conf.into_command().unwrap(), Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { + assert_eq!(conf.into_command().unwrap().cmd, Cmd::Blockchain(BlockchainCmd::Export(ExportBlockchain { spec: Default::default(), - logger_config: Default::default(), cache_config: Default::default(), dirs: Default::default(), file_path: Some("blockchain.json".into()), @@ -826,14 +831,16 @@ mod tests { let args = vec!["parity", "signer", "new-token"]; let conf = parse(&args); let expected = replace_home("$HOME/.parity/signer"); - assert_eq!(conf.into_command().unwrap(), Cmd::SignerToken(expected)); + assert_eq!(conf.into_command().unwrap().cmd, Cmd::SignerToken(SignerCommand { + path: expected, + })); } #[test] fn test_run_cmd() { let args = vec!["parity"]; let conf = parse(&args); - assert_eq!(conf.into_command().unwrap(), Cmd::Run(RunCmd { + assert_eq!(conf.into_command().unwrap().cmd, Cmd::Run(RunCmd { cache_config: Default::default(), dirs: Default::default(), spec: Default::default(), diff --git a/parity/main.rs b/parity/main.rs index 83598231a..8fe752216 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -118,8 +118,9 @@ use std::io::BufReader; use std::fs::File; use util::sha3::sha3; use cli::Args; -use configuration::{Cmd, Configuration}; +use configuration::{Cmd, Execute, Configuration}; use deprecated::find_deprecated; +use ethcore_logger::setup_log; fn print_hash_of(maybe_file: Option) -> Result { if let Some(file) = maybe_file { @@ -131,10 +132,12 @@ fn print_hash_of(maybe_file: Option) -> Result { } } -fn execute(command: Cmd) -> Result { - match command { +fn execute(command: Execute) -> Result { + let logger = setup_log(&command.logger).expect("Logger is initialized only once; qed"); + + match command.cmd { Cmd::Run(run_cmd) => { - try!(run::execute(run_cmd)); + try!(run::execute(run_cmd, logger)); Ok("".into()) }, Cmd::Version => Ok(Args::print_version()), @@ -142,7 +145,7 @@ fn execute(command: Cmd) -> Result { Cmd::Account(account_cmd) => account::execute(account_cmd), Cmd::ImportPresaleWallet(presale_cmd) => presale::execute(presale_cmd), Cmd::Blockchain(blockchain_cmd) => blockchain::execute(blockchain_cmd), - Cmd::SignerToken(path) => signer::new_token(path), + Cmd::SignerToken(signer_cmd) => signer::execute(signer_cmd), Cmd::Snapshot(snapshot_cmd) => snapshot::execute(snapshot_cmd), } } @@ -198,7 +201,7 @@ fn sync_main() -> bool { fn main() { // Always print backtrace on panic. ::std::env::set_var("RUST_BACKTRACE", "1"); - + if sync_main() { return; } diff --git a/parity/run.rs b/parity/run.rs index 8a3ae06ec..b0f8dce68 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -17,11 +17,11 @@ use std::sync::{Arc, Mutex, Condvar}; use ctrlc::CtrlC; use fdlimit::raise_fd_limit; -use ethcore_logger::{Config as LogConfig, setup_log}; use ethcore_rpc::{NetworkSettings, is_major_importing}; use ethsync::NetworkConfiguration; -use util::{Colour, version, U256}; +use util::{Colour, version, U256, RotatingLogger}; use io::{MayPanic, ForwardPanic, PanicHandler}; +use ethcore_logger::{Config as LogConfig}; use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, ChainNotify, BlockChainClient}; use ethcore::service::ClientService; use ethcore::account_provider::AccountProvider; @@ -93,13 +93,10 @@ pub struct RunCmd { pub check_seal: bool, } -pub fn execute(cmd: RunCmd) -> Result<(), String> { +pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { // set up panic handler let panic_handler = PanicHandler::new_in_arc(); - // set up logger - let logger = try!(setup_log(&cmd.logger_config)); - // increase max number of open files raise_fd_limit(); diff --git a/parity/signer.rs b/parity/signer.rs index 5097192ad..b3047c739 100644 --- a/parity/signer.rs +++ b/parity/signer.rs @@ -68,8 +68,13 @@ fn codes_path(path: String) -> PathBuf { p } -pub fn new_token(path: String) -> Result { - generate_new_token(path) +#[derive(Debug, PartialEq)] +pub struct SignerCommand { + pub path: String, +} + +pub fn execute(cmd: SignerCommand) -> Result { + generate_new_token(cmd.path) .map(|code| format!("This key code will authorise your System Signer UI: {}", Colour::White.bold().paint(code))) .map_err(|err| format!("Error generating token: {:?}", err)) } diff --git a/parity/snapshot.rs b/parity/snapshot.rs index 4cd972230..85e1f90eb 100644 --- a/parity/snapshot.rs +++ b/parity/snapshot.rs @@ -20,7 +20,6 @@ use std::time::Duration; use std::path::{Path, PathBuf}; use std::sync::Arc; -use ethcore_logger::{setup_log, Config as LogConfig}; use ethcore::snapshot::{Progress, RestorationStatus, SnapshotService as SS}; use ethcore::snapshot::io::{SnapshotReader, PackedReader, PackedWriter}; use ethcore::snapshot::service::Service as SnapshotService; @@ -55,7 +54,6 @@ pub struct SnapshotCommand { pub spec: SpecType, pub pruning: Pruning, pub pruning_history: u64, - pub logger_config: LogConfig, pub mode: Mode, pub tracing: Switch, pub fat_db: Switch, @@ -141,9 +139,6 @@ impl SnapshotCommand { // load user defaults let user_defaults = try!(UserDefaults::load(&user_defaults_path)); - // Setup logging - let _logger = setup_log(&self.logger_config); - fdlimit::raise_fd_limit(); // select pruning algorithm From 36d17d5c2832ca0e7d7b1b8fce3d2875546c4d28 Mon Sep 17 00:00:00 2001 From: keorn Date: Wed, 2 Nov 2016 18:43:21 +0000 Subject: [PATCH 02/29] Add RPC enode lookup (#3096) * add enode rpc * spaces -> tabs --- rpc/src/v1/helpers/errors.rs | 9 +++++++++ rpc/src/v1/impls/ethcore.rs | 4 ++++ rpc/src/v1/tests/helpers/sync_provider.rs | 4 ++++ rpc/src/v1/traits/ethcore.rs | 4 ++++ sync/src/api.rs | 7 +++++++ 5 files changed, 28 insertions(+) diff --git a/rpc/src/v1/helpers/errors.rs b/rpc/src/v1/helpers/errors.rs index 413ce7e7f..fad044866 100644 --- a/rpc/src/v1/helpers/errors.rs +++ b/rpc/src/v1/helpers/errors.rs @@ -41,6 +41,7 @@ mod codes { pub const ACCOUNT_ERROR: i64 = -32023; pub const SIGNER_DISABLED: i64 = -32030; pub const DAPPS_DISABLED: i64 = -32031; + pub const NETWORK_DISABLED: i64 = -32035; pub const REQUEST_REJECTED: i64 = -32040; pub const REQUEST_REJECTED_LIMIT: i64 = -32041; pub const REQUEST_NOT_FOUND: i64 = -32042; @@ -185,6 +186,14 @@ pub fn dapps_disabled() -> Error { } } +pub fn network_disabled() -> Error { + Error { + code: ErrorCode::ServerError(codes::NETWORK_DISABLED), + message: "Network is disabled or not yet up.".into(), + data: None + } +} + pub fn encryption_error(error: T) -> Error { Error { code: ErrorCode::ServerError(codes::ENCRYPTION_ERROR), diff --git a/rpc/src/v1/impls/ethcore.rs b/rpc/src/v1/impls/ethcore.rs index dbb27037c..52aecb465 100644 --- a/rpc/src/v1/impls/ethcore.rs +++ b/rpc/src/v1/impls/ethcore.rs @@ -353,4 +353,8 @@ impl Ethcore for EthcoreClient where Mode::Active => "active", }.into()) } + + fn enode(&self) -> Result { + take_weak!(self.sync).enode().ok_or_else(errors::network_disabled) + } } diff --git a/rpc/src/v1/tests/helpers/sync_provider.rs b/rpc/src/v1/tests/helpers/sync_provider.rs index c85fddea5..531b9bd2d 100644 --- a/rpc/src/v1/tests/helpers/sync_provider.rs +++ b/rpc/src/v1/tests/helpers/sync_provider.rs @@ -93,5 +93,9 @@ impl SyncProvider for TestSyncProvider { } ] } + + fn enode(&self) -> Option { + None + } } diff --git a/rpc/src/v1/traits/ethcore.rs b/rpc/src/v1/traits/ethcore.rs index 18279d364..8e9f58fb3 100644 --- a/rpc/src/v1/traits/ethcore.rs +++ b/rpc/src/v1/traits/ethcore.rs @@ -133,5 +133,9 @@ build_rpc_trait! { /// Get the mode. Results one of: "active", "passive", "dark", "off". #[rpc(name = "ethcore_mode")] fn mode(&self) -> Result; + + /// Get the enode of this node. + #[rpc(name = "ethcore_enode")] + fn enode(&self) -> Result; } } diff --git a/sync/src/api.rs b/sync/src/api.rs index abe18c3cd..2e18fd5cc 100644 --- a/sync/src/api.rs +++ b/sync/src/api.rs @@ -73,6 +73,9 @@ pub trait SyncProvider: Send + Sync { /// Get peers information fn peers(&self) -> Vec; + + /// Get the enode if available. + fn enode(&self) -> Option; } /// Peer connection information @@ -143,6 +146,10 @@ impl SyncProvider for EthSync { self.handler.sync.write().peers(&sync_io) }).unwrap_or(Vec::new()) } + + fn enode(&self) -> Option { + self.network.external_url() + } } struct SyncProtocolHandler { From 6098f008ce19beee8e6907cbde3fe16f81392d12 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 2 Nov 2016 22:03:03 +0000 Subject: [PATCH 03/29] [ci skip] js-precompiled 20161102-220144 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index cf860c18e..275d7bba4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#10a57a7df153360b4abeddff99e5b8f34a85ff53" +source = "git+https://github.com/ethcore/js-precompiled.git#28bed6095e6bec331c457866ecaad9f6efcff7aa" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index ec66486d9..28841ba0c 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.1.37", + "version": "0.1.38", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 391f408653bfaa7af045c5a8619e764f0d382a4e Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Wed, 2 Nov 2016 23:09:00 +0100 Subject: [PATCH 04/29] Sort by ETH balance and contract by date (#3107) * Added timestamps to contract creation // Sort by date (#3070) * Added sort by ETH balance (#3070) * Added timestamp meta to accounts / addresses entry creations (#3107) --- js/src/modals/AddAddress/addAddress.js | 1 + js/src/modals/AddContract/addContract.js | 1 + js/src/modals/CreateAccount/createAccount.js | 15 +++- .../modals/DeployContract/deployContract.js | 1 + js/src/ui/Actionbar/Sort/sort.js | 61 +++++++++++-- js/src/ui/Container/container.css | 1 + js/src/views/Accounts/List/list.js | 86 +++++++++++++++---- js/src/views/Contracts/contracts.js | 7 +- 8 files changed, 147 insertions(+), 26 deletions(-) diff --git a/js/src/modals/AddAddress/addAddress.js b/js/src/modals/AddAddress/addAddress.js index 5ab8bbe80..ebcf78815 100644 --- a/js/src/modals/AddAddress/addAddress.js +++ b/js/src/modals/AddAddress/addAddress.js @@ -136,6 +136,7 @@ export default class AddAddress extends Component { api.personal.setAccountName(address, name), api.personal.setAccountMeta(address, { description, + timestamp: Date.now(), deleted: false }) ]).catch((error) => { diff --git a/js/src/modals/AddContract/addContract.js b/js/src/modals/AddContract/addContract.js index ebad86807..ad7345430 100644 --- a/js/src/modals/AddContract/addContract.js +++ b/js/src/modals/AddContract/addContract.js @@ -145,6 +145,7 @@ export default class AddContract extends Component { api.personal.setAccountMeta(address, { contract: true, deleted: false, + timestamp: Date.now(), abi: abiParsed, description }) diff --git a/js/src/modals/CreateAccount/createAccount.js b/js/src/modals/CreateAccount/createAccount.js index e0808b47b..aacc91d5e 100644 --- a/js/src/modals/CreateAccount/createAccount.js +++ b/js/src/modals/CreateAccount/createAccount.js @@ -214,7 +214,10 @@ export default class CreateAccount extends Component { this.setState({ address }); return api.personal .setAccountName(address, this.state.name) - .then(() => api.personal.setAccountMeta(address, { passwordHint: this.state.passwordHint })); + .then(() => api.personal.setAccountMeta(address, { + timestamp: Date.now(), + passwordHint: this.state.passwordHint + })); }) .then(() => { this.onNext(); @@ -236,7 +239,10 @@ export default class CreateAccount extends Component { this.setState({ address }); return api.personal .setAccountName(address, this.state.name) - .then(() => api.personal.setAccountMeta(address, { passwordHint: this.state.passwordHint })); + .then(() => api.personal.setAccountMeta(address, { + timestamp: Date.now(), + passwordHint: this.state.passwordHint + })); }) .then(() => { this.onNext(); @@ -285,7 +291,10 @@ export default class CreateAccount extends Component { return api.personal .setAccountName(address, this.state.name) - .then(() => api.personal.setAccountMeta(address, { passwordHint: this.state.passwordHint })); + .then(() => api.personal.setAccountMeta(address, { + timestamp: Date.now(), + passwordHint: this.state.passwordHint + })); }) .then(() => { this.onNext(); diff --git a/js/src/modals/DeployContract/deployContract.js b/js/src/modals/DeployContract/deployContract.js index 3e1b10599..c62b968b5 100644 --- a/js/src/modals/DeployContract/deployContract.js +++ b/js/src/modals/DeployContract/deployContract.js @@ -216,6 +216,7 @@ export default class DeployContract extends Component { api.personal.setAccountMeta(address, { abi: abiParsed, contract: true, + timestamp: Date.now(), deleted: false, description }) diff --git a/js/src/ui/Actionbar/Sort/sort.js b/js/src/ui/Actionbar/Sort/sort.js index 35c51e5a1..474ef465b 100644 --- a/js/src/ui/Actionbar/Sort/sort.js +++ b/js/src/ui/Actionbar/Sort/sort.js @@ -27,14 +27,23 @@ import styles from './sort.css'; export default class ActionbarSort extends Component { static propTypes = { onChange: PropTypes.func.isRequired, - order: PropTypes.string + order: PropTypes.string, + showDefault: PropTypes.bool, + metas: PropTypes.array }; + static defaultProps = { + metas: [], + showDefault: true + } + state = { menuOpen: false } render () { + const { showDefault } = this.props; + return ( - - - + touchTapCloseDelay={ 0 } + > + { + showDefault + ? this.renderMenuItem('', 'Default') + : null + } + { this.renderMenuItem('tags', 'Sort by tags') } + { this.renderMenuItem('name', 'Sort by name') } + { this.renderMenuItem('eth', 'Sort by ETH') } + + { this.renderSortByMetas() } ); } + renderSortByMetas () { + const { metas } = this.props; + + return metas + .map((meta, index) => { + return this + .renderMenuItem(meta.key, `Sort by ${meta.label}`, index); + }); + } + + renderMenuItem (value, label, key = null) { + const { order } = this.props; + + const props = {}; + + if (key !== null) { + props.key = key; + } + + const checked = order === value; + + return ( + + ); + } + handleSortChange = (event, child) => { const order = child.props.value; this.props.onChange(order); diff --git a/js/src/ui/Container/container.css b/js/src/ui/Container/container.css index 1071125bd..d5c2ef336 100644 --- a/js/src/ui/Container/container.css +++ b/js/src/ui/Container/container.css @@ -18,6 +18,7 @@ flex: 1; padding: 0em; background: rgba(0, 0, 0, 0.8); + height: 100%; } .compact, diff --git a/js/src/views/Accounts/List/list.js b/js/src/views/Accounts/List/list.js index bcdf7b072..ab231a53a 100644 --- a/js/src/views/Accounts/List/list.js +++ b/js/src/views/Accounts/List/list.js @@ -29,6 +29,7 @@ export default class List extends Component { search: PropTypes.array, empty: PropTypes.bool, order: PropTypes.string, + orderFallback: PropTypes.string, handleAddSearchToken: PropTypes.func }; @@ -79,9 +80,9 @@ export default class List extends Component { } sortAddresses (addresses) { - const { order } = this.props; + const { order, orderFallback } = this.props; - if (!order || ['tags', 'name'].indexOf(order) === -1) { + if (!order) { return addresses; } @@ -91,26 +92,77 @@ export default class List extends Component { const accountA = accounts[addressA]; const accountB = accounts[addressB]; - if (order === 'name') { - return accountA.name.localeCompare(accountB.name); + const sort = this.compareAccounts(accountA, accountB, order); + + if (sort === 0 && orderFallback) { + return this.compareAccounts(accountA, accountB, orderFallback); } - if (order === 'tags') { - const tagsA = [].concat(accountA.meta.tags) - .filter(t => t) - .sort(); - const tagsB = [].concat(accountB.meta.tags) - .filter(t => t) - .sort(); - - if (tagsA.length === 0) return 1; - if (tagsB.length === 0) return -1; - - return tagsA.join('').localeCompare(tagsB.join('')); - } + return sort; }); } + compareAccounts (accountA, accountB, key) { + if (key === 'name') { + return accountA.name.localeCompare(accountB.name); + } + + if (key === 'eth') { + const { balances } = this.props; + + const balanceA = balances[accountA.address]; + const balanceB = balances[accountB.address]; + + if (!balanceA && !balanceB) return 0; + if (balanceA && !balanceB) return -1; + if (!balanceA && balanceB) return 1; + + const ethA = balanceA.tokens + .find(token => token.token.tag.toLowerCase() === 'eth') + .value; + const ethB = balanceB.tokens + .find(token => token.token.tag.toLowerCase() === 'eth') + .value; + + return -1 * ethA.comparedTo(ethB); + } + + if (key === 'tags') { + const tagsA = [].concat(accountA.meta.tags) + .filter(t => t) + .sort() + .join(''); + + const tagsB = [].concat(accountB.meta.tags) + .filter(t => t) + .sort() + .join(''); + + if (!tagsA && !tagsB) return 0; + if (tagsA && !tagsB) return -1; + if (!tagsA && tagsB) return 1; + + return tagsA.localeCompare(tagsB); + } + + const metaA = accountA.meta[key]; + const metaB = accountB.meta[key]; + + if (!metaA && !metaB) { + return 0; + } + + if ((metaA && !metaB) || (metaA < metaB)) { + return -1; + } + + if ((!metaA && metaB) || (metaA > metaB)) { + return 1; + } + + return 0; + } + getFilteredAddresses () { const { accounts, search } = this.props; const searchValues = (search || []).map(v => v.toLowerCase()); diff --git a/js/src/views/Contracts/contracts.js b/js/src/views/Contracts/contracts.js index f4cbc6dcc..ef408cae2 100644 --- a/js/src/views/Contracts/contracts.js +++ b/js/src/views/Contracts/contracts.js @@ -42,7 +42,7 @@ class Contracts extends Component { state = { addContract: false, deployContract: false, - sortOrder: '', + sortOrder: 'timestamp', searchValues: [], searchTokens: [] } @@ -65,6 +65,7 @@ class Contracts extends Component { balances={ balances } empty={ !hasContracts } order={ sortOrder } + orderFallback='name' handleAddSearchToken={ this.onAddSearchToken } /> @@ -80,6 +81,10 @@ class Contracts extends Component { ); } From 0dd9b2a2f144f9b9127992ab779d6b5a6fc3aebf Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Wed, 2 Nov 2016 22:37:59 +0000 Subject: [PATCH 05/29] [ci skip] js-precompiled 20161102-223654 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 275d7bba4..5074e8840 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#28bed6095e6bec331c457866ecaad9f6efcff7aa" +source = "git+https://github.com/ethcore/js-precompiled.git#9ff49b3e7515517e1c2f2278a8408976655a725f" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 28841ba0c..52a1d93ef 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.1.38", + "version": "0.1.39", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From f9a389c4b659abe5db9c842cf46f7068811b08dd Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Thu, 3 Nov 2016 08:22:35 +0100 Subject: [PATCH 06/29] Minor typo fixed. (#3110) --- util/src/stats.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/util/src/stats.rs b/util/src/stats.rs index f05c1b757..fa73b5bea 100644 --- a/util/src/stats.rs +++ b/util/src/stats.rs @@ -30,7 +30,7 @@ pub struct Histogram { impl Histogram { /// Histogram if a sorted corpus is at least fills the buckets. pub fn new(corpus: &[U256], bucket_number: usize) -> Option { - if corpus.len() < bucket_number { return None; } + if corpus.len() <= bucket_number { return None; } let corpus_end = corpus.last().expect("there are at least bucket_number elements; qed").clone(); // If there are extremely few transactions, go from zero. let corpus_start = corpus.first().expect("there are at least bucket_number elements; qed").clone(); @@ -67,4 +67,9 @@ mod tests { assert!(Histogram::new(&vec_into![1, 2], 5).is_none()); } + + #[test] + fn should_not_panic_when_asking_for_bucket_too_big() { + assert!(Histogram::new(&vec_into![1, 2], 2).is_none()); + } } From d7eeda334184645492aa78ba181134e82150d428 Mon Sep 17 00:00:00 2001 From: GitLab Build Bot Date: Thu, 3 Nov 2016 07:35:41 +0000 Subject: [PATCH 07/29] [ci skip] js-precompiled 20161103-073434 --- Cargo.lock | 2 +- js/package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5074e8840..3ea2d243b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1235,7 +1235,7 @@ dependencies = [ [[package]] name = "parity-ui-precompiled" version = "1.4.0" -source = "git+https://github.com/ethcore/js-precompiled.git#9ff49b3e7515517e1c2f2278a8408976655a725f" +source = "git+https://github.com/ethcore/js-precompiled.git#7530295f1f6eabbea3b078ccca6f7fa12b7e1403" dependencies = [ "parity-dapps-glue 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] diff --git a/js/package.json b/js/package.json index 52a1d93ef..d91820bfe 100644 --- a/js/package.json +++ b/js/package.json @@ -1,6 +1,6 @@ { "name": "parity.js", - "version": "0.1.39", + "version": "0.1.40", "main": "release/index.js", "jsnext:main": "src/index.js", "author": "Parity Team ", From 0f9451efe8a06d54af59a1a24a72551d26e97729 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Thu, 3 Nov 2016 11:52:44 +0100 Subject: [PATCH 08/29] Bump mio (#3117) --- Cargo.lock | 98 +++++++++++++++++++++++++++++++----------------------- 1 file changed, 56 insertions(+), 42 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3ea2d243b..ff10c86a5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,7 +185,7 @@ version = "1.1.1" source = "git+https://github.com/ethcore/rust-ctrlc.git#f4927770f89eca80ec250911eea3adcbf579ac48" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -194,7 +194,7 @@ name = "daemonize" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -244,7 +244,7 @@ source = "git+https://github.com/ethcore/rust-secp256k1#a9a0b1be1f39560ca86e8fc8 dependencies = [ "arrayvec 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)", "gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -370,7 +370,7 @@ version = "1.4.0" dependencies = [ "crossbeam 0.2.9 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.0 (git+https://github.com/carllerche/mio)", + "mio 0.6.1 (git+https://github.com/carllerche/mio)", "parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -458,9 +458,9 @@ dependencies = [ "ethcrypto 0.1.0", "ethkey 0.2.0", "igd 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "mio 0.6.0 (git+https://github.com/carllerche/mio)", + "mio 0.6.1 (git+https://github.com/carllerche/mio)", "parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rlp 0.1.0", @@ -554,7 +554,7 @@ dependencies = [ "heapsize 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "lru-cache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", @@ -618,7 +618,7 @@ dependencies = [ "ethkey 0.2.0", "itertools 0.4.13 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "parking_lot 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rust-crypto 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", @@ -663,7 +663,7 @@ dependencies = [ name = "fdlimit" version = "0.1.0" dependencies = [ - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -681,7 +681,7 @@ name = "flate2" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -802,7 +802,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -894,7 +894,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libc" -version = "0.2.15" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -930,7 +930,7 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -958,7 +958,7 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -967,7 +967,7 @@ version = "0.5.1" source = "git+https://github.com/ethcore/mio?branch=v0.5.x#3842d3b250ffd7bd9b16f9586b875ddcbac2b0dd" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", @@ -983,7 +983,7 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bytes 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", @@ -999,7 +999,7 @@ version = "0.6.0-dev" source = "git+https://github.com/ethcore/mio?branch=timer-fix#31eccc40ece3d47abaefaf23bb2114033175b972" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1010,16 +1010,16 @@ dependencies = [ [[package]] name = "mio" -version = "0.6.0" -source = "git+https://github.com/carllerche/mio#9f17b70d6fecbf912168267ea74cf536f2cba705" +version = "0.6.1" +source = "git+https://github.com/carllerche/mio#56f8663510196fdca04bdf7c5f4d60b24297826f" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)", - "nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "slab 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1050,7 +1050,7 @@ name = "nanomsg" version = "0.5.1" source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00" dependencies = [ - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "nanomsg-sys 0.5.0 (git+https://github.com/ethcore/nanomsg.rs.git)", ] @@ -1060,7 +1060,7 @@ version = "0.5.0" source = "git+https://github.com/ethcore/nanomsg.rs.git#c40fe442c9afaea5b38009a3d992ca044dcceb00" dependencies = [ "gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1070,7 +1070,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1081,7 +1081,7 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1091,7 +1091,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", + "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "nix" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bitflags 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", + "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1181,7 +1194,7 @@ name = "num_cpus" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1246,7 +1259,7 @@ version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1266,7 +1279,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1405,7 +1418,7 @@ name = "rand" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1459,7 +1472,7 @@ name = "rocksdb" version = "0.4.5" source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58" dependencies = [ - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "rocksdb-sys 0.3.0 (git+https://github.com/ethcore/rust-rocksdb)", ] @@ -1469,7 +1482,7 @@ version = "0.3.0" source = "git+https://github.com/ethcore/rust-rocksdb#64c63ccbe1f62c2e2b39262486f9ba813793af58" dependencies = [ "gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1489,7 +1502,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "termios 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1500,7 +1513,7 @@ version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "gcc 0.3.35 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1671,7 +1684,7 @@ name = "syntex_errors" version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_pos 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1693,7 +1706,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1706,7 +1719,7 @@ version = "0.42.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_errors 0.42.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1747,7 +1760,7 @@ name = "termios" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1756,7 +1769,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1773,7 +1786,7 @@ version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2012,7 +2025,7 @@ dependencies = [ "checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "49247ec2a285bb3dcb23cbd9c35193c025e7251bfce77c1d5da97e6362dffe7f" "checksum lazycell 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ce12306c4739d86ee97c23139f3a34ddf0387bbf181bc7929d287025a8c3ef6b" -"checksum libc 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "23e3757828fa702a20072c37ff47938e9dd331b92fac6e223d26d4b7a55f7ee2" +"checksum libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)" = "044d1360593a78f5c8e5e710beccdc24ab71d1f01bc19a29bcacdba22e8475d8" "checksum linked-hash-map 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bda158e0dabeb97ee8a401f4d17e479d6b891a14de0bba79d5cc2d4d325b5e48" "checksum linked-hash-map 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d262045c5b87c0861b3f004610afd0e2c851e2908d08b6c870cbb9d5f494ecd" "checksum log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ab83497bf8bf4ed2a74259c1c802351fcd67a65baa86394b6ba73c36f4838054" @@ -2024,8 +2037,8 @@ dependencies = [ "checksum miniz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "9d1f4d337a01c32e1f2122510fed46393d53ca35a7f429cb0450abaedfa3ed54" "checksum mio 0.5.1 (git+https://github.com/ethcore/mio?branch=v0.5.x)" = "" "checksum mio 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a637d1ca14eacae06296a008fa7ad955347e34efcb5891cfd8ba05491a37907e" -"checksum mio 0.6.0 (git+https://github.com/carllerche/mio)" = "" "checksum mio 0.6.0-dev (git+https://github.com/ethcore/mio?branch=timer-fix)" = "" +"checksum mio 0.6.1 (git+https://github.com/carllerche/mio)" = "" "checksum miow 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "d5bfc6782530ac8ace97af10a540054a37126b63b0702ddaaa243b73b5745b9a" "checksum msdos_time 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "c04b68cc63a8480fb2550343695f7be72effdec953a9d4508161c3e69041c7d8" "checksum nanomsg 0.5.1 (git+https://github.com/ethcore/nanomsg.rs.git)" = "" @@ -2033,6 +2046,7 @@ dependencies = [ "checksum net2 0.2.23 (registry+https://github.com/rust-lang/crates.io-index)" = "6a816012ca11cb47009693c1e0c6130e26d39e4d97ee2a13c50e868ec83e3204" "checksum nix 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f05c2fc965fc1cd6b73fa57fa7b89f288178737f2f3ce9e63e4a6a141189000e" "checksum nix 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a7bb1da2be7da3cbffda73fc681d509ffd9e665af478d2bee1907cee0bc64b2" +"checksum nix 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a0d95c5fa8b641c10ad0b8887454ebaafa3c92b5cd5350f8fc693adafd178e7b" "checksum nodrop 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "4d9a22dbcebdeef7bf275cbf444d6521d4e7a2fee187b72d80dba0817120dd8f" "checksum nom 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6caab12c5f97aa316cb249725aa32115118e1522b445e26c257dd77cad5ffd4e" "checksum num 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)" = "c04bd954dbf96f76bab6e5bd6cef6f1ce1262d15268ce4f926d2b5b778fa7af2" From 5ae737f307b13c6797ddd8d1edfceb1e455e7da3 Mon Sep 17 00:00:00 2001 From: Jannis Redmann Date: Thu, 3 Nov 2016 11:57:43 +0100 Subject: [PATCH 09/29] new InputAddressSelect component (#3071) * basic address autocomplete * validate input, propagate changes * show IdentityIcon in menu * show IdentityIcon next to input * refactoring, better variable names, linting * show default IdentityIcon if search by name * port #3065 over * show accounts in the beginning * show accounts before contacts * filter deleted accounts * UX improvements - limit number of search results shown - hint text * only render identity icon if valid address * UX improvements - align IdentityIcon - better hint text * align label & error with other inputs This probably needs to be changed soon again. Therefore this ugly hack has been put in place. * Align component with coding style for app * Use standard/tested AddressAutocmplete (WIP) * Address selection & inputs operational * Update TODOs, remove unused CSS * only handle input changes when editing * Simplify * Cleanup unused modules * Add contracts to address search * Updates Address Selector to handle valid input address #3071 * Added Address Selector to contracts read queries --- js/src/ui/Form/AddressSelect/addressSelect.js | 97 +++++++++++++------ js/src/ui/Form/AutoComplete/autocomplete.js | 20 ++-- .../InputAddressSelect/inputAddressSelect.css | 31 ------ .../InputAddressSelect/inputAddressSelect.js | 79 +++------------ js/src/views/Contract/Queries/inputQuery.js | 19 +++- 5 files changed, 110 insertions(+), 136 deletions(-) delete mode 100644 js/src/ui/Form/InputAddressSelect/inputAddressSelect.css diff --git a/js/src/ui/Form/AddressSelect/addressSelect.js b/js/src/ui/Form/AddressSelect/addressSelect.js index 72e80f432..ac7f2da51 100644 --- a/js/src/ui/Form/AddressSelect/addressSelect.js +++ b/js/src/ui/Form/AddressSelect/addressSelect.js @@ -16,6 +16,7 @@ import React, { Component, PropTypes } from 'react'; import { MenuItem } from 'material-ui'; +import { isEqual } from 'lodash'; import AutoComplete from '../AutoComplete'; import IdentityIcon from '../../IdentityIcon'; @@ -24,57 +25,82 @@ import IdentityName from '../../IdentityName'; import styles from './addressSelect.css'; export default class AddressSelect extends Component { + static contextTypes = { + api: PropTypes.object.isRequired + } + static propTypes = { disabled: PropTypes.bool, accounts: PropTypes.object, contacts: PropTypes.object, + contracts: PropTypes.object, label: PropTypes.string, hint: PropTypes.string, error: PropTypes.string, value: PropTypes.string, tokens: PropTypes.object, - onChange: PropTypes.func.isRequired + onChange: PropTypes.func.isRequired, + allowInput: PropTypes.bool } state = { entries: {}, + addresses: [], value: '' } + entriesFromProps (props = this.props) { + const { accounts, contacts, contracts } = props; + const entries = Object.assign({}, accounts || {}, contacts || {}, contracts || {}); + return entries; + } + componentWillMount () { - const { accounts, contacts, value } = this.props; - const entries = Object.assign({}, accounts || {}, contacts || {}); - this.setState({ entries, value }); + const { value } = this.props; + const entries = this.entriesFromProps(); + const addresses = Object.keys(entries).sort(); + + this.setState({ entries, addresses, value }); } componentWillReceiveProps (newProps) { - const { accounts, contacts } = newProps; - const entries = Object.assign({}, accounts || {}, contacts || {}); - this.setState({ entries }); + const entries = this.entriesFromProps(); + const addresses = Object.keys(entries).sort(); + + if (!isEqual(addresses, this.state.addresses)) { + this.setState({ entries, addresses }); + } + + if (newProps.value !== this.props.value) { + this.setState({ value: newProps.value }); + } } render () { - const { disabled, error, hint, label } = this.props; - const { entries } = this.state; - const value = this.getSearchText(); + const { allowInput, disabled, error, hint, label } = this.props; + const { entries, value } = this.state; + + const searchText = this.getSearchText(); + const icon = this.renderIdentityIcon(value); return (
- - { this.renderIdentityIcon(value) } + { icon }
); } @@ -82,7 +108,7 @@ export default class AddressSelect extends Component { renderIdentityIcon (inputValue) { const { error, value } = this.props; - if (error || !inputValue) { + if (error || !inputValue || value.length !== 42) { return null; } @@ -96,8 +122,9 @@ export default class AddressSelect extends Component { renderItem = (entry) => { return { - text: entry.address, - value: this.renderSelectEntry(entry) + text: entry.name && entry.name.toUpperCase() || entry.address, + value: this.renderSelectEntry(entry), + address: entry.address }; } @@ -127,32 +154,48 @@ export default class AddressSelect extends Component { getSearchText () { const entry = this.getEntry(); - if (!entry) return ''; + const { value } = this.state; - return entry.name ? entry.name.toUpperCase() : ''; + return entry && entry.name + ? entry.name.toUpperCase() + : value; } getEntry () { - const { value } = this.props; - if (!value) return ''; - - const { entries } = this.state; - return entries[value]; + const { entries, value } = this.state; + return value ? entries[value] : null; } - handleFilter = (searchText, address) => { + handleFilter = (searchText, name, item) => { + const { address } = item; const entry = this.state.entries[address]; const lowCaseSearch = searchText.toLowerCase(); - return [ entry.name, entry.address ] + return [entry.name, entry.address] .some(text => text.toLowerCase().indexOf(lowCaseSearch) !== -1); } onChange = (entry, empty) => { + const { allowInput } = this.props; + const { value } = this.state; + const address = entry && entry.address ? entry.address - : (empty ? '' : this.state.value); + : ((empty && !allowInput) ? '' : value); this.props.onChange(null, address); } + + onUpdateInput = (query, choices) => { + const { api } = this.context; + + const address = query.trim(); + + if (!/^0x/.test(address) && api.util.isAddressValid(`0x${address}`)) { + const checksumed = api.util.toChecksumAddress(`0x${address}`); + return this.props.onChange(null, checksumed); + } + + this.props.onChange(null, address); + }; } diff --git a/js/src/ui/Form/AutoComplete/autocomplete.js b/js/src/ui/Form/AutoComplete/autocomplete.js index 353b4d73c..b0958da31 100644 --- a/js/src/ui/Form/AutoComplete/autocomplete.js +++ b/js/src/ui/Form/AutoComplete/autocomplete.js @@ -21,6 +21,7 @@ import { PopoverAnimationVertical } from 'material-ui/Popover'; export default class AutoComplete extends Component { static propTypes = { onChange: PropTypes.func.isRequired, + onUpdateInput: PropTypes.func, disabled: PropTypes.bool, label: PropTypes.string, hint: PropTypes.string, @@ -43,7 +44,7 @@ export default class AutoComplete extends Component { } render () { - const { disabled, error, hint, label, value, className, filter } = this.props; + const { disabled, error, hint, label, value, className, filter, onUpdateInput } = this.props; const { open } = this.state; return ( @@ -54,11 +55,11 @@ export default class AutoComplete extends Component { hintText={ hint } errorText={ error } onNewRequest={ this.onChange } + onUpdateInput={ onUpdateInput } searchText={ value } onFocus={ this.onFocus } onBlur={ this.onBlur } animation={ PopoverAnimationVertical } - filter={ filter } popoverProps={ { open } } openOnFocus @@ -108,11 +109,17 @@ export default class AutoComplete extends Component { } onBlur = () => { - window.setTimeout(() => { - const { entry } = this.state; + const { onUpdateInput } = this.props; - this.handleOnChange(entry); - }, 100); + // TODO: Handle blur gracefully where we use onUpdateInput (currently replaces input + // input where text is allowed with the last selected value from the dropdown) + if (!onUpdateInput) { + window.setTimeout(() => { + const { entry } = this.state; + + this.handleOnChange(entry); + }, 100); + } } onFocus = () => { @@ -131,5 +138,4 @@ export default class AutoComplete extends Component { this.props.onChange(value, empty); } } - } diff --git a/js/src/ui/Form/InputAddressSelect/inputAddressSelect.css b/js/src/ui/Form/InputAddressSelect/inputAddressSelect.css deleted file mode 100644 index 0211ef568..000000000 --- a/js/src/ui/Form/InputAddressSelect/inputAddressSelect.css +++ /dev/null @@ -1,31 +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 . -*/ -.inputselect { - position: relative; -} - -.inputselect svg { - padding-right: 84px; -} - -.toggle { - position: absolute !important; - top: 38px; - right: 0; - display: inline-block !important; - width: auto !important; -} diff --git a/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js b/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js index e2c5e8a1d..e77043aed 100644 --- a/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js +++ b/js/src/ui/Form/InputAddressSelect/inputAddressSelect.js @@ -17,103 +17,46 @@ import React, { Component, PropTypes } from 'react'; import { connect } from 'react-redux'; import { bindActionCreators } from 'redux'; -import { Toggle } from 'material-ui'; import AddressSelect from '../AddressSelect'; -import InputAddress from '../InputAddress'; - -import styles from './inputAddressSelect.css'; class InputAddressSelect extends Component { static propTypes = { - accounts: PropTypes.object, - contacts: PropTypes.object, - disabled: PropTypes.bool, - editing: PropTypes.bool, + accounts: PropTypes.object.isRequired, + contacts: PropTypes.object.isRequired, + contracts: PropTypes.object.isRequired, error: PropTypes.string, label: PropTypes.string, hint: PropTypes.string, value: PropTypes.string, - tokens: PropTypes.object, onChange: PropTypes.func }; - state = { - editing: this.props.editing || false, - entries: [] - } - render () { - const { editing } = this.state; - - return ( -
- { editing ? this.renderInput() : this.renderSelect() } - -
- ); - } - - renderInput () { - const { disabled, error, hint, label, value, tokens } = this.props; - - return ( - - ); - } - - renderSelect () { - const { accounts, contacts, disabled, error, hint, label, value, tokens } = this.props; + const { accounts, contacts, contracts, label, hint, error, value, onChange } = this.props; return ( + onChange={ onChange } /> ); } - - onToggle = () => { - const { editing } = this.state; - - this.setState({ - editing: !editing - }); - } - - onChangeInput = (event, value) => { - this.props.onChange(event, value); - } - - onChangeSelect = (event, value) => { - this.props.onChange(event, value); - } } function mapStateToProps (state) { - const { accounts, contacts } = state.personal; + const { accounts, contacts, contracts } = state.personal; return { accounts, - contacts + contacts, + contracts }; } diff --git a/js/src/views/Contract/Queries/inputQuery.js b/js/src/views/Contract/Queries/inputQuery.js index f21379a23..94c569412 100644 --- a/js/src/views/Contract/Queries/inputQuery.js +++ b/js/src/views/Contract/Queries/inputQuery.js @@ -19,7 +19,7 @@ import React, { Component, PropTypes } from 'react'; import LinearProgress from 'material-ui/LinearProgress'; import { Card, CardActions, CardTitle, CardText } from 'material-ui/Card'; -import { Button, Input } from '../../../ui'; +import { Button, Input, InputAddressSelect } from '../../../ui'; import styles from './queries.css'; @@ -124,8 +124,8 @@ export default class InputQuery extends Component { const { name, type } = input; const label = `${name ? `${name}: ` : ''}${type}`; - const onChange = (event) => { - const value = event.target.value; + const onChange = (event, input) => { + const value = event && event.target.value || input; const { values } = this.state; this.setState({ @@ -136,6 +136,19 @@ export default class InputQuery extends Component { }); }; + if (type === 'address') { + return ( +
+ +
+ ); + } + return (
Date: Thu, 3 Nov 2016 12:00:26 +0100 Subject: [PATCH 10/29] Displaying errors on stderr (#3116) --- parity/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/parity/main.rs b/parity/main.rs index 8fe752216..0fc88d8e7 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -114,7 +114,7 @@ mod user_defaults; mod stratum; use std::{process, env}; -use std::io::BufReader; +use std::io::{self as stdio, BufReader, Write}; use std::fs::File; use util::sha3::sha3; use cli::Args; @@ -213,7 +213,7 @@ fn main() { info!("{}", result); }, Err(err) => { - info!("{}", err); + writeln!(&mut stdio::stderr(), "{}", err).expect("StdErr available; qed"); process::exit(1); } } From d101cb52479c69a673114d59f6455be0a69d66a0 Mon Sep 17 00:00:00 2001 From: Arkadiy Paronyan Date: Thu, 3 Nov 2016 12:00:39 +0100 Subject: [PATCH 11/29] Windows tray app (#3103) * Check if already running in ui command * Windows tray app * Upadted CI scripts --- .gitlab-ci.yml | 2 + appveyor.yml | 2 + nsis/installer.nsi | 54 ++++++- nsis/logo.ico | Bin 99678 -> 67646 bytes parity/run.rs | 10 ++ windows/ptray/ptray.cpp | 274 ++++++++++++++++++++++++++++++++++++ windows/ptray/ptray.ico | Bin 0 -> 67646 bytes windows/ptray/ptray.rc | Bin 0 -> 4364 bytes windows/ptray/ptray.vcxproj | 155 ++++++++++++++++++++ windows/ptray/resource.h | Bin 0 -> 1788 bytes windows/ptray/targetver.h | 8 ++ 11 files changed, 500 insertions(+), 5 deletions(-) create mode 100644 windows/ptray/ptray.cpp create mode 100644 windows/ptray/ptray.ico create mode 100644 windows/ptray/ptray.rc create mode 100644 windows/ptray/ptray.vcxproj create mode 100644 windows/ptray/resource.h create mode 100644 windows/ptray/targetver.h diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c1fdabd12..b50ca7b22 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -322,6 +322,8 @@ windows: - curl -sL --url "https://github.com/ethcore/win-build/raw/master/SimpleFC.dll" -o nsis\SimpleFC.dll - curl -sL --url "https://github.com/ethcore/win-build/raw/master/vc_redist.x64.exe" -o nsis\vc_redist.x64.exe - signtool sign /f %keyfile% /p %certpass% target\release\parity.exe + - msbuild windows\ptray\ptray.vcxproj /p:Platform=x86 /p:Configuration=Release + - signtool sign /f %keyfile% /p %certpass% windows\ptray\release\ptray.exe - cd nsis - makensis.exe installer.nsi - copy installer.exe InstallParity.exe diff --git a/appveyor.yml b/appveyor.yml index 75a2da7cb..e04caf233 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -38,6 +38,8 @@ after_test: - cargo build --verbose --release - ps: if($env:cert) { Start-FileDownload $env:cert -FileName $env:keyfile } - ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass target\release\parity.exe } + - msbuild windows\ptray\ptray.vcxproj /p:Platform=x86 /p:Configuration=Release + - ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass windows\ptray\release\ptray.exe } - makensis.exe nsis\installer.nsi - ps: if($env:cert) { signtool sign /f $env:keyfile /p $env:certpass nsis\installer.exe } diff --git a/nsis/installer.nsi b/nsis/installer.nsi index 2c7c37428..448618bbf 100644 --- a/nsis/installer.nsi +++ b/nsis/installer.nsi @@ -1,10 +1,17 @@ +!include WinMessages.nsh +!define WND_CLASS "Parity" +!define WND_TITLE "Parity" +!define WAIT_MS 5000 +!define SYNC_TERM 0x00100001 + !define APPNAME "Parity" !define COMPANYNAME "Ethcore" !define DESCRIPTION "Fast, light, robust Ethereum implementation" !define VERSIONMAJOR 1 !define VERSIONMINOR 4 !define VERSIONBUILD 0 +!define ARGS "--warp --mode=passive" !addplugindir .\ @@ -13,6 +20,10 @@ !define ABOUTURL "https://github.com/ethcore/parity" # "Publisher" link !define INSTALLSIZE 26120 +!define termMsg "Installer cannot stop running ${WND_TITLE}.$\nDo you want to terminate process?" +!define stopMsg "Stopping ${WND_TITLE} Application" + + RequestExecutionLevel admin ;Require admin rights on NT6+ (When UAC is turned on) InstallDir "$PROGRAMFILES64\${COMPANYNAME}\${APPNAME}" @@ -26,7 +37,7 @@ outFile "installer.exe" page license page directory -Page instfiles +page instfiles !macro VerifyUserIsAdmin UserInfo::GetAccountType @@ -38,6 +49,31 @@ ${If} $0 != "admin" ;Require admin rights on NT4+ ${EndIf} !macroend +!macro TerminateApp + Push $0 ; window handle + Push $1 + Push $2 ; process handle + DetailPrint "$(stopMsg)" + FindWindow $0 '${WND_CLASS}' '' + IntCmp $0 0 done + System::Call 'user32.dll::GetWindowThreadProcessId(i r0, *i .r1) i .r2' + System::Call 'kernel32.dll::OpenProcess(i ${SYNC_TERM}, i 0, i r1) i .r2' + SendMessage $0 ${WM_CLOSE} 0 0 /TIMEOUT=${TO_MS} + System::Call 'kernel32.dll::WaitForSingleObject(i r2, i ${WAIT_MS}) i .r1' + IntCmp $1 0 close + MessageBox MB_YESNOCANCEL|MB_ICONEXCLAMATION "$(termMsg)" /SD IDYES IDYES terminate IDNO close + System::Call 'kernel32.dll::CloseHandle(i r2) i .r1' + Quit + terminate: + System::Call 'kernel32.dll::TerminateProcess(i r2, i 0) i .r1' + close: + System::Call 'kernel32.dll::CloseHandle(i r2) i .r1' + done: + Pop $2 + Pop $1 + Pop $0 +!macroend + function .onInit setShellVarContext all !insertmacro VerifyUserIsAdmin @@ -48,10 +84,13 @@ section "install" setOutPath $INSTDIR # Files added here should be removed by the uninstaller (see section "uninstall") file /oname=parity.exe ..\target\release\parity.exe + file /oname=ptray.exe ..\windows\ptray\Release\ptray.exe + file "logo.ico" file vc_redist.x64.exe ExecWait '"$INSTDIR\vc_redist.x64.exe" /passive /norestart' + delete $INSTDIR\vc_redist.x64.exe # Add any other files for the install directory (license files, app data, etc) here # Uninstaller - See function un.onInit and section "uninstall" for configuration @@ -79,11 +118,11 @@ section "install" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /S" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "InstallLocation" "$\"$INSTDIR$\"" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayIcon" "$\"$INSTDIR\logo.ico$\"" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "$\"${COMPANYNAME}$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "Publisher" "${COMPANYNAME}" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "HelpLink" "$\"${HELPURL}$\"" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLUpdateInfo" "$\"${UPDATEURL}$\"" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "URLInfoAbout" "$\"${ABOUTURL}$\"" - WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "$\"${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}$\"" + WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "DisplayVersion" "${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONBUILD}" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMajor" ${VERSIONMAJOR} WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "VersionMinor" ${VERSIONMINOR} # There is no option for modifying or repairing the install @@ -91,6 +130,9 @@ section "install" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "NoRepair" 1 # Set the INSTALLSIZE constant (!defined at the top of this script) so Add/Remove Programs can accurately report the size WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" "EstimatedSize" ${INSTALLSIZE} + + WriteRegStr HKEY_LOCAL_MACHINE "Software\Microsoft\Windows\CurrentVersion\Run" ${APPNAME} "$INSTDIR\ptray.exe ${ARGS}" + ExecShell "" "$INSTDIR\ptray.exe" "${ARGS}" sectionEnd # Uninstaller @@ -107,7 +149,7 @@ function un.onInit functionEnd section "uninstall" - + !insertmacro TerminateApp # Remove Start Menu launcher delete "$SMPROGRAMS\${COMPANYNAME}\${APPNAME}.lnk" # Try to remove the Start Menu folder - this will only happen if it is empty @@ -115,6 +157,7 @@ section "uninstall" # Remove files delete $INSTDIR\parity.exe + delete $INSTDIR\ptray.exe delete $INSTDIR\logo.ico # Always delete uninstaller as the last action @@ -131,5 +174,6 @@ section "uninstall" # Remove uninstaller information from the registry DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${COMPANYNAME} ${APPNAME}" - + DeleteRegValue HKLM "Software\Microsoft\Windows\CurrentVersion\Run" "${APPNAME}" sectionEnd + diff --git a/nsis/logo.ico b/nsis/logo.ico index 4fbaa4d39cff498e1cba9deda57b3fd031e98ae4..61e68b90b86ccb69f7c13a0595bba8ffd1943c4c 100644 GIT binary patch literal 67646 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. use std::sync::{Arc, Mutex, Condvar}; +use std::net::{TcpListener}; use ctrlc::CtrlC; use fdlimit::raise_fd_limit; use ethcore_rpc::{NetworkSettings, is_major_importing}; @@ -94,6 +95,15 @@ pub struct RunCmd { } pub fn execute(cmd: RunCmd, logger: Arc) -> Result<(), String> { + if cmd.ui && cmd.dapps_conf.enabled { + // Check if Parity is already running + let addr = format!("{}:{}", cmd.dapps_conf.interface, cmd.dapps_conf.port); + if !TcpListener::bind(&addr as &str).is_ok() { + url::open(&format!("http://{}:{}/", cmd.dapps_conf.interface, cmd.dapps_conf.port)); + return Ok(()); + } + } + // set up panic handler let panic_handler = PanicHandler::new_in_arc(); diff --git a/windows/ptray/ptray.cpp b/windows/ptray/ptray.cpp new file mode 100644 index 000000000..ac2f197a7 --- /dev/null +++ b/windows/ptray/ptray.cpp @@ -0,0 +1,274 @@ +// 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 . + +#define WIN32_LEAN_AND_MEAN +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "resource.h" + +#pragma comment(lib, "shlwapi.lib") + +#define MAX_LOADSTRING 100 +#define IDM_EXIT 100 +#define IDM_OPEN 101 +#define WM_USER_SHELLICON WM_USER + 1 + +HANDLE parityHandle = INVALID_HANDLE_VALUE; +DWORD parityProcId = 0; +NOTIFYICONDATA nidApp; +WCHAR szTitle[MAX_LOADSTRING]; +WCHAR szWindowClass[MAX_LOADSTRING]; + +LPCWSTR cParityExe = _T("parity.exe"); + +ATOM MyRegisterClass(HINSTANCE hInstance); +bool InitInstance(HINSTANCE, int, LPWSTR cmdLine); +LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); +void KillParity(); +void OpenUI(); +bool ParityIsRunning(); + +bool GetParityExePath(TCHAR* dest, size_t destSize) +{ + if (!dest || MAX_PATH > destSize) + return false; + GetModuleFileName(NULL, dest, destSize); + if (!PathRemoveFileSpec(dest)) + return false; + return PathAppend(dest, _T("parity.exe")) == TRUE; +} + +int APIENTRY wWinMain(_In_ HINSTANCE hInstance, + _In_opt_ HINSTANCE hPrevInstance, + _In_ LPWSTR lpCmdLine, + _In_ int nCmdShow) +{ + UNREFERENCED_PARAMETER(hPrevInstance); + UNREFERENCED_PARAMETER(lpCmdLine); + + CreateMutex(0, FALSE, _T("Local\\ParityTray")); + if (GetLastError() == ERROR_ALREADY_EXISTS) + return -1; + + LoadStringW(hInstance, IDS_APP_TITLE, szTitle, MAX_LOADSTRING); + LoadStringW(hInstance, IDC_PTRAY, szWindowClass, MAX_LOADSTRING); + MyRegisterClass(hInstance); + + if (!InitInstance(hInstance, nCmdShow, lpCmdLine)) + return FALSE; + + HACCEL hAccelTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDC_PTRAY)); + MSG msg; + // Main message loop: + while (GetMessage(&msg, nullptr, 0, 0)) + { + if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg)) + { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + } + + return (int)msg.wParam; +} + +ATOM MyRegisterClass(HINSTANCE hInstance) +{ + WNDCLASSEXW wcex; + + wcex.cbSize = sizeof(WNDCLASSEX); + + wcex.style = CS_HREDRAW | CS_VREDRAW; + wcex.lpfnWndProc = WndProc; + wcex.cbClsExtra = 0; + wcex.cbWndExtra = 0; + wcex.hInstance = hInstance; + wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_PTRAY)); + wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); + wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wcex.lpszMenuName = MAKEINTRESOURCEW(IDC_PTRAY); + wcex.lpszClassName = szWindowClass; + wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_SMALL)); + + return RegisterClassExW(&wcex); +} + + +bool InitInstance(HINSTANCE hInstance, int nCmdShow, LPWSTR cmdLine) +{ + // Check if already running + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + if (Process32First(snapshot, &entry) == TRUE) + { + while (Process32Next(snapshot, &entry) == TRUE) + { + if (lstrcmp(entry.szExeFile, cParityExe) == 0) + { + parityHandle = OpenProcess(PROCESS_ALL_ACCESS, FALSE, entry.th32ProcessID); + parityProcId = entry.th32ProcessID; + break; + } + } + } + + CloseHandle(snapshot); + + if (parityHandle == INVALID_HANDLE_VALUE) + { + // Launch parity + TCHAR path[MAX_PATH] = { 0 }; + if (!GetParityExePath(path, MAX_PATH)) + return false; + + PROCESS_INFORMATION procInfo = { 0 }; + STARTUPINFO startupInfo = { sizeof(STARTUPINFO) }; + + LPWSTR cmd = new WCHAR[lstrlen(cmdLine) + lstrlen(path) + 2]; + lstrcpy(cmd, path); + lstrcat(cmd, _T(" ")); + lstrcat(cmd, cmdLine); + if (!CreateProcess(nullptr, cmd, nullptr, nullptr, false, CREATE_NO_WINDOW, nullptr, nullptr, &startupInfo, &procInfo)) + return false; + delete[] cmd; + parityHandle = procInfo.hProcess; + parityProcId = procInfo.dwProcessId; + } + + HWND hWnd = CreateWindowW(szWindowClass, szTitle, 0, + CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); + + if (!hWnd) + return false; + + HICON hMainIcon = LoadIcon(hInstance, (LPCTSTR)MAKEINTRESOURCE(IDI_PTRAY)); + + nidApp.cbSize = sizeof(NOTIFYICONDATA); // sizeof the struct in bytes + nidApp.hWnd = (HWND)hWnd; //handle of the window which will process this app. messages + nidApp.uID = IDI_PTRAY; //ID of the icon that willl appear in the system tray + nidApp.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; //ORing of all the flags + nidApp.hIcon = hMainIcon; // handle of the Icon to be displayed, obtained from LoadIcon + nidApp.uCallbackMessage = WM_USER_SHELLICON; + LoadString(hInstance, IDS_CONTROL_PARITY, nidApp.szTip, MAX_LOADSTRING); + Shell_NotifyIcon(NIM_ADD, &nidApp); + return TRUE; + +} + +LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + switch (message) + { + case WM_USER_SHELLICON: + // systray msg callback + POINT lpClickPoint; + switch (LOWORD(lParam)) + { + case WM_LBUTTONDOWN: + OpenUI(); + break; + case WM_RBUTTONDOWN: + UINT uFlag = MF_BYPOSITION | MF_STRING; + GetCursorPos(&lpClickPoint); + HMENU hPopMenu = CreatePopupMenu(); + InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_EXIT, _T("Open")); + InsertMenu(hPopMenu, 0xFFFFFFFF, MF_SEPARATOR | MF_BYPOSITION, 0, nullptr); + InsertMenu(hPopMenu, 0xFFFFFFFF, MF_BYPOSITION | MF_STRING, IDM_EXIT, _T("Exit")); + + SetForegroundWindow(hWnd); + TrackPopupMenu(hPopMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_BOTTOMALIGN, lpClickPoint.x, lpClickPoint.y, 0, hWnd, NULL); + return TRUE; + + } + break; + case WM_COMMAND: + { + int wmId = LOWORD(wParam); + // Parse the menu selections: + switch (wmId) + { + case IDM_EXIT: + DestroyWindow(hWnd); + break; + case IDM_OPEN: + OpenUI(); + break; + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + } + break; + case WM_DESTROY: + Shell_NotifyIcon(NIM_DELETE, &nidApp); + KillParity(); + PostQuitMessage(0); + break; + case WM_TIMER: + if (!ParityIsRunning()) + DestroyWindow(hWnd); + default: + return DefWindowProc(hWnd, message, wParam, lParam); + } + return 0; +} + +void KillParity() +{ + DWORD procId = parityProcId; + //This does not require the console window to be visible. + if (AttachConsole(procId)) + { + // Disable Ctrl-C handling for our program + SetConsoleCtrlHandler(nullptr, true); + GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); + FreeConsole(); + + //Re-enable Ctrl-C handling or any subsequently started + //programs will inherit the disabled state. + SetConsoleCtrlHandler(nullptr, false); + } + WaitForSingleObject(parityHandle, INFINITE); +} + +bool ParityIsRunning() +{ + return WaitForSingleObject(parityHandle, 0) == WAIT_TIMEOUT; +} + +void OpenUI() +{ + // Launch parity + TCHAR path[MAX_PATH] = { 0 }; + if (!GetParityExePath(path, MAX_PATH)) + return; + + PROCESS_INFORMATION procInfo = { 0 }; + STARTUPINFO startupInfo = { sizeof(STARTUPINFO) }; + + LPWSTR cmd = _T("parity.exe ui"); + CreateProcess(path, cmd, nullptr, nullptr, false, CREATE_NO_WINDOW, nullptr, nullptr, &startupInfo, &procInfo); +} \ No newline at end of file diff --git a/windows/ptray/ptray.ico b/windows/ptray/ptray.ico new file mode 100644 index 0000000000000000000000000000000000000000..61e68b90b86ccb69f7c13a0595bba8ffd1943c4c GIT binary patch literal 67646 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 UthP^z@jit)bXR-G*R9U_4@oN!P5=M^ literal 0 HcmV?d00001 diff --git a/windows/ptray/ptray.vcxproj b/windows/ptray/ptray.vcxproj new file mode 100644 index 000000000..efe1d2e48 --- /dev/null +++ b/windows/ptray/ptray.vcxproj @@ -0,0 +1,155 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {37C89E90-8C9E-4FFC-AAE7-B3695D5EB6F4} + Win32Proj + ptray + 8.1 + + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + Application + true + v140 + Unicode + + + Application + false + v140 + true + Unicode + + + + + + + + + + + + + + + + + + + + + true + + + true + + + false + + + false + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + + + + + Use + Level3 + Disabled + _DEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + + + + + Level3 + NotUsing + MaxSpeed + true + true + WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + Level3 + Use + MaxSpeed + true + true + NDEBUG;_WINDOWS;%(PreprocessorDefinitions) + + + Windows + true + true + true + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/windows/ptray/resource.h b/windows/ptray/resource.h new file mode 100644 index 0000000000000000000000000000000000000000..1f4b023431b96fb9c6edaf617bd5e9ac940c75c7 GIT binary patch literal 1788 zcmb7_QEw7K6ot>TiT_~}AB|Gbr24o58X`*xONo6VmV&`F#XwOT<6pOaXQreS*ab3K zX6EjlbI;zn=W_qAqKf`7Iy%!(4K>x}>4a5Bb+y>#`pbB(XZTi>DAQCU%{9}4XQEqd z18fT|^;OsG>UzmY^i5;k@XWE_GGAevVj1DJWH)9%!E(*&^;(2;u%h>+?(q4+X=B{s zHRtq-i{}>s$0&}Jz_>yDAuKOg*ZHmv*270FpzlI$fZA1GJ*8kfTP;>?B7EA5U)70< zk#o>eu)EBCQ2U@Z_hQCxGV+^3&RIbI0LODs56^-l`oslybJ3A?B^S>yK zRgT;WNUJ;_&-t{FvypQu!?)HhWw;&HoZl59yW$>eWBYWO#mMgqk$WJUaf>?s7-}y& zqBd%KIOp6|%44OJaol|Oit4cXR!U4@H`eda3#{u2YF(mh_lBRWt@kmiw%%uGI0s)CAqto;U@Hxnob#q^WN+P#jImrIicqFWqOs9ct5`3a@MZVYUo-K+5bIP M{4Y!%B0b&z2Q6jVQ2+n{ literal 0 HcmV?d00001 diff --git a/windows/ptray/targetver.h b/windows/ptray/targetver.h new file mode 100644 index 000000000..87c0086de --- /dev/null +++ b/windows/ptray/targetver.h @@ -0,0 +1,8 @@ +#pragma once + +// Including SDKDDKVer.h defines the highest available Windows platform. + +// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and +// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. + +#include From d99f1b517c22d1df839c750cd3bd40feda21a7a4 Mon Sep 17 00:00:00 2001 From: Nicolas Gotchac Date: Thu, 3 Nov 2016 12:04:25 +0100 Subject: [PATCH 12/29] Use enode RPC in UI (#3108) * add enode rpc * spaces -> tabs * Added Enode to JSAPI // Use it to display in Status (#3106) * Added enode to JSON RPC Interfaces #3108 --- js/src/api/rpc/ethcore/ethcore.js | 5 +++++ js/src/jsonrpc/interfaces/ethcore.js | 9 +++++++++ js/src/redux/providers/status.js | 20 ++++++++++++++++++++ js/src/redux/providers/statusReducer.js | 1 + js/src/views/Application/Status/status.css | 10 +++++++++- js/src/views/Application/Status/status.js | 19 ++++++++++++++++++- 6 files changed, 62 insertions(+), 2 deletions(-) diff --git a/js/src/api/rpc/ethcore/ethcore.js b/js/src/api/rpc/ethcore/ethcore.js index 663143f0d..e21c83193 100644 --- a/js/src/api/rpc/ethcore/ethcore.js +++ b/js/src/api/rpc/ethcore/ethcore.js @@ -58,6 +58,11 @@ export default class Ethcore { .execute('ethcore_dropNonReservedPeers'); } + enode () { + return this._transport + .execute('ethcore_enode'); + } + extraData () { return this._transport .execute('ethcore_extraData'); diff --git a/js/src/jsonrpc/interfaces/ethcore.js b/js/src/jsonrpc/interfaces/ethcore.js index c81a82f76..ce1761382 100644 --- a/js/src/jsonrpc/interfaces/ethcore.js +++ b/js/src/jsonrpc/interfaces/ethcore.js @@ -85,6 +85,15 @@ export default { } }, + enode: { + desc: 'Returns the node enode URI', + params: [], + returns: { + type: String, + desc: 'Enode URI' + } + }, + extraData: { desc: 'Returns currently set extra data', params: [], diff --git a/js/src/redux/providers/status.js b/js/src/redux/providers/status.js index 8d5874698..aff453517 100644 --- a/js/src/redux/providers/status.js +++ b/js/src/redux/providers/status.js @@ -29,6 +29,20 @@ export default class Status { this._pollPing(); this._pollStatus(); this._pollLogs(); + this._fetchEnode(); + } + + _fetchEnode () { + this._api + .ethcore.enode() + .then((enode) => { + this._store.dispatch(statusCollection({ enode })); + }) + .catch(() => { + window.setTimeout(() => { + this._fetchEnode(); + }, 1000); + }); } _subscribeBlockNumber () { @@ -68,11 +82,17 @@ export default class Status { _pollStatus = () => { const { secureToken, isConnected, isConnecting, needsToken } = this._api; + const nextTimeout = (timeout = 1000) => { setTimeout(this._pollStatus, timeout); }; + if (isConnected !== this._store.getState().nodeStatus.isConnected) { + this._fetchEnode(); + } + this._store.dispatch(statusCollection({ isConnected, isConnecting, needsToken, secureToken })); + if (!isConnected) { nextTimeout(250); return; diff --git a/js/src/redux/providers/statusReducer.js b/js/src/redux/providers/statusReducer.js index d75f62429..98bb536ae 100644 --- a/js/src/redux/providers/statusReducer.js +++ b/js/src/redux/providers/statusReducer.js @@ -25,6 +25,7 @@ const initialState = { clientVersion: '', coinbase: '', defaultExtraData: '', + enode: '', extraData: '', gasFloorTarget: new BigNumber(0), hashrate: new BigNumber(0), diff --git a/js/src/views/Application/Status/status.css b/js/src/views/Application/Status/status.css index 974c3e990..3ce33f51b 100644 --- a/js/src/views/Application/Status/status.css +++ b/js/src/views/Application/Status/status.css @@ -15,16 +15,24 @@ /* along with Parity. If not, see . */ .status { - clear: both; padding: 1.5em; text-align: right; color: #ddd; + display: flex; + flex-direction: column; + align-items: flex-end; } .title { margin: 0 0.5em 0 2em; } +.enode { + width: 45em; + word-wrap: break-word; + margin: 0.5em 0 0.25em; +} + .block { } diff --git a/js/src/views/Application/Status/status.js b/js/src/views/Application/Status/status.js index 5b1da779d..f86d5e4ff 100644 --- a/js/src/views/Application/Status/status.js +++ b/js/src/views/Application/Status/status.js @@ -26,6 +26,7 @@ class Status extends Component { static propTypes = { blockNumber: PropTypes.object.isRequired, clientVersion: PropTypes.string, + enode: PropTypes.string, netPeers: PropTypes.object, netChain: PropTypes.string, isTest: PropTypes.bool @@ -44,6 +45,7 @@ class Status extends Component {
{ clientVersion }
+ { this.renderEnode() }
@@ -58,14 +60,29 @@ class Status extends Component {
); } + + renderEnode () { + const { enode } = this.props; + + if (!enode) { + return null; + } + + return ( +
+ { enode } +
+ ); + } } function mapStateToProps (state) { - const { blockNumber, clientVersion, netPeers, netChain, isTest } = state.nodeStatus; + const { blockNumber, clientVersion, enode, netPeers, netChain, isTest } = state.nodeStatus; return { blockNumber, clientVersion, + enode, netPeers, netChain, isTest From e251fd49a11a1023276c223399dc9342ef3ed27a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Thu, 3 Nov 2016 12:22:28 +0100 Subject: [PATCH 13/29] Dapps errors embeddable on signer (#3115) --- dapps/src/apps/fetcher.rs | 18 +++++++++++------- dapps/src/handlers/content.rs | 6 +----- dapps/src/handlers/fetch.rs | 15 ++++++++++++--- dapps/src/page/handler.rs | 2 +- dapps/src/router/auth.rs | 3 ++- dapps/src/router/host_validation.rs | 3 ++- dapps/src/router/mod.rs | 2 ++ dapps/src/tests/fetch.rs | 6 +++--- dapps/src/tests/redirection.rs | 6 +++--- 9 files changed, 37 insertions(+), 24 deletions(-) diff --git a/dapps/src/apps/fetcher.rs b/dapps/src/apps/fetcher.rs index e4ee63fea..6c2e5416d 100644 --- a/dapps/src/apps/fetcher.rs +++ b/dapps/src/apps/fetcher.rs @@ -71,12 +71,13 @@ impl ContentFetcher { } } - fn still_syncing() -> Box { + fn still_syncing(port: Option) -> Box { Box::new(ContentHandler::error( StatusCode::ServiceUnavailable, "Sync In Progress", "Your node is still syncing. We cannot resolve any content before it's fully synced.", - Some("Refresh") + Some("Refresh"), + port, )) } @@ -143,7 +144,7 @@ impl ContentFetcher { match content { // Don't serve dapps if we are still syncing (but serve content) Some(URLHintResult::Dapp(_)) if self.sync.is_major_importing() => { - (None, Self::still_syncing()) + (None, Self::still_syncing(self.embeddable_at)) }, Some(URLHintResult::Dapp(dapp)) => { let (handler, fetch_control) = ContentFetcherHandler::new( @@ -155,7 +156,8 @@ impl ContentFetcher { dapps_path: self.dapps_path.clone(), on_done: Box::new(on_done), embeddable_at: self.embeddable_at, - } + }, + self.embeddable_at, ); (Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box) @@ -170,13 +172,14 @@ impl ContentFetcher { mime: content.mime, content_path: self.dapps_path.clone(), on_done: Box::new(on_done), - } + }, + self.embeddable_at, ); (Some(ContentStatus::Fetching(fetch_control)), Box::new(handler) as Box) }, None if self.sync.is_major_importing() => { - (None, Self::still_syncing()) + (None, Self::still_syncing(self.embeddable_at)) }, None => { // This may happen when sync status changes in between @@ -185,7 +188,8 @@ impl ContentFetcher { StatusCode::NotFound, "Resource Not Found", "Requested resource was not found.", - None + None, + self.embeddable_at, )) as Box) }, } diff --git a/dapps/src/handlers/content.rs b/dapps/src/handlers/content.rs index 6bde9cf84..a67fbcd0b 100644 --- a/dapps/src/handlers/content.rs +++ b/dapps/src/handlers/content.rs @@ -48,11 +48,7 @@ impl ContentHandler { Self::new_embeddable(code, content, mime!(Text/Html), embeddable_at) } - pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>) -> Self { - Self::error_embeddable(code, title, message, details, None) - } - - pub fn error_embeddable(code: StatusCode, title: &str, message: &str, details: Option<&str>, embeddable_at: Option) -> Self { + pub fn error(code: StatusCode, title: &str, message: &str, details: Option<&str>, embeddable_at: Option) -> Self { Self::html(code, format!( include_str!("../error_tpl.html"), title=title, diff --git a/dapps/src/handlers/fetch.rs b/dapps/src/handlers/fetch.rs index 639fc7497..e5acfc50b 100644 --- a/dapps/src/handlers/fetch.rs +++ b/dapps/src/handlers/fetch.rs @@ -138,6 +138,7 @@ pub struct ContentFetcherHandler { client: Option, using_dapps_domains: bool, installer: H, + embeddable_at: Option, } impl Drop for ContentFetcherHandler { @@ -156,7 +157,9 @@ impl ContentFetcherHandler { url: String, control: Control, using_dapps_domains: bool, - handler: H) -> (Self, Arc) { + handler: H, + embeddable_at: Option, + ) -> (Self, Arc) { let fetch_control = Arc::new(FetchControl::default()); let client = Client::default(); @@ -167,6 +170,7 @@ impl ContentFetcherHandler { status: FetchState::NotStarted(url), using_dapps_domains: using_dapps_domains, installer: handler, + embeddable_at: embeddable_at, }; (handler, fetch_control) @@ -204,6 +208,7 @@ impl server::Handler for ContentFetcherHandler< "Unable To Start Dapp Download", "Could not initialize download of the dapp. It might be a problem with the remote server.", Some(&format!("{}", e)), + self.embeddable_at, )), } }, @@ -213,6 +218,7 @@ impl server::Handler for ContentFetcherHandler< "Method Not Allowed", "Only GET requests are allowed.", None, + self.embeddable_at, )), }) } else { None }; @@ -234,7 +240,8 @@ impl server::Handler for ContentFetcherHandler< StatusCode::GatewayTimeout, "Download Timeout", &format!("Could not fetch content within {} seconds.", FETCH_TIMEOUT), - None + None, + self.embeddable_at, ); Self::close_client(&mut self.client); (Some(FetchState::Error(timeout)), Next::write()) @@ -255,7 +262,8 @@ impl server::Handler for ContentFetcherHandler< StatusCode::BadGateway, "Invalid Dapp", "Downloaded bundle does not contain a valid content.", - Some(&format!("{:?}", e)) + Some(&format!("{:?}", e)), + self.embeddable_at, )) }, Ok((id, result)) => { @@ -274,6 +282,7 @@ impl server::Handler for ContentFetcherHandler< "Download Error", "There was an error when fetching the content.", Some(&format!("{:?}", e)), + self.embeddable_at, ); (Some(FetchState::Error(error)), Next::write()) }, diff --git a/dapps/src/page/handler.rs b/dapps/src/page/handler.rs index 0962f22c7..a2ca5e258 100644 --- a/dapps/src/page/handler.rs +++ b/dapps/src/page/handler.rs @@ -59,7 +59,7 @@ pub enum ServedFile { impl ServedFile { pub fn new(embeddable_at: Option) -> Self { - ServedFile::Error(ContentHandler::error_embeddable( + ServedFile::Error(ContentHandler::error( StatusCode::NotFound, "404 Not Found", "Requested dapp resource was not found.", diff --git a/dapps/src/router/auth.rs b/dapps/src/router/auth.rs index ff05420bc..a220e2ab0 100644 --- a/dapps/src/router/auth.rs +++ b/dapps/src/router/auth.rs @@ -59,7 +59,8 @@ impl Authorization for HttpBasicAuth { status::StatusCode::Unauthorized, "Unauthorized", "You need to provide valid credentials to access this page.", - None + None, + None, ))) }, Access::AuthRequired => { diff --git a/dapps/src/router/host_validation.rs b/dapps/src/router/host_validation.rs index 2b7e6c9e4..802466efd 100644 --- a/dapps/src/router/host_validation.rs +++ b/dapps/src/router/host_validation.rs @@ -41,6 +41,7 @@ pub fn host_invalid_response() -> Box + Send> { Box::new(ContentHandler::error(StatusCode::Forbidden, "Current Host Is Disallowed", "You are trying to access your node using incorrect address.", - Some("Use allowed URL or specify different hosts CLI options.") + Some("Use allowed URL or specify different hosts CLI options."), + None, )) } diff --git a/dapps/src/router/mod.rs b/dapps/src/router/mod.rs index 6ca453d6d..fe8061ef0 100644 --- a/dapps/src/router/mod.rs +++ b/dapps/src/router/mod.rs @@ -117,6 +117,7 @@ impl server::Handler for Router { "404 Not Found", "Requested content was not found.", None, + self.signer_port, )) }, // Redirect any other GET request to signer. @@ -131,6 +132,7 @@ impl server::Handler for Router { "404 Not Found", "Your homepage is not available when Trusted Signer is disabled.", Some("You can still access dapps by writing a correct address, though. Re-enabled Signer to get your homepage back."), + self.signer_port, )) } }, diff --git a/dapps/src/tests/fetch.rs b/dapps/src/tests/fetch.rs index 1cabca5cb..d50b2bdde 100644 --- a/dapps/src/tests/fetch.rs +++ b/dapps/src/tests/fetch.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use tests::helpers::{serve_with_registrar, serve_with_registrar_and_sync, request, assert_security_headers}; +use tests::helpers::{serve_with_registrar, serve_with_registrar_and_sync, request, assert_security_headers_for_embed}; #[test] fn should_resolve_dapp() { @@ -34,7 +34,7 @@ fn should_resolve_dapp() { // then assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); assert_eq!(registrar.calls.lock().len(), 2); - assert_security_headers(&response.headers); + assert_security_headers_for_embed(&response.headers); } #[test] @@ -63,5 +63,5 @@ fn should_return_503_when_syncing_but_should_make_the_calls() { // then assert_eq!(response.status, "HTTP/1.1 503 Service Unavailable".to_owned()); assert_eq!(registrar.calls.lock().len(), 4); - assert_security_headers(&response.headers); + assert_security_headers_for_embed(&response.headers); } diff --git a/dapps/src/tests/redirection.rs b/dapps/src/tests/redirection.rs index 398d08774..b0a5ca9a2 100644 --- a/dapps/src/tests/redirection.rs +++ b/dapps/src/tests/redirection.rs @@ -14,7 +14,7 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -use tests::helpers::{serve, request, assert_security_headers}; +use tests::helpers::{serve, request, assert_security_headers, assert_security_headers_for_embed}; #[test] fn should_redirect_to_home() { @@ -93,7 +93,7 @@ fn should_display_404_on_invalid_dapp() { // then assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); - assert_security_headers(&response.headers); + assert_security_headers_for_embed(&response.headers); } #[test] @@ -113,7 +113,7 @@ fn should_display_404_on_invalid_dapp_with_domain() { // then assert_eq!(response.status, "HTTP/1.1 404 Not Found".to_owned()); - assert_security_headers(&response.headers); + assert_security_headers_for_embed(&response.headers); } #[test] From e4fcf4da2b2bbc646063683da3443519586bcd7e Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Thu, 3 Nov 2016 12:23:25 +0100 Subject: [PATCH 14/29] Add basic validation for contract execute values (#3118) --- .../modals/ExecuteContract/executeContract.js | 20 +++++++++----- js/src/util/validation.js | 27 ++++++++++++++++++- 2 files changed, 39 insertions(+), 8 deletions(-) diff --git a/js/src/modals/ExecuteContract/executeContract.js b/js/src/modals/ExecuteContract/executeContract.js index 67f350feb..e0462982d 100644 --- a/js/src/modals/ExecuteContract/executeContract.js +++ b/js/src/modals/ExecuteContract/executeContract.js @@ -19,7 +19,7 @@ import ActionDoneAll from 'material-ui/svg-icons/action/done-all'; import ContentClear from 'material-ui/svg-icons/content/clear'; import { BusyStep, CompletedStep, Button, IdentityIcon, Modal, TxHash } from '../../ui'; -import { validateAddress } from '../../util/validation'; +import { validateAddress, validateUint } from '../../util/validation'; import DetailsStep from './DetailsStep'; @@ -41,6 +41,7 @@ export default class ExecuteContract extends Component { state = { amount: '0', amountError: null, + fromAddressError: null, func: null, funcError: null, values: [], @@ -77,7 +78,8 @@ export default class ExecuteContract extends Component { renderDialogActions () { const { onClose, fromAddress } = this.props; - const { sending, step } = this.state; + const { sending, step, fromAddressError, valuesError } = this.state; + const hasError = fromAddressError || valuesError.find((error) => error); const cancelBtn = (