Support external eth_sign (#5481)

* Display a QR for eth_sign requests.

* Support raw confirmation of eth_sign

* Fix ethkey issue on nightly.

* Fixing test.

* Fixing test.
This commit is contained in:
Tomasz Drwięga
2017-04-27 18:23:22 +02:00
committed by Gav Wood
parent 43175f17e4
commit 28dcbc6426
17 changed files with 313 additions and 93 deletions

View File

@@ -87,14 +87,20 @@ export default class SignerMiddleware {
return this._hwstore.signLedger(transaction);
})
.then((rawTx) => {
return this.confirmRawTransaction(store, id, rawTx);
return this.confirmRawRequest(store, id, rawTx);
});
}
confirmRawTransaction (store, id, rawTx) {
confirmRawRequest (store, id, rawData) {
const handlePromise = this._createConfirmPromiseHandler(store, id);
return handlePromise(this._api.signer.confirmRequestRaw(id, rawTx));
return handlePromise(this._api.signer.confirmRequestRaw(id, rawData));
}
confirmSignedData (store, id, dataSigned) {
const { signature } = dataSigned;
return this.confirmRawRequest(store, id, signature);
}
confirmSignedTransaction (store, id, txSigned) {
@@ -102,7 +108,7 @@ export default class SignerMiddleware {
const { signature, tx } = txSigned;
const { rlp } = createSignedTx(netVersion, signature, tx);
return this.confirmRawTransaction(store, id, rlp);
return this.confirmRawRequest(store, id, rlp);
}
confirmWalletTransaction (store, id, transaction, wallet, password) {
@@ -138,7 +144,7 @@ export default class SignerMiddleware {
return signer.signTransaction(txData);
})
.then((rawTx) => {
return this.confirmRawTransaction(store, id, rawTx);
return this.confirmRawRequest(store, id, rawTx);
})
.catch((error) => {
console.error(error.message);
@@ -147,7 +153,7 @@ export default class SignerMiddleware {
}
onConfirmStart = (store, action) => {
const { condition, gas = 0, gasPrice = 0, id, password, payload, txSigned, wallet } = action.payload;
const { condition, gas = 0, gasPrice = 0, id, password, payload, txSigned, dataSigned, wallet } = action.payload;
const handlePromise = this._createConfirmPromiseHandler(store, id);
const transaction = payload.sendTransaction || payload.signTransaction;
@@ -170,6 +176,11 @@ export default class SignerMiddleware {
}
}
// TODO [ToDr] Support eth_sign for external wallet (wallet && !transction)
if (dataSigned) {
return this.confirmSignedData(store, id, dataSigned);
}
return handlePromise(this._api.signer.confirmRequest(id, { gas, gasPrice, condition }, password));
}

View File

@@ -19,8 +19,8 @@ import Transaction from 'ethereumjs-tx';
import { inAddress, inHex, inNumber10 } from '~/api/format/input';
import { sha3 } from '~/api/util/sha3';
export function createUnsignedTx (api, netVersion, gasStore, transaction) {
const { data, from, gas, gasPrice, to, value } = gasStore.overrideTransaction(transaction);
export function createUnsignedTx (api, netVersion, transaction) {
const { data, from, gas, gasPrice, to, value } = transaction;
return api.parity
.nextNonce(from)
@@ -111,8 +111,18 @@ export function generateQr (from, tx, hash, rlp) {
});
}
export function generateTxQr (api, netVersion, gasStore, transaction) {
return createUnsignedTx(api, netVersion, gasStore, transaction)
export function generateDataQr (data) {
return Promise.resolve({
data,
value: JSON.stringify({
action: 'signData',
data
})
});
}
export function generateTxQr (api, netVersion, transaction) {
return createUnsignedTx(api, netVersion, transaction)
.then((qr) => {
qr.value = generateQr(transaction.from, qr.tx, qr.hash, qr.rlp);

View File

@@ -85,7 +85,7 @@ export default class RequestOrigin extends Component {
<span>
<FormattedMessage
id='signer.requestOrigin.rpc'
defaultMessage='via RPC {rpc}'
defaultMessage='via RPC {url}'
values={ {
url: (
<span className={ styles.url }>

View File

@@ -19,6 +19,8 @@ import React, { Component, PropTypes } from 'react';
import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import HardwareStore from '~/mobx/hardwareStore';
import Account from '../Account';
import TransactionPendingForm from '../TransactionPendingForm';
import RequestOrigin from '../RequestOrigin';
@@ -68,6 +70,8 @@ class SignRequest extends Component {
}
};
hardwareStore = HardwareStore.get(this.context.api);
componentWillMount () {
const { address, signerStore } = this.props;
@@ -155,8 +159,9 @@ class SignRequest extends Component {
}
renderActions () {
const { accounts, address, focus, isFinished, status } = this.props;
const account = accounts[address];
const { accounts, address, focus, isFinished, status, data } = this.props;
const account = accounts[address] || {};
const disabled = account.hardware && !this.hardwareStore.isConnected(address);
if (isFinished) {
if (status === 'confirmed') {
@@ -188,21 +193,23 @@ class SignRequest extends Component {
<TransactionPendingForm
account={ account }
address={ address }
disabled={ disabled }
focus={ focus }
isSending={ this.props.isSending }
netVersion={ this.props.netVersion }
onConfirm={ this.onConfirm }
onReject={ this.onReject }
className={ styles.actions }
dataToSign={ { data } }
/>
);
}
onConfirm = (data) => {
const { id } = this.props;
const { password } = data;
const { password, dataSigned, wallet } = data;
this.props.onConfirm({ id, password });
this.props.onConfirm({ id, password, dataSigned, wallet });
}
onReject = () => {

View File

@@ -98,7 +98,9 @@ class TransactionPending extends Component {
}
renderTransaction () {
const { accounts, className, focus, id, isSending, netVersion, origin, signerStore, transaction } = this.props;
const transaction = this.gasStore.overrideTransaction(this.props.transaction);
const { accounts, className, focus, id, isSending, netVersion, origin, signerStore } = this.props;
const { totalValue } = this.state;
const { balances, externalLink } = signerStore;
const { from, value } = transaction;
@@ -127,12 +129,11 @@ class TransactionPending extends Component {
address={ from }
disabled={ disabled }
focus={ focus }
gasStore={ this.gasStore }
isSending={ isSending }
netVersion={ netVersion }
onConfirm={ this.onConfirm }
onReject={ this.onReject }
transaction={ transaction }
dataToSign={ { transaction } }
/>
</div>
);

View File

@@ -22,7 +22,7 @@ import { FormattedMessage } from 'react-intl';
import ReactTooltip from 'react-tooltip';
import { Form, Input, IdentityIcon, QrCode, QrScan } from '~/ui';
import { generateTxQr } from '~/util/qrscan';
import { generateTxQr, generateDataQr } from '~/util/qrscan';
import styles from './transactionPendingFormConfirm.css';
@@ -40,11 +40,10 @@ export default class TransactionPendingFormConfirm extends Component {
address: PropTypes.string.isRequired,
disabled: PropTypes.bool,
focus: PropTypes.bool,
gasStore: PropTypes.object.isRequired,
netVersion: PropTypes.string.isRequired,
isSending: PropTypes.bool.isRequired,
onConfirm: PropTypes.func.isRequired,
transaction: PropTypes.object.isRequired
dataToSign: PropTypes.object.isRequired
};
static defaultProps = {
@@ -406,7 +405,7 @@ export default class TransactionPendingFormConfirm extends Component {
}
onScanTx = (signature) => {
const { chainId, rlp, tx } = this.state.qr;
const { chainId, rlp, tx, data } = this.state.qr;
if (signature && signature.substr(0, 2) !== '0x') {
signature = `0x${signature}`;
@@ -414,12 +413,22 @@ export default class TransactionPendingFormConfirm extends Component {
this.setState({ qrState: QR_COMPLETED });
if (tx) {
this.props.onConfirm({
txSigned: {
chainId,
rlp,
signature,
tx
}
});
return;
}
this.props.onConfirm({
txSigned: {
chainId,
rlp,
signature,
tx
dataSigned: {
data,
signature
}
});
}
@@ -487,13 +496,20 @@ export default class TransactionPendingFormConfirm extends Component {
});
}
generateTxQr = () => {
generateQr = () => {
const { api } = this.context;
const { netVersion, gasStore, transaction } = this.props;
generateTxQr(api, netVersion, gasStore, transaction).then((qr) => {
const { netVersion, dataToSign } = this.props;
const { transaction, data } = dataToSign;
const setState = qr => {
this.setState({ qr });
});
};
if (transaction) {
generateTxQr(api, netVersion, transaction).then(setState);
return;
}
generateDataQr(data).then(setState);
}
onKeyDown = (event) => {
@@ -528,19 +544,25 @@ export default class TransactionPendingFormConfirm extends Component {
readNonce = () => {
const { api } = this.context;
const { account } = this.props;
const { account, dataToSign } = this.props;
const { qr } = this.state;
if (!account || !account.external || !api.transport.isConnected) {
if (dataToSign.data && qr && !qr.value) {
this.generateQr();
return;
}
if (!account || !account.external || !api.transport.isConnected || !dataToSign.transaction) {
return;
}
return api.parity
.nextNonce(account.address)
.then((nonce) => {
const { qr } = this.state;
.then((newNonce) => {
const { nonce } = this.state.qr;
if (!qr.nonce || !nonce.eq(qr.nonce)) {
this.generateTxQr();
if (!nonce || !newNonce.eq(nonce)) {
this.generateQr();
}
});
}

View File

@@ -52,6 +52,7 @@ function render (address) {
address={ address }
onConfirm={ onConfirm }
isSending={ false }
dataToSign={ {} }
/>
);
instance = component.instance();

View File

@@ -30,12 +30,18 @@ export default class TransactionPendingForm extends Component {
className: PropTypes.string,
disabled: PropTypes.bool,
focus: PropTypes.bool,
gasStore: PropTypes.object.isRequired,
netVersion: PropTypes.string.isRequired,
isSending: PropTypes.bool.isRequired,
onConfirm: PropTypes.func.isRequired,
onReject: PropTypes.func.isRequired,
transaction: PropTypes.object.isRequired
dataToSign: PropTypes.oneOfType([
PropTypes.shape({
transaction: PropTypes.object.isRequired
}),
PropTypes.shape({
data: PropTypes.string.isRequired
})
]).isRequired
};
static defaultProps = {
@@ -59,7 +65,7 @@ export default class TransactionPendingForm extends Component {
}
renderForm () {
const { account, address, disabled, focus, gasStore, isSending, netVersion, onConfirm, onReject, transaction } = this.props;
const { account, address, disabled, focus, isSending, netVersion, onConfirm, onReject, dataToSign } = this.props;
if (this.state.isRejectOpen) {
return (
@@ -73,11 +79,10 @@ export default class TransactionPendingForm extends Component {
account={ account }
disabled={ disabled }
focus={ focus }
gasStore={ gasStore }
netVersion={ netVersion }
isSending={ isSending }
onConfirm={ onConfirm }
transaction={ transaction }
dataToSign={ dataToSign }
/>
);
}