Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parity Signer #1349

Merged
merged 20 commits into from
Apr 6, 2018
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
b9a6319
Parity Signer Squashed
maciejhirsz Mar 20, 2018
83feb9a
ParitySigner to be a container
maciejhirsz Mar 21, 2018
8749a8a
Merge branch 'develop' of github.com:MyCryptoHQ/MyCrypto into paritys…
maciejhirsz Mar 21, 2018
ac6149b
Parity Signer: style and polish
maciejhirsz Mar 26, 2018
5ee0013
Merge branch 'develop' of github.com:MyCryptoHQ/MyCrypto into paritys…
maciejhirsz Mar 26, 2018
b73a4b4
target blank on appstore links
maciejhirsz Mar 26, 2018
4a7dd0f
PR fixes
maciejhirsz Mar 26, 2018
ecdfab7
Move QrSignerModal to SendTransaction container
maciejhirsz Mar 28, 2018
e04e2e6
Rework redux, use signing saga
maciejhirsz Apr 6, 2018
a7380d0
Cleanup
maciejhirsz Apr 6, 2018
1979c68
Merge branch 'develop' of github.com:MyCryptoHQ/MyCrypto into paritys…
maciejhirsz Apr 6, 2018
047b7a9
Use new logo, change helpLink
maciejhirsz Apr 6, 2018
89397e9
Rework finalize actions and types a bit
wbobeirne Apr 6, 2018
42323be
Merge branch 'develop' into paritysigner-adjustments
wbobeirne Apr 6, 2018
0753ce8
Webcam info + wiki link on unlock screen
maciejhirsz Apr 6, 2018
5c4396d
Make the Parity QR Signer its own component, that has error messaging…
wbobeirne Apr 6, 2018
e64aa22
Merged paritysigner back in.
wbobeirne Apr 6, 2018
05419a6
Unneded l10n
wbobeirne Apr 6, 2018
234d199
Merge pull request #2 from MyCryptoHQ/paritysigner-adjustments
maciejhirsz Apr 6, 2018
3f4a578
Merge branch 'develop' of github.com:MyCryptoHQ/MyCrypto into paritys…
maciejhirsz Apr 6, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions common/actions/paritySigner/actionCreators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import * as types from './actionTypes';
import { TypeKeys } from './constants';

export type TRequestSignature = typeof requestSignature;
export function requestSignature(from: string, rlp: string): types.RequestSignatureAction {
return {
type: TypeKeys.PARITY_SIGNER_REQUEST_SIGNATURE,
payload: {
from,
rlp
}
};
}

export type TFinalize = typeof finalize;
export function finalize(signature: string | null): types.FinalizeAction {
return {
type: TypeKeys.PARITY_SIGNER_FINALIZE,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very minor, but I think it would improve code understanding if the word "signature" was a part of this action as well.

payload: signature
};
}
17 changes: 17 additions & 0 deletions common/actions/paritySigner/actionTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { TypeKeys } from './constants';

export interface RequestSignatureAction {
type: TypeKeys.PARITY_SIGNER_REQUEST_SIGNATURE;
payload: {
rlp: string;
from: string;
};
}

export interface FinalizeAction {
type: TypeKeys.PARITY_SIGNER_FINALIZE;
payload: string | null;
}

/*** Union Type ***/
export type ParitySignerAction = RequestSignatureAction | FinalizeAction;
4 changes: 4 additions & 0 deletions common/actions/paritySigner/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export enum TypeKeys {
PARITY_SIGNER_REQUEST_SIGNATURE = 'PARITY_SIGNER_REQUEST_SIGNATURE',
PARITY_SIGNER_FINALIZE = 'PARITY_SIGNER_FINALIZE'
}
3 changes: 3 additions & 0 deletions common/actions/paritySigner/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './constants';
export * from './actionTypes';
export * from './actionCreators';
Binary file added common/assets/images/mobile/app-store-badge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added common/assets/images/mobile/google-play-badge.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions common/assets/images/wallets/paritysigner.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions common/components/WalletDecrypt/WalletDecrypt.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import {
WalletButton,
InsecureWalletWarning
} from './components';
import ParitySignerDecrypt from 'containers/ParitySignerDecrypt';
import { AppState } from 'reducers';
import { showNotification, TShowNotification } from 'actions/notifications';
import { getDisabledWallets } from 'selectors/wallet';
Expand All @@ -49,6 +50,7 @@ import LedgerIcon from 'assets/images/wallets/ledger.svg';
import MetamaskIcon from 'assets/images/wallets/metamask.svg';
import MistIcon from 'assets/images/wallets/mist.svg';
import TrezorIcon from 'assets/images/wallets/trezor.svg';
import ParitySignerIcon from 'assets/images/wallets/paritysigner.svg';
import './WalletDecrypt.scss';
import { withRouter, RouteComponentProps } from 'react-router';

