Merge branch 'check-updates' of github.com:ethcore/parity into check-updates

This commit is contained in:
Gav Wood 2016-12-13 17:25:16 +01:00
commit 575afd8bac
No known key found for this signature in database
GPG Key ID: C49C1ACA1CC9B252
16 changed files with 572 additions and 52 deletions

2
.gitignore vendored
View File

@ -33,4 +33,4 @@ out/
.vscode .vscode
parity.* /parity.*

View File

@ -36,7 +36,7 @@
"ci:build:npm": "NODE_ENV=production webpack --config webpack/npm", "ci:build:npm": "NODE_ENV=production webpack --config webpack/npm",
"start": "npm install && npm run build:lib && npm run build:dll && npm run start:app", "start": "npm install && npm run build:lib && npm run build:dll && npm run start:app",
"start:app": "node webpack/dev.server", "start:app": "node webpack/dev.server",
"clean": "rm -rf ./build ./coverage", "clean": "rm -rf ./.build ./.coverage ./.happypack ./.npmjs ./build",
"coveralls": "npm run testCoverage && coveralls < coverage/lcov.info", "coveralls": "npm run testCoverage && coveralls < coverage/lcov.info",
"lint": "eslint --ignore-path .gitignore ./src/", "lint": "eslint --ignore-path .gitignore ./src/",
"lint:cached": "eslint --cache --ignore-path .gitignore ./src/", "lint:cached": "eslint --cache --ignore-path .gitignore ./src/",

View File

@ -54,6 +54,11 @@ export default class Parity {
.execute('parity_checkRequest', inNumber16(requestId)); .execute('parity_checkRequest', inNumber16(requestId));
} }
consensusCapability () {
return this._transport
.execute('parity_consensusCapability');
}
dappsPort () { dappsPort () {
return this._transport return this._transport
.execute('parity_dappsPort') .execute('parity_dappsPort')
@ -90,6 +95,11 @@ export default class Parity {
.execute('parity_enode'); .execute('parity_enode');
} }
executeUpgrade () {
return this._transport
.execute('parity_executeUpgrade');
}
extraData () { extraData () {
return this._transport return this._transport
.execute('parity_extraData'); .execute('parity_extraData');
@ -243,6 +253,11 @@ export default class Parity {
.then(outAddress); .then(outAddress);
} }
releasesInfo () {
return this._transport
.execute('parity_releasesInfo');
}
removeReservedPeer (encode) { removeReservedPeer (encode) {
return this._transport return this._transport
.execute('parity_removeReservedPeer', encode); .execute('parity_removeReservedPeer', encode);
@ -316,28 +331,13 @@ export default class Parity {
.then(outNumber); .then(outNumber);
} }
consensusCapability () { upgradeReady () {
return this._transport return this._transport
.execute('parity_consensusCapability'); .execute('parity_upgradeReady');
} }
versionInfo () { versionInfo () {
return this._transport return this._transport
.execute('parity_versionInfo'); .execute('parity_versionInfo');
} }
releasesInfo () {
return this._transport
.execute('parity_releasesInfo');
}
upgradeReady () {
return this._transport
.execute('parity_upgradeReady');
}
executeUpgrade () {
return this._transport
.execute('parity_executeUpgrade');
}
} }

View File

@ -100,6 +100,15 @@ export default {
} }
}, },
consensusCapability: {
desc: 'Returns an object or string detailing the state of parity capability of maintaining consensus',
params: [],
returns: {
type: Object,
desc: 'Either "capable", {"capableUntil":N}, {"incapableSince":N} or "unknown" (N is a block number)'
}
},
dappsPort: { dappsPort: {
desc: 'Returns the port the dapps are running on, error if not enabled', desc: 'Returns the port the dapps are running on, error if not enabled',
params: [], params: [],
@ -163,6 +172,15 @@ export default {
} }
}, },
executeUpgrade: {
desc: 'Performs an upgrade',
params: [],
returns: {
type: Boolean,
desc: 'returns true if the upgrade to the release specified in parity_upgradeReady was successfully executed, false if not'
}
},
extraData: { extraData: {
desc: 'Returns currently set extra data', desc: 'Returns currently set extra data',
params: [], params: [],
@ -468,6 +486,15 @@ export default {
} }
}, },
releasesInfo: {
desc: 'returns a ReleasesInfo object describing the current status of releases',
params: [],
returns: {
type: Object,
desc: '"fork":N,"minor":null,"this_fork":MN,"track":R} (N is a block number representing the latest known fork of this chain which may be in the future, MN is a block number representing the latest known fork that the currently running binary can sync past or null if not known, R is a ReleaseInfo object describing the latest release in this release track)'
}
},
removeReservedPeer: { removeReservedPeer: {
desc: '?', desc: '?',
params: [ params: [
@ -651,5 +678,23 @@ export default {
type: Quantity, type: Quantity,
desc: 'Number of unsigned transactions' desc: 'Number of unsigned transactions'
} }
},
upgradeReady: {
desc: 'returns a ReleaseInfo object describing the release which is available for upgrade or null if none is available',
params: [],
returns: {
type: Object,
desc: '{"binary":H,"fork":15100,"is_critical":true,"version":V} where H is the Keccak-256 checksum of the release parity binary and V is a VersionInfo object describing the release'
}
},
versionInfo: {
desc: 'returns a VersionInfo object describing our current version',
params: [],
returns: {
type: Object,
desc: '{"hash":H,"track":T,"version":{"major":N,"minor":N,"patch":N}} (H is a 160-bit Git commit hash, T is a ReleaseTrack, either "stable", "beta", "nightly" or "unknown" and N is a version number)'
}
} }
}; };

