diff --git a/js/src/redux/providers/signerMiddleware.js b/js/src/redux/providers/signerMiddleware.js index 869a7d802..282e93601 100644 --- a/js/src/redux/providers/signerMiddleware.js +++ b/js/src/redux/providers/signerMiddleware.js @@ -103,6 +103,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; @@ -153,7 +159,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; @@ -176,10 +182,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/util/qrscan.js b/js/src/util/qrscan.js index da73d5b8f..97fa70c84 100644 --- a/js/src/util/qrscan.js +++ b/js/src/util/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/views/Signer/components/DecryptRequest/decryptRequest.js b/js/src/views/Signer/components/DecryptRequest/decryptRequest.js index f4626c755..703582c9e 100644 --- a/js/src/views/Signer/components/DecryptRequest/decryptRequest.js +++ b/js/src/views/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/views/Signer/components/RequestPending/requestPending.spec.js b/js/src/views/Signer/components/RequestPending/requestPending.spec.js index 130a9d319..3c2d29cf0 100644 --- a/js/src/views/Signer/components/RequestPending/requestPending.spec.js +++ b/js/src/views/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/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/transactionPendingFormConfirm.js b/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/transactionPendingFormConfirm.js index 8d4e4a9cc..f6c4883a8 100644 --- a/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/transactionPendingFormConfirm.js +++ b/js/src/views/Signer/components/TransactionPendingForm/TransactionPendingFormConfirm/transactionPendingFormConfirm.js @@ -22,7 +22,7 @@ import { FormattedMessage } from 'react-intl'; import ReactTooltip from 'react-tooltip'; import { Form, Input, IdentityIcon, QrCode, QrScan } from '~/ui'; -import { generateTxQr, generateDataQr } from '~/util/qrscan'; +import { generateTxQr, generateDataQr, generateDecryptQr } from '~/util/qrscan'; import styles from './transactionPendingFormConfirm.css'; @@ -352,7 +352,7 @@ export default class TransactionPendingFormConfirm extends Component { return ( ); } @@ -404,8 +404,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}`; @@ -425,6 +425,16 @@ export default class TransactionPendingFormConfirm extends Component { return; } + if (decrypt) { + this.props.onConfirm({ + decrypted: { + decrypt, + msg: signature + } + }); + return; + } + this.props.onConfirm({ dataSigned: { data, @@ -499,7 +509,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 }); }; @@ -509,6 +519,11 @@ export default class TransactionPendingFormConfirm extends Component { return; } + if (decrypt) { + generateDecryptQr(decrypt).then(setState); + return; + } + generateDataQr(data).then(setState); } @@ -547,7 +562,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/views/Signer/components/TransactionPendingForm/transactionPendingForm.js b/js/src/views/Signer/components/TransactionPendingForm/transactionPendingForm.js index e176b97e9..e7248e1ab 100644 --- a/js/src/views/Signer/components/TransactionPendingForm/transactionPendingForm.js +++ b/js/src/views/Signer/components/TransactionPendingForm/transactionPendingForm.js @@ -40,6 +40,9 @@ export default class TransactionPendingForm extends Component { }), PropTypes.shape({ data: PropTypes.string.isRequired + }), + PropTypes.shape({ + decrypt: PropTypes.string.isRequired }) ]).isRequired }; diff --git a/rpc/src/v1/impls/signer.rs b/rpc/src/v1/impls/signer.rs index 569d9f4db..0e09d1db2 100644 --- a/rpc/src/v1/impls/signer.rs +++ b/rpc/src/v1/impls/signer.rs @@ -201,8 +201,10 @@ impl Signer for SignerClient { Err(err) => Err(errors::invalid_params("Invalid signature received.", err)), } }, - // TODO [ToDr]: Decrypt - pass through? - _ => Err(errors::unimplemented(Some("Non-transaction requests does not support RAW signing yet.".into()))), + ConfirmationPayload::Decrypt(_address, _data) => { + // TODO [ToDr]: Decrypt can we verify if the answer is correct? + Ok(ConfirmationResponse::Decrypt(bytes)) + }, }; if let Ok(ref response) = result { signer.request_confirmed(id, Ok(response.clone())); diff --git a/rpc/src/v1/tests/mocked/signer.rs b/rpc/src/v1/tests/mocked/signer.rs index c0cc7ef3e..d9f7b96a9 100644 --- a/rpc/src/v1/tests/mocked/signer.rs +++ b/rpc/src/v1/tests/mocked/signer.rs @@ -28,7 +28,7 @@ use jsonrpc_core::IoHandler; use v1::{SignerClient, Signer, Origin}; use v1::metadata::Metadata; use v1::tests::helpers::TestMinerService; -use v1::types::H520; +use v1::types::{Bytes as RpcBytes, H520}; use v1::helpers::{SigningQueue, SignerService, FilledTransactionRequest, ConfirmationPayload}; use v1::helpers::dispatch::{FullDispatcher, eth_data_hash}; @@ -479,7 +479,6 @@ fn should_confirm_sign_transaction_with_rlp() { assert_eq!(tester.miner.imported_transactions.lock().len(), 0); } - #[test] fn should_confirm_data_sign_with_signature() { // given @@ -510,6 +509,34 @@ fn should_confirm_data_sign_with_signature() { assert_eq!(tester.miner.imported_transactions.lock().len(), 0); } +#[test] +fn should_confirm_decrypt_with_phrase() { + // given + let tester = signer_tester(); + let address = tester.accounts.new_account("test").unwrap(); + tester.signer.add_request(ConfirmationPayload::Decrypt( + address, + vec![1, 2, 3, 4].into(), + ), Origin::Unknown).unwrap(); + assert_eq!(tester.signer.requests().len(), 1); + + let decrypted = serde_json::to_string(&RpcBytes::new(b"phrase".to_vec())).unwrap(); + + // when + let request = r#"{ + "jsonrpc":"2.0", + "method":"signer_confirmRequestRaw", + "params":["0x1", "#.to_owned() + &decrypted + r#"], + "id":1 + }"#; + let response = r#"{"jsonrpc":"2.0","result":"#.to_owned() + &decrypted + r#","id":1}"#; + + // then + assert_eq!(tester.io.handle_request_sync(&request), Some(response.to_owned())); + assert_eq!(tester.signer.requests().len(), 0); + assert_eq!(tester.miner.imported_transactions.lock().len(), 0); +} + #[test] fn should_generate_new_token() { // given