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] [root]
name = "parity" name = "parity"
version = "1.6.0" version = "1.7.0"
dependencies = [ dependencies = [
"ansi_term 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)", "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)", "app_dirs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)",

View File

@ -1,7 +1,7 @@
[package] [package]
description = "Parity Ethereum client" description = "Parity Ethereum client"
name = "parity" name = "parity"
version = "1.6.0" version = "1.7.0"
license = "GPL-3.0" license = "GPL-3.0"
authors = ["Parity Technologies <admin@parity.io>"] authors = ["Parity Technologies <admin@parity.io>"]

View File

@ -151,8 +151,9 @@ pub struct Client {
factories: Factories, factories: Factories,
history: u64, history: u64,
rng: Mutex<OsRng>, 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>>, registrar: Mutex<Option<Registry>>,
exit_handler: Mutex<Option<Box<Fn(bool, Option<String>) + 'static + Send>>>,
} }
impl Client { impl Client {
@ -240,8 +241,9 @@ impl Client {
factories: factories, factories: factories,
history: history, history: history,
rng: Mutex::new(OsRng::new().map_err(::util::UtilError::StdIo)?), 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), registrar: Mutex::new(None),
exit_handler: Mutex::new(None),
}); });
{ {
@ -276,6 +278,11 @@ impl Client {
self.notify.write().push(Arc::downgrade(&target)); 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. /// Returns engine reference.
pub fn engine(&self) -> &Engine { pub fn engine(&self) -> &Engine {
&*self.engine &*self.engine
@ -294,9 +301,9 @@ impl Client {
self.registrar.lock() self.registrar.lock()
} }
/// Register an action to be done if a mode change happens. /// Register an action to be done if a mode/spec_name change happens.
pub fn on_mode_change<F>(&self, f: F) where F: 'static + FnMut(&Mode) + Send { pub fn on_user_defaults_change<F>(&self, f: F) where F: 'static + FnMut(Option<Mode>) + Send {
*self.on_mode_change.lock() = Some(Box::new(f)); *self.on_user_defaults_change.lock() = Some(Box::new(f));
} }
/// Flush the block import queue. /// Flush the block import queue.
@ -651,7 +658,6 @@ impl Client {
self.miner.clone() self.miner.clone()
} }
/// Replace io channel. Useful for testing. /// Replace io channel. Useful for testing.
pub fn set_io_channel(&self, io_channel: IoChannel<ClientIoMessage>) { pub fn set_io_channel(&self, io_channel: IoChannel<ClientIoMessage>) {
*self.io_channel.lock() = io_channel; *self.io_channel.lock() = io_channel;
@ -1030,9 +1036,9 @@ impl BlockChainClient for Client {
let mut mode = self.mode.lock(); let mut mode = self.mode.lock();
*mode = new_mode.clone().into(); *mode = new_mode.clone().into();
trace!(target: "mode", "Mode now {:?}", &*mode); 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..."); trace!(target: "mode", "Making callback...");
f(&*mode) f(Some((&*mode).clone()))
} }
} }
match new_mode { 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 { fn best_block_header(&self) -> encoded::Header {
self.chain.read().best_block_header() self.chain.read().best_block_header()
} }

View File

@ -123,6 +123,8 @@ pub struct ClientConfig {
pub db_wal: bool, pub db_wal: bool,
/// Operating mode /// Operating mode
pub mode: Mode, pub mode: Mode,
/// The chain spec name
pub spec_name: String,
/// Type of block verifier used by client. /// Type of block verifier used by client.
pub verifier_type: VerifierType, pub verifier_type: VerifierType,
/// State db cache-size. /// State db cache-size.

View File

@ -721,6 +721,10 @@ impl BlockChainClient for TestBlockChainClient {
fn set_mode(&self, _: Mode) { unimplemented!(); } 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 disable(&self) { unimplemented!(); }
fn pruning_info(&self) -> PruningInfo { fn pruning_info(&self) -> PruningInfo {

View File

@ -241,6 +241,12 @@ pub trait BlockChainClient : Sync + Send {
/// Set the mode. /// Set the mode.
fn set_mode(&self, mode: 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 /// 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. /// that a subsystem has reason to believe this executable incapable of syncing the chain.
fn disable(&self); fn disable(&self);

View File

@ -284,9 +284,15 @@ export default class Parity {
.execute('parity_mode'); .execute('parity_mode');
} }
// DEPRECATED - use chain instead.
netChain () { netChain () {
return this._transport return this._transport
.execute('parity_netChain'); .execute('parity_chain');
}
chain () {
return this._transport
.execute('parity_chain');
} }
netPeers () { netPeers () {
@ -454,6 +460,11 @@ export default class Parity {
.execute('parity_setMode', mode); .execute('parity_setMode', mode);
} }
setChain (specName) {
return this._transport
.execute('parity_setChain', specName);
}
setNewDappsAddresses (addresses) { setNewDappsAddresses (addresses) {
return this._transport return this._transport
.execute('parity_setNewDappsAddresses', addresses ? inAddresses(addresses) : null); .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 Configured for DevelopmentChain using InstantSeal engine',
'2017-01-20 18:14:19 Operating mode: active', '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 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(); features = FeaturesStore.get();
componentWillMount () { componentWillMount () {
this.store.loadChain();
return this.store.loadMode(); return this.store.loadMode();
} }
@ -50,11 +51,12 @@ export default class Parity extends Component {
<div> <div>
<FormattedMessage <FormattedMessage
id='settings.parity.overview_0' 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> </div>
<div className={ layout.details }> <div className={ layout.details }>
{ this.renderChains() }
{ this.renderModes() } { this.renderModes() }
<Features /> <Features />
<LanguageSelector /> <LanguageSelector />
@ -65,12 +67,12 @@ export default class Parity extends Component {
); );
} }
renderItem (mode, label) { renderItem (name, label) {
return ( return (
<MenuItem <MenuItem
key={ mode } key={ name }
label={ label } label={ label }
value={ mode } value={ name }
> >
{ label } { label }
</MenuItem> </MenuItem>
@ -134,7 +136,7 @@ export default class Parity extends Component {
hint={ hint={
<FormattedMessage <FormattedMessage
id='settings.parity.modes.hint' id='settings.parity.modes.hint'
defaultMessage='the syning mode for the Parity node' defaultMessage='the syncing mode for the Parity node'
/> />
} }
label={ 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) => { onChangeMode = (event, index, mode) => {
this.store.changeMode(mode || event.target.value); 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(() => { beforeEach(() => {
render(); render();
sinon.spy(instance.store, 'loadMode'); sinon.spy(instance.store, 'loadMode');
sinon.spy(instance.store, 'loadChain');
}); });
afterEach(() => { afterEach(() => {
instance.store.loadMode.restore(); instance.store.loadMode.restore();
instance.store.loadChain.restore();
}); });
it('renders defaults', () => { it('renders defaults', () => {
@ -56,6 +58,10 @@ describe('views/Settings/Parity', () => {
it('loads the mode in the store', () => { it('loads the mode in the store', () => {
expect(instance.store.loadMode).to.have.been.called; 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', () => { describe('components', () => {
@ -94,5 +100,27 @@ describe('views/Settings/Parity', () => {
expect(instance.store.changeMode).to.have.been.calledWith('dark'); 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 { return {
parity: { parity: {
mode: sinon.stub().resolves('passive'), 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'; import { LOG_KEYS } from '~/config';
const DEFAULT_MODE = 'active'; const DEFAULT_MODE = 'active';
const DEFAULT_CHAIN = 'foundation';
const LOGLEVEL_OPTIONS = Object const LOGLEVEL_OPTIONS = Object
.keys(LogLevel.levels) .keys(LogLevel.levels)
.map((name) => { .map((name) => {
@ -32,6 +33,7 @@ const LOGLEVEL_OPTIONS = Object
export default class Store { export default class Store {
@observable logLevels = {}; @observable logLevels = {};
@observable mode = DEFAULT_MODE; @observable mode = DEFAULT_MODE;
@observable chain = DEFAULT_CHAIN;
constructor (api) { constructor (api) {
this._api = api; this._api = api;
@ -51,6 +53,10 @@ export default class Store {
this.mode = mode; this.mode = mode;
} }
@action setChain = (chain) => {
this.chain = chain;
}
changeMode (mode) { changeMode (mode) {
return this._api.parity return this._api.parity
.setMode(mode) .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 () { loadLogLevels () {
this.setLogLevels( this.setLogLevels(
Object Object
@ -98,6 +117,17 @@ export default class Store {
console.warn('loadMode', error); console.warn('loadMode', error);
}); });
} }
loadChain () {
return this._api.parity
.chain()
.then((chain) => {
this.setChain(chain);
})
.catch((error) => {
console.warn('loadChain', error);
});
}
} }
export { export {

View File

@ -33,16 +33,22 @@ describe('views/Settings/Parity/Store', () => {
beforeEach(() => { beforeEach(() => {
createStore(); createStore();
sinon.spy(store, 'setMode'); sinon.spy(store, 'setMode');
sinon.spy(store, 'setChain');
}); });
afterEach(() => { afterEach(() => {
store.setMode.restore(); store.setMode.restore();
store.setChain.restore();
}); });
it('defaults to mode === active', () => { it('defaults to mode === active', () => {
expect(store.mode).to.equal('active'); expect(store.mode).to.equal('active');
}); });
it('defaults to chain === foundation', () => {
expect(store.chain).to.equal('foundation');
});
describe('@action', () => { describe('@action', () => {
describe('setMode', () => { describe('setMode', () => {
it('sets the mode', () => { it('sets the mode', () => {
@ -50,6 +56,13 @@ describe('views/Settings/Parity/Store', () => {
expect(store.mode).to.equal('offline'); expect(store.mode).to.equal('offline');
}); });
}); });
describe('setChain', () => {
it('sets the chain', () => {
store.setChain('dev');
expect(store.chain).to.equal('dev');
});
});
}); });
describe('operations', () => { describe('operations', () => {
@ -80,5 +93,33 @@ describe('views/Settings/Parity/Store', () => {
expect(store.setMode).to.have.been.calledWith('passive'); 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 // prepare client config
let mut client_config = to_client_config( let mut client_config = to_client_config(
&cmd.cache_config, &cmd.cache_config,
spec.name.to_lowercase(),
Mode::Active, Mode::Active,
tracing, tracing,
fat_db, fat_db,
@ -361,6 +362,7 @@ fn start_client(
// prepare client config // prepare client config
let client_config = to_client_config( let client_config = to_client_config(
&cache_config, &cache_config,
spec.name.to_lowercase(),
Mode::Active, Mode::Active,
tracing, tracing,
fat_db, fat_db,

View File

@ -90,7 +90,7 @@ usage! {
flag_release_track: String = "current", or |c: &Config| otry!(c.parity).release_track.clone(), 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_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_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_keys_path: String = "$BASE/keys", or |c: &Config| otry!(c.parity).keys_path.clone(),
flag_identity: String = "", or |c: &Config| otry!(c.parity).identity.clone(), flag_identity: String = "", or |c: &Config| otry!(c.parity).identity.clone(),

View File

@ -85,14 +85,16 @@ pub struct Execute {
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub struct Configuration { pub struct Configuration {
pub args: Args, pub args: Args,
pub spec_name_override: Option<String>,
} }
impl Configuration { 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 args = Args::parse(command)?;
let config = Configuration { let config = Configuration {
args: args, args: args,
spec_name_override: spec_name_override,
}; };
Ok(config) Ok(config)
@ -103,7 +105,11 @@ impl Configuration {
let pruning = self.args.flag_pruning.parse()?; let pruning = self.args.flag_pruning.parse()?;
let pruning_history = self.args.flag_pruning_history; let pruning_history = self.args.flag_pruning_history;
let vm_type = self.vm_type()?; 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 update_policy = self.update_policy()?;
let logger_config = self.logger_config(); let logger_config = self.logger_config();
let http_conf = self.http_config()?; let http_conf = self.http_config()?;
@ -111,7 +117,6 @@ impl Configuration {
let net_conf = self.net_config()?; let net_conf = self.net_config()?;
let network_id = self.network_id(); let network_id = self.network_id();
let cache_config = self.cache_config(); let cache_config = self.cache_config();
let spec = self.chain().parse()?;
let tracing = self.args.flag_tracing.parse()?; let tracing = self.args.flag_tracing.parse()?;
let fat_db = self.args.flag_fat_db.parse()?; let fat_db = self.args.flag_fat_db.parse()?;
let compaction = self.args.flag_db_compaction.parse()?; let compaction = self.args.flag_db_compaction.parse()?;
@ -437,7 +442,10 @@ impl Configuration {
} }
fn chain(&self) -> String { 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() "testnet".to_owned()
} else { } else {
self.args.flag_chain.clone() self.args.flag_chain.clone()
@ -972,6 +980,7 @@ mod tests {
fn parse(args: &[&str]) -> Configuration { fn parse(args: &[&str]) -> Configuration {
Configuration { Configuration {
args: Args::parse_without_config(args).unwrap(), 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"; let filename = temp.as_str().to_owned() + "/peers";
File::create(filename.clone()).unwrap().write_all(b" \n\t\n").unwrap(); File::create(filename.clone()).unwrap().write_all(b" \n\t\n").unwrap();
let args = vec!["parity", "--reserved-peers", &filename]; 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()); 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))] #[cfg_attr(feature = "dev", allow(too_many_arguments))]
pub fn to_client_config( pub fn to_client_config(
cache_config: &CacheConfig, cache_config: &CacheConfig,
spec_name: String,
mode: Mode, mode: Mode,
tracing: bool, tracing: bool,
fat_db: bool, fat_db: bool,
@ -261,6 +262,7 @@ pub fn to_client_config(
client_config.vm_type = vm_type; client_config.vm_type = vm_type;
client_config.name = name; client_config.name = name;
client_config.verifier_type = if check_seal { VerifierType::Canon } else { VerifierType::CanonNoSeal }; client_config.verifier_type = if check_seal { VerifierType::Canon } else { VerifierType::CanonNoSeal };
client_config.spec_name = spec_name;
client_config 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()])); 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::{process, env};
use std::collections::HashMap; use std::collections::HashMap;
use std::io::{self as stdio, BufReader, Read, Write}; 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 std::path::PathBuf;
use util::sha3::sha3; use util::sha3::sha3;
use cli::Args; use cli::Args;
@ -143,7 +143,7 @@ fn print_hash_of(maybe_file: Option<String>) -> Result<String, String> {
enum PostExecutionAction { enum PostExecutionAction {
Print(String), Print(String),
Restart, Restart(Option<String>),
Quit, Quit,
} }
@ -152,8 +152,8 @@ fn execute(command: Execute, can_restart: bool) -> Result<PostExecutionAction, S
match command.cmd { match command.cmd {
Cmd::Run(run_cmd) => { Cmd::Run(run_cmd) => {
let restart = run::execute(run_cmd, can_restart, logger)?; let (restart, spec_name) = run::execute(run_cmd, can_restart, logger)?;
Ok(if restart { PostExecutionAction::Restart } else { PostExecutionAction::Quit }) Ok(if restart { PostExecutionAction::Restart(spec_name) } else { PostExecutionAction::Quit })
}, },
Cmd::Version => Ok(PostExecutionAction::Print(Args::print_version())), Cmd::Version => Ok(PostExecutionAction::Print(Args::print_version())),
Cmd::Hash(maybe_file) => print_hash_of(maybe_file).map(|s| PostExecutionAction::Print(s)), 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> { fn start(can_restart: bool) -> Result<PostExecutionAction, String> {
let args: Vec<String> = env::args().collect(); 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); let deprecated = find_deprecated(&conf.args);
for d in deprecated { 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)) }) .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)] #[cfg(windows)]
fn global_cleanup() { fn global_cleanup() {
extern "system" { pub fn WSACleanup() -> i32; } extern "system" { pub fn WSACleanup() -> i32; }
@ -250,7 +266,12 @@ fn main_direct(can_restart: bool) -> i32 {
match start(can_restart) { match start(can_restart) {
Ok(result) => match result { Ok(result) => match result {
PostExecutionAction::Print(s) => { println!("{}", s); 0 }, 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, PostExecutionAction::Quit => 0,
}, },
Err(err) => { Err(err) => {
@ -303,7 +324,7 @@ fn main() {
(Some(latest_exe_time), Some(this_exe_time)) if latest_exe_time > this_exe_time => true, (Some(latest_exe_time), Some(this_exe_time)) if latest_exe_time > this_exe_time => true,
_ => false, _ => 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 { 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()); 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) }) 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 io::{MayPanic, ForwardPanic, PanicHandler};
use ethcore_logger::{Config as LogConfig}; use ethcore_logger::{Config as LogConfig};
use ethcore::miner::{StratumOptions, Stratum}; 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::service::ClientService;
use ethcore::account_provider::{AccountProvider, AccountProviderSettings}; use ethcore::account_provider::{AccountProvider, AccountProviderSettings};
use ethcore::miner::{Miner, MinerService, ExternalMiner, MinerOptions}; 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 { if cmd.ui && cmd.dapps_conf.enabled {
// Check if Parity is already running // Check if Parity is already running
let addr = format!("{}:{}", cmd.dapps_conf.interface, cmd.dapps_conf.port); let addr = format!("{}:{}", cmd.dapps_conf.interface, cmd.dapps_conf.port);
if !TcpListener::bind(&addr as &str).is_ok() { 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 // create client config
let mut client_config = to_client_config( let mut client_config = to_client_config(
&cmd.cache_config, &cmd.cache_config,
spec.name.to_lowercase(),
mode.clone(), mode.clone(),
tracing, tracing,
fat_db, fat_db,
@ -493,8 +494,10 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
user_defaults.save(&user_defaults_path)?; user_defaults.save(&user_defaults_path)?;
// tell client how to save the default mode if it gets changed. // tell client how to save the default mode if it gets changed.
client.on_mode_change(move |mode: &Mode| { client.on_user_defaults_change(move |mode: Option<Mode>| {
user_defaults.mode = mode.clone(); if let Some(mode) = mode {
user_defaults.mode = mode;
}
let _ = user_defaults.save(&user_defaults_path); // discard failures - there's nothing we can do 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, true => None,
false => { false => {
let sync = sync_provider.clone(); let sync = sync_provider.clone();
let client = client.clone();
let watcher = Arc::new(snapshot::Watcher::new( let watcher = Arc::new(snapshot::Watcher::new(
service.client(), service.client(),
move || is_major_importing(Some(sync.status().state), client.queue_info()), 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 // 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 this stuff as soon as exit detected.
drop((http_server, ipc_server, dapps_server, signer_server, secretstore_key_server, ipfs_server, event_loop)); 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( fn wait_for_exit(
panic_handler: Arc<PanicHandler>, panic_handler: Arc<PanicHandler>,
updater: Option<Arc<Updater>>, updater: Option<Arc<Updater>>,
client: Option<Arc<Client>>,
can_restart: bool can_restart: bool
) -> bool { ) -> (bool, Option<String>) {
let exit = Arc::new((Mutex::new(false), Condvar::new())); let exit = Arc::new((Mutex::new((false, None)), Condvar::new()));
// Handle possible exits // Handle possible exits
let e = exit.clone(); let e = exit.clone();
@ -616,18 +621,24 @@ fn wait_for_exit(
let e = exit.clone(); let e = exit.clone();
panic_handler.on_panic(move |_reason| { e.1.notify_all(); }); panic_handler.on_panic(move |_reason| { e.1.notify_all(); });
if can_restart {
if let Some(updater) = updater { if let Some(updater) = updater {
// Handle updater wanting to restart us // Handle updater wanting to restart us
if can_restart {
let e = exit.clone(); let e = exit.clone();
updater.set_exit_handler(move || { *e.0.lock() = true; e.1.notify_all(); }); updater.set_exit_handler(move || { *e.0.lock() = (true, None); e.1.notify_all(); });
} else {
updater.set_exit_handler(|| info!("Update installed; ready for restart."));
} }
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 // Wait for signal
let mut l = exit.0.lock(); let mut l = exit.0.lock();
let _ = exit.1.wait(&mut l); let _ = exit.1.wait(&mut l);
*l l.clone()
} }

View File

@ -173,6 +173,7 @@ impl SnapshotCommand {
// prepare client config // prepare client config
let client_config = to_client_config( let client_config = to_client_config(
&self.cache_config, &self.cache_config,
spec.name.to_lowercase(),
Mode::Active, Mode::Active,
tracing, tracing,
fat_db, fat_db,

View File

@ -36,7 +36,7 @@ impl Default for NetworkSettings {
fn default() -> Self { fn default() -> Self {
NetworkSettings { NetworkSettings {
name: "".into(), name: "".into(),
chain: "homestead".into(), chain: "foundation".into(),
network_port: 30303, network_port: 30303,
rpc_enabled: true, rpc_enabled: true,
rpc_interface: "local".into(), rpc_interface: "local".into(),

View File

@ -303,6 +303,10 @@ impl Parity for ParityClient {
Err(errors::light_unimplemented(None)) Err(errors::light_unimplemented(None))
} }
fn chain(&self) -> Result<String, Error> {
Ok(self.settings.chain.clone())
}
fn enode(&self) -> Result<String, Error> { fn enode(&self) -> Result<String, Error> {
self.light_dispatch.sync.enode().ok_or_else(errors::network_disabled) 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)) 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> { fn hash_content(&self, url: String) -> BoxFuture<H256, Error> {
self.fetch.process(self.fetch.fetch(&url).then(move |result| { self.fetch.process(self.fetch.fetch(&url).then(move |result| {
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()) 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> { fn net_peers(&self) -> Result<Peers, Error> {
let sync = take_weak!(self.sync); let sync = take_weak!(self.sync);
let sync_status = sync.status(); 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) 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> { fn hash_content(&self, url: String) -> BoxFuture<H256, Error> {
self.fetch.process(self.fetch.fetch(&url).then(move |result| { self.fetch.process(self.fetch.fetch(&url).then(move |result| {
result result

View File

@ -285,6 +285,17 @@ fn rpc_parity_net_chain() {
assert_eq!(io.handle_request_sync(request), Some(response.to_owned())); 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] #[test]
fn rpc_parity_net_peers() { fn rpc_parity_net_peers() {
let deps = Dependencies::new(); let deps = Dependencies::new();

View File

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

View File

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