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

Parity Signer #1349

merged 20 commits into from Apr 6, 2018

Conversation

maciejhirsz
Copy link
Contributor

@maciejhirsz maciejhirsz commented Mar 20, 2018

Closes #1056

Description

Introduces Parity Signer as another secure wallet option on the main screen. Account is unlocked by scanning a QR code of the address from the mobile app. Signing transactions is done in a modal overlaying the UI that first displays the QR code of the RLP of the transaction and then scans for the signature QR code from the mobile app.

Changes

  • New secure wallet has been added.
  • New modal has been added to allow signing of the transaction while the Promise is being resolved.
  • New reducers and actions have been added in order to pipe through the transaction from the UI to the Promise required by the IFullWallet interface.
  • Added @parity/qr-signer as a new dependency.

Steps to Test

  1. Download the Parity Signer mobile app (iOS / Android). Create an account on it, send some ETH to the account (works with testnets).
  2. Click on the "Parity Signer" option on the main screen.
  3. Scan the account QR code from Parity Signer to unlock the wallet.
  4. Create any kind of a transaction.
  5. Sign the transaction using the app.
  6. Make sure that the transaction goes through and is mined on chain.

Due to interactions with the webcam, integration tests might be tricky unless a lot of the stuff gets mocked away.

TODO / Known issues

  • Needs a proper icon on the home screen.
  • ParitySigner.tsx needs to be moved to the containers.
  • UI needs some love (things are off-center, some paddings would be nice, some copy explaining what it is with links to the app would be nice).
  • Parity Signer needs a help website (linking to README.md for now...).

Screenshots



@dternyak
Copy link
Contributor

dternyak commented Mar 21, 2018

Exciting to see this in action! 😁

@maciejhirsz Are there any plans to support testnets on the parity mobile signer?

Additionally, we'll want to disable Parity when non-ethereum networks are selected (e.g. disable parity option when network is ETC).

We'll also want to disable Parity Signer as an option for signing messages, similar to how we disable Trezor (I see there is a red alert once parity is selected to sign a message, but it would be better disable the option altogether)
screen shot 2018-03-21 at 1 42 21 pm

@maciejhirsz
Copy link
Contributor Author

Testnets are already supported, or did you run into issues? The UI doesn't separate accounts by network, but the chainId is correctly handled when signing the transaction.

Signing messages should be possible with the Parity Signer, I haven't investigated that yet, the UI (on the mobile app) might be a bit dumb about it, so will disable the panel there for now. Also will look at disabling it for non-Ethereum networks.

@dternyak
Copy link
Contributor

Got it. I didn't attempt a testnet tx as I didn't see a way to select testnet on the app, but it's good to see that's supported.

👍 to message signing as a later functionality.

@maciejhirsz
Copy link
Contributor Author

maciejhirsz commented Mar 26, 2018

@dternyak I've been looking into it, and I don't really see any issues why the signer shouldn't be able to handle ETC and other networks as well. Basically anything that can be handled by keystore file / privatekey currently, should work with the app, unless there are some differences in how those networks sign txs/messages that I'm unaware of?

@maciejhirsz maciejhirsz changed the title [WIP] Parity Signer integration Parity Signer integration Mar 26, 2018
@maciejhirsz
Copy link
Contributor Author

maciejhirsz commented Mar 26, 2018

Updated the PR:

  • All UI stuff is fixed, latest develop merged back.
  • Disabled Parity Signer for signing transactions (subject to future PR).
  • Removed the [WIP] 🎉.

FYI: there will be a nicer logo / icon in near future.

Copy link
Contributor

@wbobeirne wbobeirne left a comment

Choose a reason for hiding this comment

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

Definitely let me know if any of this feedback seems off base!

@@ -219,6 +220,7 @@ export default class Footer extends React.PureComponent<Props, State> {
</div>
</footer>

<QrSignerModal />
Copy link
Contributor

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.

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.

margin: 0 0.4em;
}