Expand Down Expand Up @@ -168,6 +170,15 @@ const WalletDecrypt = withRouter<Props>(
unlock: this.props.setWallet,
helpLink: 'https://doc.satoshilabs.com/trezor-apps/mew.html'
},
[SecureWalletName.PARITY_SIGNER]: {
lid: 'X_PARITYSIGNER',
icon: ParitySignerIcon,
description: 'ADD_PARITY_DESC',
component: ParitySignerDecrypt,
initialParams: {},
unlock: this.props.setWallet,
helpLink: 'https://github.com/paritytech/parity-signer/blob/master/README.md'
},
[InsecureWalletName.KEYSTORE_FILE]: {
lid: 'X_KEYSTORE2',
example: 'UTC--2017-12-15T17-35-22.547Z--6be6e49e82425a5aa56396db03512f2cc10e95e8',
Expand Down
3 changes: 2 additions & 1 deletion common/components/WalletDecrypt/disables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,10 @@ export const DISABLE_WALLETS: { [key in WalletMode]: DisabledWallets } = {
}
},
[WalletMode.UNABLE_TO_SIGN]: {
wallets: [SecureWalletName.TREZOR, MiscWalletName.VIEW_ONLY],
wallets: [SecureWalletName.TREZOR, SecureWalletName.PARITY_SIGNER, MiscWalletName.VIEW_ONLY],
reasons: {
[SecureWalletName.TREZOR]: 'This wallet can’t sign messages',
[SecureWalletName.PARITY_SIGNER]: 'This wallet can’t sign messages',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not your responsibility, but just a reminder that we should be translating this.

[MiscWalletName.VIEW_ONLY]: 'This wallet can’t sign messages'
}
}
Expand Down
3 changes: 2 additions & 1 deletion common/config/data.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,8 @@ export const ethercardReferralURL =
export enum SecureWalletName {
WEB3 = 'web3',
LEDGER_NANO_S = 'ledgerNanoS',
TREZOR = 'trezor'
TREZOR = 'trezor',
PARITY_SIGNER = 'paritySigner'
}

export enum HardwareWalletName {
Expand Down
14 changes: 14 additions & 0 deletions common/containers/ParitySignerDecrypt/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.ParitySignerUnlock {
&-badge {
display: inline-block;
height: 3em;
margin: 0 0.4em;
}

&-qr-bounds {
width: 300px;
height: 300px;
display: inline-block;
margin-bottom: 1.5em;
}
}
53 changes: 53 additions & 0 deletions common/containers/ParitySignerDecrypt/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import React, { PureComponent } from 'react';
import { connect } from 'react-redux';
import translate from 'translations';
import { NewTabLink } from 'components/ui';
import QrSigner from '@parity/qr-signer';
import { isValidETHAddress } from 'libs/validators';
import { ParitySignerWallet } from 'libs/wallet';
import { showNotification, TShowNotification } from 'actions/notifications';
import './index.scss';
import AppStoreBadge from 'assets/images/mobile/app-store-badge.png';
import GooglePlayBadge from 'assets/images/mobile/google-play-badge.png';

interface Props {
showNotification: TShowNotification;
onUnlock(param: any): void;
}

class ParitySignerDecrypt extends PureComponent<Props> {
public render() {
return (
<div className="ParitySignerUnlock">
<div className="ParitySignerUnlock-qr-bounds">
<QrSigner
size={300}
scan={true}
onScan={this.unlockAddress}
styles={{ display: 'inline-block' }}
/>
</div>
<p>{translate('ADD_PARITY_2')}</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At some point we should put together a help article that explains this process, or have some additional descriptive copy here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We are working on a proper help page that describes how to use the signer, I'll add a link to it here once it's done (ideally before publicity but can be done after the PR is merged).

<p>
<NewTabLink href="https://itunes.apple.com/us/app/parity-signer/id1218174838">
<img className="ParitySignerUnlock-badge" src={AppStoreBadge} alt="App Store" />
</NewTabLink>
<NewTabLink href="https://play.google.com/store/apps/details?id=com.nativesigner">
<img className="ParitySignerUnlock-badge" src={GooglePlayBadge} alt="Google Play" />
</NewTabLink>
</p>
</div>
);
}

private unlockAddress = (address: string) => {
if (!isValidETHAddress(address)) {
this.props.showNotification('danger', 'Not a valid address!');
return;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the user experience for when this happens? Does the signer keep watching for a potentially valid address?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, it will keep on scanning until a QR code with a valid address is presented.


this.props.onUnlock(new ParitySignerWallet(address));
};
}

export default connect(() => ({}), { showNotification })(ParitySignerDecrypt);
8 changes: 8 additions & 0 deletions common/containers/QrSignerModal/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.QrSignerModal {
&-qr-bounds {
width: 300px;
height: 300px;
display: inline-block;
margin: 0.3em 0;
}
}
110 changes: 110 additions & 0 deletions common/containers/QrSignerModal/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import React from 'react';
import { connect } from 'react-redux';
import translate, { translateRaw } from 'translations';
import QrSigner from '@parity/qr-signer';
import { AppState } from 'reducers';
import Modal, { IButton } from 'components/ui/Modal';
import { TFinalize, finalize } from 'actions/paritySigner';
import './index.scss';

interface State {
scan: boolean;
}

interface PropsClosed {
isOpen: false;
finalize: TFinalize;
}

interface PropsOpen {
isOpen: true;
rlp: string;
from: string;
finalize: TFinalize;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We usually split out props into props that get specified by parent component (OwnProps), props that are state from redux (StateProps), and props that are actions (ActionProps), so that we can properly type everything. See something like TXMetaDataPanel for example.

}

type Props = PropsClosed | PropsOpen;

class QrSignerModal extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
scan: false
};
}

