Add insert key functionality (#1683)
This commit is contained in:
parent
38faa6adb9
commit
7c1ff04500
138
packages/app-staking/src/Actions/Account/InjectKeys.tsx
Normal file
138
packages/app-staking/src/Actions/Account/InjectKeys.tsx
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
// Copyright 2017-2019 @polkadot/app-staking 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 { I18nProps } from '@polkadot/react-components/types';
|
||||||
|
import { KeypairType } from '@polkadot/util-crypto/types';
|
||||||
|
|
||||||
|
import React, { useContext, useEffect, useState } from 'react';
|
||||||
|
import { Button, Dropdown, Icon, Input, Modal } from '@polkadot/react-components';
|
||||||
|
import { QueueContext } from '@polkadot/react-components/Status';
|
||||||
|
import keyring from '@polkadot/ui-keyring';
|
||||||
|
import { assert, u8aToHex } from '@polkadot/util';
|
||||||
|
import { keyExtractSuri, mnemonicValidate } from '@polkadot/util-crypto';
|
||||||
|
|
||||||
|
import translate from '../../translate';
|
||||||
|
|
||||||
|
interface Props extends I18nProps {
|
||||||
|
isOpen?: boolean;
|
||||||
|
onClose: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CRYPTO_MAP: Record<string, KeypairType[]> = {
|
||||||
|
aura: ['ed25519', 'sr25519'],
|
||||||
|
babe: ['sr25519'],
|
||||||
|
gran: ['ed25519'],
|
||||||
|
imon: ['ed25519', 'sr25519'],
|
||||||
|
para: ['sr25519']
|
||||||
|
};
|
||||||
|
|
||||||
|
const EMPTY_KEY = '0x';
|
||||||
|
|
||||||
|
function InjectKeys ({ isOpen = true, onClose, t }: Props): React.ReactElement<Props> | null {
|
||||||
|
const { queueRpc } = useContext(QueueContext);
|
||||||
|
// this needs to align with what is set as the first value in `type`
|
||||||
|
const [crypto, setCrypto] = useState<KeypairType>('sr25519');
|
||||||
|
const [publicKey, setPublicKey] = useState(EMPTY_KEY);
|
||||||
|
const [suri, setSuri] = useState('');
|
||||||
|
const [type, setType] = useState('babe');
|
||||||
|
|
||||||
|
useEffect((): void => {
|
||||||
|
setCrypto(CRYPTO_MAP[type][0]);
|
||||||
|
}, [type]);
|
||||||
|
|
||||||
|
useEffect((): void => {
|
||||||
|
try {
|
||||||
|
const { phrase } = keyExtractSuri(suri);
|
||||||
|
|
||||||
|
assert(mnemonicValidate(phrase), 'Invalid mnemonic phrase');
|
||||||
|
|
||||||
|
setPublicKey(u8aToHex(keyring.createFromUri(suri, {}, crypto).publicKey));
|
||||||
|
} catch (error) {
|
||||||
|
setPublicKey(EMPTY_KEY);
|
||||||
|
}
|
||||||
|
}, [crypto, suri]);
|
||||||
|
|
||||||
|
if (!isOpen) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const _onSubmit = (): void => {
|
||||||
|
queueRpc({
|
||||||
|
rpc: { section: 'author', method: 'insertKey' } as any,
|
||||||
|
values: [type, suri, publicKey]
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const _cryptoOptions = CRYPTO_MAP[type].map((value): { text: string; value: KeypairType } => ({
|
||||||
|
text: value === 'ed25519'
|
||||||
|
? t('ed25519, Edwards')
|
||||||
|
: t('sr15519, Schnorrkel'),
|
||||||
|
value
|
||||||
|
}));
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
dimmer='inverted'
|
||||||
|
open
|
||||||
|
size='small'
|
||||||
|
>
|
||||||
|
<Modal.Header>
|
||||||
|
{t('Inject Keys')}
|
||||||
|
</Modal.Header>
|
||||||
|
<Modal.Content>
|
||||||
|
<Input
|
||||||
|
isError={publicKey.length !== 66}
|
||||||
|
label={t('suri (seed & derivation)')}
|
||||||
|
onChange={setSuri}
|
||||||
|
value={suri}
|
||||||
|
/>
|
||||||
|
<Dropdown
|
||||||
|
label={t('key type to set')}
|
||||||
|
onChange={setType}
|
||||||
|
options={[
|
||||||
|
{ text: t('Aura'), value: 'aura' },
|
||||||
|
{ text: t('Babe'), value: 'babe' },
|
||||||
|
{ text: t('Grandpa'), value: 'gran' },
|
||||||
|
{ text: t('I\'m Online'), value: 'imon' },
|
||||||
|
{ text: t('Parachains'), value: 'para' }
|
||||||
|
]}
|
||||||
|
value={type}
|
||||||
|
/>
|
||||||
|
<Dropdown
|
||||||
|
isDisabled={_cryptoOptions.length === 1}
|
||||||
|
label={t('crypto type to use')}
|
||||||
|
onChange={setCrypto}
|
||||||
|
options={_cryptoOptions}
|
||||||
|
value={crypto}
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
isDisabled
|
||||||
|
label={t('generated public key')}
|
||||||
|
value={publicKey}
|
||||||
|
/>
|
||||||
|
<article className='warning'>
|
||||||
|
<div><Icon name='warning sign' />{t('This operation will submit the seed via an RPC call. Do not perform this operation on a public RPC node, but ensure that the node is local, connected to your validator and secure.')}</div>
|
||||||
|
</article>
|
||||||
|
</Modal.Content>
|
||||||
|
<Modal.Actions>
|
||||||
|
<Button.Group>
|
||||||
|
<Button
|
||||||
|
icon='cancel'
|
||||||
|
isNegative
|
||||||
|
label={t('Cancel')}
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
<Button.Or />
|
||||||
|
<Button
|
||||||
|
icon='sign-in'
|
||||||
|
isPrimary
|
||||||
|
label={t('Submit key')}
|
||||||
|
onClick={_onSubmit}
|
||||||
|
/>
|
||||||
|
</Button.Group>
|
||||||
|
</Modal.Actions>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default translate(InjectKeys);
|
||||||
@ -16,6 +16,7 @@ import { AddressCard, AddressInfo, AddressMini, AddressRow, Button, Menu, Online
|
|||||||
import { withCalls, withMulti } from '@polkadot/react-api';
|
import { withCalls, withMulti } from '@polkadot/react-api';
|
||||||
|
|
||||||
import BondExtra from './BondExtra';
|
import BondExtra from './BondExtra';
|
||||||
|
import InjectKeys from './InjectKeys';
|
||||||
import Nominate from './Nominate';
|
import Nominate from './Nominate';
|
||||||
import SetControllerAccount from './SetControllerAccount';
|
import SetControllerAccount from './SetControllerAccount';
|
||||||
import SetRewardDestination from './SetRewardDestination';
|
import SetRewardDestination from './SetRewardDestination';
|
||||||
@ -42,6 +43,7 @@ interface State {
|
|||||||
destination: number;
|
destination: number;
|
||||||
hexSessionId: string | null;
|
hexSessionId: string | null;
|
||||||
isBondExtraOpen: boolean;
|
isBondExtraOpen: boolean;
|
||||||
|
isInjectOpen: boolean;
|
||||||
isNominateOpen: boolean;
|
isNominateOpen: boolean;
|
||||||
isSetControllerAccountOpen: boolean;
|
isSetControllerAccountOpen: boolean;
|
||||||
isSetRewardDestinationOpen: boolean;
|
isSetRewardDestinationOpen: boolean;
|
||||||
@ -72,6 +74,7 @@ class Account extends React.PureComponent<Props, State> {
|
|||||||
destination: 0,
|
destination: 0,
|
||||||
hexSessionId: null,
|
hexSessionId: null,
|
||||||
isBondExtraOpen: false,
|
isBondExtraOpen: false,
|
||||||
|
isInjectOpen: false,
|
||||||
isNominateOpen: false,
|
isNominateOpen: false,
|
||||||
isSetControllerAccountOpen: false,
|
isSetControllerAccountOpen: false,
|
||||||
isSettingPopupOpen: false,
|
isSettingPopupOpen: false,
|
||||||
@ -122,7 +125,7 @@ class Account extends React.PureComponent<Props, State> {
|
|||||||
|
|
||||||
public render (): React.ReactNode {
|
public render (): React.ReactNode {
|
||||||
const { className, isSubstrateV2, t } = this.props;
|
const { className, isSubstrateV2, t } = this.props;
|
||||||
const { controllerId, hexSessionId, isBondExtraOpen, isStashValidating, isUnbondOpen, nominees, sessionIds, stashId } = this.state;
|
const { controllerId, hexSessionId, isBondExtraOpen, isInjectOpen, isStashValidating, isUnbondOpen, nominees, sessionIds, stashId } = this.state;
|
||||||
|
|
||||||
if (!stashId) {
|
if (!stashId) {
|
||||||
return null;
|
return null;
|
||||||
@ -160,6 +163,9 @@ class Account extends React.PureComponent<Props, State> {
|
|||||||
onClose={this.toggleUnbond}
|
onClose={this.toggleUnbond}
|
||||||
stashId={stashId}
|
stashId={stashId}
|
||||||
/>
|
/>
|
||||||
|
{isInjectOpen && (
|
||||||
|
<InjectKeys onClose={this.toggleInject} />
|
||||||
|
)}
|
||||||
{this.renderSetValidatorPrefs()}
|
{this.renderSetValidatorPrefs()}
|
||||||
{this.renderNominate()}
|
{this.renderNominate()}
|
||||||
{this.renderSetControllerAccount()}
|
{this.renderSetControllerAccount()}
|
||||||
@ -418,9 +424,9 @@ class Account extends React.PureComponent<Props, State> {
|
|||||||
{t('Change validator preferences')}
|
{t('Change validator preferences')}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
}
|
}
|
||||||
{(!!sessionIds.length || (isSubstrateV2 && hexSessionId !== '0x')) &&
|
{!isStashNominating && (!!sessionIds.length || (isSubstrateV2 && hexSessionId !== '0x')) &&
|
||||||
<Menu.Item onClick={this.toggleSetSessionAccount}>
|
<Menu.Item onClick={this.toggleSetSessionAccount}>
|
||||||
{isSubstrateV2 ? t('Change session keys') : t('Change session account')}
|
{isSubstrateV2 ? t('Rotate session keys') : t('Change session account')}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
}
|
}
|
||||||
{isStashNominating &&
|
{isStashNominating &&
|
||||||
@ -428,6 +434,11 @@ class Account extends React.PureComponent<Props, State> {
|
|||||||
{t('Change nominee(s)')}
|
{t('Change nominee(s)')}
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
}
|
}
|
||||||
|
{!isStashNominating && isSubstrateV2 &&
|
||||||
|
<Menu.Item onClick={this.toggleInject}>
|
||||||
|
{t('Inject session keys (advanced)')}
|
||||||
|
</Menu.Item>
|
||||||
|
}
|
||||||
</Menu>
|
</Menu>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -507,6 +518,12 @@ class Account extends React.PureComponent<Props, State> {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private toggleInject = (): void => {
|
||||||
|
this.setState(({ isInjectOpen }): Pick<State, never> => ({
|
||||||
|
isInjectOpen: !isInjectOpen
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
private toggleNominate = (): void => {
|
private toggleNominate = (): void => {
|
||||||
this.setState(({ isNominateOpen }): Pick<State, never> => ({
|
this.setState(({ isNominateOpen }): Pick<State, never> => ({
|
||||||
isNominateOpen: !isNominateOpen
|
isNominateOpen: !isNominateOpen
|
||||||
|
|||||||
@ -14,8 +14,11 @@ import Button from '../Button';
|
|||||||
import Icon from '../Icon';
|
import Icon from '../Icon';
|
||||||
import translate from '../translate';
|
import translate from '../translate';
|
||||||
import { classes } from '../util';
|
import { classes } from '../util';
|
||||||
|
import QueueContext from './Context';
|
||||||
import { STATUS_COMPLETE } from './constants';
|
import { STATUS_COMPLETE } from './constants';
|
||||||
|
|
||||||
|
export { QueueContext };
|
||||||
|
|
||||||
interface Props extends I18nProps {
|
interface Props extends I18nProps {
|
||||||
stqueue?: QueueStatus[];
|
stqueue?: QueueStatus[];
|
||||||
txqueue?: QueueTx[];
|
txqueue?: QueueTx[];
|
||||||
@ -188,7 +191,7 @@ export default translate(
|
|||||||
top: 0.25rem;
|
top: 0.25rem;
|
||||||
width: 20rem;
|
width: 20rem;
|
||||||
z-index: 1001;
|
z-index: 1001;
|
||||||
|
|
||||||
.dismiss {
|
.dismiss {
|
||||||
margin-bottom: 0.25rem;
|
margin-bottom: 0.25rem;
|
||||||
}
|
}
|
||||||
@ -227,11 +230,11 @@ export default translate(
|
|||||||
line-height: 1;
|
line-height: 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.padded {
|
.padded {
|
||||||
padding: 0.25rem 0 0 0 !important;
|
padding: 0.25rem 0 0 0 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
i.close {
|
i.close {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0.25rem;
|
top: 0.25rem;
|
||||||
|
|||||||
@ -60,6 +60,11 @@ export default createGlobalStyle`
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.error,
|
||||||
|
&.warning {
|
||||||
|
margin-left: 2.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
&.error {
|
&.error {
|
||||||
background: #fff6f6;
|
background: #fff6f6;
|
||||||
border-color: #e0b4b4;
|
border-color: #e0b4b4;
|
||||||
|
|||||||
@ -486,6 +486,8 @@ class Signer extends React.PureComponent<Props, State> {
|
|||||||
const { api } = this.props;
|
const { api } = this.props;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
assert(isFunction((api.rpc as any)[section] && (api.rpc as any)[section][method]), `api.rpc.${section}.${method} does not exist`);
|
||||||
|
|
||||||
const result = await (api.rpc as any)[section][method](...values);
|
const result = await (api.rpc as any)[section][method](...values);
|
||||||
|
|
||||||
console.log('submitRpc: result ::', format(result));
|
console.log('submitRpc: result ::', format(result));
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user