Mode configuration backported to beta (#3213)

* Mode improvements for UI (#3109)

* `--mode=off` now works.

* Add Mode::Off as a persistent CLI option.

* "last" not "auto" as default.

* Commit accidentally unsaved file.

* Whitespace

[ci:skip]

* Mode CLI parse fix

* or offline

* Save mode when it gets changed.

* Fix Offline mode

* Fix up mode operations.

* Make passive default, but not overriding.

* Fix test

* Maybe not everyone wants to run an archive node...

* Parity configuration settings, i.e. mode (#3212)

* Add initial page

* Add parity icon

* opacity for parity icon

* Mode selector

* Actually set mode when value changes


Former-commit-id: 64386d94adfa58aa30bab2005eeb64f6343c8248
This commit is contained in:
Arkadiy Paronyan 2016-11-05 14:37:45 +01:00 committed by Gav Wood
parent 8b5a9b701a
commit e727d92e0f
27 changed files with 325 additions and 105 deletions

View File

@ -147,6 +147,7 @@ pub struct Client {
factories: Factories,
history: u64,
rng: Mutex<OsRng>,
on_mode_change: Mutex<Option<Box<FnMut(&Mode) + 'static + Send>>>,
}
impl Client {
@ -211,7 +212,7 @@ impl Client {
let panic_handler = PanicHandler::new_in_arc();
panic_handler.forward_from(&block_queue);
let awake = match config.mode { Mode::Dark(..) => false, _ => true };
let awake = match config.mode { Mode::Dark(..) | Mode::Off => false, _ => true };
let factories = Factories {
vm: EvmFactory::new(config.vm_type.clone(), config.jump_table_size),
@ -243,6 +244,7 @@ impl Client {
factories: factories,
history: history,
rng: Mutex::new(try!(OsRng::new().map_err(::util::UtilError::StdIo))),
on_mode_change: Mutex::new(None),
};
Ok(Arc::new(client))
}
@ -260,6 +262,11 @@ impl Client {
}
}
/// 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));
}
/// Flush the block import queue.
pub fn flush_queue(&self) {
self.block_queue.flush();
@ -856,18 +863,37 @@ impl BlockChainClient for Client {
}
fn keep_alive(&self) {
let mode = self.mode.lock().clone();
if mode != Mode::Active {
let should_wake = match &*self.mode.lock() {
&Mode::Dark(..) | &Mode::Passive(..) => true,
_ => false,
};
if should_wake {
self.wake_up();
(*self.sleep_state.lock()).last_activity = Some(Instant::now());
}
}
fn mode(&self) -> IpcMode { self.mode.lock().clone().into() }
fn mode(&self) -> IpcMode {
let r = self.mode.lock().clone().into();
trace!(target: "mode", "Asked for mode = {:?}. returning {:?}", &*self.mode.lock(), r);
r
}
fn set_mode(&self, mode: IpcMode) {
*self.mode.lock() = mode.clone().into();
match mode {
fn set_mode(&self, new_mode: IpcMode) {
trace!(target: "mode", "Client::set_mode({:?})", new_mode);
{
let mut mode = self.mode.lock();
*mode = new_mode.clone().into();
trace!(target: "mode", "Mode now {:?}", &*mode);
match *self.on_mode_change.lock() {
Some(ref mut f) => {
trace!(target: "mode", "Making callback...");
f(&*mode)
},
_ => {}
}
}
match new_mode {
IpcMode::Active => self.wake_up(),
IpcMode::Off => self.sleep(),
_ => {(*self.sleep_state.lock()).last_activity = Some(Instant::now()); }

View File

@ -16,6 +16,7 @@
use std::str::FromStr;
use std::path::Path;
use std::fmt::{Display, Formatter, Error as FmtError};
pub use std::time::Duration;
pub use blockchain::Config as BlockChainConfig;
pub use trace::Config as TraceConfig;
@ -86,6 +87,17 @@ impl Default for Mode {
}
}
impl Display for Mode {
fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
match *self {
Mode::Active => write!(f, "active"),
Mode::Passive(..) => write!(f, "passive"),
Mode::Dark(..) => write!(f, "dark"),
Mode::Off => write!(f, "offline"),
}
}
}
/// Client configuration. Includes configs for all sub-systems.
#[derive(Debug, PartialEq, Default)]
pub struct ClientConfig {

View File

@ -20,7 +20,7 @@ pub use std::time::Duration;
use client::Mode as ClientMode;
/// IPC-capable shadow-type for client::config::Mode
#[derive(Clone, Binary)]
#[derive(Clone, Binary, Debug)]
pub enum Mode {
/// Same as ClientMode::Off.
Off,

View File

@ -31,7 +31,7 @@ import ContractInstances from './contracts';
import { initStore } from './redux';
import { ContextProvider, muiTheme } from './ui';
import { Accounts, Account, Addresses, Address, Application, Contract, Contracts, Dapp, Dapps, Settings, SettingsBackground, SettingsProxy, SettingsViews, Signer, Status } from './views';
import { Accounts, Account, Addresses, Address, Application, Contract, Contracts, Dapp, Dapps, Settings, SettingsBackground, SettingsParity, SettingsProxy, SettingsViews, Signer, Status } from './views';
import { setApi } from './redux/providers/apiActions';
@ -79,6 +79,7 @@ ReactDOM.render(
<Route path='background' component={ SettingsBackground } />
<Route path='proxy' component={ SettingsProxy } />
<Route path='views' component={ SettingsViews } />
<Route path='parity' component={ SettingsParity } />
</Route>
<Route path='signer' component={ Signer } />
<Route path='status' component={ Status } />

View File

@ -166,7 +166,7 @@ export default {
},
mode: {
desc: 'Get the mode. Results one of: "active", "passive", "dark", "off".',
desc: 'Get the mode. Results one of: "active", "passive", "dark", "offline".',
params: [],
returns: {
type: String,
@ -330,7 +330,7 @@ export default {
params: [
{
type: String,
desc: 'The mode to set, one of "active", "passive", "dark", "off"'
desc: 'The mode to set, one of "active", "passive", "dark", "offline"'
}
],
returns: {

View File

@ -0,0 +1,17 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './parity';

View File

@ -0,0 +1,116 @@
// Copyright 2015, 2016 Ethcore (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import React, { Component, PropTypes } from 'react';
import { MenuItem } from 'material-ui';
import { Select, Container, ContainerTitle } from '../../../ui';
import layout from '../layout.css';
const MODES = {
'active': 'Parity continuously syncs the chain',
'passive': 'Parity syncs initially, then sleeps and wakes regularly to resync',
'dark': 'Parity syncs only when the RPC is active',
'offline': 'Parity doesn\'t sync'
};
export default class Parity extends Component {
static contextTypes = {
api: PropTypes.object.isRequired
}
state = {
mode: 'active'
}
componentWillMount () {
this.loadMode();
}
render () {
return (
<Container>
<ContainerTitle title='Parity' />
<div className={ layout.layout }>
<div className={ layout.overview }>
<div>Control the Parity node settings and mode of operation via this interface.</div>
</div>
<div className={ layout.details }>
{ this.renderModes() }
</div>
</div>
</Container>
);
}
renderModes () {
const modes = Object
.keys(MODES)
.map((mode) => {
const description = MODES[mode];
return (
<MenuItem
key={ mode }
value={ mode }
label={ description }>
{ description }
</MenuItem>
);
});
const { mode } = this.state;
return (
<Select
label='mode of operation'
hint='the syning mode for the Parity node'
value={ mode }
onChange={ this.onChangeMode }>
{ modes }
</Select>
);
}
onChangeMode = (event, index, mode) => {
const { api } = this.context;
api.ethcore
.setMode(mode)
.then((result) => {
if (result) {
this.setState({ mode });
}
})
.catch((error) => {
console.warn('onChangeMode', error);
});
}
loadMode () {
const { api } = this.context;
api.ethcore
.mode()
.then((mode) => {
this.setState({ mode });
})
.catch((error) => {
console.warn('loadMode', error);
});
}
}

View File

@ -17,6 +17,7 @@
import settingsReducer from './reducers';
import { toggleView, updateBackground } from './actions';
import SettingsBackground from './Background';
import SettingsParity from './Parity';
import SettingsProxy from './Proxy';
import SettingsViews, { defaultViews } from './Views';
@ -24,6 +25,7 @@ export default from './settings';
export {
SettingsBackground,
SettingsParity,
SettingsProxy,
SettingsViews,
defaultViews,

View File

@ -61,3 +61,13 @@
vertical-align: top;
display: inline-block;
}
.imageIcon {
height: 20px;
margin: 2px 0.5em 2px 0 !important;
opacity: 0.5;
}
.tabactive .imageIcon {
opacity: 1;
}

View File

@ -14,8 +14,6 @@
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
// 0xecf69634885f27a8f78161e530f15a8d3b57d39e755c222c92cf297b6e25aaaa
import React, { Component, PropTypes } from 'react';
import { Tab, Tabs } from 'material-ui';
import ActionSettingsEthernet from 'material-ui/svg-icons/action/settings-ethernet';
@ -23,6 +21,7 @@ import ImageBlurOn from 'material-ui/svg-icons/image/blur-on';
import ImageRemoveRedEye from 'material-ui/svg-icons/image/remove-red-eye';
import { Actionbar, Page } from '../../ui';
import imagesEthcoreBlock from '../../../assets/images/parity-logo-white-no-text.svg';
import styles from './settings.css';
@ -37,11 +36,23 @@ export default class Settings extends Component {
render () {
const { children } = this.props;
const hash = (window.location.hash || '').split('?')[0].split('/')[2];
const isProxied = window.location.hostname.indexOf('.parity') !== -1;
let proxy = null;
if (!isProxied) {
proxy = this.renderTab(hash, 'proxy', <ActionSettingsEthernet />);
}
return (
<div className={ styles.layout }>
<Actionbar title='settings' className={ styles.bar }>
{ this.renderTabs() }
<Tabs className={ styles.tabs } value={ hash }>
{ this.renderTab(hash, 'views', <ImageRemoveRedEye />) }
{ this.renderTab(hash, 'background', <ImageBlurOn />) }
{ proxy }
{ this.renderTab(hash, 'parity', <img src={ imagesEthcoreBlock } className={ styles.imageIcon } />) }
</Tabs>
</Actionbar>
<Page>
{ children }
@ -50,41 +61,15 @@ export default class Settings extends Component {
);
}
renderTabs () {
const hash = (window.location.hash || '').split('?')[0].split('/')[2];
const isProxied = window.location.hostname.indexOf('.parity') !== -1;
let proxy = null;
if (!isProxied) {
proxy = (
<Tab
className={ hash === 'proxy' ? styles.tabactive : styles.tab }
value='proxy'
key='proxy'
icon={ <ActionSettingsEthernet /> }
label={ <div className={ styles.menu }>proxy</div> }
onActive={ this.onActivate('proxy') } />
);
}
renderTab (hash, section, icon) {
return (
<Tabs className={ styles.tabs } value={ hash }>
<Tab
className={ hash === 'views' ? styles.tabactive : styles.tab }
value='views'
key='views'
icon={ <ImageRemoveRedEye /> }
label={ <div className={ styles.menu }>views</div> }
onActive={ this.onActivate('views') } />
<Tab
className={ hash === 'background' ? styles.tabactive : styles.tab }
value='background'
key='background'
icon={ <ImageBlurOn /> }
label={ <div className={ styles.menu }>background</div> }
onActive={ this.onActivate('background') } />
{ proxy }
</Tabs>
<Tab
className={ hash === section ? styles.tabactive : styles.tab }
value={ section }
key={ section }
icon={ icon }
label={ <div className={ styles.menu }>{ section }</div> }
onActive={ this.onActivate(section) } />
);
}

View File

@ -926,6 +926,16 @@
],
"outputFormatter": null
},
{
"name": "ethcore_setMode",
"desc": "Changes current operating mode",
"params": [
"`DATA`- Mode name"
],
"returns": "`Boolean` - whether the call was successful",
"inputFormatters": null,
"outputFormatter": null
},
{
"name": "ethcore_setMinGasPrice",
"desc": "Changes minimal gas price for transaction to be accepted to the queue.",
@ -1030,6 +1040,14 @@
"inputFormatters": [],
"outputFormatter": null
},
{
"name": "ethcore_mode",
"desc": "Returns current mode",
"params": [],
"returns": "`DATA` - Mode",
"inputFormatters": [],
"outputFormatter": null
},
{
"name": "trace_filter",
"desc": "Returns traces matching given filter",

View File

@ -24,7 +24,7 @@ import Contracts from './Contracts';
import Dapp from './Dapp';
import Dapps from './Dapps';
import ParityBar from './ParityBar';
import Settings, { SettingsBackground, SettingsProxy, SettingsViews } from './Settings';
import Settings, { SettingsBackground, SettingsParity, SettingsProxy, SettingsViews } from './Settings';
import Signer from './Signer';
import Status from './Status';
@ -41,6 +41,7 @@ export {
ParityBar,
Settings,
SettingsBackground,
SettingsParity,
SettingsProxy,
SettingsViews,
Signer,

View File

@ -11,8 +11,6 @@ cat > $HOME/Library/LaunchAgents/io.parity.ethereum.plist <<EOF
<key>ProgramArguments</key>
<array>
<string>/usr/local/libexec/parity</string>
<string>--mode</string>
<string>passive</string>
<string>--warp</string>
</array>
<key>KeepAlive</key>
@ -26,9 +24,11 @@ cat > $HOME/Library/LaunchAgents/io.parity.ethereum.plist <<EOF
</dict>
</plist>
EOF
mkdir -p $HOME/.parity
chown $USER $HOME/.parity $HOME/Library/LaunchAgents $HOME/Library/LaunchAgents/io.parity.ethereum.plist
mkdir -p $HOME/.parity/906a34e69aec8c0d
echo -n '{"fat_db":false,"mode":"passive","pruning":"fast","tracing":false}' > $HOME/.parity/906a34e69aec8c0d/user_defaults
chown -R $USER $HOME/.parity $HOME/Library/LaunchAgents $HOME/Library/LaunchAgents/io.parity.ethereum.plist
su $USER -c "launchctl load $HOME/Library/LaunchAgents/io.parity.ethereum.plist"
sleep 1

View File

@ -78,7 +78,6 @@ pub struct ImportBlockchain {
pub pruning_history: u64,
pub compaction: DatabaseCompactionProfile,
pub wal: bool,
pub mode: Mode,
pub tracing: Switch,
pub fat_db: Switch,
pub vm_type: VMType,
@ -97,7 +96,6 @@ pub struct ExportBlockchain {
pub pruning_history: u64,
pub compaction: DatabaseCompactionProfile,
pub wal: bool,
pub mode: Mode,
pub fat_db: Switch,
pub tracing: Switch,
pub from_block: BlockID,
@ -155,7 +153,7 @@ fn execute_import(cmd: ImportBlockchain) -> Result<String, String> {
try!(execute_upgrades(&db_dirs, algorithm, cmd.compaction.compaction_profile(db_dirs.fork_path().as_path())));
// prepare client config
let client_config = to_client_config(&cmd.cache_config, cmd.mode, tracing, fat_db, cmd.compaction, cmd.wal, cmd.vm_type, "".into(), algorithm, cmd.pruning_history, cmd.check_seal);
let client_config = to_client_config(&cmd.cache_config, Mode::Active, tracing, fat_db, cmd.compaction, cmd.wal, cmd.vm_type, "".into(), algorithm, cmd.pruning_history, cmd.check_seal);
// build client
let service = try!(ClientService::start(
@ -303,7 +301,7 @@ fn execute_export(cmd: ExportBlockchain) -> Result<String, String> {
try!(execute_upgrades(&db_dirs, algorithm, cmd.compaction.compaction_profile(db_dirs.fork_path().as_path())));
// prepare client config
let client_config = to_client_config(&cmd.cache_config, cmd.mode, tracing, fat_db, cmd.compaction, cmd.wal, VMType::default(), "".into(), algorithm, cmd.pruning_history, cmd.check_seal);
let client_config = to_client_config(&cmd.cache_config, Mode::Active, tracing, fat_db, cmd.compaction, cmd.wal, VMType::default(), "".into(), algorithm, cmd.pruning_history, cmd.check_seal);
let service = try!(ClientService::start(
client_config,

View File

@ -1,5 +1,5 @@
[parity]
mode = "active"
mode = "last"
mode_timeout = 300
mode_alarm = 3600
chain = "homestead"

View File

@ -74,7 +74,7 @@ usage! {
}
{
// -- Operating Options
flag_mode: String = "active", or |c: &Config| otry!(c.parity).mode.clone(),
flag_mode: String = "last", or |c: &Config| otry!(c.parity).mode.clone(),
flag_mode_timeout: u64 = 300u64, or |c: &Config| otry!(c.parity).mode_timeout.clone(),
flag_mode_alarm: u64 = 3600u64, or |c: &Config| otry!(c.parity).mode_alarm.clone(),
flag_chain: String = "homestead", or |c: &Config| otry!(c.parity).chain.clone(),
@ -104,8 +104,6 @@ usage! {
flag_signer_no_validation: bool = false, or |_| None,
// -- Networking Options
flag_no_network: bool = false,
or |c: &Config| otry!(c.network).disable.clone(),
flag_warp: bool = false,
or |c: &Config| otry!(c.network).warp.clone(),
flag_port: u16 = 30303u16,
@ -500,7 +498,7 @@ mod tests {
arg_path: vec![],
// -- Operating Options
flag_mode: "active".into(),
flag_mode: "last".into(),
flag_mode_timeout: 300u64,
flag_mode_alarm: 3600u64,
flag_chain: "xyz".into(),
@ -521,7 +519,6 @@ mod tests {
flag_signer_no_validation: false,
// -- Networking Options
flag_no_network: false,
flag_warp: true,
flag_port: 30303u16,
flag_min_peers: 25u16,

View File

@ -18,11 +18,12 @@ Usage:
Operating Options:
--mode MODE Set the operating mode. MODE can be one of:
last - Uses the last-used mode, active if none.
active - Parity continuously syncs the chain.
passive - Parity syncs initially, then sleeps and
wakes regularly to resync.
dark - Parity syncs only when an external interface
is active. (default: {flag_mode}).
dark - Parity syncs only when the RPC is active.
offline - Parity doesn't sync. (default: {flag_mode}).
--mode-timeout SECS Specify the number of seconds before inactivity
timeout occurs when mode is dark or passive
(default: {flag_mode_timeout}).
@ -66,7 +67,6 @@ Account Options:
development. (default: {flag_signer_no_validation})
Networking Options:
--no-network Disable p2p networking. (default: {flag_no_network})
--warp Enable syncing from the snapshot over the network. (default: {flag_warp})
--port PORT Override the port on which the node should listen
(default: {flag_port}).

View File

@ -23,7 +23,7 @@ use cli::{Args, ArgsError};
use util::{Hashable, U256, Uint, Bytes, version_data, Secret, Address};
use util::log::Colour;
use ethsync::{NetworkConfiguration, is_valid_node_url, AllowIP};
use ethcore::client::{VMType, Mode};
use ethcore::client::VMType;
use ethcore::miner::{MinerOptions, Banning};
use rpc::{IpcConfiguration, HttpConfiguration};
@ -80,7 +80,7 @@ impl Configuration {
let pruning = try!(self.args.flag_pruning.parse());
let pruning_history = self.args.flag_pruning_history;
let vm_type = try!(self.vm_type());
let mode = try!(to_mode(&self.args.flag_mode, self.args.flag_mode_timeout, self.args.flag_mode_alarm));
let mode = match self.args.flag_mode.as_ref() { "last" => None, mode => Some(try!(to_mode(&mode, self.args.flag_mode_timeout, self.args.flag_mode_alarm))), };
let miner_options = try!(self.miner_options());
let logger_config = self.logger_config();
let http_conf = try!(self.http_config());
@ -93,7 +93,6 @@ impl Configuration {
let fat_db = try!(self.args.flag_fat_db.parse());
let compaction = try!(self.args.flag_db_compaction.parse());
let wal = !self.args.flag_fast_and_loose;
let enable_network = self.enable_network(&mode);
let warp_sync = self.args.flag_warp;
let geth_compatibility = self.args.flag_geth;
let signer_port = self.signer_port();
@ -156,7 +155,6 @@ impl Configuration {
pruning_history: pruning_history,
compaction: compaction,
wal: wal,
mode: mode,
tracing: tracing,
fat_db: fat_db,
vm_type: vm_type,
@ -175,7 +173,6 @@ impl Configuration {
pruning_history: pruning_history,
compaction: compaction,
wal: wal,
mode: mode,
tracing: tracing,
fat_db: fat_db,
from_block: try!(to_block_id(&self.args.flag_from)),
@ -190,7 +187,6 @@ impl Configuration {
spec: spec,
pruning: pruning,
pruning_history: pruning_history,
mode: mode,
tracing: tracing,
fat_db: fat_db,
compaction: compaction,
@ -207,7 +203,6 @@ impl Configuration {
spec: spec,
pruning: pruning,
pruning_history: pruning_history,
mode: mode,
tracing: tracing,
fat_db: fat_db,
compaction: compaction,
@ -246,7 +241,6 @@ impl Configuration {
compaction: compaction,
wal: wal,
vm_type: vm_type,
enable_network: enable_network,
warp_sync: warp_sync,
geth_compatibility: geth_compatibility,
signer_port: signer_port,
@ -268,13 +262,6 @@ impl Configuration {
})
}
fn enable_network(&self, mode: &Mode) -> bool {
match *mode {
Mode::Dark(_) => false,
_ => !self.args.flag_no_network,
}
}
fn vm_type(&self) -> Result<VMType, String> {
if self.args.flag_jitvm {
VMType::jit().ok_or("Parity is built without the JIT EVM.".into())
@ -767,7 +754,6 @@ mod tests {
pruning_history: 64,
compaction: Default::default(),
wal: true,
mode: Default::default(),
tracing: Default::default(),
fat_db: Default::default(),
vm_type: VMType::Interpreter,
@ -790,7 +776,6 @@ mod tests {
format: Default::default(),
compaction: Default::default(),
wal: true,
mode: Default::default(),
tracing: Default::default(),
fat_db: Default::default(),
from_block: BlockID::Number(1),
@ -813,7 +798,6 @@ mod tests {
format: Some(DataFormat::Hex),
compaction: Default::default(),
wal: true,
mode: Default::default(),
tracing: Default::default(),
fat_db: Default::default(),
from_block: BlockID::Number(1),
@ -858,7 +842,6 @@ mod tests {
compaction: Default::default(),
wal: true,
vm_type: Default::default(),
enable_network: true,
geth_compatibility: false,
signer_port: Some(8180),
net_settings: Default::default(),

View File

@ -46,7 +46,7 @@ fn to_seconds(s: &str) -> Result<u64, String> {
"hourly" | "1hour" | "1 hour" | "hour" => Ok(60 * 60),
"daily" | "1day" | "1 day" | "day" => Ok(24 * 60 * 60),
x if x.ends_with("seconds") => x[0..x.len() - 7].parse().map_err(bad),
x if x.ends_with("minutes") => x[0..x.len() -7].parse::<u64>().map_err(bad).map(|x| x * 60),
x if x.ends_with("minutes") => x[0..x.len() - 7].parse::<u64>().map_err(bad).map(|x| x * 60),
x if x.ends_with("hours") => x[0..x.len() - 5].parse::<u64>().map_err(bad).map(|x| x * 60 * 60),
x if x.ends_with("days") => x[0..x.len() - 4].parse::<u64>().map_err(bad).map(|x| x * 24 * 60 * 60),
x => x.parse().map_err(bad),
@ -58,7 +58,8 @@ pub fn to_mode(s: &str, timeout: u64, alarm: u64) -> Result<Mode, String> {
"active" => Ok(Mode::Active),
"passive" => Ok(Mode::Passive(Duration::from_secs(timeout), Duration::from_secs(alarm))),
"dark" => Ok(Mode::Dark(Duration::from_secs(timeout))),
_ => Err(format!("{}: Invalid address for --mode. Must be one of active, passive or dark.", s)),
"offline" => Ok(Mode::Off),
_ => Err(format!("{}: Invalid value for --mode. Must be one of active, passive, dark or offline.", s)),
}
}

View File

@ -20,6 +20,7 @@ use util::{Address, U256, version_data};
use util::journaldb::Algorithm;
use ethcore::spec::Spec;
use ethcore::ethereum;
use ethcore::client::Mode;
use ethcore::miner::{GasPricer, GasPriceCalibratorOptions};
use user_defaults::UserDefaults;
@ -264,6 +265,10 @@ pub fn fatdb_switch_to_bool(switch: Switch, user_defaults: &UserDefaults, algori
result
}
pub fn mode_switch_to_bool(switch: Option<Mode>, user_defaults: &UserDefaults) -> Result<Mode, String> {
Ok(switch.unwrap_or(user_defaults.mode.clone()))
}
#[cfg(test)]
mod tests {
use util::journaldb::Algorithm;

View File

@ -37,7 +37,7 @@ use dapps::WebappServer;
use io_handler::ClientIoHandler;
use params::{
SpecType, Pruning, AccountsConfig, GasPricerConfig, MinerExtras, Switch,
tracing_switch_to_bool, fatdb_switch_to_bool,
tracing_switch_to_bool, fatdb_switch_to_bool, mode_switch_to_bool
};
use helpers::{to_client_config, execute_upgrades, passwords_from_files};
use dir::Directories;
@ -75,13 +75,12 @@ pub struct RunCmd {
pub acc_conf: AccountsConfig,
pub gas_pricer: GasPricerConfig,
pub miner_extras: MinerExtras,
pub mode: Mode,
pub mode: Option<Mode>,
pub tracing: Switch,
pub fat_db: Switch,
pub compaction: DatabaseCompactionProfile,
pub wal: bool,
pub vm_type: VMType,
pub enable_network: bool,
pub geth_compatibility: bool,
pub signer_port: Option<u16>,
pub net_settings: NetworkSettings,
@ -137,6 +136,11 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
// check if fatdb is on
let fat_db = try!(fatdb_switch_to_bool(cmd.fat_db, &user_defaults, algorithm));
// get the mode
let mode = try!(mode_switch_to_bool(cmd.mode, &user_defaults));
trace!(target: "mode", "mode is {:?}", mode);
let network_enabled = match &mode { &Mode::Dark(_) | &Mode::Off => false, _ => true, };
// prepare client and snapshot paths.
let client_path = db_dirs.client_path(algorithm);
let snapshot_path = db_dirs.snapshot_path();
@ -162,6 +166,7 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
false => "".to_owned(),
}
);
info!("Operating mode: {}", Colour::White.bold().paint(format!("{}", mode)));
// display warning about using experimental journaldb alorithm
if !algorithm.is_stable() {
@ -196,7 +201,7 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
// create client config
let client_config = to_client_config(
&cmd.cache_config,
cmd.mode,
mode,
tracing,
fat_db,
cmd.compaction,
@ -248,7 +253,7 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
service.add_notify(chain_notify.clone());
// start network
if cmd.enable_network {
if network_enabled {
chain_notify.start();
}
@ -321,6 +326,19 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
});
service.register_io_handler(io_handler.clone()).expect("Error registering IO handler");
// save user defaults
user_defaults.pruning = algorithm;
user_defaults.tracing = tracing;
try!(user_defaults.save(&user_defaults_path));
let on_mode_change = move |mode: &Mode| {
user_defaults.mode = mode.clone();
let _ = user_defaults.save(&user_defaults_path); // discard failures - there's nothing we can do
};
// tell client how to save the default mode if it gets changed.
client.on_mode_change(on_mode_change);
// the watcher must be kept alive.
let _watcher = match cmd.no_periodic_snapshot {
true => None,
@ -347,11 +365,6 @@ pub fn execute(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<(), String> {
url::open(&format!("http://{}:{}/", cmd.dapps_conf.interface, cmd.dapps_conf.port));
}
// save user defaults
user_defaults.pruning = algorithm;
user_defaults.tracing = tracing;
try!(user_defaults.save(&user_defaults_path));
// Handle exit
wait_for_exit(panic_handler, http_server, ipc_server, dapps_server, signer_server);

View File

@ -54,7 +54,6 @@ pub struct SnapshotCommand {
pub spec: SpecType,
pub pruning: Pruning,
pub pruning_history: u64,
pub mode: Mode,
pub tracing: Switch,
pub fat_db: Switch,
pub compaction: DatabaseCompactionProfile,
@ -158,7 +157,7 @@ impl SnapshotCommand {
try!(execute_upgrades(&db_dirs, algorithm, self.compaction.compaction_profile(db_dirs.fork_path().as_path())));
// prepare client config
let client_config = to_client_config(&self.cache_config, self.mode, tracing, fat_db, self.compaction, self.wal, VMType::default(), "".into(), algorithm, self.pruning_history, true);
let client_config = to_client_config(&self.cache_config, Mode::Active, tracing, fat_db, self.compaction, self.wal, VMType::default(), "".into(), algorithm, self.pruning_history, true);
let service = try!(ClientService::start(
client_config,

View File

@ -18,6 +18,7 @@ use std::fs::File;
use std::io::Write;
use std::path::Path;
use std::collections::BTreeMap;
use std::time::Duration;
use serde::{Serialize, Serializer, Error, Deserialize, Deserializer};
use serde::de::{Visitor, MapVisitor};
use serde::de::impls::BTreeMapVisitor;
@ -25,12 +26,14 @@ use serde_json::Value;
use serde_json::de::from_reader;
use serde_json::ser::to_string;
use util::journaldb::Algorithm;
use ethcore::client::Mode;
pub struct UserDefaults {
pub is_first_launch: bool,
pub pruning: Algorithm,
pub tracing: bool,
pub fat_db: bool,
pub mode: Mode,
}
impl Serialize for UserDefaults {
@ -40,6 +43,21 @@ impl Serialize for UserDefaults {
map.insert("pruning".into(), Value::String(self.pruning.as_str().into()));
map.insert("tracing".into(), Value::Bool(self.tracing));
map.insert("fat_db".into(), Value::Bool(self.fat_db));
let mode_str = match self.mode {
Mode::Off => "offline",
Mode::Dark(timeout) => {
map.insert("mode.timeout".into(), Value::U64(timeout.as_secs()));
"dark"
},
Mode::Passive(timeout, alarm) => {
map.insert("mode.timeout".into(), Value::U64(timeout.as_secs()));
map.insert("mode.alarm".into(), Value::U64(alarm.as_secs()));
"passive"
},
Mode::Active => "active",
};
map.insert("mode".into(), Value::String(mode_str.into()));
map.serialize(serializer)
}
}
@ -67,11 +85,28 @@ impl Visitor for UserDefaultsVisitor {
let fat_db: Value = map.remove("fat_db".into()).unwrap_or_else(|| Value::Bool(false));
let fat_db = try!(fat_db.as_bool().ok_or_else(|| Error::custom("invalid fat_db value")));
let mode: Value = map.remove("mode".into()).unwrap_or_else(|| Value::String("active".to_owned()));
let mode = match try!(mode.as_str().ok_or_else(|| Error::custom("invalid mode value"))) {
"offline" => Mode::Off,
"dark" => {
let timeout = try!(map.remove("mode.timeout".into()).and_then(|v| v.as_u64()).ok_or_else(|| Error::custom("invalid/missing mode.timeout value")));
Mode::Dark(Duration::from_secs(timeout))
},
"passive" => {
let timeout = try!(map.remove("mode.timeout".into()).and_then(|v| v.as_u64()).ok_or_else(|| Error::custom("invalid/missing mode.timeout value")));
let alarm = try!(map.remove("mode.alarm".into()).and_then(|v| v.as_u64()).ok_or_else(|| Error::custom("invalid/missing mode.alarm value")));
Mode::Passive(Duration::from_secs(timeout), Duration::from_secs(alarm))
},
"active" => Mode::Active,
_ => { return Err(Error::custom("invalid mode value")); },
};
let user_defaults = UserDefaults {
is_first_launch: false,
pruning: pruning,
tracing: tracing,
fat_db: fat_db,
mode: mode,
};
Ok(user_defaults)
@ -85,6 +120,7 @@ impl Default for UserDefaults {
pruning: Algorithm::default(),
tracing: false,
fat_db: false,
mode: Mode::Active,
}
}
}
@ -97,7 +133,7 @@ impl UserDefaults {
}
}
pub fn save<P>(self, path: P) -> Result<(), String> where P: AsRef<Path> {
pub fn save<P>(&self, path: P) -> Result<(), String> where P: AsRef<Path> {
let mut file: File = try!(File::create(path).map_err(|_| "Cannot create user defaults file".to_owned()));
file.write_all(to_string(&self).unwrap().as_bytes()).map_err(|_| "Failed to save user defaults".to_owned())
}

View File

@ -351,7 +351,7 @@ impl<C, M, S: ?Sized, F> Ethcore for EthcoreClient<C, M, S, F> where
fn mode(&self) -> Result<String, Error> {
Ok(match take_weak!(self.client).mode() {
Mode::Off => "off",
Mode::Off => "offline",
Mode::Dark(..) => "dark",
Mode::Passive(..) => "passive",
Mode::Active => "active",

View File

@ -151,7 +151,7 @@ impl<C, M> EthcoreSet for EthcoreSetClient<C, M> where
fn set_mode(&self, mode: String) -> Result<bool, Error> {
take_weak!(self.client).set_mode(match mode.as_str() {
"off" => Mode::Off,
"offline" => Mode::Off,
"dark" => Mode::Dark(300),
"passive" => Mode::Passive(300, 3600),
"active" => Mode::Active,

View File

@ -130,7 +130,7 @@ build_rpc_trait! {
#[rpc(name = "ethcore_nextNonce")]
fn next_nonce(&self, H160) -> Result<U256, Error>;
/// Get the mode. Results one of: "active", "passive", "dark", "off".
/// Get the mode. Results one of: "active", "passive", "dark", "offline".
#[rpc(name = "ethcore_mode")]
fn mode(&self) -> Result<String, Error>;

View File

@ -76,11 +76,11 @@ build_rpc_trait! {
/// Stop the network.
///
/// Deprecated. Use `set_mode("off")` instead.
/// Deprecated. Use `set_mode("offline")` instead.
#[rpc(name = "ethcore_stopNetwork")]
fn stop_network(&self) -> Result<bool, Error>;
/// Set the mode. Argument must be one of: "active", "passive", "dark", "off".
/// Set the mode. Argument must be one of: "active", "passive", "dark", "offline".
#[rpc(name = "ethcore_setMode")]
fn set_mode(&self, String) -> Result<bool, Error>;
}