Skip to content

Keystone USB integration #119

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

Open
wants to merge 80 commits into
base: main
Choose a base branch
from

Conversation

Charon-Fan
Copy link

Description

  • Supports X/P chain tx signing via Keystone3

Changes

  • Added USB signing process via Keystone3

Testing

Screenshots:

Checklist for the author

Tick each of them when done or if not applicable.

  • I've covered new/modified business logic with Jest test cases.
  • I've tested the changes myself before sending it to code review and QA.

ww3512687 and others added 30 commits November 26, 2024 18:13
@Charon-Fan Charon-Fan marked this pull request as ready for review May 20, 2025 01:30
@Charon-Fan Charon-Fan changed the title [WIP] Keystone USB integration Keystone USB integration May 20, 2025
Copy link
Member

@meeh0w meeh0w left a comment

Choose a reason for hiding this comment

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

I left a few comments. Besides those, please run the following commands and fix the errors:

  1. yarn typecheck
  2. yarn lint
  3. yarn test

Also run yarn scanner -- it will update the translation files.

Comment on lines +878 to +885
if (!hasAVMPublicKey) {
const publicKeyAVM = AddressPublicKey.fromExtendedPublicKeys(
secrets.extendedPublicKeys,
'secp256k1',
derivationPathAVM,
).toJSON();
newPublicKeys.push(publicKeyAVM);
}
Copy link
Member

Choose a reason for hiding this comment

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

I believe this will break onboarding and adding accounts for previous Keystone iterations, where the extended public key for Avalanche is not provided.

It should be iffed and only run for Keystone3 (iThink™)

Comment on lines +577 to +616
if (
secretType === SecretType.Keystone ||
secretType === SecretType.Keystone3Pro
) {
const accountIndexToUse =
accountIndex === undefined ? secrets.account.index : accountIndex;

const derivationPathEVM = getAddressDerivationPath(
accountIndexToUse,
secrets.derivationPathSpec,
'EVM',
);
const derivationPathAVM = getAddressDerivationPath(
accountIndexToUse,
secrets.derivationPathSpec,
'AVM',
);
const evmExtendedPubKey = getExtendedPublicKeyFor(
secrets.extendedPublicKeys,
derivationPathEVM,
'secp256k1',
);
const avmExtendedPubKey = getExtendedPublicKeyFor(
secrets.extendedPublicKeys,
derivationPathAVM,
'secp256k1',
);

assertPresent(evmExtendedPubKey, SecretsError.PublicKeyNotFound);

return new KeystoneWallet(
secrets.masterFingerprint,
accountIndexToUse,
this.keystoneService,
network.chainId,
tabId,
evmExtendedPubKey.key,
avmExtendedPubKey ? avmExtendedPubKey.key : undefined,
);
}
Copy link
Member

Choose a reason for hiding this comment

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

Since this whole clause will only run for X- and P- chains, I think we should error out for SecretType.Keystone and assert the AVM key being present for SecretType.Keystone3Pro.

await getAddressFromXpubKey(xpubValue, accountIndex + 1, newAddresses);
}
if (accountIndex >= 2) {
capture('OnboardingKeystoneConnected');
Copy link
Member

Choose a reason for hiding this comment

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

Let's make analytics events specific to Keystone 3, this way we know how many users use each of the device versions.

Suggested change
capture('OnboardingKeystoneConnected');
capture('OnboardingKeystone3Connected');

Same for other capture(...) calls.

Copy link
Member

Choose a reason for hiding this comment

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

Copy paste mistake, I guess. This whole file talks about Ledger, not Keystone.

Comment on lines +50 to +52
const app = new KeystoneUSBEthSDK(
(await createKeystoneTransport()) as any,
);
Copy link
Member

Choose a reason for hiding this comment

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

My Keystone3 is acting up (security upgrade is required and it doesn't seem to be able to go above the required 40% battery charge...).

With that in mind, I can't really test it at the moment, but I'm struggling to understand how this part of the code is supposed to work. I took a look at the createKeystoneTransport function in your SDKs and I'm pretty sure it simply cannot work in the extension realm. It uses the navigator.usb.requestDevice internally, which is not available in the extension's service worker.

This is exactly why with Ledger, we're implementing our own LedgerTransport which pushes out data into the extension's popup, from which we actually can access the USB transport.

Please take a look at these diagrams:

  1. Ledger architecture
  2. Ledger signing flow

Have you tested the X- and P-Chain signing on this branch with Keystone3 connected?

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.

9 participants