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.
This commit is contained in:
Gav Wood 2017-03-13 12:10:53 +01:00 committed by GitHub
parent 8a67a0a80a
commit 3041c95408
28 changed files with 372 additions and 50 deletions

2
Cargo.lock generated
View File

@ -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)",

View File

@ -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 <admin@parity.io>"]

View File

@ -151,8 +151,9 @@ pub struct Client {
factories: Factories,
history: u64,
rng: Mutex<OsRng>,
on_mode_change: Mutex<Option<Box<FnMut(&Mode) + 'static + Send>>>,
on_user_defaults_change: Mutex<Option<Box<FnMut(Option<Mode>) + 'static + Send>>>,
registrar: Mutex<Option<Registry>>,
exit_handler: Mutex<Option<Box<Fn(bool, Option<String>) + '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<F>(&self, f: F) where F: Fn(bool, Option<String>) + '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<F>(&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<F>(&self, f: F) where F: 'static + FnMut(Option<Mode>) + 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<ClientIoMessage>) {
*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()
}

View File

@ -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.

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -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'
]
}
},

View File

@ -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 {
<div>
<FormattedMessage
id='settings.parity.overview_0'
defaultMessage='Control the Parity node settings and mode of operation via this interface.'
defaultMessage='Control the Parity node settings and nature of syncing via this interface.'
/>
</div>
</div>
<div className={ layout.details }>
{ this.renderChains() }
{ this.renderModes() }
<Features />
<LanguageSelector />
@ -65,12 +67,12 @@ export default class Parity extends Component {
);
}
renderItem (mode, label) {
renderItem (name, label) {
return (
<MenuItem
key={ mode }
key={ name }
label={ label }
value={ mode }
value={ name }
>
{ label }
</MenuItem>
@ -134,7 +136,7 @@ export default class Parity extends Component {
hint={
<FormattedMessage
id='settings.parity.modes.hint'
defaultMessage='the syning mode for the Parity node'
defaultMessage='the syncing mode for the Parity node'
/>
}
label={
@ -182,7 +184,100 @@ export default class Parity extends Component {
);
}
renderChains () {
const { chain } = this.store;
return (
<Select
id='parityChainSelect'
hint={
<FormattedMessage
id='settings.parity.chains.hint'
defaultMessage='the chain for the Parity node to sync to'
/>
}
label={
<FormattedMessage
id='settings.parity.chains.label'
defaultMessage='chain/network to sync'
/>
}
onChange={ this.onChangeChain }
value={ chain }
>
{
this.renderItem('foundation', (
<FormattedMessage
id='settings.parity.chains.chain_foundation'
defaultMessage='Parity syncs to the Ethereum network launched by the Ethereum Foundation'
/>
))
}
{
this.renderItem('kovan', (
<FormattedMessage
id='settings.parity.chains.chain_kovan'
defaultMessage='Parity syncs to the Kovan test network'
/>
))
}
{
this.renderItem('olympic', (
<FormattedMessage
id='settings.parity.chains.chain_olympic'
defaultMessage='Parity syncs to the Olympic test network'
/>
))
}
{
this.renderItem('morden', (
<FormattedMessage
id='settings.parity.chains.cmorden_kovan'
defaultMessage='Parity syncs to Morden (Classic) test network'
/>
))
}
{
this.renderItem('ropsten', (
<FormattedMessage
id='settings.parity.chains.chain_ropsten'
defaultMessage='Parity syncs to the Ropsten test network'
/>
))
}
{
this.renderItem('classic', (
<FormattedMessage
id='settings.parity.chains.chain_classic'
defaultMessage='Parity syncs to the Ethereum Classic network'
/>
))
}
{
this.renderItem('expanse', (
<FormattedMessage
id='settings.parity.chains.chain_expanse'
defaultMessage='Parity syncs to the Expanse network'
/>
))
}
{
this.renderItem('dev', (
<FormattedMessage
id='settings.parity.chains.chain_dev'
defaultMessage='Parity uses a local development chain'
/>
))
}
</Select>
);
}
onChangeMode = (event, index, mode) => {
this.store.changeMode(mode || event.target.value);
}
onChangeChain = (event, index, chain) => {
this.store.changeChain(chain || event.target.value);
}
}

View File

@ -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');
});
});
});
});

View File

@ -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)
}
};
}

View File

@ -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 {

View File

@ -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');
});
});
});
});

View File

@ -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,

View File

@ -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(),

View File

