diff --git a/ethcore/res/ethereum/kovan.json b/ethcore/res/ethereum/kovan.json index 01ea9956a..1c6a029d1 100644 --- a/ethcore/res/ethereum/kovan.json +++ b/ethcore/res/ethereum/kovan.json @@ -25,7 +25,8 @@ ] }, "validateScoreTransition": 1000000, - "eip155Transition": 1000000 + "eip155Transition": 1000000, + "validateStepTransition": 1500000 } } }, diff --git a/ethcore/src/engines/authority_round.rs b/ethcore/src/engines/authority_round.rs index d2ae144d5..d3910b851 100644 --- a/ethcore/src/engines/authority_round.rs +++ b/ethcore/src/engines/authority_round.rs @@ -57,6 +57,8 @@ pub struct AuthorityRoundParams { pub validate_score_transition: u64, /// Number of first block where EIP-155 rules are validated. pub eip155_transition: u64, + /// Monotonic step validation transition block. + pub validate_step_transition: u64, } impl From for AuthorityRoundParams { @@ -70,6 +72,7 @@ impl From for AuthorityRoundParams { start_step: p.start_step.map(Into::into), validate_score_transition: p.validate_score_transition.map_or(0, Into::into), eip155_transition: p.eip155_transition.map_or(0, Into::into), + validate_step_transition: p.validate_step_transition.map_or(0, Into::into), } } } @@ -128,6 +131,7 @@ pub struct AuthorityRound { validators: Box, validate_score_transition: u64, eip155_transition: u64, + validate_step_transition: u64, } // header-chain validator. @@ -208,6 +212,7 @@ impl AuthorityRound { validators: new_validator_set(our_params.validators), validate_score_transition: our_params.validate_score_transition, eip155_transition: our_params.eip155_transition, + validate_step_transition: our_params.validate_step_transition, }); // Do not initialize timeouts for tests. if should_timeout { @@ -379,7 +384,8 @@ impl Engine for AuthorityRound { // Ensure header is from the step after parent. let parent_step = header_step(parent)?; - if step <= parent_step { + if step == parent_step + || (header.number() >= self.validate_step_transition && step <= parent_step) { trace!(target: "engine", "Multiple blocks proposed for step {}.", parent_step); self.validators.report_malicious(header.author(), header.number(), Default::default()); Err(EngineError::DoubleVote(header.author().clone()))?; diff --git a/js/src/api/rpc/parity/parity.js b/js/src/api/rpc/parity/parity.js index 674091563..22b3f9751 100644 --- a/js/src/api/rpc/parity/parity.js +++ b/js/src/api/rpc/parity/parity.js @@ -39,9 +39,9 @@ export default class Parity { .then(outAccountInfo); } - addReservedPeer (encode) { + addReservedPeer (enode) { return this._transport - .execute('parity_addReservedPeer', encode); + .execute('parity_addReservedPeer', enode); } chainStatus () { @@ -429,9 +429,9 @@ export default class Parity { .execute('parity_releasesInfo'); } - removeReservedPeer (encode) { + removeReservedPeer (enode) { return this._transport - .execute('parity_removeReservedPeer', encode); + .execute('parity_removeReservedPeer', enode); } removeTransaction (hash) { diff --git a/js/src/shared/redux/providers/signerMiddleware.js b/js/src/shared/redux/providers/signerMiddleware.js index 3e4608f90..4d83ea1ed 100644 --- a/js/src/shared/redux/providers/signerMiddleware.js +++ b/js/src/shared/redux/providers/signerMiddleware.js @@ -104,6 +104,12 @@ export default class SignerMiddleware { return this.confirmRawRequest(store, id, signature); } + confirmDecryptedMsg (store, id, decrypted) { + const { msg } = decrypted; + + return this.confirmRawRequest(store, id, msg); + } + confirmSignedTransaction (store, id, txSigned) { const { netVersion } = store.getState().nodeStatus; const { signature, tx } = txSigned; @@ -154,7 +160,7 @@ export default class SignerMiddleware { } onConfirmStart = (store, action) => { - const { condition, gas = 0, gasPrice = 0, id, password, payload, txSigned, dataSigned, wallet } = action.payload; + const { condition, gas = 0, gasPrice = 0, id, password, payload, txSigned, dataSigned, decrypted, wallet } = action.payload; const handlePromise = this._createConfirmPromiseHandler(store, id); const transaction = payload.sendTransaction || payload.signTransaction; @@ -177,10 +183,14 @@ export default class SignerMiddleware { } } - // TODO [ToDr] Support eth_sign for external wallet (wallet && !transction) + // TODO [ToDr] Support eth_sign for external wallet (wallet && dataSigned) if (dataSigned) { return this.confirmSignedData(store, id, dataSigned); } + // TODO [ToDr] Support parity_decrypt for external wallet (wallet && decrypted) + if (decrypted) { + return this.confirmDecryptedMsg(store, id, decrypted); + } return handlePromise(this._api.signer.confirmRequest(id, { gas, gasPrice, condition }, password)); } diff --git a/js/src/shell/Signer/components/DecryptRequest/decryptRequest.js b/js/src/shell/Signer/components/DecryptRequest/decryptRequest.js index f4626c755..703582c9e 100644 --- a/js/src/shell/Signer/components/DecryptRequest/decryptRequest.js +++ b/js/src/shell/Signer/components/DecryptRequest/decryptRequest.js @@ -114,7 +114,7 @@ class DecryptRequest extends Component { } renderActions () { - const { accounts, address, focus, isFinished, status } = this.props; + const { accounts, address, focus, isFinished, status, data } = this.props; const account = accounts[address]; if (isFinished) { @@ -153,15 +153,16 @@ class DecryptRequest extends Component { onConfirm={ this.onConfirm } onReject={ this.onReject } className={ styles.actions } + dataToSign={ { decrypt: data } } /> ); } onConfirm = (data) => { const { id } = this.props; - const { password } = data; + const { password, decrypted, wallet } = data; - this.props.onConfirm({ id, password }); + this.props.onConfirm({ id, password, decrypted, wallet }); } onReject = () => { diff --git a/js/src/shell/Signer/components/RequestPending/requestPending.spec.js b/js/src/shell/Signer/components/RequestPending/requestPending.spec.js index 130a9d319..3c2d29cf0 100644 --- a/js/src/shell/Signer/components/RequestPending/requestPending.spec.js +++ b/js/src/shell/Signer/components/RequestPending/requestPending.spec.js @@ -40,6 +40,12 @@ const PAYLOAD_SIGN = { const PAYLOAD_SIGNTX = { signTransaction: TRANSACTION }; +const PAYLOAD_DECRYPT = { + decrypt: { + address: ADDRESS, + msg: 'testing' + } +}; let component; let onConfirm; @@ -109,4 +115,18 @@ describe('views/Signer/RequestPending', () => { expect(component.find('Connect(TransactionPending)')).to.have.length(1); }); }); + + describe('decrypt', () => { + beforeEach(() => { + render(PAYLOAD_DECRYPT); + }); + + it('renders defaults', () => { + expect(component).to.be.ok; + }); + + it('renders DecryptRequest component', () => { + expect(component.find('Connect(DecryptRequest)')).to.have.length(1); + }); + }); }); diff --git a/js/src/shell/Signer/components/TransactionPendingForm/transactionPendingForm.js b/js/src/shell/Signer/components/TransactionPendingForm/transactionPendingForm.js index a31f8c4df..106c38fb5 100644 --- a/js/src/shell/Signer/components/TransactionPendingForm/transactionPendingForm.js +++ b/js/src/shell/Signer/components/TransactionPendingForm/transactionPendingForm.js @@ -41,6 +41,9 @@ export default class TransactionPendingForm extends Component { }), PropTypes.shape({ data: PropTypes.string.isRequired + }), + PropTypes.shape({ + decrypt: PropTypes.string.isRequired }) ]).isRequired }; diff --git a/js/src/shell/Signer/components/TransactionPendingFormConfirm/transactionPendingFormConfirm.js b/js/src/shell/Signer/components/TransactionPendingFormConfirm/transactionPendingFormConfirm.js index d68da955a..fb7531a86 100644 --- a/js/src/shell/Signer/components/TransactionPendingFormConfirm/transactionPendingFormConfirm.js +++ b/js/src/shell/Signer/components/TransactionPendingFormConfirm/transactionPendingFormConfirm.js @@ -22,7 +22,7 @@ import ReactTooltip from 'react-tooltip'; import { Button, Form, Input, IdentityIcon, QrCode, QrScan } from '@parity/ui'; -import { generateTxQr, generateDataQr } from '~/shell/Signer/utils/qrscan'; +import { generateTxQr, generateDecryptQr, generateDataQr } from '~/shell/Signer/utils/qrscan'; import styles from './transactionPendingFormConfirm.css'; @@ -344,7 +344,7 @@ export default class TransactionPendingFormConfirm extends Component { return ( ); } @@ -396,8 +396,8 @@ export default class TransactionPendingFormConfirm extends Component { ); } - onScanTx = (signature) => { - const { chainId, rlp, tx, data } = this.state.qr; + onScan = (signature) => { + const { chainId, rlp, tx, data, decrypt } = this.state.qr; if (signature && signature.substr(0, 2) !== '0x') { signature = `0x${signature}`; @@ -417,6 +417,16 @@ export default class TransactionPendingFormConfirm extends Component { return; } + if (decrypt) { + this.props.onConfirm({ + decrypted: { + decrypt, + msg: signature + } + }); + return; + } + this.props.onConfirm({ dataSigned: { data, @@ -491,7 +501,7 @@ export default class TransactionPendingFormConfirm extends Component { generateQr = () => { const { api } = this.context; const { netVersion, dataToSign } = this.props; - const { transaction, data } = dataToSign; + const { transaction, data, decrypt } = dataToSign; const setState = qr => { this.setState({ qr }); }; @@ -501,6 +511,11 @@ export default class TransactionPendingFormConfirm extends Component { return; } + if (decrypt) { + generateDecryptQr(decrypt).then(setState); + return; + } + generateDataQr(data).then(setState); } @@ -539,7 +554,7 @@ export default class TransactionPendingFormConfirm extends Component { const { account, dataToSign } = this.props; const { qr } = this.state; - if (dataToSign.data && qr && !qr.value) { + if ((dataToSign.data || dataToSign.decrypt) && qr && !qr.value) { this.generateQr(); return; } diff --git a/js/src/shell/Signer/utils/qrscan.js b/js/src/shell/Signer/utils/qrscan.js index 6055cd97e..6f6b0a82b 100644 --- a/js/src/shell/Signer/utils/qrscan.js +++ b/js/src/shell/Signer/utils/qrscan.js @@ -121,6 +121,16 @@ export function generateDataQr (data) { }); } +export function generateDecryptQr (data) { + return Promise.resolve({ + decrypt: data, + value: JSON.stringify({ + action: 'decrypt', + data + }) + }); +} + export function generateTxQr (api, netVersion, transaction) { return createUnsignedTx(api, netVersion, transaction) .then((qr) => { diff --git a/js/src/ui/Container/Title/Actions/actions.js b/js/src/ui/Container/Title/Actions/actions.js new file mode 100644 index 000000000..0da706afa --- /dev/null +++ b/js/src/ui/Container/Title/Actions/actions.js @@ -0,0 +1,35 @@ +// Copyright 2015-2017 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 . + +import React, { PropTypes } from 'react'; + +import styles from '../title.css'; + +export default function Actions ({ actions }) { + if (!actions || !actions.length) { + return null; + } + + return ( +
+ { actions } +
+ ); +} + +Actions.propTypes = { + actions: PropTypes.array +}; diff --git a/js/src/ui/Container/Title/Actions/index.js b/js/src/ui/Container/Title/Actions/index.js new file mode 100644 index 000000000..912011fa1 --- /dev/null +++ b/js/src/ui/Container/Title/Actions/index.js @@ -0,0 +1,17 @@ +// Copyright 2015-2017 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 . + +export default from './actions'; diff --git a/js/src/ui/Container/Title/title.css b/js/src/ui/Container/Title/title.css index 64317600c..91ae4990f 100644 --- a/js/src/ui/Container/Title/title.css +++ b/js/src/ui/Container/Title/title.css @@ -21,6 +21,15 @@ $bylineMaxHeight: 2.4rem; $titleLineHeight: 2rem; $smallFontSize: 0.75rem; +.container { + display: flex; + flex-direction: row; + justify-content: space-between; +} + +.actions { +} + .byline, .description { color: $bylineColor; diff --git a/js/src/ui/Container/Title/title.js b/js/src/ui/Container/Title/title.js index c2543a581..bff3aa84c 100644 --- a/js/src/ui/Container/Title/title.js +++ b/js/src/ui/Container/Title/title.js @@ -18,24 +18,29 @@ import React, { PropTypes } from 'react'; import { nodeOrStringProptype } from '@parity/shared/util/proptypes'; +import Actions from './Actions'; import Byline from './Byline'; import Description from './Description'; import styles from './title.css'; -export default function Title ({ byline, className, description, title }) { +export default function Title ({ actions, byline, className, description, title }) { return (
-

