feat: support import with mini secret key (#2944)

* feat: support import with mini secret key

* fix validation check

* re-order import

* reoder imports

* refactor

* validation logic

* renaming

* remove duplicated

* fix lint

* Update packages/page-accounts/src/Accounts/PasswordInput.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* Update packages/page-accounts/src/Accounts/PasswordInput.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* Update packages/page-accounts/src/Accounts/modals/Create.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

* Update packages/page-accounts/src/Accounts/modals/Qr.tsx

Co-authored-by: Jaco Greeff <jacogr@gmail.com>

Co-authored-by: Jaco Greeff <jacogr@gmail.com>
This commit is contained in:
Hanwen Cheng 2020-06-17 08:13:59 +02:00 committed by GitHub
parent 206ba75499
commit e790db93b0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 129 additions and 45 deletions

View File

@ -0,0 +1,76 @@
// Copyright 2017-2020 @polkadot/app-accounts authors & contributors
// This software may be modified and distributed under the terms
// of the Apache-2.0 license. See the LICENSE file for details.
import { Modal, Password } from '@polkadot/react-components';
import keyring from '@polkadot/ui-keyring';
import React, { useCallback, useState } from 'react';
import { useTranslation } from '../translate';
type Props = {
password: string;
onChange: (password: string, isPasswordValid: boolean) => void;
onEnter: () => void;
}
export default function PasswordInput ({ onChange, onEnter, password }: Props): React.ReactElement {
const { t } = useTranslation();
const [isPassValid, setPassValid] = useState<boolean>(false);
const [{ isPass2Valid, password2 }, setPassword2] = useState({ isPass2Valid: false, password2: '' });
const _onPasswordChange = useCallback(
(password: string) => {
const isPassValid = keyring.isPassValid(password);
setPassValid(isPassValid);
const isValid = isPassValid && isPass2Valid;
onChange(password, isValid);
},
[onChange, isPass2Valid]
);
const onPassword2Change = useCallback(
(password2: string) => {
const isPass2Valid = keyring.isPassValid(password2) && (password2 === password);
setPassword2({ isPass2Valid, password2 });
const isValid = isPassValid && isPass2Valid;
onChange(password, isValid);
},
[password, onChange, isPassValid]
);
return (
<Modal.Columns>
<Modal.Column>
<Password
className='full'
help={t<string>('This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).')}
isError={!isPassValid}
label={t<string>('password')}
onChange={_onPasswordChange}
onEnter={onEnter}
value={password}
/>
<Password
className='full'
help={t<string>('Verify the password entered above.')}
isError={!isPass2Valid}
label={t<string>('password (repeat)')}
onChange={onPassword2Change}
onEnter={onEnter}
value={password2}
/>
</Modal.Column>
<Modal.Column>
<p>{t<string>('The password and password confirmation for this account. This is required to authenticate any transactions made and to encrypt the keypair.')}</p>
</Modal.Column>
</Modal.Columns>
);
}

View File

@ -11,7 +11,7 @@ import FileSaver from 'file-saver';
import React, { useCallback, useMemo, useState } from 'react';
import styled from 'styled-components';
import { DEV_PHRASE } from '@polkadot/keyring/defaults';
import { AddressRow, Button, Dropdown, Expander, Input, InputAddress, Modal, Password } from '@polkadot/react-components';
import { AddressRow, Button, Dropdown, Expander, Input, InputAddress, Modal } from '@polkadot/react-components';
import { useApi, useToggle } from '@polkadot/react-hooks';
import keyring from '@polkadot/ui-keyring';
import uiSettings from '@polkadot/ui-settings';
@ -20,6 +20,7 @@ import { keyExtractSuri, mnemonicGenerate, mnemonicValidate, randomAsU8a } from
import { getEnvironment } from '@polkadot/react-api/util';
import { useTranslation } from '../../translate';
import PasswordInput from '../PasswordInput';
import CreateConfirmation from './CreateConfirmation';
interface Props extends ModalProps {
@ -166,9 +167,8 @@ function Create ({ className = '', onClose, onStatusChange, seed: propsSeed, typ
const [{ address, deriveError, derivePath, isSeedValid, pairType, seed, seedType }, setAddress] = useState<AddressState>(generateSeed(propsSeed, '', propsSeed ? 'raw' : 'bip', propsType));
const [isConfirmationOpen, toggleConfirmation] = useToggle();
const [{ isNameValid, name }, setName] = useState({ isNameValid: false, name: '' });
const [{ isPassValid, password }, setPassword] = useState({ isPassValid: false, password: '' });
const [{ isPass2Valid, password2 }, setPassword2] = useState({ isPass2Valid: false, password2: '' });
const isValid = !!address && !deriveError && isNameValid && isPassValid && isPass2Valid && isSeedValid;
const [{ isPasswordValid, password }, setPassword] = useState({ isPasswordValid: false, password: '' });
const isValid = !!address && !deriveError && isNameValid && isPasswordValid && isSeedValid;
const seedOpt = useMemo(() => (
isDevelopment
? [{ text: t<string>('Development'), value: 'dev' }]
@ -178,16 +178,6 @@ function Create ({ className = '', onClose, onStatusChange, seed: propsSeed, typ
{ text: t<string>('Raw seed'), value: 'raw' }
), [isDevelopment, t]);
const _onChangePass = useCallback(
(password: string) => setPassword({ isPassValid: keyring.isPassValid(password), password }),
[]
);
const _onChangePass2 = useCallback(
(password2: string) => setPassword2({ isPass2Valid: keyring.isPassValid(password2) && (password2 === password), password2 }),
[password]
);
const _onChangeDerive = useCallback(
(newDerivePath: string) => setAddress(updateAddress(seed, newDerivePath, seedType, pairType)),
[pairType, seed, seedType]
@ -217,6 +207,11 @@ function Create ({ className = '', onClose, onStatusChange, seed: propsSeed, typ
[]
);
const _onPasswordChange = useCallback(
(password: string, isPasswordValid: boolean) => setPassword({ isPasswordValid, password }),
[]
);
const _onCommit = useCallback(
(): void => {
if (!isValid) {
@ -306,31 +301,10 @@ function Create ({ className = '', onClose, onStatusChange, seed: propsSeed, typ
<p>{t<string>('The secret seed value for this account. Ensure that you keep this in a safe place, with access to the seed you can re-create the account.')}</p>
</Modal.Column>
</Modal.Columns>
<Modal.Columns>
<Modal.Column>
<Password
className='full'
help={t<string>('This password is used to encrypt your private key. It must be strong and unique! You will need it to sign transactions with this account. You can recover this account using this password together with the backup file (generated in the next step).')}
isError={!isPassValid}
label={t<string>('password')}
onChange={_onChangePass}
onEnter={_onCommit}
value={password}
/>
<Password
className='full'
help={t<string>('Verify the password entered above.')}
isError={!isPass2Valid}
label={t<string>('password (repeat)')}
onChange={_onChangePass2}
onEnter={_onCommit}
value={password2}
/>
</Modal.Column>
<Modal.Column>
<p>{t<string>('The password and password confirmation for this account. This is required to authenticate any transactions made and to encrypt the keypair.')}</p>
</Modal.Column>
</Modal.Columns>
<PasswordInput
onChange={_onPasswordChange}
onEnter={_onCommit}
password={password}/>
<Expander
className='accounts--Creator-advanced'
isOpen

View File

@ -10,6 +10,7 @@ import { AddressRow, Button, Input, InputAddress, Modal } from '@polkadot/react-
import { QrScanAddress } from '@polkadot/react-qr';
import keyring from '@polkadot/ui-keyring';
import PasswordInput from '../PasswordInput';
import { useTranslation } from '../../translate';
interface Scanned {
@ -27,31 +28,57 @@ function QrModal ({ className = '', onClose, onStatusChange }: Props): React.Rea
const { t } = useTranslation();
const [{ isNameValid, name }, setName] = useState({ isNameValid: false, name: '' });
const [scanned, setScanned] = useState<Scanned | null>(null);
const [isAddress, setIsAddress] = useState<boolean>(false);
const [address, setAddress] = useState<string>('');
const [{ isPasswordValid, password }, setPassword] = useState({ isPasswordValid: false, password: '' });
const isValid = !!address && isNameValid && (isAddress || isPasswordValid);
const _onNameChange = useCallback(
(name: string) => setName({ isNameValid: !!name.trim(), name }),
[]
);
const _onPasswordChange = useCallback(
(password: string, isPasswordValid: boolean) => setPassword({ isPasswordValid, password }),
[]
);
const _onScan = useCallback(
(scanned: Scanned): void => {
setScanned(scanned);
const { content, genesisHash } = scanned;
setIsAddress(scanned.isAddress);
if (scanned.isAddress) {
setAddress(content);
} else {
const pair = keyring.createFromUri(content, { genesisHash, name: name.trim() }, 'sr25519');
setAddress(pair.address);
}
if (scanned.name) {
_onNameChange(scanned.name);
}
},
[_onNameChange]
[_onNameChange, name]
);
const _onSave = useCallback(
(): void => {
if (!scanned || !isNameValid) {
if (!scanned || !isValid) {
return;
}
// TODO Handle where !isAddress
const { content, genesisHash } = scanned;
const { content, genesisHash, isAddress } = scanned;
if (isAddress) {
keyring.addExternal(content, { genesisHash, name: name.trim() });
} else {
keyring.addUri(content, password, { genesisHash, name: name.trim() }, 'sr25519');
}
keyring.addExternal(content, { genesisHash, name: name.trim() });
InputAddress.setLastValue('account', content);
@ -64,7 +91,7 @@ function QrModal ({ className = '', onClose, onStatusChange }: Props): React.Rea
});
onClose();
},
[isNameValid, name, onClose, onStatusChange, scanned, t]
[isValid, name, onClose, onStatusChange, password, scanned, t]
);
return (
@ -103,6 +130,13 @@ function QrModal ({ className = '', onClose, onStatusChange }: Props): React.Rea
<p>{t<string>('The local name for this account. Changing this does not affect your on-line identity, so this is only used to indicate the name of the account locally.')}</p>
</Modal.Column>
</Modal.Columns>
{!isAddress && (
<PasswordInput
onChange={_onPasswordChange}
onEnter={_onSave}
password={password}
/>
)}
</>
)
: (
@ -122,7 +156,7 @@ function QrModal ({ className = '', onClose, onStatusChange }: Props): React.Rea
<Modal.Actions onCancel={onClose}>
<Button
icon='sign-in'
isDisabled={!scanned || !isNameValid}
isDisabled={!scanned || !isValid}
isPrimary
label={t<string>('Create')}
onClick={_onSave}