From acaa40e22198ba80349cbc2f865822a50ff0e7c4 Mon Sep 17 00:00:00 2001 From: Jaco Greeff Date: Sun, 30 Oct 2016 09:54:21 +0100 Subject: [PATCH] Import raw private key (#2945) * Initial selection screen * UI in-place * Make actual import API calls * Simplify value checking logic --- .../CreationType/creationType.js | 3 + .../CreateAccount/NewAccount/newAccount.js | 2 + js/src/modals/CreateAccount/RawKey/index.js | 17 ++ js/src/modals/CreateAccount/RawKey/rawKey.js | 189 ++++++++++++++++++ js/src/modals/CreateAccount/createAccount.js | 51 ++++- 5 files changed, 255 insertions(+), 7 deletions(-) create mode 100644 js/src/modals/CreateAccount/RawKey/index.js create mode 100644 js/src/modals/CreateAccount/RawKey/rawKey.js diff --git a/js/src/modals/CreateAccount/CreationType/creationType.js b/js/src/modals/CreateAccount/CreationType/creationType.js index 866b33236..fe982a297 100644 --- a/js/src/modals/CreateAccount/CreationType/creationType.js +++ b/js/src/modals/CreateAccount/CreationType/creationType.js @@ -50,6 +50,9 @@ export default class CreationType extends Component { + ); diff --git a/js/src/modals/CreateAccount/NewAccount/newAccount.js b/js/src/modals/CreateAccount/NewAccount/newAccount.js index 282efb5ab..323152213 100644 --- a/js/src/modals/CreateAccount/NewAccount/newAccount.js +++ b/js/src/modals/CreateAccount/NewAccount/newAccount.js @@ -26,6 +26,8 @@ import styles from '../createAccount.css'; const ERRORS = { noName: 'you need to specify a valid name for the account', noPhrase: 'you need to specify the recovery phrase', + noKey: 'you need to provide the raw private key', + invalidKey: 'the raw key needs to be hex, 64 characters in length', invalidPassword: 'you need to specify a password >= 8 characters', noMatchPassword: 'the supplied passwords does not match' }; diff --git a/js/src/modals/CreateAccount/RawKey/index.js b/js/src/modals/CreateAccount/RawKey/index.js new file mode 100644 index 000000000..6e372f475 --- /dev/null +++ b/js/src/modals/CreateAccount/RawKey/index.js @@ -0,0 +1,17 @@ +// Copyright 2015, 2016 Ethcore (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 './rawKey'; diff --git a/js/src/modals/CreateAccount/RawKey/rawKey.js b/js/src/modals/CreateAccount/RawKey/rawKey.js new file mode 100644 index 000000000..6e03874d8 --- /dev/null +++ b/js/src/modals/CreateAccount/RawKey/rawKey.js @@ -0,0 +1,189 @@ +// Copyright 2015, 2016 Ethcore (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, { Component, PropTypes } from 'react'; + +import { Form, Input } from '../../../ui'; + +import styles from '../createAccount.css'; + +import { ERRORS } from '../NewAccount'; + +export default class RawKey extends Component { + static contextTypes = { + api: PropTypes.object.isRequired + } + + static propTypes = { + onChange: PropTypes.func.isRequired + } + + state = { + rawKey: '', + rawKeyError: ERRORS.noKey, + accountName: '', + accountNameError: ERRORS.noName, + passwordHint: '', + password1: '', + password1Error: ERRORS.invalidPassword, + password2: '', + password2Error: ERRORS.noMatchPassword, + isValidPass: false, + isValidName: false, + isValidKey: false + } + + componentWillMount () { + this.props.onChange(false, {}); + } + + render () { + const { accountName, accountNameError, passwordHint, password1, password1Error, password2, password2Error, rawKey, rawKeyError } = this.state; + + return ( +
+ + + +
+
+ +
+
+ +
+
+
+ ); + } + + updateParent = () => { + const { isValidName, isValidPass, isValidKey, accountName, passwordHint, password1, rawKey } = this.state; + const isValid = isValidName && isValidPass && isValidKey; + + this.props.onChange(isValid, { + name: accountName, + passwordHint, + password: password1, + rawKey + }); + } + + onEditPasswordHint = (event, value) => { + this.setState({ + passwordHint: value + }); + } + + onEditKey = (event) => { + const { api } = this.context; + const rawKey = event.target.value; + let rawKeyError = null; + + console.log(rawKey.length, rawKey); + + if (!rawKey || !rawKey.trim().length) { + rawKeyError = ERRORS.noKey; + } else if (rawKey.substr(0, 2) !== '0x' || rawKey.substr(2).length !== 64 || !api.util.isHex(rawKey)) { + rawKeyError = ERRORS.invalidKey; + } + + this.setState({ + rawKey, + rawKeyError, + isValidKey: !rawKeyError + }, this.updateParent); + } + + onEditAccountName = (event) => { + const accountName = event.target.value; + let accountNameError = null; + + if (!accountName || accountName.trim().length < 2) { + accountNameError = ERRORS.noName; + } + + this.setState({ + accountName, + accountNameError, + isValidName: !accountNameError + }, this.updateParent); + } + + onEditPassword1 = (event) => { + const value = event.target.value; + let error1 = null; + let error2 = null; + + if (!value || value.trim().length < 8) { + error1 = ERRORS.invalidPassword; + } + + if (value !== this.state.password2) { + error2 = ERRORS.noMatchPassword; + } + + this.setState({ + password1: value, + password1Error: error1, + password2Error: error2, + isValidPass: !error1 && !error2 + }, this.updateParent); + } + + onEditPassword2 = (event) => { + const value = event.target.value; + let error2 = null; + + if (value !== this.state.password1) { + error2 = ERRORS.noMatchPassword; + } + + this.setState({ + password2: value, + password2Error: error2, + isValidPass: !error2 + }, this.updateParent); + } +} diff --git a/js/src/modals/CreateAccount/createAccount.js b/js/src/modals/CreateAccount/createAccount.js index e4b502571..e0808b47b 100644 --- a/js/src/modals/CreateAccount/createAccount.js +++ b/js/src/modals/CreateAccount/createAccount.js @@ -29,6 +29,7 @@ import CreationType from './CreationType'; import NewAccount from './NewAccount'; import NewGeth from './NewGeth'; import NewImport from './NewImport'; +import RawKey from './RawKey'; import RecoveryPhrase from './RecoveryPhrase'; const TITLES = { @@ -58,6 +59,7 @@ export default class CreateAccount extends Component { passwordHint: null, password: null, phrase: null, + rawKey: null, json: null, canCreate: false, createType: null, @@ -110,6 +112,11 @@ export default class CreateAccount extends Component { ); + } else if (createType === 'fromRaw') { + return ( + + ); } return ( @@ -220,6 +227,28 @@ export default class CreateAccount extends Component { canCreate: true }); + this.newError(error); + }); + } else if (createType === 'fromRaw') { + return api.personal + .newAccountFromSecret(this.state.rawKey, this.state.password) + .then((address) => { + this.setState({ address }); + return api.personal + .setAccountName(address, this.state.name) + .then(() => api.personal.setAccountMeta(address, { passwordHint: this.state.passwordHint })); + }) + .then(() => { + this.onNext(); + this.props.onUpdate && this.props.onUpdate(); + }) + .catch((error) => { + console.error('onCreate', error); + + this.setState({ + canCreate: true + }); + this.newError(error); }); } else if (createType === 'fromGeth') { @@ -288,27 +317,35 @@ export default class CreateAccount extends Component { }); } - onChangeDetails = (valid, { name, passwordHint, address, password, phrase }) => { + onChangeDetails = (canCreate, { name, passwordHint, address, password, phrase, rawKey }) => { this.setState({ - canCreate: valid, + canCreate, name, passwordHint, address, password, - phrase + phrase, + rawKey }); } - onChangeGeth = (valid, gethAddresses) => { + onChangeRaw = (canCreate, rawKey) => { this.setState({ - canCreate: valid, + canCreate, + rawKey + }); + } + + onChangeGeth = (canCreate, gethAddresses) => { + this.setState({ + canCreate, gethAddresses }); } - onChangeWallet = (valid, { name, passwordHint, password, json }) => { + onChangeWallet = (canCreate, { name, passwordHint, password, json }) => { this.setState({ - canCreate: valid, + canCreate, name, passwordHint, password,