Enable Parity Signer Message Signing (#1663)
* Enable Parity Signer to sign messages * Verify that message signature is correct * Type systems are awesome :)
This commit is contained in:
parent
5542791af8
commit
cf59688896
|
@ -0,0 +1,28 @@
|
||||||
|
import * as interfaces from './actionTypes';
|
||||||
|
import { TypeKeys } from './constants';
|
||||||
|
import { ISignedMessage } from 'libs/signing';
|
||||||
|
|
||||||
|
export type TSignMessageRequested = typeof signMessageRequested;
|
||||||
|
export function signMessageRequested(payload: string): interfaces.SignMessageRequestedAction {
|
||||||
|
return {
|
||||||
|
type: TypeKeys.SIGN_MESSAGE_REQUESTED,
|
||||||
|
payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSignLocalMessageSucceeded = typeof signLocalMessageSucceeded;
|
||||||
|
export function signLocalMessageSucceeded(
|
||||||
|
payload: ISignedMessage
|
||||||
|
): interfaces.SignLocalMessageSucceededAction {
|
||||||
|
return {
|
||||||
|
type: TypeKeys.SIGN_LOCAL_MESSAGE_SUCCEEDED,
|
||||||
|
payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TSignMessageFailed = typeof signMessageFailed;
|
||||||
|
export function signMessageFailed(): interfaces.SignMessageFailedAction {
|
||||||
|
return {
|
||||||
|
type: TypeKeys.SIGN_MESSAGE_FAILED
|
||||||
|
};
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { TypeKeys } from './constants';
|
||||||
|
import { ISignedMessage } from 'libs/signing';
|
||||||
|
|
||||||
|
export interface SignMessageRequestedAction {
|
||||||
|
type: TypeKeys.SIGN_MESSAGE_REQUESTED;
|
||||||
|
payload: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SignLocalMessageSucceededAction {
|
||||||
|
type: TypeKeys.SIGN_LOCAL_MESSAGE_SUCCEEDED;
|
||||||
|
payload: ISignedMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SignMessageFailedAction {
|
||||||
|
type: TypeKeys.SIGN_MESSAGE_FAILED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** Union Type ***/
|
||||||
|
export type MessageAction =
|
||||||
|
| SignMessageRequestedAction
|
||||||
|
| SignLocalMessageSucceededAction
|
||||||
|
| SignMessageFailedAction;
|
|
@ -0,0 +1,5 @@
|
||||||
|
export enum TypeKeys {
|
||||||
|
SIGN_MESSAGE_REQUESTED = 'SIGN_MESSAGE_REQUESTED',
|
||||||
|
SIGN_LOCAL_MESSAGE_SUCCEEDED = 'SIGN_LOCAL_MESSAGE_SUCCEEDED',
|
||||||
|
SIGN_MESSAGE_FAILED = 'SIGN_MESSAGE_FAILED'
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
export * from './actionCreators';
|
||||||
|
export * from './constants';
|
||||||
|
export * from './actionTypes';
|
|
@ -1,13 +1,32 @@
|
||||||
import * as types from './actionTypes';
|
import * as types from './actionTypes';
|
||||||
import { TypeKeys } from './constants';
|
import { TypeKeys } from './constants';
|
||||||
|
|
||||||
export type TRequestSignature = typeof requestSignature;
|
export type TRequestTransactionSignature = typeof requestTransactionSignature;
|
||||||
export function requestSignature(from: string, rlp: string): types.RequestSignatureAction {
|
export function requestTransactionSignature(
|
||||||
|
from: string,
|
||||||
|
data: string
|
||||||
|
): types.RequestTransactionSignatureAction {
|
||||||
return {
|
return {
|
||||||
type: TypeKeys.PARITY_SIGNER_REQUEST_SIGNATURE,
|
type: TypeKeys.PARITY_SIGNER_REQUEST_TX_SIGNATURE,
|
||||||
payload: {
|
payload: {
|
||||||
|
isMessage: false,
|
||||||
from,
|
from,
|
||||||
rlp
|
data
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TRequestMessageSignature = typeof requestMessageSignature;
|
||||||
|
export function requestMessageSignature(
|
||||||
|
from: string,
|
||||||
|
data: string
|
||||||
|
): types.RequestMessageSignatureAction {
|
||||||
|
return {
|
||||||
|
type: TypeKeys.PARITY_SIGNER_REQUEST_MSG_SIGNATURE,
|
||||||
|
payload: {
|
||||||
|
isMessage: true,
|
||||||
|
from,
|
||||||
|
data
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,19 @@
|
||||||
import { TypeKeys } from './constants';
|
import { TypeKeys } from './constants';
|
||||||
|
|
||||||
export interface RequestSignatureAction {
|
export interface RequestTransactionSignatureAction {
|
||||||
type: TypeKeys.PARITY_SIGNER_REQUEST_SIGNATURE;
|
type: TypeKeys.PARITY_SIGNER_REQUEST_TX_SIGNATURE;
|
||||||
payload: {
|
payload: {
|
||||||
rlp: string;
|
isMessage: false;
|
||||||
|
data: string;
|
||||||
|
from: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RequestMessageSignatureAction {
|
||||||
|
type: TypeKeys.PARITY_SIGNER_REQUEST_MSG_SIGNATURE;
|
||||||
|
payload: {
|
||||||
|
isMessage: true;
|
||||||
|
data: string;
|
||||||
from: string;
|
from: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -14,4 +24,7 @@ export interface FinalizeSignatureAction {
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** Union Type ***/
|
/*** Union Type ***/
|
||||||
export type ParitySignerAction = RequestSignatureAction | FinalizeSignatureAction;
|
export type ParitySignerAction =
|
||||||
|
| RequestTransactionSignatureAction
|
||||||
|
| RequestMessageSignatureAction
|
||||||
|
| FinalizeSignatureAction;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export enum TypeKeys {
|
export enum TypeKeys {
|
||||||
PARITY_SIGNER_REQUEST_SIGNATURE = 'PARITY_SIGNER_REQUEST_SIGNATURE',
|
PARITY_SIGNER_REQUEST_TX_SIGNATURE = 'PARITY_SIGNER_REQUEST_TX_SIGNATURE',
|
||||||
|
PARITY_SIGNER_REQUEST_MSG_SIGNATURE = 'PARITY_SIGNER_REQUEST_MSG_SIGNATURE',
|
||||||
PARITY_SIGNER_FINALIZE_SIGNATURE = 'PARITY_SIGNER_FINALIZE_SIGNATURE'
|
PARITY_SIGNER_FINALIZE_SIGNATURE = 'PARITY_SIGNER_FINALIZE_SIGNATURE'
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,8 @@ interface ScanProps {
|
||||||
interface ShowProps {
|
interface ShowProps {
|
||||||
scan: false;
|
scan: false;
|
||||||
account: string;
|
account: string;
|
||||||
rlp: string;
|
data?: string;
|
||||||
|
rlp?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SharedProps {
|
interface SharedProps {
|
||||||
|
|
|
@ -22,10 +22,9 @@ export const DISABLE_WALLETS: { [key in WalletMode]: DisabledWallets } = {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[WalletMode.UNABLE_TO_SIGN]: {
|
[WalletMode.UNABLE_TO_SIGN]: {
|
||||||
wallets: [SecureWalletName.TREZOR, SecureWalletName.PARITY_SIGNER, MiscWalletName.VIEW_ONLY],
|
wallets: [SecureWalletName.TREZOR, MiscWalletName.VIEW_ONLY],
|
||||||
reasons: {
|
reasons: {
|
||||||
[SecureWalletName.TREZOR]: 'This wallet can’t sign messages',
|
[SecureWalletName.TREZOR]: 'This wallet can’t sign messages',
|
||||||
[SecureWalletName.PARITY_SIGNER]: 'This wallet can’t sign messages',
|
|
||||||
[MiscWalletName.VIEW_ONLY]: 'This wallet can’t sign messages'
|
[MiscWalletName.VIEW_ONLY]: 'This wallet can’t sign messages'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,9 @@ interface PropsClosed {
|
||||||
|
|
||||||
interface PropsOpen {
|
interface PropsOpen {
|
||||||
isOpen: true;
|
isOpen: true;
|
||||||
rlp: string;
|
isMessage: boolean;
|
||||||
from: string;
|
from: string;
|
||||||
|
data: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ActionProps {
|
interface ActionProps {
|
||||||
|
@ -40,7 +41,7 @@ class QrSignerModal extends React.Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
const { scan } = this.state;
|
const { scan } = this.state;
|
||||||
const { from, rlp } = this.props;
|
const { from, data, isMessage } = this.props;
|
||||||
|
|
||||||
const buttons: IButton[] = [
|
const buttons: IButton[] = [
|
||||||
{
|
{
|
||||||
|
@ -60,7 +61,7 @@ class QrSignerModal extends React.Component<Props, State> {
|
||||||
return (
|
return (
|
||||||
<div className="QrSignerModal">
|
<div className="QrSignerModal">
|
||||||
<Modal
|
<Modal
|
||||||
title={translateRaw('DEP_SIGNTX')}
|
title={translateRaw(isMessage ? 'NAV_SIGNMSG' : 'DEP_SIGNTX')}
|
||||||
isOpen={true}
|
isOpen={true}
|
||||||
buttons={buttons}
|
buttons={buttons}
|
||||||
handleClose={this.onClose}
|
handleClose={this.onClose}
|
||||||
|
@ -68,8 +69,10 @@ class QrSignerModal extends React.Component<Props, State> {
|
||||||
<div className="QrSignerModal-qr-bounds">
|
<div className="QrSignerModal-qr-bounds">
|
||||||
{scan ? (
|
{scan ? (
|
||||||
<ParityQrSigner scan={true} onScan={this.onScan} />
|
<ParityQrSigner scan={true} onScan={this.onScan} />
|
||||||
|
) : isMessage ? (
|
||||||
|
<ParityQrSigner scan={false} account={from} data={data} />
|
||||||
) : (
|
) : (
|
||||||
<ParityQrSigner scan={false} account={from} rlp={rlp} />
|
<ParityQrSigner scan={false} account={from} rlp={data} />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
@ -103,12 +106,9 @@ function mapStateToProps(state: AppState): PropsClosed | PropsOpen {
|
||||||
return { isOpen: false };
|
return { isOpen: false };
|
||||||
}
|
}
|
||||||
|
|
||||||
const { from, rlp } = requested;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isOpen: true,
|
isOpen: true,
|
||||||
from,
|
...requested
|
||||||
rlp
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,10 @@
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import translate from 'translations';
|
import translate from 'translations';
|
||||||
import { ISignedMessage } from 'libs/signing';
|
import { TSignMessageRequested } from 'actions/message';
|
||||||
import { IFullWallet } from 'libs/wallet';
|
|
||||||
import { TShowNotification } from 'actions/notifications';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
wallet: IFullWallet;
|
|
||||||
message: string;
|
message: string;
|
||||||
showNotification: TShowNotification;
|
signMessageRequested: TSignMessageRequested;
|
||||||
onSignMessage(msg: ISignedMessage): any;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class SignMessageButton extends React.Component<Props, {}> {
|
export default class SignMessageButton extends React.Component<Props, {}> {
|
||||||
|
@ -20,24 +16,9 @@ export default class SignMessageButton extends React.Component<Props, {}> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private handleSignMessage = async () => {
|
private handleSignMessage = () => {
|
||||||
const { wallet, message, showNotification, onSignMessage } = this.props;
|
const { signMessageRequested, message } = this.props;
|
||||||
|
|
||||||
try {
|
signMessageRequested(message);
|
||||||
const signedMessage: ISignedMessage = {
|
|
||||||
address: wallet.getAddressString(),
|
|
||||||
msg: message,
|
|
||||||
sig: await wallet.signMessage(message),
|
|
||||||
version: '2'
|
|
||||||
};
|
|
||||||
|
|
||||||
onSignMessage(signedMessage);
|
|
||||||
showNotification(
|
|
||||||
'success',
|
|
||||||
translate('SIGN_MSG_SUCCESS', { $address: signedMessage.address })
|
|
||||||
);
|
|
||||||
} catch (err) {
|
|
||||||
showNotification('danger', translate('SIGN_MSG_FAIL', { $err: err.message }));
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import WalletDecrypt, { DISABLE_WALLETS } from 'components/WalletDecrypt';
|
import WalletDecrypt, { DISABLE_WALLETS } from 'components/WalletDecrypt';
|
||||||
import translate, { translateRaw } from 'translations';
|
import translate, { translateRaw } from 'translations';
|
||||||
import { showNotification, TShowNotification } from 'actions/notifications';
|
import { signMessageRequested, TSignMessageRequested } from 'actions/message';
|
||||||
import { resetWallet, TResetWallet } from 'actions/wallet';
|
import { resetWallet, TResetWallet } from 'actions/wallet';
|
||||||
import { ISignedMessage } from 'libs/signing';
|
import { ISignedMessage } from 'libs/signing';
|
||||||
import { IFullWallet } from 'libs/wallet';
|
import { IFullWallet } from 'libs/wallet';
|
||||||
|
@ -15,18 +15,17 @@ import { TextArea, CodeBlock } from 'components/ui';
|
||||||
interface Props {
|
interface Props {
|
||||||
wallet: IFullWallet;
|
wallet: IFullWallet;
|
||||||
unlocked: boolean;
|
unlocked: boolean;
|
||||||
showNotification: TShowNotification;
|
signMessageRequested: TSignMessageRequested;
|
||||||
|
signedMessage: ISignedMessage | null;
|
||||||
resetWallet: TResetWallet;
|
resetWallet: TResetWallet;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface State {
|
interface State {
|
||||||
message: string;
|
message: string;
|
||||||
signedMessage: ISignedMessage | null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: State = {
|
const initialState: State = {
|
||||||
message: '',
|
message: ''
|
||||||
signedMessage: null
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const messagePlaceholder = translateRaw('SIGN_MSG_PLACEHOLDER');
|
const messagePlaceholder = translateRaw('SIGN_MSG_PLACEHOLDER');
|
||||||
|
@ -39,8 +38,8 @@ export class SignMessage extends Component<Props, State> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public render() {
|
public render() {
|
||||||
const { wallet, unlocked } = this.props;
|
const { unlocked, signedMessage } = this.props;
|
||||||
const { message, signedMessage } = this.state;
|
const { message } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
@ -68,10 +67,8 @@ export class SignMessage extends Component<Props, State> {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SignButton
|
<SignButton
|
||||||
wallet={wallet}
|
|
||||||
message={this.state.message}
|
message={this.state.message}
|
||||||
showNotification={this.props.showNotification}
|
signMessageRequested={this.props.signMessageRequested}
|
||||||
onSignMessage={this.onSignMessage}
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{signedMessage && (
|
{signedMessage && (
|
||||||
|
@ -97,21 +94,17 @@ export class SignMessage extends Component<Props, State> {
|
||||||
this.setState({ message });
|
this.setState({ message });
|
||||||
};
|
};
|
||||||
|
|
||||||
private onSignMessage = (signedMessage: ISignedMessage) => {
|
|
||||||
this.setState({ signedMessage });
|
|
||||||
};
|
|
||||||
|
|
||||||
private changeWallet = () => {
|
private changeWallet = () => {
|
||||||
this.props.resetWallet();
|
this.props.resetWallet();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const mapStateToProps = (state: AppState) => ({
|
const mapStateToProps = (state: AppState) => ({
|
||||||
wallet: state.wallet.inst,
|
signedMessage: state.message.signed,
|
||||||
unlocked: isWalletFullyUnlocked(state)
|
unlocked: isWalletFullyUnlocked(state)
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps, {
|
export default connect(mapStateToProps, {
|
||||||
showNotification,
|
signMessageRequested,
|
||||||
resetWallet
|
resetWallet
|
||||||
})(SignMessage);
|
})(SignMessage);
|
||||||
|
|
|
@ -92,7 +92,7 @@ export class VerifyMessage extends Component<Props, State> {
|
||||||
this.props.showNotification('success', translate('SUCCESS_7'));
|
this.props.showNotification('success', translate('SUCCESS_7'));
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
this.clearVerifiedData();
|
this.clearVerifiedData();
|
||||||
this.props.showNotification('danger', translate('ERROR_12'));
|
this.props.showNotification('danger', translate('ERROR_38'));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,9 @@ export default class ParitySignerWallet implements IFullWallet {
|
||||||
}
|
}
|
||||||
|
|
||||||
public signRawTransaction = () =>
|
public signRawTransaction = () =>
|
||||||
Promise.reject(new Error('Web3 wallets cannot sign raw transactions.'));
|
Promise.reject(new Error('Parity Signer cannot sign raw transactions.'));
|
||||||
|
|
||||||
public signMessage = () =>
|
public signMessage = () => Promise.reject(new Error('Parity Signer cannot sign messages.'));
|
||||||
Promise.reject(new Error('Signing via Parity Signer not yet supported.'));
|
|
||||||
|
|
||||||
public getAddressString() {
|
public getAddressString() {
|
||||||
return this.address;
|
return this.address;
|
||||||
|
|
|
@ -9,6 +9,7 @@ import { rates, State as RatesState } from './rates';
|
||||||
import { State as SwapState, swap } from './swap';
|
import { State as SwapState, swap } from './swap';
|
||||||
import { State as WalletState, wallet } from './wallet';
|
import { State as WalletState, wallet } from './wallet';
|
||||||
import { State as TransactionState, transaction } from './transaction';
|
import { State as TransactionState, transaction } from './transaction';
|
||||||
|
import { State as MessageState, message } from './message';
|
||||||
import { State as GasState, gas } from './gas';
|
import { State as GasState, gas } from './gas';
|
||||||
import { onboardStatus, State as OnboardStatusState } from './onboardStatus';
|
import { onboardStatus, State as OnboardStatusState } from './onboardStatus';
|
||||||
import { State as TransactionsState, transactions } from './transactions';
|
import { State as TransactionsState, transactions } from './transactions';
|
||||||
|
@ -28,6 +29,7 @@ export interface AppState {
|
||||||
swap: SwapState;
|
swap: SwapState;
|
||||||
transaction: TransactionState;
|
transaction: TransactionState;
|
||||||
transactions: TransactionsState;
|
transactions: TransactionsState;
|
||||||
|
message: MessageState;
|
||||||
paritySigner: ParitySignerState;
|
paritySigner: ParitySignerState;
|
||||||
gas: GasState;
|
gas: GasState;
|
||||||
schedule: ScheduleState;
|
schedule: ScheduleState;
|
||||||
|
@ -47,6 +49,7 @@ export default combineReducers<AppState>({
|
||||||
deterministicWallets,
|
deterministicWallets,
|
||||||
transaction,
|
transaction,
|
||||||
transactions,
|
transactions,
|
||||||
|
message,
|
||||||
paritySigner,
|
paritySigner,
|
||||||
gas,
|
gas,
|
||||||
schedule,
|
schedule,
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import { MessageAction, SignLocalMessageSucceededAction, TypeKeys } from 'actions/message';
|
||||||
|
import { ISignedMessage } from 'libs/signing';
|
||||||
|
|
||||||
|
export interface State {
|
||||||
|
signed?: ISignedMessage | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const INITIAL_STATE: State = {
|
||||||
|
signed: null
|
||||||
|
};
|
||||||
|
|
||||||
|
function signLocalMessageSucceeded(state: State, action: SignLocalMessageSucceededAction): State {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
signed: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function signMessageFailed(state: State): State {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
signed: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function message(state: State = INITIAL_STATE, action: MessageAction): State {
|
||||||
|
switch (action.type) {
|
||||||
|
case TypeKeys.SIGN_LOCAL_MESSAGE_SUCCEEDED:
|
||||||
|
return signLocalMessageSucceeded(state, action);
|
||||||
|
case TypeKeys.SIGN_MESSAGE_FAILED:
|
||||||
|
return signMessageFailed(state);
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,35 @@
|
||||||
import { ParitySignerAction, RequestSignatureAction, TypeKeys } from 'actions/paritySigner';
|
import {
|
||||||
|
ParitySignerAction,
|
||||||
|
RequestTransactionSignatureAction,
|
||||||
|
RequestMessageSignatureAction,
|
||||||
|
TypeKeys
|
||||||
|
} from 'actions/paritySigner';
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
requested?: QrSignatureState | null;
|
requested?: QrSignatureState | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface QrSignatureState {
|
interface QrSignatureState {
|
||||||
|
isMessage: boolean;
|
||||||
from: string;
|
from: string;
|
||||||
rlp: string;
|
data: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const INITIAL_STATE: State = {
|
export const INITIAL_STATE: State = {
|
||||||
requested: null
|
requested: null
|
||||||
};
|
};
|
||||||
|
|
||||||
function requestSignature(state: State, action: RequestSignatureAction): State {
|
function requestTransactionSignature(
|
||||||
|
state: State,
|
||||||
|
action: RequestTransactionSignatureAction
|
||||||
|
): State {
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
requested: action.payload
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestMessageSignature(state: State, action: RequestMessageSignatureAction): State {
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
requested: action.payload
|
requested: action.payload
|
||||||
|
@ -29,8 +45,10 @@ function finalizeSignature(state: State): State {
|
||||||
|
|
||||||
export function paritySigner(state: State = INITIAL_STATE, action: ParitySignerAction): State {
|
export function paritySigner(state: State = INITIAL_STATE, action: ParitySignerAction): State {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case TypeKeys.PARITY_SIGNER_REQUEST_SIGNATURE:
|
case TypeKeys.PARITY_SIGNER_REQUEST_TX_SIGNATURE:
|
||||||
return requestSignature(state, action);
|
return requestTransactionSignature(state, action);
|
||||||
|
case TypeKeys.PARITY_SIGNER_REQUEST_MSG_SIGNATURE:
|
||||||
|
return requestMessageSignature(state, action);
|
||||||
case TypeKeys.PARITY_SIGNER_FINALIZE_SIGNATURE:
|
case TypeKeys.PARITY_SIGNER_FINALIZE_SIGNATURE:
|
||||||
return finalizeSignature(state);
|
return finalizeSignature(state);
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -8,6 +8,7 @@ import swapRates from './swap/rates';
|
||||||
import wallet from './wallet';
|
import wallet from './wallet';
|
||||||
import { ens } from './ens';
|
import { ens } from './ens';
|
||||||
import { transaction } from './transaction';
|
import { transaction } from './transaction';
|
||||||
|
import { message } from './message';
|
||||||
import transactions from './transactions';
|
import transactions from './transactions';
|
||||||
import gas from './gas';
|
import gas from './gas';
|
||||||
import { schedule } from './schedule';
|
import { schedule } from './schedule';
|
||||||
|
@ -21,6 +22,7 @@ export default {
|
||||||
notifications,
|
notifications,
|
||||||
wallet,
|
wallet,
|
||||||
transaction,
|
transaction,
|
||||||
|
message,
|
||||||
deterministicWallets,
|
deterministicWallets,
|
||||||
rates,
|
rates,
|
||||||
transactions,
|
transactions,
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import { SagaIterator } from 'redux-saga';
|
||||||
|
import { put, call, select } from 'redux-saga/effects';
|
||||||
|
import translate from 'translations';
|
||||||
|
import { padLeftEven } from 'libs/values';
|
||||||
|
import { showNotification } from 'actions/notifications';
|
||||||
|
import { getWalletInst } from 'selectors/wallet';
|
||||||
|
import { IFullWallet } from 'libs/wallet';
|
||||||
|
import { signMessageFailed, SignMessageRequestedAction } from 'actions/message';
|
||||||
|
|
||||||
|
export function* signingWrapper(
|
||||||
|
handler: (wallet: IFullWallet, message: string) => SagaIterator,
|
||||||
|
action: SignMessageRequestedAction
|
||||||
|
): SagaIterator {
|
||||||
|
const message = action.payload;
|
||||||
|
const wallet = yield select(getWalletInst);
|
||||||
|
|
||||||
|
try {
|
||||||
|
yield call(handler, wallet, message);
|
||||||
|
} catch (err) {
|
||||||
|
yield put(showNotification('danger', translate('SIGN_MSG_FAIL', { $err: err.message }), 5000));
|
||||||
|
yield put(signMessageFailed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turns a string into hex-encoded UTF-8 byte array, `0x` prefixed.
|
||||||
|
*
|
||||||
|
* @param {string} message to encode
|
||||||
|
* @return {string}
|
||||||
|
*/
|
||||||
|
export function messageToData(message: string): string {
|
||||||
|
return (
|
||||||
|
'0x' +
|
||||||
|
Array.from(Buffer.from(message, 'utf8'))
|
||||||
|
.map(n => padLeftEven(n.toString(16)))
|
||||||
|
.join('')
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { SagaIterator } from 'redux-saga';
|
||||||
|
import { all } from 'redux-saga/effects';
|
||||||
|
import { signing } from './signing';
|
||||||
|
|
||||||
|
export function* message(): SagaIterator {
|
||||||
|
yield all([...signing]);
|
||||||
|
}
|
|
@ -0,0 +1,89 @@
|
||||||
|
import { SagaIterator } from 'redux-saga';
|
||||||
|
import { put, take, apply, takeEvery, call, select } from 'redux-saga/effects';
|
||||||
|
import translate, { translateRaw } from 'translations';
|
||||||
|
import { showNotification } from 'actions/notifications';
|
||||||
|
import { verifySignedMessage } from 'libs/signing';
|
||||||
|
import {
|
||||||
|
TypeKeys,
|
||||||
|
SignMessageRequestedAction,
|
||||||
|
signLocalMessageSucceeded,
|
||||||
|
SignLocalMessageSucceededAction,
|
||||||
|
signMessageFailed
|
||||||
|
} from 'actions/message';
|
||||||
|
import {
|
||||||
|
requestMessageSignature,
|
||||||
|
FinalizeSignatureAction,
|
||||||
|
TypeKeys as ParityKeys
|
||||||
|
} from 'actions/paritySigner';
|
||||||
|
import { IFullWallet } from 'libs/wallet';
|
||||||
|
import { getWalletType, IWalletType } from 'selectors/wallet';
|
||||||
|
import { messageToData, signingWrapper } from './helpers';
|
||||||
|
|
||||||
|
function* signLocalMessage(wallet: IFullWallet, msg: string): SagaIterator {
|
||||||
|
const address = yield apply(wallet, wallet.getAddressString);
|
||||||
|
const sig: string = yield apply(wallet, wallet.signMessage, [msg]);
|
||||||
|
|
||||||
|
yield put(
|
||||||
|
signLocalMessageSucceeded({
|
||||||
|
address,
|
||||||
|
msg,
|
||||||
|
sig,
|
||||||
|
version: '2'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function* signParitySignerMessage(wallet: IFullWallet, msg: string): SagaIterator {
|
||||||
|
const address = yield apply(wallet, wallet.getAddressString);
|
||||||
|
const data = yield call(messageToData, msg);
|
||||||
|
|
||||||
|
yield put(requestMessageSignature(address, data));
|
||||||
|
|
||||||
|
const { payload: sig }: FinalizeSignatureAction = yield take(
|
||||||
|
ParityKeys.PARITY_SIGNER_FINALIZE_SIGNATURE
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!sig) {
|
||||||
|
throw new Error(translateRaw('ERROR_38'));
|
||||||
|
}
|
||||||
|
|
||||||
|
yield put(
|
||||||
|
signLocalMessageSucceeded({
|
||||||
|
address,
|
||||||
|
msg,
|
||||||
|
sig,
|
||||||
|
version: '2'
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function* handleMessageRequest(action: SignMessageRequestedAction): SagaIterator {
|
||||||
|
const walletType: IWalletType = yield select(getWalletType);
|
||||||
|
|
||||||
|
const signingHandler = walletType.isParitySignerWallet
|
||||||
|
? signParitySignerMessage
|
||||||
|
: signLocalMessage;
|
||||||
|
|
||||||
|
return yield call(signingWrapper, signingHandler, action);
|
||||||
|
}
|
||||||
|
|
||||||
|
function* verifySignature(action: SignLocalMessageSucceededAction): SagaIterator {
|
||||||
|
const success = yield call(verifySignedMessage, action.payload);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
yield put(
|
||||||
|
showNotification(
|
||||||
|
'success',
|
||||||
|
translate('SIGN_MSG_SUCCESS', { $address: action.payload.address })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
yield put(signMessageFailed());
|
||||||
|
yield put(showNotification('danger', translate('ERROR_38')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const signing = [
|
||||||
|
takeEvery(TypeKeys.SIGN_MESSAGE_REQUESTED, handleMessageRequest),
|
||||||
|
takeEvery(TypeKeys.SIGN_LOCAL_MESSAGE_SUCCEEDED, verifySignature)
|
||||||
|
];
|
|
@ -15,7 +15,7 @@ import { computeIndexingHash } from 'libs/transaction';
|
||||||
import { serializedAndTransactionFieldsMatch } from 'selectors/transaction';
|
import { serializedAndTransactionFieldsMatch } from 'selectors/transaction';
|
||||||
import { showNotification } from 'actions/notifications';
|
import { showNotification } from 'actions/notifications';
|
||||||
import {
|
import {
|
||||||
requestSignature,
|
requestTransactionSignature,
|
||||||
FinalizeSignatureAction,
|
FinalizeSignatureAction,
|
||||||
TypeKeys as ParityKeys
|
TypeKeys as ParityKeys
|
||||||
} from 'actions/paritySigner';
|
} from 'actions/paritySigner';
|
||||||
|
@ -53,7 +53,7 @@ export function* signParitySignerTransactionHandler({
|
||||||
const from = yield apply(wallet, wallet.getAddressString);
|
const from = yield apply(wallet, wallet.getAddressString);
|
||||||
const rlp = yield call(transactionToRLP, tx);
|
const rlp = yield call(transactionToRLP, tx);
|
||||||
|
|
||||||
yield put(requestSignature(from, rlp));
|
yield put(requestTransactionSignature(from, rlp));
|
||||||
|
|
||||||
const { payload }: FinalizeSignatureAction = yield take(
|
const { payload }: FinalizeSignatureAction = yield take(
|
||||||
ParityKeys.PARITY_SIGNER_FINALIZE_SIGNATURE
|
ParityKeys.PARITY_SIGNER_FINALIZE_SIGNATURE
|
||||||
|
|
|
@ -266,6 +266,7 @@
|
||||||
"ERROR_34": "The name you are attempting to reveal does not match the name you have entered. ",
|
"ERROR_34": "The name you are attempting to reveal does not match the name you have entered. ",
|
||||||
"ERROR_36": "Enter valid TX hash",
|
"ERROR_36": "Enter valid TX hash",
|
||||||
"ERROR_37": "Enter valid hex string (0-9, a-f)",
|
"ERROR_37": "Enter valid hex string (0-9, a-f)",
|
||||||
|
"ERROR_38": "Invalid signed message. ",
|
||||||
"SUCCESS_1": "Valid address ",
|
"SUCCESS_1": "Valid address ",
|
||||||
"SUCCESS_2": "Wallet successfully decrypted ",
|
"SUCCESS_2": "Wallet successfully decrypted ",
|
||||||
"SUCCESS_3": "Your TX has been broadcast to the network. It is waiting to be mined & confirmed. During ICOs, it may take 3+ hours to confirm. Use the Verify & Check buttons below to see. TX Hash: ",
|
"SUCCESS_3": "Your TX has been broadcast to the network. It is waiting to be mined & confirmed. During ICOs, it may take 3+ hours to confirm. Use the Verify & Check buttons below to see. TX Hash: ",
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"npm": ">= 5.0.0"
|
"npm": ">= 5.0.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@parity/qr-signer": "0.1.1",
|
"@parity/qr-signer": "0.2.0",
|
||||||
"babel-polyfill": "6.26.0",
|
"babel-polyfill": "6.26.0",
|
||||||
"bip39": "2.5.0",
|
"bip39": "2.5.0",
|
||||||
"bn.js": "4.11.8",
|
"bn.js": "4.11.8",
|
||||||
|
|
Loading…
Reference in New Issue