.qr-bounds {
Copy link
Contributor

Choose a reason for hiding this comment

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

We use SuitCSS naming convention to avoid class name conflicts. Likewise, the a img above would be better suited by class names.

</div>
<p>{translate('ADD_PARITY_2')}</p>
<p>
<a href="https://itunes.apple.com/us/app/parity-signer/id1218174838" rel="nofollow">
Copy link
Contributor

Choose a reason for hiding this comment

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

Use the NewTabLink component here.

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).

if (!isValidETHAddress(address)) {
console.error('Invalid 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.

@@ -0,0 +1,8 @@
.QrSignerModal {
.qr-bounds {
Copy link
Contributor

Choose a reason for hiding this comment

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

Prefer SuitCSS naming convention.

// values in: wallets/non-deterministic/parity.ts
tx.v = Buffer.from([tx._chainId]);
tx.r = Buffer.from([0]);
tx.s = Buffer.from([0]);
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like you're mutating the tx props object here. If you want to alter something passed in as props, you'll need to copy it and save it in state. Something like...

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 });
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 (tx.serialize(false) to match tx.hash(false)), but that's not the option. I can make a helper function for it here, and make a PR to ethereumjs.

Copy link
Contributor

Choose a reason for hiding this comment

The 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

const tx = { ...this.props.tx };

in render.

resolve(tx.serialize());
};

this.setWalletQrTransaction(tx, from, onSignature, onCancel);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason this is a callback argument in constructor, instead of just resolving the promise with these variables? Would be nice if the wallet was more agnostic to where it gets used, so that you could use the same wallet instance to sign transactions with two different implementations.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, this is the part I'm least happy about, and it goes along with how QrSignerModal is done. The callbacks go up to the container and are in turn placed in redux state for the modal to use.

The component that instantiates the wallet (ParitySignerDecrypt container in this case) has no knowledge of the modal, and is unmounted after unlocking so it cannot host the QrSignerModal itself, hence I'm passing these things over redux, which works but also feels over-engineered for my tastes.

Ideally I'd be able to pass the callbacks from the wallet to the modal as props without going through all these gymnastics, but I haven't been able to figure out how to do it any cleaner so far (my understanding of the existing code base is still pretty shallow).

Copy link
Contributor

Choose a reason for hiding this comment

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

We came up with a migration strategy to move these things to Redux via slack DMs. Those changes probably won't land for a little while though.

export const INITIAL_STATE: State = {
inst: null,
config: null,
signViaQr: null,
Copy link
Contributor

Choose a reason for hiding this comment

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

IMO (Would definitely like additional opinions) this should probably be its own reducer with its own set of actions / sagas. I think this is pretty separate from the other wallet processes, and is specific to only the parity signer.

Copy link
Contributor

Choose a reason for hiding this comment

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

I definitely see maintainability improvements with such a separation of concerns 👍

@dternyak dternyak changed the title Parity Signer integration Parity Signer Mar 26, 2018
@coveralls
Copy link

coveralls commented Apr 6, 2018

Coverage Status

Coverage decreased (-0.4%) to 33.438% when pulling 3f4a578 on maciejhirsz:paritysigner into c65296d on MyCryptoHQ:develop.

wbobeirne
wbobeirne previously approved these changes Apr 6, 2018
Copy link
Contributor

@wbobeirne wbobeirne left a comment

Choose a reason for hiding this comment

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

Great refactor, tested on https://mycryptobuilds.com/047b7a9ff762bb9b7634a1cd886ce9af53f05cfa and it still works great.

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.

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.


export default class ParityWallet implements IFullWallet {
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 👍

@@ -177,7 +177,7 @@ const WalletDecrypt = withRouter<Props>(
component: ParitySignerDecrypt,
initialParams: {},
unlock: this.props.setWallet,
helpLink: 'https://github.com/paritytech/parity-signer/blob/master/README.md'
helpLink: 'https://wiki.parity.io/Parity-Signer-Mobile-App-MyCrypto-tutorial.md'
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm assuming this will go online after launch?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@wbobeirne wbobeirne mentioned this pull request Apr 6, 2018
3 tasks
wbobeirne
wbobeirne previously approved these changes Apr 6, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Integrate Parity Signer
4 participants