-
Notifications
You must be signed in to change notification settings - Fork 8
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
refactor: change how addresses are derived #133
base: main
Are you sure you want to change the base?
Conversation
src/background/services/storage/schemaMigrations/migrations/wallet_v4/utils/getSecretsType.ts
Outdated
Show resolved
Hide resolved
342d67b
to
8d7d399
Compare
8d7d399
to
850bc76
Compare
src/background/services/storage/schemaMigrations/migrations/wallet_v5/wallet_v5.ts
Outdated
Show resolved
Hide resolved
.nvmrc
Outdated
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.
IIRC it's the slip10
package that we use for deriving keys with ed25519
curve that needed this version of node (or newer).
export const EVM_BASE_DERIVATION_PATH = "m/44'/60'/0'"; | ||
export const AVALANCHE_BASE_DERIVATION_PATH = "m/44'/9000'/0'"; |
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.
These could probably use better names (those are paths for the legacy xpub
and xpubXP
).
|
||
case 'ed25519': { | ||
const hdKey = slip10.fromMasterSeed(seed); | ||
key = hex.encode(hdKey.derive(derivationPath).publicKey); |
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.
Use .publicKeyRaw
here -- publicKey
is 0-prefixed (33 bytes total), we need the raw 32 bytes.
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.
Done in d218327
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.
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.
Very nice work. Especially like the documentation and the new clean secret storage model.
// it('should throw error if derivation path is missing', async () => { | ||
// moduleManager.modules = [ | ||
// { | ||
// buildDerivationPath: jest.fn().mockResolvedValue({ | ||
// EVM: 'path1', | ||
// }), | ||
// } as unknown as Module, | ||
// ]; | ||
|
||
// await expect( | ||
// addressResolver.getDerivationPathsByVM(0, DerivationPath.BIP44, [ | ||
// 'AVM', | ||
// ]), | ||
// ).rejects.toThrow(SecretsError.DerivationPathMissing); | ||
// }); | ||
}); |
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.
Please remove
init(moduleManager: ModuleManager) { | ||
this.#moduleManager = moduleManager; | ||
} |
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.
Is there a particular reason why the module manager not injected like the Network and Secrets service?
get modules(): Module[] { | ||
return this.#modules; | ||
} | ||
|
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.
Should we use some more specific getter which doesn't assume all of the modules are always loaded?
@@ -1586,7 +1678,10 @@ describe('background/services/wallet/WalletService.ts', () => { | |||
|
|||
it('returns the correct list of addresses', async () => { | |||
secretsService.getPrimaryAccountSecrets.mockResolvedValueOnce({ | |||
xpubXP, | |||
extendedPublicKeys: [ | |||
// buildExtendedPublicKey('xpub', EVM_BASE_DERIVATION_PATH), |
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.
please remove if not needed
@@ -30,6 +32,8 @@ export class BackgroundRuntime { | |||
this.lockService.activate(); | |||
this.onboardingService.activate(); | |||
this.moduleManager.activate(); | |||
|
|||
this.addressResolver.init(this.moduleManager); |
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.
Do we need to define the addressResolver here?
Can it just inject the moduleManager via DI like it does with it's other dependencies
xpub: 'xpub', | ||
xpubXP: 'xpubXP', | ||
secretType: 'ledger', | ||
btcWalletPolicyDetails: { |
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.
Should we have test scenarios where the optional properties are missing?
|
||
export default { | ||
previousSchema: legacySecretsSchema, | ||
dependencyKeys: [ACCOUNTS_STORAGE_KEY], |
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.
How do we ensure version consistency here?
Since the ACCOUNTS_STORAGE_KEY's migrations are running earlier than the wallet migrations (for now) it is possible that a future migration on the account storage will break this V5 migration. We should make sure the key's migration order stays the same.
Maybe we should use the types from the account service? Then at least a change in the types would trigger a TS error in here.
Description
Changes
Includes a schema migration for the secrets:
xpub
andxpubXP
, all primary wallets now store a collection ofpublicKeys: AddressPublicKey[]
.AddressPublicKey
includes the key itself (hex-encoded) and information on the derivation path that was used when it was derived (derivationPath
andcurve
(eithersecp256k1
ored25519
)).AddressPublicKey
s can also include abtcWalletPolicyDetails
(which did not go through any changes).Ledger
andKeystone
) will also storeextendedPublicKeys
collection.Addresses are now computed using VM modules. To compute the addresses, VM Modules will build the required derivation paths and request public keys to be derived (so they can use them to compute the addresses):
Includes the refactoring needed to support the new schema + updated tests.
In the migrations code, I added a new type of migration - one that requires information from a different storage key.
Rationale
The previous approach was not very generic -- it relied on passing multiple parameters to VM Modules, many of which were ignored by some modules and required by others. For example:
EvmModule
only relied on the extended public key (passed in asxpub
)BitcoinModule
relied on the extended public key (xpub
) andnetwork.isTestnet
.AvalancheModule
relied on the X/P extended public key (xpubXP
) and multiple properties of theNetwork
(isTestnet
andisDevnet
, which boiled down to the network's HRP)Some implementations even relied on
WalletType
-- an implementation detail of the wallet that the modules should not care about at all.Others did not care about most of the parameters they received. This made the interface for
getAddress()
method quite complex/bloated.Notes
PubKeyType
{ evm: string; xp?: string; ed25519?: string }
is still alive in the code.avalanche_getAccountPubKey
, which I wanted to keep backwards-compatible (but we should update the response for that somewhat soon, as with the addition ofed25519
it became a bit confusing).Testing
Import with <XYZ>
flowsAdd new address
flowsExport private key
flowFor all of the above, we need to make sure the wallet's state remains the same:
9000
coin type instead of60
(same as X/P chains, as opposed to EVM).BIP44
andLedger Live
derivation pathsScreenshots:
No visual changes
Checklist for the author