Decrypting for external accounts. (#5581)

This commit is contained in:
Tomasz Drwięga 2017-05-15 18:59:41 +02:00 committed by GitHub
parent 4df1772078
commit 879195397e
8 changed files with 103 additions and 15 deletions

View File

@ -103,6 +103,12 @@ export default class SignerMiddleware {
return this.confirmRawRequest(store, id, signature); return this.confirmRawRequest(store, id, signature);
} }
confirmDecryptedMsg (store, id, decrypted) {
const { msg } = decrypted;
return this.confirmRawRequest(store, id, msg);
}
confirmSignedTransaction (store, id, txSigned) { confirmSignedTransaction (store, id, txSigned) {
const { netVersion } = store.getState().nodeStatus; const { netVersion } = store.getState().nodeStatus;
const { signature, tx } = txSigned; const { signature, tx } = txSigned;
@ -153,7 +159,7 @@ export default class SignerMiddleware {
} }
onConfirmStart = (store, action) => { 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 handlePromise = this._createConfirmPromiseHandler(store, id);
const transaction = payload.sendTransaction || payload.signTransaction; 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) { if (dataSigned) {
return this.confirmSignedData(store, id, 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)); return handlePromise(this._api.signer.confirmRequest(id, { gas, gasPrice, condition }, password));
} }

View File

@ -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) { export function generateTxQr (api, netVersion, transaction) {
return createUnsignedTx(api, netVersion, transaction) return createUnsignedTx(api, netVersion, transaction)
.then((qr) => { .then((qr) => {

View File

@ -114,7 +114,7 @@ class DecryptRequest extends Component {
} }
renderActions () { renderActions () {
const { accounts, address, focus, isFinished, status } = this.props; const { accounts, address, focus, isFinished, status, data } = this.props;
const account = accounts[address]; const account = accounts[address];
if (isFinished) { if (isFinished) {
@ -153,15 +153,16 @@ class DecryptRequest extends Component {
onConfirm={ this.onConfirm } onConfirm={ this.onConfirm }
onReject={ this.onReject } onReject={ this.onReject }
className={ styles.actions } className={ styles.actions }
dataToSign={ { decrypt: data } }
/> />
); );
} }
onConfirm = (data) => { onConfirm = (data) => {
const { id } = this.props; 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 = () => { onReject = () => {

View File

@ -40,6 +40,12 @@ const PAYLOAD_SIGN = {
const PAYLOAD_SIGNTX = { const PAYLOAD_SIGNTX = {
signTransaction: TRANSACTION signTransaction: TRANSACTION
}; };
const PAYLOAD_DECRYPT = {
decrypt: {
address: ADDRESS,
msg: 'testing'
}
};
let component; let component;
let onConfirm; let onConfirm;
@ -109,4 +115,18 @@ describe('views/Signer/RequestPending', () => {
expect(component.find('Connect(TransactionPending)')).to.have.length(1); 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);
});
});
}); });

View File

@ -22,7 +22,7 @@ import { FormattedMessage } from 'react-intl';
import ReactTooltip from 'react-tooltip'; import ReactTooltip from 'react-tooltip';
import { Form, Input, IdentityIcon, QrCode, QrScan } from '~/ui'; 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'; import styles from './transactionPendingFormConfirm.css';
@ -352,7 +352,7 @@ export default class TransactionPendingFormConfirm extends Component {
return ( return (
<QrScan <QrScan
className={ styles.camera } className={ styles.camera }
onScan={ this.onScanTx } onScan={ this.onScan }
/> />
); );
} }
@ -404,8 +404,8 @@ export default class TransactionPendingFormConfirm extends Component {
); );
} }
onScanTx = (signature) => { onScan = (signature) => {
const { chainId, rlp, tx, data } = this.state.qr; const { chainId, rlp, tx, data, decrypt } = this.state.qr;
if (signature && signature.substr(0, 2) !== '0x') { if (signature && signature.substr(0, 2) !== '0x') {
signature = `0x${signature}`; signature = `0x${signature}`;
@ -425,6 +425,16 @@ export default class TransactionPendingFormConfirm extends Component {
return; return;
} }
if (decrypt) {
this.props.onConfirm({
decrypted: {
decrypt,
msg: signature
}
});
return;
}
this.props.onConfirm({ this.props.onConfirm({
dataSigned: { dataSigned: {
data, data,
@ -499,7 +509,7 @@ export default class TransactionPendingFormConfirm extends Component {
generateQr = () => { generateQr = () => {
const { api } = this.context; const { api } = this.context;
const { netVersion, dataToSign } = this.props; const { netVersion, dataToSign } = this.props;
const { transaction, data } = dataToSign; const { transaction, data, decrypt } = dataToSign;
const setState = qr => { const setState = qr => {
this.setState({ qr }); this.setState({ qr });
}; };
@ -509,6 +519,11 @@ export default class TransactionPendingFormConfirm extends Component {
return; return;
} }
if (decrypt) {
generateDecryptQr(decrypt).then(setState);
return;
}
generateDataQr(data).then(setState); generateDataQr(data).then(setState);
} }
@ -547,7 +562,7 @@ export default class TransactionPendingFormConfirm extends Component {
const { account, dataToSign } = this.props; const { account, dataToSign } = this.props;
const { qr } = this.state; const { qr } = this.state;
if (dataToSign.data && qr && !qr.value) { if ((dataToSign.data || dataToSign.decrypt) && qr && !qr.value) {
this.generateQr(); this.generateQr();
return; return;
} }

View File

@ -40,6 +40,9 @@ export default class TransactionPendingForm extends Component {
}), }),
PropTypes.shape({ PropTypes.shape({
data: PropTypes.string.isRequired data: PropTypes.string.isRequired
}),
PropTypes.shape({
decrypt: PropTypes.string.isRequired
}) })
]).isRequired ]).isRequired
}; };

View File

@ -201,8 +201,10 @@ impl<D: Dispatcher + 'static> Signer for SignerClient<D> {
Err(err) => Err(errors::invalid_params("Invalid signature received.", err)), Err(err) => Err(errors::invalid_params("Invalid signature received.", err)),
} }
}, },
// TODO [ToDr]: Decrypt - pass through? ConfirmationPayload::Decrypt(_address, _data) => {
_ => Err(errors::unimplemented(Some("Non-transaction requests does not support RAW signing yet.".into()))), // TODO [ToDr]: Decrypt can we verify if the answer is correct?
Ok(ConfirmationResponse::Decrypt(bytes))
},
}; };
if let Ok(ref response) = result { if let Ok(ref response) = result {
signer.request_confirmed(id, Ok(response.clone())); signer.request_confirmed(id, Ok(response.clone()));

View File

@ -28,7 +28,7 @@ use jsonrpc_core::IoHandler;
use v1::{SignerClient, Signer, Origin}; use v1::{SignerClient, Signer, Origin};
use v1::metadata::Metadata; use v1::metadata::Metadata;
use v1::tests::helpers::TestMinerService; 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::{SigningQueue, SignerService, FilledTransactionRequest, ConfirmationPayload};
use v1::helpers::dispatch::{FullDispatcher, eth_data_hash}; 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); assert_eq!(tester.miner.imported_transactions.lock().len(), 0);
} }
#[test] #[test]
fn should_confirm_data_sign_with_signature() { fn should_confirm_data_sign_with_signature() {
// given // given
@ -510,6 +509,34 @@ fn should_confirm_data_sign_with_signature() {
assert_eq!(tester.miner.imported_transactions.lock().len(), 0); 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] #[test]
fn should_generate_new_token() { fn should_generate_new_token() {
// given // given