View File

@ -0,0 +1,17 @@
// Copyright 2015, 2016 Parity Technologies (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 './upgradeParity';

View File

@ -0,0 +1,97 @@
// Copyright 2015, 2016 Parity Technologies (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 { action, computed, observable, transaction } from 'mobx';
import store from 'store';
const LS_UPDATE = '_parity::update';
const A_DAY = 24 * 60 * 60 * 1000;
const STEP_INFO = 1;
const STEP_UPDATING = 2;
const STEP_COMPLETED = 3;
const STEP_ERROR = 4;
export default class ModalStore {
@observable closed = false;
@observable error = null;
@observable step = 0;
@observable upgrade = null;
constructor (upgradeStore) {
this.upgrade = upgradeStore;
this.loadStorage();
}
@computed get showUpgrade () {
return !this.closed && Date.now() >= this.remindAt;
}
@action closeModal = () => {
transaction(() => {
this.closed = true;
this.setStep(STEP_INFO);
});
}
@action loadStorage = () => {
const values = store.get(LS_UPDATE) || {};
this.remindAt = values.remindAt ? values.remindAt : 0;
return values;
}
@action setStep = (step, error = null) => {
transaction(() => {
this.error = error;
this.setp = step;
});
}
@action snoozeTillTomorrow = () => {
this.remindAt = Date.now() + A_DAY;
store.set(LS_UPDATE, Object.assign(this.loadStorage(), { remindAt: this.remindAt }));
}
@action upgradeNow = () => {
this.setStep(STEP_UPDATING);
this.upgrade
.executeUpgrade()
.then((result) => {
if (!result) {
throw new Error('Unable to complete update');
}
this.setStep(STEP_COMPLETED);
})
.catch((error) => {
console.error('upgradeNow', error);
this.setStep(STEP_ERROR, error);
});
}
}
export {
STEP_COMPLETED,
STEP_ERROR,
STEP_INFO,
STEP_UPDATING
};

View File

@ -0,0 +1,26 @@
/* Copyright 2015, 2016 Parity Technologies (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/>.
*/
.error {
padding-top: 1.5em;
}
.infoStep {
div+div {
padding-top: 1.5em;
}
}

View File

