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:
parent
8a67a0a80a
commit
3041c95408
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -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)",
|
||||
|
@ -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>"]
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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'
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
||||
|
@ -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()]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) })
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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,
|
||||
|
@ -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(),
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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>;
|
||||
|
@ -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>;
|
||||
|
Loading…
Reference in New Issue
Block a user