feat: add qr keyring package#60
Conversation
05effd9 to
ccd708e
Compare
|
Thank you @mikesposito! Maybe we should import this using |
|
@danroc this PR is not moving the package from another source - this is a new implementation, and only shares a similar account derivation strategy from URs with the original keyring package currently used on clients Do you think we should bring in its commit history anyway? Also, the package is in a Keystone monorepo, which would include commits to other packages potentially unrelated |
|
Sorry @mikesposito, I didn't realise it was a new implementation! In this case it makes sense to start fresh, thank you for the clarification. |
|
This PR is marked as stale because it has been open for 60 days with no activity. Please remove the stale label or leave a comment, or it will be closed in 14 days. |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
All alerts resolved. Learn more about Socket for GitHub. This PR previously contained dependency changes with security issues that have been resolved, removed, or ignored. Ignoring alerts on:
|
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
|
Preview builds have been published. See these instructions (from the Expand for full list of packages and versions. |
8ea8799 to
a694085
Compare
cryptodev-2s
left a comment
There was a problem hiding this comment.
LGTM! I’ll just leave one small suggestion for the changelog so we won’t need to reference the initial PR later. Everything else looks good!
The new author seems trustworthy |
## Explanation <!-- Thanks for your contribution! Take a moment to answer these questions so that reviewers have the information they need to properly understand your changes: * What is the current state of things and why does it need to change? * What is the solution your changes offer and how does it work? * Are there any changes whose purpose might not obvious to those unfamiliar with the domain? * If your primary goal was to update one package but you found you had to update another one along the way, why did you do so? * If you had to upgrade a dependency, why did you do so? --> Dependent on: - ~~MetaMask/accounts#60 This PR removes all code related to the QRKeyring from KeystoneHQ, which we intend to deprecate in favor of the new QRKeyring implementation in the MetaMask accounts monorepo, that fully supports our Keyring type. ## References <!-- Are there any issues that this pull request is tied to? Are there other links that reviewers should consult to understand these changes better? Are there client or consumer pull requests to adopt any breaking changes? For example: * Fixes #12345 * Related to #67890 --> * Fixes #4341 ## Changelog <!-- THIS SECTION IS NO LONGER NEEDED. The process for updating changelogs has changed. Please consult the "Updating changelogs" section of the Contributing doc for more. --> ## Checklist - [ ] I've updated the test suite for new or updated code as appropriate - [ ] I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate - [ ] I've communicated my changes to consumers by [updating changelogs for packages I've changed](https://github.com/MetaMask/core/tree/main/docs/contributing.md#updating-changelogs), highlighting breaking changes as necessary - [ ] I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes
<!-- Please submit this PR as a draft initially. Do not mark it as "Ready for review" until the template has been completely filled out, and PR status checks have passed at least once. --> dependent on: - ~~MetaMask/accounts#60 - ~~MetaMask/core#6031 ## **Description** <!-- Write a short description of the changes included in this pull request, also include relevant motivation and context. Have in mind the following questions: 1. What is the reason for the change? 2. What is the improvement/solution? --> This PR uses the `@metamask/eth-qr-keyring` package to handle QR code keyring functionality, replacing the previous implementation from `@keystonehq/metamask-airgapped-keyring`. The change should be transparent to users, as the package is a drop-in replacement. Though, under the hood, it allows to greatly simplify code related to QRKeyring-related scanning and signing, and should improve the overall ergonomics of interactions with QRKeyring. Moreover, this PR adds the first e2e tests for QR code hardware wallets, covering: - Pairing a QR code hardware wallet - Adding multiple accounts from the QR code hardware wallet [](https://codespaces.new/MetaMask/metamask-extension/pull/33893?quickstart=1) ## **Related issues** Fixes: ## **Manual testing steps** ### **Does it work with new HW devices?** 1. Checkout to this branch, and run `yarn && yarn webpack` or `yarn && yarn start` to build the extension. 2. Install the extension in your browser, and pair a QR-based hardware wallet (e.g. a Keystone device, or install AirGap Vault or imToken on an iOS/Android device). 3. Add some (3) accounts to the QRKeyring, and ensure that you can see them in the extension. 4. Go to `https://metamask.github.io/test-dapp/`, connect to the extension. 5. Ensure that you can: - Sign transactions - Sign typed data - Sign personal messages 6. Remove one QR account 7. Forget the QR device ### **Does it work with HW devices already paired?** 1. Checkout to main, and run `yarn && yarn webpack` or `yarn && yarn start` to build the extension. 2. Install the extension in your browser, and pair a QR-based hardware wallet (e.g. a Keystone device, or install AirGap Vault on an iOS/Android device). 3. Add some (3) accounts to the QRKeyring, and ensure that you can see them in the extension. 4. Checkout to this branch, and run `yarn && yarn webpack` or `yarn && yarn start` to build the extension. 5. Go to `chrome://extensions/`, and reload the extension. 6. Login to the extension, and ensure that you can see the same accounts as before. ## **Screenshots/Recordings** <!-- If applicable, add screenshots and/or recordings to visualize the before and after of your change. --> ### **Before** <!-- [screenshots/recordings] --> ### **After** <!-- [screenshots/recordings] --> ## **Pre-merge author checklist** - [ ] I've followed [MetaMask Contributor Docs](https://github.com/MetaMask/contributor-docs) and [MetaMask Extension Coding Standards](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/CODING_GUIDELINES.md). - [ ] I've completed the PR template to the best of my ability - [ ] I’ve included tests if applicable - [ ] I’ve documented my code using [JSDoc](https://jsdoc.app/) format if applicable - [ ] I’ve applied the right labels on the PR (see [labeling guidelines](https://github.com/MetaMask/metamask-extension/blob/main/.github/guidelines/LABELING_GUIDELINES.md)). Not required for external contributors. ## **Pre-merge reviewer checklist** - [ ] I've manually tested the PR (e.g. pull and build branch, run the app, test code being changed). - [ ] I confirm that this PR addresses all acceptance criteria described in the ticket it closes and includes the necessary testing evidence such as recordings and or screenshots. --------- Co-authored-by: MetaMask Bot <metamaskbot@users.noreply.github.com>
This is a new Keyring implementation compatible with QR-based wallets, meant to replace
@keystonehq/metamask-airgapped-keyring.The goals are:
This package can be tested on Extension through this PR: MetaMask/metamask-extension#33893
QRKeyringimplementation #144How does it work?
The QrKeyring in this package is split into two main components:
QrKeyring: The main keyring class that exposes the Keyring API. Some of its logic is implemented in internal utility classes:Device: A stateful internal wrapper for the keyring's public keys derivation logic and account management. In the code it's implemented by a separate class, but it can be seen as an internal component of the keyring, and It is not exported from the package.QrKeyringBridge: The interface that a transport bridge must implement: it allows the client to control how QR codes are scanned. It's supposed to be implemented by a class that is injected by the client: when using KeyringController, it can be injected through a custom keyring builder (See this keyring builder that will be used by Extension).QrKeyringScannerBridge: A generic bridge that allows the client to pass function hooks at construction time, which will be called when the keyring needs to scan a QR code.Device pairing flow
Pairing is the process of initializing the keyring with a device, which is done by submitting the device details to the keyring in the form of a serialized UR, which usually comes from the "sync" QR scanned on the device. The client can trigger this process in three ways:
SerializedURobject to theQrKeyringconstructor.QrKeyring.pairDevice()method, which allows the client to pass aSerializedURobject at any time.QrKeyring.getFirstPage()method, which will trigger a scan request if no device is paired yet.Extension uses
getFirstPage()to trigger the pairing flow, which will then request a scan through the bridgeLike for other Hardare wallets, the pairing flow is initiated in
connectHardware()method. See the sequence diagram below for the pairing flow on Extension, and see the ExtensionrequestScancallback for the implementation of the scan logic.sequenceDiagram autonumber participant A as MM Extension create participant B as QrKeyring A->>B: getFirstPage() alt if not initialized create participant C as Bridge B->>C: requestScan(PAIRING) C-->>+A: note over A,C: The client controls the bridge and implements the scan logic A-->>-C: destroy C C->>B: return CBOR create participant D as Int. derivation logic B->>D: init(CBOR) end B->>D: accountAtIndex(0..4) destroy D D->>B: return accounts destroy B B->>A: return accountsMobile
TBD - Likely using
.pairDevice()given the current implementation of QR interactions on mobile.Signing flow
sequenceDiagram autonumber participant A as Client create participant B as QrKeyring A->>B: sign{Transaction, PersonalMessage, TypedData}() alt not initialized B->>A: Fail end create participant C as Bridge B->>C: requestScan(SIGN) C-->>+A: note over A,C: The client controls the bridge and implements the scan logic A-->>-C: destroy C C->>B: return signature CBOR destroy B B->>A: return hex signatureAccount addition flow
No scan required after the initial pairing.
sequenceDiagram autonumber participant A as Client alt non sequential account addition create participant B as QrKeyring A->>B: setAccountToUnlock( i ) end A->>B: addAccounts( n ) loop n-times create participant D as Int. derivation logic B->>D: accountAtIndex( i ) destroy D D->>B: return account end B->>A: return created accountsExamples