@ -0,0 +1,229 @@
// Copyright 2015, 2016 Parity Technologies (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 { observer } from 'mobx-react';
import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { Button } from '~/ui';
import { CancelIcon, DoneIcon, NextIcon, SnoozeIcon } from '~/ui/Icons';
import Modal, { Busy, Completed } from '~/ui/Modal';
import ModalStore, { STEP_COMPLETED, STEP_ERROR, STEP_INFO, STEP_UPDATING } from './modalStore';
import UpgradeStore from './upgradeStore';
import styles from './upgradeParity.css';
@observer
export default class UpgradeParity extends Component {
static contextTypes = {
api: PropTypes.object.isRequired
};
store = new ModalStore(new UpgradeStore(this.context.api));
render () {
if (!this.store.upgrade.available || !this.store.showUpgrade) {
return null;
}
return (
<Modal
actions={ this.renderActions() }
current={ this.store.step }
steps={ [
<FormattedMessage
id='upgradeParity.step.info'
defaultMessage='upgrade available' />,
<FormattedMessage
id='upgradeParity.step.updating'
defaultMessage='upgrading parity' />,
this.store.step === STEP_ERROR
? <FormattedMessage
id='upgradeParity.step.completed'
defaultMessage='upgrade completed' />
: <FormattedMessage
id='upgradeParity.step.error'
defaultMessage='error' />
] }
visible>
{ this.renderStep() }
</Modal>
);
}
renderActions () {
const closeButton =
<Button
icon={ <CancelIcon /> }
label={
<FormattedMessage
id='upgradeParity.button.close'
defaultMessage='close' />
}
onClick={ this.store.closeModal } />;
const doneButton =
<Button
icon={ <DoneIcon /> }
label={
<FormattedMessage
id='upgradeParity.button.done'
defaultMessage='done' />
}
onClick={ this.store.closeModal } />;
switch (this.store.step) {
case STEP_INFO:
return [
<Button
icon={ <SnoozeIcon /> }
label={
<FormattedMessage
id='upgradeParity.button.snooze'
defaultMessage='ask me tomorrow' />
}
onClick={ this.store.snoozeTillTomorrow } />,
<Button
icon={ <NextIcon /> }
label={
<FormattedMessage
id='upgradeParity.button.upgrade'
defaultMessage='upgrade now' />
}
onClick={ this.store.upgradeNow } />,
closeButton
];
case STEP_UPDATING:
return [
closeButton
];
case STEP_COMPLETED:
case STEP_ERROR:
return [
doneButton
];
}
}
renderStep () {
const { available, consensusCapability, error, upgrading, version } = this.store.upgrade;
const currentversion = this.renderVersion(version);
const newversion = upgrading
? this.renderVersion(upgrading.version)
: this.renderVersion(available.version);
switch (this.store.step) {
case STEP_INFO:
let consensusInfo = null;
if (consensusCapability === 'capable') {
consensusInfo = (
<div>
<FormattedMessage
id='upgradeParity.consensus.capable'
defaultMessage='Your current Parity version is capable of handling the nework requirements.' />
</div>
);
} else if (consensusCapability.capableUntil) {
consensusInfo = (
<div>
<FormattedMessage
id='upgradeParity.consensus.capableUntil'
defaultMessage='Your current Parity version is capable of handling the nework requirements until block {blockNumber}'
values={ {
blockNumber: consensusCapability.capableUntil
} } />
</div>
);
} else if (consensusCapability.incapableSince) {
consensusInfo = (
<div>
<FormattedMessage
id='upgradeParity.consensus.incapableSince'
defaultMessage='Your current Parity version is incapable of handling the nework requirements since block {blockNumber}'
values={ {
blockNumber: consensusCapability.incapableSince
} } />
</div>
);
}
return (
<div className={ styles.infoStep }>
<div>
<FormattedMessage
id='upgradeParity.info.upgrade'
defaultMessage='A new version of Parity, version {newversion} is available as an upgrade from your current version {currentversion}'
values={ {
currentversion,
newversion
} } />
</div>
{ consensusInfo }
</div>
);
case STEP_UPDATING:
return (
<Busy
title={
<FormattedMessage
id='upgradeParity.busy'
defaultMessage='Your upgrade to Parity {newversion} is currently in progress'
values={ {
newversion
} } />
} />
);
case STEP_COMPLETED:
return (
<Completed>
<FormattedMessage
id='upgradeParity.completed'
defaultMessage='Your upgrade to Parity {newversion} has been successfully completed.'
values={ {
newversion
} } />
</Completed>
);
case STEP_ERROR:
return (
<Completed>
<div>
<FormattedMessage
id='upgradeParity.failed'
defaultMessage='Your upgrade to Parity {newversion} has failed with an error.'
values={ {
newversion
} } />
</div>
<div className={ styles.error }>
{ error.message }
</div>
</Completed>
);
}
}
renderVersion (versionInfo) {
const { track, version } = versionInfo;
return `${version.major}.${version.minor}.${version.patch}-${track}`;
}
}

View File

