From 3041c95408ce753d5fdd77726ce8580b657e0b16 Mon Sep 17 00:00:00 2001 From: Gav Wood Date: Mon, 13 Mar 2017 12:10:53 +0100 Subject: [PATCH] Chain-selection from UI (#4859) * First little bits for chain-selection. * Provide RPCs and get settings through to user defaults. * Hasty stash. * Fix updater accidentally redownloading. * Finish up. * Add JS tests. * Hypervisor should never run a binary modified before itself. * Style. * Help tweak. * Fix test compile. * Fix JS test * Build fix for tests. * Revert default chain name * Another test * Use spec name via client. * Fix mock up. * whitespace [ci:skip] * whitespace [ci:skip] * remove exit/restart endpoints. --- Cargo.lock | 2 +- Cargo.toml | 2 +- ethcore/src/client/client.rs | 38 +++++-- ethcore/src/client/config.rs | 2 + ethcore/src/client/test_client.rs | 4 + ethcore/src/client/traits.rs | 6 ++ js/src/api/rpc/parity/parity.js | 13 ++- js/src/jsonrpc/interfaces/parity.js | 2 +- js/src/views/Settings/Parity/parity.js | 105 +++++++++++++++++++- js/src/views/Settings/Parity/parity.spec.js | 28 ++++++ js/src/views/Settings/Parity/parity.test.js | 4 +- js/src/views/Settings/Parity/store.js | 30 ++++++ js/src/views/Settings/Parity/store.spec.js | 41 ++++++++ parity/blockchain.rs | 2 + parity/cli/mod.rs | 2 +- parity/configuration.rs | 19 +++- parity/helpers.rs | 3 +- parity/main.rs | 35 +++++-- parity/run.rs | 41 +++++--- parity/snapshot.rs | 1 + rpc/src/v1/helpers/network_settings.rs | 2 +- rpc/src/v1/impls/light/parity.rs | 4 + rpc/src/v1/impls/light/parity_set.rs | 4 + rpc/src/v1/impls/parity.rs | 4 + rpc/src/v1/impls/parity_set.rs | 5 + rpc/src/v1/tests/mocked/parity.rs | 11 ++ rpc/src/v1/traits/parity.rs | 8 +- rpc/src/v1/traits/parity_set.rs | 4 + 28 files changed, 372 insertions(+), 50 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1ed07b70..f5e41f647 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [root] name = "parity" -version = "1.6.0" +version = "1.7.0" dependencies = [ "ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/Cargo.toml b/Cargo.toml index 51ecbcdf9..2a01d2c8e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Parity Ethereum client" name = "parity" -version = "1.6.0" +version = "1.7.0" license = "GPL-3.0" authors = ["Parity Technologies "] diff --git a/ethcore/src/client/client.rs b/ethcore/src/client/client.rs index e7e2d439d..5ddc83762 100644 --- a/ethcore/src/client/client.rs +++ b/ethcore/src/client/client.rs @@ -151,8 +151,9 @@ pub struct Client { factories: Factories, history: u64, rng: Mutex, - on_mode_change: Mutex>>, + on_user_defaults_change: Mutex) + 'static + Send>>>, registrar: Mutex>, + exit_handler: Mutex) + 'static + Send>>>, } impl Client { @@ -240,8 +241,9 @@ impl Client { factories: factories, history: history, rng: Mutex::new(OsRng::new().map_err(::util::UtilError::StdIo)?), - on_mode_change: Mutex::new(None), + on_user_defaults_change: Mutex::new(None), registrar: Mutex::new(None), + exit_handler: Mutex::new(None), }); { @@ -276,6 +278,11 @@ impl Client { self.notify.write().push(Arc::downgrade(&target)); } + /// Set a closure to call when we want to restart the client + pub fn set_exit_handler(&self, f: F) where F: Fn(bool, Option) + 'static + Send { + *self.exit_handler.lock() = Some(Box::new(f)); + } + /// Returns engine reference. pub fn engine(&self) -> &Engine { &*self.engine @@ -294,9 +301,9 @@ impl Client { self.registrar.lock() } - /// Register an action to be done if a mode change happens. - pub fn on_mode_change(&self, f: F) where F: 'static + FnMut(&Mode) + Send { - *self.on_mode_change.lock() = Some(Box::new(f)); + /// Register an action to be done if a mode/spec_name change happens. + pub fn on_user_defaults_change(&self, f: F) where F: 'static + FnMut(Option) + Send { + *self.on_user_defaults_change.lock() = Some(Box::new(f)); } /// Flush the block import queue. @@ -651,7 +658,6 @@ impl Client { self.miner.clone() } - /// Replace io channel. Useful for testing. pub fn set_io_channel(&self, io_channel: IoChannel) { *self.io_channel.lock() = io_channel; @@ -1030,9 +1036,9 @@ impl BlockChainClient for Client { let mut mode = self.mode.lock(); *mode = new_mode.clone().into(); trace!(target: "mode", "Mode now {:?}", &*mode); - if let Some(ref mut f) = *self.on_mode_change.lock() { + if let Some(ref mut f) = *self.on_user_defaults_change.lock() { trace!(target: "mode", "Making callback..."); - f(&*mode) + f(Some((&*mode).clone())) } } match new_mode { @@ -1042,6 +1048,22 @@ impl BlockChainClient for Client { } } + fn spec_name(&self) -> String { + self.config.spec_name.clone() + } + + fn set_spec_name(&self, new_spec_name: String) { + trace!(target: "mode", "Client::set_spec_name({:?})", new_spec_name); + if !self.enabled.load(AtomicOrdering::Relaxed) { + return; + } + if let Some(ref h) = *self.exit_handler.lock() { + (*h)(true, Some(new_spec_name)); + } else { + warn!("Not hypervised; cannot change chain."); + } + } + fn best_block_header(&self) -> encoded::Header { self.chain.read().best_block_header() } diff --git a/ethcore/src/client/config.rs b/ethcore/src/client/config.rs index 11078b17b..5c7cf9471 100644 --- a/ethcore/src/client/config.rs +++ b/ethcore/src/client/config.rs @@ -123,6 +123,8 @@ pub struct ClientConfig { pub db_wal: bool, /// Operating mode pub mode: Mode, + /// The chain spec name + pub spec_name: String, /// Type of block verifier used by client. pub verifier_type: VerifierType, /// State db cache-size. diff --git a/ethcore/src/client/test_client.rs b/ethcore/src/client/test_client.rs index 6a613fa7d..d96b305de 100644 --- a/ethcore/src/client/test_client.rs +++ b/ethcore/src/client/test_client.rs @@ -721,6 +721,10 @@ impl BlockChainClient for TestBlockChainClient { fn set_mode(&self, _: Mode) { unimplemented!(); } + fn spec_name(&self) -> String { "foundation".into() } + + fn set_spec_name(&self, _: String) { unimplemented!(); } + fn disable(&self) { unimplemented!(); } fn pruning_info(&self) -> PruningInfo { diff --git a/ethcore/src/client/traits.rs b/ethcore/src/client/traits.rs index e31712852..b4c284f11 100644 --- a/ethcore/src/client/traits.rs +++ b/ethcore/src/client/traits.rs @@ -241,6 +241,12 @@ pub trait BlockChainClient : Sync + Send { /// Set the mode. fn set_mode(&self, mode: Mode); + /// Get the chain spec name. + fn spec_name(&self) -> String; + + /// Set the chain via a spec name. + fn set_spec_name(&self, spec_name: String); + /// Disable the client from importing blocks. This cannot be undone in this session and indicates /// that a subsystem has reason to believe this executable incapable of syncing the chain. fn disable(&self); diff --git a/js/src/api/rpc/parity/parity.js b/js/src/api/rpc/parity/parity.js index 31de948dc..dba6e5d30 100644 --- a/js/src/api/rpc/parity/parity.js +++ b/js/src/api/rpc/parity/parity.js @@ -284,9 +284,15 @@ export default class Parity { .execute('parity_mode'); } + // DEPRECATED - use chain instead. netChain () { return this._transport - .execute('parity_netChain'); + .execute('parity_chain'); + } + + chain () { + return this._transport + .execute('parity_chain'); } netPeers () { @@ -454,6 +460,11 @@ export default class Parity { .execute('parity_setMode', mode); } + setChain (specName) { + return this._transport + .execute('parity_setChain', specName); + } + setNewDappsAddresses (addresses) { return this._transport .execute('parity_setNewDappsAddresses', addresses ? inAddresses(addresses) : null); diff --git a/js/src/jsonrpc/interfaces/parity.js b/js/src/jsonrpc/interfaces/parity.js index cf62213a5..1e6df05de 100644 --- a/js/src/jsonrpc/interfaces/parity.js +++ b/js/src/jsonrpc/interfaces/parity.js @@ -199,7 +199,7 @@ export default { '2017-01-20 18:14:19 Configured for DevelopmentChain using InstantSeal engine', '2017-01-20 18:14:19 Operating mode: active', '2017-01-20 18:14:19 State DB configuration: fast', - '2017-01-20 18:14:19 Starting Parity/v1.6.0-unstable-2ae8b4c-20170120/x86_64-linux-gnu/rustc1.14.0' + '2017-01-20 18:14:19 Starting Parity/v1.7.0-unstable-2ae8b4c-20170120/x86_64-linux-gnu/rustc1.14.0' ] } }, diff --git a/js/src/views/Settings/Parity/parity.js b/js/src/views/Settings/Parity/parity.js index 769345d0a..7de9b8061 100644 --- a/js/src/views/Settings/Parity/parity.js +++ b/js/src/views/Settings/Parity/parity.js @@ -35,6 +35,7 @@ export default class Parity extends Component { features = FeaturesStore.get(); componentWillMount () { + this.store.loadChain(); return this.store.loadMode(); } @@ -50,11 +51,12 @@ export default class Parity extends Component {
+ { this.renderChains() } { this.renderModes() } @@ -65,12 +67,12 @@ export default class Parity extends Component { ); } - renderItem (mode, label) { + renderItem (name, label) { return ( { label } @@ -134,7 +136,7 @@ export default class Parity extends Component { hint={ } label={ @@ -182,7 +184,100 @@ export default class Parity extends Component { ); } + renderChains () { + const { chain } = this.store; + + return ( + + ); + } + onChangeMode = (event, index, mode) => { this.store.changeMode(mode || event.target.value); } + + onChangeChain = (event, index, chain) => { + this.store.changeChain(chain || event.target.value); + } } diff --git a/js/src/views/Settings/Parity/parity.spec.js b/js/src/views/Settings/Parity/parity.spec.js index 53465b2fc..8b5ff61eb 100644 --- a/js/src/views/Settings/Parity/parity.spec.js +++ b/js/src/views/Settings/Parity/parity.spec.js @@ -38,10 +38,12 @@ describe('views/Settings/Parity', () => { beforeEach(() => { render(); sinon.spy(instance.store, 'loadMode'); + sinon.spy(instance.store, 'loadChain'); }); afterEach(() => { instance.store.loadMode.restore(); + instance.store.loadChain.restore(); }); it('renders defaults', () => { @@ -56,6 +58,10 @@ describe('views/Settings/Parity', () => { it('loads the mode in the store', () => { expect(instance.store.loadMode).to.have.been.called; }); + + it('loads the chain in the store', () => { + expect(instance.store.loadChain).to.have.been.called; + }); }); describe('components', () => { @@ -94,5 +100,27 @@ describe('views/Settings/Parity', () => { expect(instance.store.changeMode).to.have.been.calledWith('dark'); }); }); + + describe('chain selector', () => { + let select; + + beforeEach(() => { + select = component.find('Select[id="parityChainSelect"]'); + sinon.spy(instance.store, 'changeChain'); + }); + + afterEach(() => { + instance.store.changeChain.restore(); + }); + + it('renders a chain selector', () => { + expect(select).to.have.length(1); + }); + + it('changes the chain on the store when changed', () => { + select.simulate('change', { target: { value: 'dark' } }); + expect(instance.store.changeChain).to.have.been.calledWith('dark'); + }); + }); }); }); diff --git a/js/src/views/Settings/Parity/parity.test.js b/js/src/views/Settings/Parity/parity.test.js index 1d21fa1b0..41b337075 100644 --- a/js/src/views/Settings/Parity/parity.test.js +++ b/js/src/views/Settings/Parity/parity.test.js @@ -20,7 +20,9 @@ function createApi () { return { parity: { mode: sinon.stub().resolves('passive'), - setMode: sinon.stub().resolves(true) + setMode: sinon.stub().resolves(true), + chain: sinon.stub().resolves('foundation'), + setChain: sinon.stub().resolves(true) } }; } diff --git a/js/src/views/Settings/Parity/store.js b/js/src/views/Settings/Parity/store.js index 3dc34a696..6b9c477e8 100644 --- a/js/src/views/Settings/Parity/store.js +++ b/js/src/views/Settings/Parity/store.js @@ -20,6 +20,7 @@ import { action, observable } from 'mobx'; import { LOG_KEYS } from '~/config'; const DEFAULT_MODE = 'active'; +const DEFAULT_CHAIN = 'foundation'; const LOGLEVEL_OPTIONS = Object .keys(LogLevel.levels) .map((name) => { @@ -32,6 +33,7 @@ const LOGLEVEL_OPTIONS = Object export default class Store { @observable logLevels = {}; @observable mode = DEFAULT_MODE; + @observable chain = DEFAULT_CHAIN; constructor (api) { this._api = api; @@ -51,6 +53,10 @@ export default class Store { this.mode = mode; } + @action setChain = (chain) => { + this.chain = chain; + } + changeMode (mode) { return this._api.parity .setMode(mode) @@ -64,6 +70,19 @@ export default class Store { }); } + changeChain (chain) { + return this._api.parity + .setChain(chain) + .then((result) => { + if (result) { + this.setChain(chain); + } + }) + .catch((error) => { + console.warn('changeChain', error); + }); + } + loadLogLevels () { this.setLogLevels( Object @@ -98,6 +117,17 @@ export default class Store { console.warn('loadMode', error); }); } + + loadChain () { + return this._api.parity + .chain() + .then((chain) => { + this.setChain(chain); + }) + .catch((error) => { + console.warn('loadChain', error); + }); + } } export { diff --git a/js/src/views/Settings/Parity/store.spec.js b/js/src/views/Settings/Parity/store.spec.js index 4bb7c5d3a..f965b9c44 100644 --- a/js/src/views/Settings/Parity/store.spec.js +++ b/js/src/views/Settings/Parity/store.spec.js @@ -33,16 +33,22 @@ describe('views/Settings/Parity/Store', () => { beforeEach(() => { createStore(); sinon.spy(store, 'setMode'); + sinon.spy(store, 'setChain'); }); afterEach(() => { store.setMode.restore(); + store.setChain.restore(); }); it('defaults to mode === active', () => { expect(store.mode).to.equal('active'); }); + it('defaults to chain === foundation', () => { + expect(store.chain).to.equal('foundation'); + }); + describe('@action', () => { describe('setMode', () => { it('sets the mode', () => { @@ -50,6 +56,13 @@ describe('views/Settings/Parity/Store', () => { expect(store.mode).to.equal('offline'); }); }); + + describe('setChain', () => { + it('sets the chain', () => { + store.setChain('dev'); + expect(store.chain).to.equal('dev'); + }); + }); }); describe('operations', () => { @@ -80,5 +93,33 @@ describe('views/Settings/Parity/Store', () => { expect(store.setMode).to.have.been.calledWith('passive'); }); }); + + describe('changeChain', () => { + beforeEach(() => { + return store.changeChain('dev'); + }); + + it('calls parity.setChain', () => { + expect(api.parity.setChain).to.have.been.calledWith('dev'); + }); + + it('sets the chain as provided', () => { + expect(store.setChain).to.have.been.calledWith('dev'); + }); + }); + + describe('loadChain', () => { + beforeEach(() => { + return store.loadChain(); + }); + + it('calls parity.chain', () => { + expect(api.parity.chain).to.have.been.called; + }); + + it('sets the chain as retrieved', () => { + expect(store.setChain).to.have.been.calledWith('foundation'); + }); + }); }); }); diff --git a/parity/blockchain.rs b/parity/blockchain.rs index 1eeb3d71e..2796e71e3 100644 --- a/parity/blockchain.rs +++ b/parity/blockchain.rs @@ -190,6 +190,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result<(), String> { // prepare client config let mut client_config = to_client_config( &cmd.cache_config, + spec.name.to_lowercase(), Mode::Active, tracing, fat_db, @@ -361,6 +362,7 @@ fn start_client( // prepare client config let client_config = to_client_config( &cache_config, + spec.name.to_lowercase(), Mode::Active, tracing, fat_db, diff --git a/parity/cli/mod.rs b/parity/cli/mod.rs index cb256f0b7..7d35301d8 100644 --- a/parity/cli/mod.rs +++ b/parity/cli/mod.rs @@ -90,7 +90,7 @@ usage! { flag_release_track: String = "current", or |c: &Config| otry!(c.parity).release_track.clone(), flag_no_download: bool = false, or |c: &Config| otry!(c.parity).no_download.clone(), flag_no_consensus: bool = false, or |c: &Config| otry!(c.parity).no_consensus.clone(), - flag_chain: String = "homestead", or |c: &Config| otry!(c.parity).chain.clone(), + flag_chain: String = "foundation", or |c: &Config| otry!(c.parity).chain.clone(), flag_keys_path: String = "$BASE/keys", or |c: &Config| otry!(c.parity).keys_path.clone(), flag_identity: String = "", or |c: &Config| otry!(c.parity).identity.clone(), diff --git a/parity/configuration.rs b/parity/configuration.rs index c0756a771..fdbd86f3e 100644 --- a/parity/configuration.rs +++ b/parity/configuration.rs @@ -85,14 +85,16 @@ pub struct Execute { #[derive(Debug, PartialEq)] pub struct Configuration { pub args: Args, + pub spec_name_override: Option, } impl Configuration { - pub fn parse>(command: &[S]) -> Result { + pub fn parse>(command: &[S], spec_name_override: Option) -> Result { let args = Args::parse(command)?; let config = Configuration { args: args, + spec_name_override: spec_name_override, }; Ok(config) @@ -103,7 +105,11 @@ impl Configuration { let pruning = self.args.flag_pruning.parse()?; let pruning_history = self.args.flag_pruning_history; let vm_type = self.vm_type()?; - let mode = match self.args.flag_mode.as_ref() { "last" => None, mode => Some(to_mode(&mode, self.args.flag_mode_timeout, self.args.flag_mode_alarm)?), }; + let spec = self.chain().parse()?; + let mode = match self.args.flag_mode.as_ref() { + "last" => None, + mode => Some(to_mode(&mode, self.args.flag_mode_timeout, self.args.flag_mode_alarm)?), + }; let update_policy = self.update_policy()?; let logger_config = self.logger_config(); let http_conf = self.http_config()?; @@ -111,7 +117,6 @@ impl Configuration { let net_conf = self.net_config()?; let network_id = self.network_id(); let cache_config = self.cache_config(); - let spec = self.chain().parse()?; let tracing = self.args.flag_tracing.parse()?; let fat_db = self.args.flag_fat_db.parse()?; let compaction = self.args.flag_db_compaction.parse()?; @@ -437,7 +442,10 @@ impl Configuration { } fn chain(&self) -> String { - if self.args.flag_testnet { + if let Some(ref s) = self.spec_name_override { + s.clone() + } + else if self.args.flag_testnet { "testnet".to_owned() } else { self.args.flag_chain.clone() @@ -972,6 +980,7 @@ mod tests { fn parse(args: &[&str]) -> Configuration { Configuration { args: Args::parse_without_config(args).unwrap(), + spec_name_override: None, } } @@ -1429,7 +1438,7 @@ mod tests { let filename = temp.as_str().to_owned() + "/peers"; File::create(filename.clone()).unwrap().write_all(b" \n\t\n").unwrap(); let args = vec!["parity", "--reserved-peers", &filename]; - let conf = Configuration::parse(&args).unwrap(); + let conf = Configuration::parse(&args, None).unwrap(); assert!(conf.init_reserved_nodes().is_ok()); } diff --git a/parity/helpers.rs b/parity/helpers.rs index f45f583a0..d691aabc6 100644 --- a/parity/helpers.rs +++ b/parity/helpers.rs @@ -215,6 +215,7 @@ pub fn default_network_config() -> ::ethsync::NetworkConfiguration { #[cfg_attr(feature = "dev", allow(too_many_arguments))] pub fn to_client_config( cache_config: &CacheConfig, + spec_name: String, mode: Mode, tracing: bool, fat_db: bool, @@ -261,6 +262,7 @@ pub fn to_client_config( client_config.vm_type = vm_type; client_config.name = name; client_config.verifier_type = if check_seal { VerifierType::Canon } else { VerifierType::CanonNoSeal }; + client_config.spec_name = spec_name; client_config } @@ -479,4 +481,3 @@ but the first password is trimmed assert_eq!(to_bootnodes(&Some(two_bootnodes.into())), Ok(vec![one_bootnode.into(), one_bootnode.into()])); } } - diff --git a/parity/main.rs b/parity/main.rs index 1882cb6f3..7d22be1c3 100644 --- a/parity/main.rs +++ b/parity/main.rs @@ -122,7 +122,7 @@ mod stratum; use std::{process, env}; use std::collections::HashMap; use std::io::{self as stdio, BufReader, Read, Write}; -use std::fs::{metadata, File}; +use std::fs::{remove_file, metadata, File}; use std::path::PathBuf; use util::sha3::sha3; use cli::Args; @@ -143,7 +143,7 @@ fn print_hash_of(maybe_file: Option) -> Result { enum PostExecutionAction { Print(String), - Restart, + Restart(Option), Quit, } @@ -152,8 +152,8 @@ fn execute(command: Execute, can_restart: bool) -> Result { - let restart = run::execute(run_cmd, can_restart, logger)?; - Ok(if restart { PostExecutionAction::Restart } else { PostExecutionAction::Quit }) + let (restart, spec_name) = run::execute(run_cmd, can_restart, logger)?; + Ok(if restart { PostExecutionAction::Restart(spec_name) } else { PostExecutionAction::Quit }) }, Cmd::Version => Ok(PostExecutionAction::Print(Args::print_version())), Cmd::Hash(maybe_file) => print_hash_of(maybe_file).map(|s| PostExecutionAction::Print(s)), @@ -170,7 +170,7 @@ fn execute(command: Execute, can_restart: bool) -> Result Result { let args: Vec = env::args().collect(); - let conf = Configuration::parse(&args).unwrap_or_else(|e| e.exit()); + let conf = Configuration::parse(&args, take_spec_name_override()).unwrap_or_else(|e| e.exit()); let deprecated = find_deprecated(&conf.args); for d in deprecated { @@ -208,6 +208,22 @@ fn latest_exe_path() -> Option { .and_then(|mut f| { let mut exe = String::new(); f.read_to_string(&mut exe).ok().map(|_| updates_path(&exe)) }) } +fn set_spec_name_override(spec_name: String) { + if let Err(e) = File::create(updates_path("spec_name_overide")) + .and_then(|mut f| f.write_all(spec_name.as_bytes())) + { + warn!("Couldn't override chain spec: {}", e); + } +} + +fn take_spec_name_override() -> Option { + let p = updates_path("spec_name_overide"); + let r = File::open(p.clone()).ok() + .and_then(|mut f| { let mut spec_name = String::new(); f.read_to_string(&mut spec_name).ok().map(|_| spec_name) }); + let _ = remove_file(p); + r +} + #[cfg(windows)] fn global_cleanup() { extern "system" { pub fn WSACleanup() -> i32; } @@ -250,7 +266,12 @@ fn main_direct(can_restart: bool) -> i32 { match start(can_restart) { Ok(result) => match result { PostExecutionAction::Print(s) => { println!("{}", s); 0 }, - PostExecutionAction::Restart => PLEASE_RESTART_EXIT_CODE, + PostExecutionAction::Restart(spec_name_override) => { + if let Some(spec_name) = spec_name_override { + set_spec_name_override(spec_name); + } + PLEASE_RESTART_EXIT_CODE + }, PostExecutionAction::Quit => 0, }, Err(err) => { @@ -303,7 +324,7 @@ fn main() { (Some(latest_exe_time), Some(this_exe_time)) if latest_exe_time > this_exe_time => true, _ => false, }; - trace_main!("Starting... (have-update: {}, non-updated-current: {})", have_update, is_non_updated_current); trace_main!("Starting... (have-update: {}, non-updated-current: {})", have_update, is_non_updated_current); + trace_main!("Starting... (have-update: {}, non-updated-current: {}, update-is-newer: {})", have_update, is_non_updated_current, update_is_newer); let exit_code = if have_update && is_non_updated_current && update_is_newer { trace_main!("Attempting to run latest update ({})...", latest_exe.as_ref().expect("guarded by have_update; latest_exe must exist for have_update; qed").display()); run_parity().unwrap_or_else(|| { trace_main!("Falling back to local..."); main_direct(true) }) diff --git a/parity/run.rs b/parity/run.rs index b25ed3188..b3313c16e 100644 --- a/parity/run.rs +++ b/parity/run.rs @@ -24,7 +24,7 @@ use util::{Colour, version, RotatingLogger, Mutex, Condvar}; use io::{MayPanic, ForwardPanic, PanicHandler}; use ethcore_logger::{Config as LogConfig}; use ethcore::miner::{StratumOptions, Stratum}; -use ethcore::client::{Mode, DatabaseCompactionProfile, VMType, BlockChainClient}; +use ethcore::client::{Client, Mode, DatabaseCompactionProfile, VMType, BlockChainClient}; use ethcore::service::ClientService; use ethcore::account_provider::{AccountProvider, AccountProviderSettings}; use ethcore::miner::{Miner, MinerService, ExternalMiner, MinerOptions}; @@ -152,12 +152,12 @@ impl ::local_store::NodeInfo for FullNodeInfo { } } -pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> Result { +pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> Result<(bool, Option), 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() { - return open_ui(&cmd.dapps_conf, &cmd.signer_conf).map(|_| false); + return open_ui(&cmd.dapps_conf, &cmd.signer_conf).map(|_| (false, None)); } } @@ -286,6 +286,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R // create client config let mut client_config = to_client_config( &cmd.cache_config, + spec.name.to_lowercase(), mode.clone(), tracing, fat_db, @@ -493,8 +494,10 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R user_defaults.save(&user_defaults_path)?; // tell client how to save the default mode if it gets changed. - client.on_mode_change(move |mode: &Mode| { - user_defaults.mode = mode.clone(); + client.on_user_defaults_change(move |mode: Option| { + if let Some(mode) = mode { + user_defaults.mode = mode; + } let _ = user_defaults.save(&user_defaults_path); // discard failures - there's nothing we can do }); @@ -503,6 +506,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R true => None, false => { let sync = sync_provider.clone(); + let client = client.clone(); let watcher = Arc::new(snapshot::Watcher::new( service.client(), move || is_major_importing(Some(sync.status().state), client.queue_info()), @@ -526,7 +530,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc) -> R } // Handle exit - let restart = wait_for_exit(panic_handler, Some(updater), can_restart); + let restart = wait_for_exit(panic_handler, Some(updater), Some(client), can_restart); // drop this stuff as soon as exit detected. drop((http_server, ipc_server, dapps_server, signer_server, secretstore_key_server, ipfs_server, event_loop)); @@ -604,9 +608,10 @@ fn build_create_account_hint(spec: &SpecType, keys: &str) -> String { fn wait_for_exit( panic_handler: Arc, updater: Option>, + client: Option>, can_restart: bool -) -> bool { - let exit = Arc::new((Mutex::new(false), Condvar::new())); +) -> (bool, Option) { + let exit = Arc::new((Mutex::new((false, None)), Condvar::new())); // Handle possible exits let e = exit.clone(); @@ -616,18 +621,24 @@ fn wait_for_exit( let e = exit.clone(); panic_handler.on_panic(move |_reason| { e.1.notify_all(); }); - if let Some(updater) = updater { - // Handle updater wanting to restart us - if can_restart { + if can_restart { + if let Some(updater) = updater { + // Handle updater wanting to restart us let e = exit.clone(); - updater.set_exit_handler(move || { *e.0.lock() = true; e.1.notify_all(); }); - } else { - updater.set_exit_handler(|| info!("Update installed; ready for restart.")); + updater.set_exit_handler(move || { *e.0.lock() = (true, None); e.1.notify_all(); }); } + + if let Some(client) = client { + // Handle updater wanting to restart us + let e = exit.clone(); + client.set_exit_handler(move |restart, new_chain: Option| { *e.0.lock() = (restart, new_chain); e.1.notify_all(); }); + } + } else { + trace!(target: "mode", "Not hypervised: not setting exit handlers."); } // Wait for signal let mut l = exit.0.lock(); let _ = exit.1.wait(&mut l); - *l + l.clone() } diff --git a/parity/snapshot.rs b/parity/snapshot.rs index 54e972757..af39bffb0 100644 --- a/parity/snapshot.rs +++ b/parity/snapshot.rs @@ -173,6 +173,7 @@ impl SnapshotCommand { // prepare client config let client_config = to_client_config( &self.cache_config, + spec.name.to_lowercase(), Mode::Active, tracing, fat_db, diff --git a/rpc/src/v1/helpers/network_settings.rs b/rpc/src/v1/helpers/network_settings.rs index 63784fb3a..cda32c658 100644 --- a/rpc/src/v1/helpers/network_settings.rs +++ b/rpc/src/v1/helpers/network_settings.rs @@ -36,7 +36,7 @@ impl Default for NetworkSettings { fn default() -> Self { NetworkSettings { name: "".into(), - chain: "homestead".into(), + chain: "foundation".into(), network_port: 30303, rpc_enabled: true, rpc_interface: "local".into(), diff --git a/rpc/src/v1/impls/light/parity.rs b/rpc/src/v1/impls/light/parity.rs index 1f056b89e..49421fd04 100644 --- a/rpc/src/v1/impls/light/parity.rs +++ b/rpc/src/v1/impls/light/parity.rs @@ -303,6 +303,10 @@ impl Parity for ParityClient { Err(errors::light_unimplemented(None)) } + fn chain(&self) -> Result { + Ok(self.settings.chain.clone()) + } + fn enode(&self) -> Result { self.light_dispatch.sync.enode().ok_or_else(errors::network_disabled) } diff --git a/rpc/src/v1/impls/light/parity_set.rs b/rpc/src/v1/impls/light/parity_set.rs index 720af0dd9..2127db345 100644 --- a/rpc/src/v1/impls/light/parity_set.rs +++ b/rpc/src/v1/impls/light/parity_set.rs @@ -117,6 +117,10 @@ impl ParitySet for ParitySetClient { Err(errors::light_unimplemented(None)) } + fn set_spec_name(&self, _spec_name: String) -> Result { + Err(errors::light_unimplemented(None)) + } + fn hash_content(&self, url: String) -> BoxFuture { self.fetch.process(self.fetch.fetch(&url).then(move |result| { result diff --git a/rpc/src/v1/impls/parity.rs b/rpc/src/v1/impls/parity.rs index 503dfcce6..aee4dc3fd 100644 --- a/rpc/src/v1/impls/parity.rs +++ b/rpc/src/v1/impls/parity.rs @@ -188,6 +188,10 @@ impl Parity for ParityClient where Ok(self.settings.chain.clone()) } + fn chain(&self) -> Result { + Ok(take_weak!(self.client).spec_name()) + } + fn net_peers(&self) -> Result { let sync = take_weak!(self.sync); let sync_status = sync.status(); diff --git a/rpc/src/v1/impls/parity_set.rs b/rpc/src/v1/impls/parity_set.rs index a6f1129ba..0c844c163 100644 --- a/rpc/src/v1/impls/parity_set.rs +++ b/rpc/src/v1/impls/parity_set.rs @@ -156,6 +156,11 @@ impl ParitySet for ParitySetClient where Ok(true) } + fn set_spec_name(&self, spec_name: String) -> Result { + take_weak!(self.client).set_spec_name(spec_name); + Ok(true) + } + fn hash_content(&self, url: String) -> BoxFuture { self.fetch.process(self.fetch.fetch(&url).then(move |result| { result diff --git a/rpc/src/v1/tests/mocked/parity.rs b/rpc/src/v1/tests/mocked/parity.rs index a587554a3..a53a86447 100644 --- a/rpc/src/v1/tests/mocked/parity.rs +++ b/rpc/src/v1/tests/mocked/parity.rs @@ -285,6 +285,17 @@ fn rpc_parity_net_chain() { assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); } +#[test] +fn rpc_parity_chain() { + let deps = Dependencies::new(); + let io = deps.default_client(); + + let request = r#"{"jsonrpc": "2.0", "method": "parity_chain", "params":[], "id": 1}"#; + let response = r#"{"jsonrpc":"2.0","result":"foundation","id":1}"#; + + assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); +} + #[test] fn rpc_parity_net_peers() { let deps = Dependencies::new(); diff --git a/rpc/src/v1/traits/parity.rs b/rpc/src/v1/traits/parity.rs index 10e3b54bd..263adf6b2 100644 --- a/rpc/src/v1/traits/parity.rs +++ b/rpc/src/v1/traits/parity.rs @@ -76,7 +76,7 @@ build_rpc_trait! { #[rpc(name = "parity_devLogsLevels")] fn dev_logs_levels(&self) -> Result; - /// Returns chain name + /// Returns chain name - DEPRECATED. Use `parity_chainName` instead. #[rpc(name = "parity_netChain")] fn net_chain(&self) -> Result; @@ -167,10 +167,14 @@ build_rpc_trait! { #[rpc(async, name = "parity_nextNonce")] fn next_nonce(&self, H160) -> BoxFuture; - /// Get the mode. Results one of: "active", "passive", "dark", "offline". + /// Get the mode. Returns one of: "active", "passive", "dark", "offline". #[rpc(name = "parity_mode")] fn mode(&self) -> Result; + /// Get the chain name. Returns one of: "foundation", "kovan", &c. of a filename. + #[rpc(name = "parity_chain")] + fn chain(&self) -> Result; + /// Get the enode of this node. #[rpc(name = "parity_enode")] fn enode(&self) -> Result; diff --git a/rpc/src/v1/traits/parity_set.rs b/rpc/src/v1/traits/parity_set.rs index cffcc787f..d5fc066fa 100644 --- a/rpc/src/v1/traits/parity_set.rs +++ b/rpc/src/v1/traits/parity_set.rs @@ -88,6 +88,10 @@ build_rpc_trait! { #[rpc(name = "parity_setMode")] fn set_mode(&self, String) -> Result; + /// Set the network spec. Argument must be one of: "foundation", "ropsten", "morden", "kovan", "olympic", "classic", "dev", "expanse" or a filename. + #[rpc(name = "parity_setChain")] + fn set_spec_name(&self, String) -> Result; + /// Hash a file content under given URL. #[rpc(async, name = "parity_hashContent")] fn hash_content(&self, String) -> BoxFuture;