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
Parity Signer #1349
Changes from 5 commits
b9a6319
83feb9a
8749a8a
ac6149b
5ee0013
b73a4b4
4a7dd0f
ecdfab7
e04e2e6
a7380d0
1979c68
047b7a9
89397e9
42323be
0753ce8
5c4396d
e64aa22
05419a6
234d199
3f4a578
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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', | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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' | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
.ParitySignerUnlock { | ||
a img { | ||
display: inline-block; | ||
height: 3em; | ||
margin: 0 0.4em; | ||
} | ||
|
||
.qr-bounds { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use SuitCSS naming convention to avoid class name conflicts. Likewise, the |
||
width: 300px; | ||
height: 300px; | ||
display: inline-block; | ||
margin-bottom: 1.5em; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
import React, { PureComponent } from 'react'; | ||
import { connect } from 'react-redux'; | ||
import translate from 'translations'; | ||
import QrSigner from '@parity/qr-signer'; | ||
import { isValidETHAddress } from 'libs/validators'; | ||
import { ParitySignerWallet } from 'libs/wallet'; | ||
import { | ||
setWalletQrTransaction, | ||
TSetWalletQrTransaction, | ||
finalizeQrTransaction, | ||
TFinalizeQrTransaction | ||
} from 'actions/wallet'; | ||
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 { | ||
setWalletQrTransaction: TSetWalletQrTransaction; | ||
finalizeQrTransaction: TFinalizeQrTransaction; | ||
onUnlock(param: any): void; | ||
} | ||
|
||
class ParitySignerDecrypt extends PureComponent<Props> { | ||
public render() { | ||
return ( | ||
<div className="ParitySignerUnlock"> | ||
<div className="qr-bounds"> | ||
<QrSigner | ||
size={300} | ||
scan={true} | ||
onScan={this.unlockAddress} | ||
styles={{ display: 'inline-block' }} | ||
/> | ||
</div> | ||
<p>{translate('ADD_PARITY_2')}</p> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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> | ||
<a href="https://itunes.apple.com/us/app/parity-signer/id1218174838" rel="nofollow"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Use the |
||
<img src={AppStoreBadge} alt="App Store" /> | ||
</a> | ||
<a href="https://play.google.com/store/apps/details?id=com.nativesigner" rel="nofollow"> | ||
<img src={GooglePlayBadge} alt="Google Play" /> | ||
</a> | ||
</p> | ||
</div> | ||
); | ||
} | ||
|
||
private unlockAddress = (address: string) => { | ||
if (!isValidETHAddress(address)) { | ||
console.error('Invalid address!'); | ||
return; | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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, | ||
this.props.setWalletQrTransaction, | ||
this.props.finalizeQrTransaction | ||
) | ||
); | ||
}; | ||
} | ||
|
||
export default connect(() => ({}), { | ||
setWalletQrTransaction, | ||
finalizeQrTransaction | ||
})(ParitySignerDecrypt); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
.QrSignerModal { | ||
.qr-bounds { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Prefer SuitCSS naming convention. |
||
width: 300px; | ||
height: 300px; | ||
display: inline-block; | ||
margin: 0.3em 0; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import React from 'react'; | ||
import { connect } from 'react-redux'; | ||
import translate, { translateRaw } from 'translations'; | ||
import EthTx from 'ethereumjs-tx'; | ||
import QrSigner from '@parity/qr-signer'; | ||
import { AppState } from 'reducers'; | ||
import Modal, { IButton } from 'components/ui/Modal'; | ||
import './index.scss'; | ||
|
||
interface State { | ||
scan: boolean; | ||
} | ||
|
||
interface PropsClosed { | ||
isOpen: false; | ||
} | ||
|
||
interface PropsOpen { | ||
isOpen: true; | ||
from: string; | ||
tx: EthTx; | ||
onSignature(signature: string): void; | ||
onCancel(): void; | ||
} | ||
|
||
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 { tx, from } = this.props; | ||
|
||
// Poor man's serialize without signature. | ||
// All those values are later overriden by actual signature | ||
// values in: wallets/non-deterministic/parity.ts | ||
tx.v = Buffer.from([tx._chainId]); | ||
tx.r = Buffer.from([0]); | ||
tx.s = Buffer.from([0]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Looks like you're mutating the public componentDidMount() {
if (this.props.tx) {
this.makeStateTx(this.props);
}
}
public componentWillReceiveProps(nextProps: Props) {
if (nextProps.tx && this.props.tx !== nextProps.tx) {
this.makeStateTx(nextProps);
}
}
private makeStateTx(props: Props) {
const tx = { ...props.tx };
tx.v = Buffer.from([tx._chainId]);
tx.r = Buffer.from([0]);
tx.s = Buffer.from([0]);
this.setState({ tx });
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The values aren't read anywhere down the line, so it shouldn't really matter. Ideally there would be a way to serialize a transaction to RLP in ethereumjs without including the signature ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem is it makes for non-deterministic redux state, makes it so there's no way to determine if the tx object has changed (without checking deep equality) and in general is just advised against in React. If you want to avoid the state boilerplate, it could at least do
in |
||
|
||
const rlp = '0x' + tx.serialize().toString('hex'); | ||
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="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.onCancel(); | ||
this.setState({ scan: false }); | ||
}; | ||
|
||
private onScan = (signature: string) => { | ||
if (!this.props.isOpen) { | ||
return; | ||
} | ||
|
||
this.props.onSignature(signature); | ||
this.setState({ scan: false }); | ||
}; | ||
} | ||
|
||
function mapStateToProps(state: AppState) { | ||
const { signViaQr } = state.wallet; | ||
|
||
if (!signViaQr) { | ||
return { isOpen: false }; | ||
} | ||
|
||
const { tx, from, onSignature, onCancel } = signViaQr; | ||
|
||
return { | ||
isOpen: true, | ||
tx, | ||
from, | ||
onSignature, | ||
onCancel | ||
}; | ||
} | ||
|
||
export default connect(mapStateToProps, {})(QrSignerModal); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would prefer this component only be rendered where it contextually makes sense, somewhere in the send form.