- { title } -

- - +
+

+ { title } +

+ + +
+
); } Title.propTypes = { + actions: PropTypes.array, byline: nodeOrStringProptype(), className: PropTypes.string, description: nodeOrStringProptype(), diff --git a/js/src/views/Status/Peers/peers.css b/js/src/views/Status/Peers/peers.css index 854d91cb0..a164aea81 100644 --- a/js/src/views/Status/Peers/peers.css +++ b/js/src/views/Status/Peers/peers.css @@ -45,3 +45,16 @@ white-space: nowrap; } } + +.form { + align-items: center; + display: flex; + flex-direction: row; + float: right; + width: 40em; + + .input { + flex: 1; + margin-right: 1em; + } +} diff --git a/js/src/views/Status/Peers/peers.js b/js/src/views/Status/Peers/peers.js index 213eba461..abbb6e0d3 100644 --- a/js/src/views/Status/Peers/peers.js +++ b/js/src/views/Status/Peers/peers.js @@ -14,91 +14,327 @@ // You should have received a copy of the GNU General Public License // along with Parity. If not, see . -import React, { PropTypes } from 'react'; +import React, { Component, PropTypes } from 'react'; import { FormattedMessage } from 'react-intl'; +import { bindActionCreators } from 'redux'; import { connect } from 'react-redux'; -import { Container } from '@parity/ui'; - -import Peer from './Peer'; +import { showSnackbar } from '@parity/shared/redux/providers/snackbarActions'; +import { newError } from '@parity/shared/redux/actions'; +import { Button, Container, ContainerTitle, Input, ScrollableText, ShortenedHash } from '@parity/ui'; import styles from './peers.css'; -function Peers ({ peers }) { - return ( - - } - > -
- - - - - - - - - - - - - { - peers.map((peer, index) => { - return ( - - ); - }) - } - -
- - - - - - - - - - - - -
-
-
- ); -} +class Peers extends Component { + static contextTypes = { + api: PropTypes.object + }; -Peers.propTypes = { - peers: PropTypes.array.isRequired -}; + static propTypes = { + peers: PropTypes.array.isRequired, + newError: PropTypes.func, + showSnackbar: PropTypes.func + }; + + state = { + action: '', + formInput: '', + showForm: false + }; + + getActions () { + return [ +