@ -85,14 +85,16 @@ pub struct Execute {
#[derive(Debug, PartialEq)]
pub struct Configuration {
pub args: Args,
pub spec_name_override: Option<String>,
}
impl Configuration {
pub fn parse<S: AsRef<str>>(command: &[S]) -> Result<Self, ArgsError> {
pub fn parse<S: AsRef<str>>(command: &[S], spec_name_override: Option<String>) -> Result<Self, ArgsError> {
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());
}

View File

@ -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()]));
}
}

View File

@ -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<String>) -> Result<String, String> {
enum PostExecutionAction {
Print(String),
Restart,
Restart(Option<String>),
Quit,
}
@ -152,8 +152,8 @@ fn execute(command: Execute, can_restart: bool) -> Result<PostExecutionAction, S
match command.cmd {
Cmd::Run(run_cmd) => {
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<PostExecutionAction, S
fn start(can_restart: bool) -> Result<PostExecutionAction, String> {
let args: Vec<String> = 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<PathBuf> {
.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<String> {
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) })

View File

@ -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<RotatingLogger>) -> Result<bool, String> {
pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> Result<(bool, Option<String>), String> {
if cmd.ui && cmd.dapps_conf.enabled {
// Check if Parity is already running
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<RotatingLogger>) -> 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<RotatingLogger>) -> 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<Mode>| {
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<RotatingLogger>) -> 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<RotatingLogger>) -> 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<PanicHandler>,
updater: Option<Arc<Updater>>,
client: Option<Arc<Client>>,
can_restart: bool
) -> bool {
let exit = Arc::new((Mutex::new(false), Condvar::new()));
) -> (bool, Option<String>) {
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<String>| { *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()
}

View File

@ -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,

View File

@ -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(),

View File

@ -303,6 +303,10 @@ impl Parity for ParityClient {
Err(errors::light_unimplemented(None))
}
fn chain(&self) -> Result<String, Error> {
Ok(self.settings.chain.clone())
}
fn enode(&self) -> Result<String, Error> {
self.light_dispatch.sync.enode().ok_or_else(errors::network_disabled)
}

View File

@ -117,6 +117,10 @@ impl<F: Fetch> ParitySet for ParitySetClient<F> {
Err(errors::light_unimplemented(None))
}
fn set_spec_name(&self, _spec_name: String) -> Result<bool, Error> {
Err(errors::light_unimplemented(None))
}
fn hash_content(&self, url: String) -> BoxFuture<H256, Error> {
self.fetch.process(self.fetch.fetch(&url).then(move |result| {
result

View File

@ -188,6 +188,10 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
Ok(self.settings.chain.clone())
}
fn chain(&self) -> Result<String, Error> {
Ok(take_weak!(self.client).spec_name())
}
fn net_peers(&self) -> Result<Peers, Error> {
let sync = take_weak!(self.sync);
let sync_status = sync.status();

View File

@ -156,6 +156,11 @@ impl<C, M, U, F> ParitySet for ParitySetClient<C, M, U, F> where
Ok(true)
}
fn set_spec_name(&self, spec_name: String) -> Result<bool, Error> {
take_weak!(self.client).set_spec_name(spec_name);
Ok(true)
}
fn hash_content(&self, url: String) -> BoxFuture<H256, Error> {
self.fetch.process(self.fetch.fetch(&url).then(move |result| {
result

View File

@ -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();

View File

@ -76,7 +76,7 @@ build_rpc_trait! {
#[rpc(name = "parity_devLogsLevels")]
fn dev_logs_levels(&self) -> Result<String, Error>;
/// Returns chain name
/// Returns chain name - DEPRECATED. Use `parity_chainName` instead.
#[rpc(name = "parity_netChain")]
fn net_chain(&self) -> Result<String, Error>;
@ -167,10 +167,14 @@ build_rpc_trait! {
#[rpc(async, name = "parity_nextNonce")]
fn next_nonce(&self, H160) -> BoxFuture<U256, Error>;
/// 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<String, Error>;
/// Get the chain name. Returns one of: "foundation", "kovan", &c. of a filename.
#[rpc(name = "parity_chain")]
fn chain(&self) -> Result<String, Error>;
/// Get the enode of this node.
#[rpc(name = "parity_enode")]
fn enode(&self) -> Result<String, Error>;

View File

@ -88,6 +88,10 @@ build_rpc_trait! {
#[rpc(name = "parity_setMode")]
fn set_mode(&self, String) -> Result<bool, Error>;
/// 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<bool, Error>;
/// Hash a file content under given URL.
#[rpc(async, name = "parity_hashContent")]
fn hash_content(&self, String) -> BoxFuture<H256, Error>;