public render() {
if (!this.props.isOpen) {
return null;
}

const { scan } = this.state;
const { from, rlp } = this.props;

const buttons: IButton[] = [
{
disabled: false,
text: translate(scan ? 'ACTION_4' : 'ADD_PARITY_3'),
type: 'primary',
onClick: () => this.setState({ scan: !scan })
},
{
disabled: false,
text: translate('ACTION_2'),
type: 'default',
onClick: this.onClose
}
];

return (
<div className="QrSignerModal">
<Modal
title={translateRaw('DEP_SIGNTX')}
isOpen={true}
buttons={buttons}
handleClose={this.onClose}
>
<div className="QrSignerModal-qr-bounds">
<QrSigner size={300} scan={scan} account={from} rlp={rlp} onScan={this.onScan} />
</div>
</Modal>
</div>
);
}

private onClose = () => {
if (!this.props.isOpen) {
return;
}

this.props.finalize(null);
this.setState({ scan: false });
};

private onScan = (signature: string) => {
if (!this.props.isOpen) {
return;
}

this.props.finalize(signature);
this.setState({ scan: false });
};
}

function mapStateToProps(state: AppState) {
const { requested } = state.paritySigner;

if (!requested) {
return { isOpen: false };
}

const { from, rlp } = requested;

return {
isOpen: true,
from,
rlp
};
}

export default connect(mapStateToProps, { finalize })(QrSignerModal);
2 changes: 2 additions & 0 deletions common/containers/Tabs/SendTransaction/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import React from 'react';
import { connect } from 'react-redux';
import translate from 'translations';
import TabSection from 'containers/TabSection';
import QrSignerModal from 'containers/QrSignerModal';
import { UnlockHeader } from 'components/ui';
import { getWalletInst } from 'selectors/wallet';
import { AppState } from 'reducers';
Expand Down Expand Up @@ -108,6 +109,7 @@ class SendTransaction extends React.Component<Props> {
</div>
)}
</section>
<QrSignerModal />
</TabSection>
);
}
Expand Down
19 changes: 19 additions & 0 deletions common/libs/wallet/non-deterministic/parity.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { IFullWallet } from '../IWallet';

export default class ParitySignerWallet implements IFullWallet {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Appreciate the consistent nomenclature 👍

public address: string;

constructor(address: string) {
this.address = address;
}

public signRawTransaction = () =>
Promise.reject(new Error('Web3 wallets cannot sign raw transactions.'));

public signMessage = () =>
Promise.reject(new Error('Signing via Parity Signer not yet supported.'));

public getAddressString() {
return this.address;
}
}
4 changes: 3 additions & 1 deletion common/libs/wallet/non-deterministic/wallets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { decryptPrivKey } from 'libs/decrypt';
import { fromV3 } from 'libs/web-workers';
import Web3Wallet from './web3';
import AddressOnlyWallet from './address';
import ParitySignerWallet from './parity';

const EncryptedPrivateKeyWallet = (encryptedPrivateKey: string, password: string) =>
signWrapper(fromPrivateKey(decryptPrivKey(encryptedPrivateKey, password)));
Expand All @@ -26,5 +27,6 @@ export {
PrivKeyWallet,
UtcWallet,
Web3Wallet,
AddressOnlyWallet
AddressOnlyWallet,
ParitySignerWallet
};
3 changes: 3 additions & 0 deletions common/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { State as TransactionState, transaction } from './transaction';
import { State as GasState, gas } from './gas';
import { onboardStatus, State as OnboardStatusState } from './onboardStatus';
import { State as TransactionsState, transactions } from './transactions';
import { State as ParitySignerState, paritySigner } from './paritySigner';

export interface AppState {
// Custom reducers
Expand All @@ -26,6 +27,7 @@ export interface AppState {
swap: SwapState;
transaction: TransactionState;
transactions: TransactionsState;
paritySigner: ParitySignerState;
gas: GasState;
// Third party reducers (TODO: Fill these out)
routing: any;
Expand All @@ -43,6 +45,7 @@ export default combineReducers<AppState>({
deterministicWallets,
transaction,
transactions,
paritySigner,
gas,
routing: routerReducer
});
Loading