@ -0,0 +1,67 @@
// Copyright 2015, 2016 Parity Technologies (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 { action, observable, transaction } from 'mobx';
const CHECK_INTERVAL = 1 * 60 * 1000;
export default class UpgradeStore {
@observable available = null;
@observable consensusCapability = null;
@observable upgrading = null;
@observable version = null;
constructor (api) {
this._api = api;
this.checkUpgrade();
setInterval(this.checkUpgrade, CHECK_INTERVAL);
}
@action setUpgrading () {
this.upgrading = this.available;
}
@action setVersions (available, version, consensusCapability) {
transaction(() => {
this.available = available;
this.consensusCapability = consensusCapability;
this.version = version;
});
}
checkUpgrade = () => {
Promise
.all([
this._api.parity.upgradeReady(),
this._api.parity.consensusCapability(),
this._api.parity.versionInfo()
])
.then(([available, consensusCapability, version]) => {
console.log('[checkUpgrade]', 'available:', available, 'version:', version, 'consensusCapability:', consensusCapability);
this.setVersions(available, version, consensusCapability);
})
.catch((error) => {
console.warn('checkUpgrade', error);
});
}
executeUpgrade = () => {
this.setUpgrading();
return this._api.parity.executeUpgrade();
}
}

View File

@ -23,12 +23,13 @@ import DeployContract from './DeployContract';
import EditMeta from './EditMeta'; import EditMeta from './EditMeta';
import ExecuteContract from './ExecuteContract'; import ExecuteContract from './ExecuteContract';
import FirstRun from './FirstRun'; import FirstRun from './FirstRun';
import LoadContract from './LoadContract';
import SaveContract from './SaveContract';
import Shapeshift from './Shapeshift'; import Shapeshift from './Shapeshift';
import SMSVerification from './SMSVerification'; import SMSVerification from './SMSVerification';
import Transfer from './Transfer'; import Transfer from './Transfer';
import PasswordManager from './PasswordManager'; import PasswordManager from './PasswordManager';
import SaveContract from './SaveContract'; import UpgradeParity from './UpgradeParity';
import LoadContract from './LoadContract';
import WalletSettings from './WalletSettings'; import WalletSettings from './WalletSettings';
export { export {
@ -41,11 +42,12 @@ export {
EditMeta, EditMeta,
ExecuteContract, ExecuteContract,
FirstRun, FirstRun,
LoadContract,
SaveContract,
Shapeshift, Shapeshift,
SMSVerification, SMSVerification,
Transfer, Transfer,
PasswordManager, PasswordManager,
LoadContract, UpgradeParity,
SaveContract,
WalletSettings WalletSettings
}; };

31
js/src/ui/Icons/index.js Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2015, 2016 Parity Technologies (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 AddIcon from 'material-ui/svg-icons/content/add';
import CancelIcon from 'material-ui/svg-icons/content/clear';
import DoneIcon from 'material-ui/svg-icons/action/done-all';
import PrevIcon from 'material-ui/svg-icons/navigation/arrow-back';
import NextIcon from 'material-ui/svg-icons/navigation/arrow-forward';
import SnoozeIcon from 'material-ui/svg-icons/av/snooze';
export {
AddIcon,
CancelIcon,
DoneIcon,
PrevIcon,
NextIcon,
SnoozeIcon
};

View File

@ -16,17 +16,19 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { nodeOrStringProptype } from '~/util/proptypes';
import styles from './busy.css'; import styles from './busy.css';
export default class Busy extends Component { export default class Busy extends Component {
static propTypes = { static propTypes = {
title: PropTypes.string, children: PropTypes.node,
state: PropTypes.string, state: nodeOrStringProptype(),
children: PropTypes.node title: nodeOrStringProptype()
} }
render () { render () {
const { children, title, state } = this.props; const { children, state, title } = this.props;
return ( return (
<div className={ styles.center }> <div className={ styles.center }>

View File

@ -14,17 +14,18 @@
/* You should have received a copy of the GNU General Public License /* You should have received a copy of the GNU General Public License
/* along with Parity. If not, see <http://www.gnu.org/licenses/>. /* along with Parity. If not, see <http://www.gnu.org/licenses/>.
*/ */
.actions { .actions {
background: rgba(0, 0, 0, 0.25) !important; background: rgba(0, 0, 0, 0.25) !important;
}
.actions button:not([disabled]) { button:not([disabled]) {
color: white !important; color: white !important;
}
.actions button:not([disabled]) svg { svg {
fill: white !important; fill: white !important;
} }
}
}
.body { .body {
padding: 0 !important; padding: 0 !important;
@ -36,27 +37,27 @@
.content { .content {
transform: translate(0px, 0px) !important; transform: translate(0px, 0px) !important;
transition: none !important; transition: none !important;
}
.content>div { &>div {
background: rgba(0, 0, 0, 0.5) !important; background: rgba(0, 0, 0, 0.5) !important;
transition: none !important; transition: none !important;
} }
.title {
padding: 1em;
margin-bottom: 0;
background: rgba(0, 0, 0, 0.25) !important;
} }
.title h3 { .title {
background: rgba(0, 0, 0, 0.25) !important;
padding: 1em;
margin-bottom: 0;
h3 {
margin: 0; margin: 0;
text-transform: uppercase; text-transform: uppercase;
} }
.title .steps { .steps {
margin-bottom: -1em; margin-bottom: -1em;
} }
}
.waiting { .waiting {
margin: 1em -1em -1em -1em; margin: 1em -1em -1em -1em;

View File

@ -33,6 +33,7 @@ import Errors from './Errors';
import Form, { AddressSelect, FormWrap, TypedInput, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select, RadioButtons } from './Form'; import Form, { AddressSelect, FormWrap, TypedInput, Input, InputAddress, InputAddressSelect, InputChip, InputInline, Select, RadioButtons } from './Form';
import GasPriceEditor from './GasPriceEditor'; import GasPriceEditor from './GasPriceEditor';
import GasPriceSelector from './GasPriceSelector'; import GasPriceSelector from './GasPriceSelector';
import Icons from './Icons';
import IdentityIcon from './IdentityIcon'; import IdentityIcon from './IdentityIcon';
import IdentityName from './IdentityName'; import IdentityName from './IdentityName';
import LanguageSelector from './LanguageSelector'; import LanguageSelector from './LanguageSelector';
@ -72,6 +73,7 @@ export {
FormWrap, FormWrap,
GasPriceEditor, GasPriceEditor,
GasPriceSelector, GasPriceSelector,
Icons,
Input, Input,
InputAddress, InputAddress,
InputAddressSelect, InputAddressSelect,

View File

@ -16,7 +16,7 @@
import React, { Component, PropTypes } from 'react'; import React, { Component, PropTypes } from 'react';
import { FirstRun } from '~/modals'; import { FirstRun, UpgradeParity } from '~/modals';
import { Errors, ParityBackground, Tooltips } from '~/ui'; import { Errors, ParityBackground, Tooltips } from '~/ui';
import styles from '../application.css'; import styles from '../application.css';
@ -28,20 +28,21 @@ export default class Container extends Component {
static propTypes = { static propTypes = {
children: PropTypes.node.isRequired, children: PropTypes.node.isRequired,
showFirstRun: PropTypes.bool, onCloseFirstRun: PropTypes.func,
onCloseFirstRun: PropTypes.func showFirstRun: PropTypes.bool
}; };
render () { render () {
const { children, showFirstRun, onCloseFirstRun } = this.props;
const { muiTheme } = this.context; const { muiTheme } = this.context;
const { children, onCloseFirstRun, showFirstRun } = this.props;
return ( return (
<ParityBackground className={ styles.container } muiTheme={ muiTheme }> <ParityBackground className={ styles.container } muiTheme={ muiTheme }>
<FirstRun <FirstRun
visible={ showFirstRun } onClose={ onCloseFirstRun }
onClose={ onCloseFirstRun } /> visible={ showFirstRun } />
<Tooltips /> <Tooltips />
<UpgradeParity />
<Errors /> <Errors />
{ children } { children }
</ParityBackground> </ParityBackground>

View File

@ -60,7 +60,7 @@ app.use(webpackHotMiddleware(compiler, {
app.use(webpackDevMiddleware(compiler, { app.use(webpackDevMiddleware(compiler, {
noInfo: false, noInfo: false,
quiet: true, quiet: false,
progress: true, progress: true,
publicPath: webpackConfig.output.publicPath, publicPath: webpackConfig.output.publicPath,
stats: { stats: {