From 6e00ea3b9319e12485bab3f3838eac5f0aeafdda Mon Sep 17 00:00:00 2001 From: Mateusz Jasiuk Date: Tue, 31 Dec 2024 12:32:58 +0100 Subject: [PATCH] feat: wip2 --- .../src/App/Accounts/ParentAccounts.tsx | 17 ++++----- .../src/Setup/Ledger/LedgerConnect.tsx | 2 ++ .../src/Setup/Ledger/LedgerImport.tsx | 7 +++- apps/extension/src/Setup/Setup.tsx | 4 +-- apps/extension/src/Setup/query.ts | 14 ++++++-- apps/extension/src/Setup/types.ts | 2 ++ .../src/background/keyring/handler.ts | 20 +++++++++-- .../src/background/keyring/keyring.ts | 36 ++++++++++++++++--- .../src/background/keyring/messages.ts | 11 +++++- .../src/background/keyring/service.ts | 8 +++-- packages/sdk/src/ledger.ts | 27 +++++++++----- packages/shared/lib/src/types/masp.rs | 11 ++++++ 12 files changed, 122 insertions(+), 37 deletions(-) diff --git a/apps/extension/src/App/Accounts/ParentAccounts.tsx b/apps/extension/src/App/Accounts/ParentAccounts.tsx index b437d43548..1befaa8cbd 100644 --- a/apps/extension/src/App/Accounts/ParentAccounts.tsx +++ b/apps/extension/src/App/Accounts/ParentAccounts.tsx @@ -7,12 +7,13 @@ import { KeyListItem, Stack, } from "@namada/components"; -import { AccountType, DerivedAccount } from "@namada/types"; +import { DerivedAccount } from "@namada/types"; import { ParentAccountsFooter } from "App/Accounts/ParentAccountsFooter"; import { PageHeader } from "App/Common"; import routes from "App/routes"; import { ParentAccount } from "background/keyring"; import { AccountContext } from "context"; +import invariant from "invariant"; import { openSetupTab } from "utils"; /** @@ -26,20 +27,14 @@ export const ParentAccounts = (): JSX.Element => { accounts: allAccounts, changeActiveAccountId, } = useContext(AccountContext); - // We check which accounts need to be re-imported const accounts = allAccounts - .filter( - (account) => account.parentId || account.type === AccountType.Ledger - ) + .filter((account) => account.parentId) .map((account) => { - const outdated = - account.type !== AccountType.Ledger && - typeof account.pseudoExtendedKey === "undefined"; + const outdated = typeof account.pseudoExtendedKey === "undefined"; - // The only account without a parent is the ledger account - const parent = - parentAccounts.find((pa) => pa.id === account.parentId) || account; + const parent = parentAccounts.find((pa) => pa.id === account.parentId); + invariant(parent, `Parent account not found for account ${account.id}`); return { ...parent, outdated }; }); diff --git a/apps/extension/src/Setup/Ledger/LedgerConnect.tsx b/apps/extension/src/Setup/Ledger/LedgerConnect.tsx index 387038e70f..4411197950 100644 --- a/apps/extension/src/Setup/Ledger/LedgerConnect.tsx +++ b/apps/extension/src/Setup/Ledger/LedgerConnect.tsx @@ -45,6 +45,8 @@ export const LedgerConnect: React.FC = ({ path, setPath }) => { state: { address, publicKey, + viewingKey: asd.xfvk, + pseudoExtendedKey: asd.pex, }, }); } catch (e) { diff --git a/apps/extension/src/Setup/Ledger/LedgerImport.tsx b/apps/extension/src/Setup/Ledger/LedgerImport.tsx index 6d9fb4ef50..32657dd92f 100644 --- a/apps/extension/src/Setup/Ledger/LedgerImport.tsx +++ b/apps/extension/src/Setup/Ledger/LedgerImport.tsx @@ -10,6 +10,8 @@ import { useLocation, useNavigate } from "react-router-dom"; type LedgerImportLocationState = { address: string; publicKey: string; + viewingKey: string; + pseudoExtendedKey: string; }; type LedgerProps = { @@ -55,12 +57,15 @@ export const LedgerImport = ({ await accountManager.savePassword(password); } - const { address, publicKey } = locationState; + const { address, publicKey, viewingKey, pseudoExtendedKey } = + locationState; const account = await accountManager.saveLedgerAccount({ alias, address, publicKey, path, + viewingKey, + pseudoExtendedKey, }); navigate(routes.ledgerComplete(), { diff --git a/apps/extension/src/Setup/Setup.tsx b/apps/extension/src/Setup/Setup.tsx index c08e1a3ea5..604ca321c7 100644 --- a/apps/extension/src/Setup/Setup.tsx +++ b/apps/extension/src/Setup/Setup.tsx @@ -110,8 +110,8 @@ export const Setup: React.FC = () => { const prettyAccountSecret = accountSecret.t === "Mnemonic" ? "mnemonic" - : accountSecret.t === "PrivateKey" ? "private key" - : assertNever(accountSecret); + : accountSecret.t === "PrivateKey" ? "private key" + : assertNever(accountSecret); setCompletionStatusInfo(`Encrypting and storing ${prettyAccountSecret}.`); // Create parent account diff --git a/apps/extension/src/Setup/query.ts b/apps/extension/src/Setup/query.ts index ea8be11553..2073e08ad2 100644 --- a/apps/extension/src/Setup/query.ts +++ b/apps/extension/src/Setup/query.ts @@ -13,7 +13,7 @@ import { DeriveAccountDetails, LedgerAccountDetails } from "./types"; // Wrap account management calls with extension requester instance export class AccountManager { - constructor(private readonly requester: ExtensionRequester) {} + constructor(private readonly requester: ExtensionRequester) { } /** * Set password for the extension @@ -88,10 +88,18 @@ export class AccountManager { async saveLedgerAccount( details: LedgerAccountDetails ): Promise { - const { alias, address, publicKey, path } = details; + const { alias, address, publicKey, path, pseudoExtendedKey, viewingKey } = + details; return (await this.requester.sendMessage( Ports.Background, - new AddLedgerAccountMsg(alias, address, publicKey, path) + new AddLedgerAccountMsg( + alias, + address, + publicKey, + path, + pseudoExtendedKey, + viewingKey + ) )) as AccountStore; } } diff --git a/apps/extension/src/Setup/types.ts b/apps/extension/src/Setup/types.ts index 4ea8253ed3..389187bd89 100644 --- a/apps/extension/src/Setup/types.ts +++ b/apps/extension/src/Setup/types.ts @@ -19,4 +19,6 @@ export type LedgerAccountDetails = { path: Bip44Path; address: string; publicKey: string; + pseudoExtendedKey: string; + viewingKey: string; }; diff --git a/apps/extension/src/background/keyring/handler.ts b/apps/extension/src/background/keyring/handler.ts index 2d86d20d21..25e39a1785 100644 --- a/apps/extension/src/background/keyring/handler.ts +++ b/apps/extension/src/background/keyring/handler.ts @@ -105,8 +105,22 @@ const handleAddLedgerAccountMsg: ( service: KeyRingService ) => InternalHandler = (service) => { return async (_, msg) => { - const { alias, address, publicKey, bip44Path } = msg; - return await service.saveLedger(alias, address, publicKey, bip44Path); + const { + alias, + address, + publicKey, + bip44Path, + pseudoExtendedKey, + viewingKey, + } = msg; + return await service.saveLedger( + alias, + address, + publicKey, + bip44Path, + pseudoExtendedKey, + viewingKey + ); }; }; @@ -179,7 +193,7 @@ const handleQueryAccountsMsg: ( const output = query && query.accountId ? await service.queryAccountsByParentId(query.accountId) - : await service.queryAccounts(); + : await service.queryAccounts(); return output; }; diff --git a/apps/extension/src/background/keyring/keyring.ts b/apps/extension/src/background/keyring/keyring.ts index 54352f3233..095156a080 100644 --- a/apps/extension/src/background/keyring/keyring.ts +++ b/apps/extension/src/background/keyring/keyring.ts @@ -63,7 +63,7 @@ export class KeyRing { protected readonly sdkService: SdkService, protected readonly utilityStore: KVStore, protected readonly localStorage: LocalStorage - ) {} + ) { } public get status(): KeyRingStatus { return this._status; @@ -101,7 +101,9 @@ export class KeyRing { alias: string, address: string, publicKey: string, - bip44Path: Bip44Path + bip44Path: Bip44Path, + pseudoExtendedKey: string, + viewingKey: string ): Promise { const id = generateId(UUID_NAMESPACE, alias, address); const accountStore: AccountStore = { @@ -125,6 +127,30 @@ export class KeyRing { sensitive, }); + const shieldedId = generateId(UUID_NAMESPACE, alias, address); + const shieldedAccountStore: AccountStore = { + id: shieldedId, + alias, + address, + publicKey, + owner: viewingKey, + path: bip44Path, + pseudoExtendedKey, + parentId: id, + type: AccountType.ShieldedKeys, + source: "imported", + timestamp: 0, + }; + + const shieldedSensitive = await this.vaultService.encryptSensitiveData({ + text: "", + passphrase: "", + }); + await this.vaultStorage.add(KeyStore, { + public: shieldedAccountStore, + sensitive: shieldedSensitive, + }); + await this.setActiveAccount(id, AccountType.Ledger); return accountStore; } @@ -436,7 +462,7 @@ export class KeyRing { const deriveFn = ( type === AccountType.PrivateKey ? this.deriveTransparentAccount - : this.deriveShieldedAccount).bind(this); + : this.deriveShieldedAccount).bind(this); const { secret } = await this.getParentSecret(parentId); const info = deriveFn(secret, derivationPath, parentAccount); @@ -719,13 +745,13 @@ export class KeyRing { const key = disposableKey ? disposableKey.privateKey - : await this.getSigningKey(signer); + : await this.getSigningKey(signer); // If disposable key is provided, use it to map real address to spending key const spendingKeys = disposableKey ? [await this.getSpendingKey(disposableKey.realAddress)] - : []; + : []; const { signing } = this.sdkService.getSdk(); diff --git a/apps/extension/src/background/keyring/messages.ts b/apps/extension/src/background/keyring/messages.ts index 927657485d..edcd9142e9 100644 --- a/apps/extension/src/background/keyring/messages.ts +++ b/apps/extension/src/background/keyring/messages.ts @@ -186,7 +186,8 @@ export class AddLedgerAccountMsg extends Message { public readonly address: string, public readonly publicKey: string, public readonly bip44Path: Bip44Path, - public readonly parentId?: string + public readonly pseudoExtendedKey: string, + public readonly viewingKey: string ) { super(); } @@ -207,6 +208,14 @@ export class AddLedgerAccountMsg extends Message { if (!this.bip44Path) { throw new Error("BIP44 Path was not provided!"); } + + if (!this.pseudoExtendedKey) { + throw new Error("Pseudo extended key was not provided!"); + } + + if (!this.viewingKey) { + throw new Error("Viewing key was not provided!"); + } } route(): string { diff --git a/apps/extension/src/background/keyring/service.ts b/apps/extension/src/background/keyring/service.ts index 4be1e92b53..369b9a3881 100644 --- a/apps/extension/src/background/keyring/service.ts +++ b/apps/extension/src/background/keyring/service.ts @@ -81,7 +81,9 @@ export class KeyRingService { alias: string, address: string, publicKey: string, - bip44Path: Bip44Path + bip44Path: Bip44Path, + pseudoExtendedKey: string, + viewingKey: string ): Promise { const account = await this._keyRing.queryAccountByAddress(address); if (account) { @@ -94,7 +96,9 @@ export class KeyRingService { alias, address, publicKey, - bip44Path + bip44Path, + pseudoExtendedKey, + viewingKey ); await this.broadcaster.updateAccounts(); diff --git a/packages/sdk/src/ledger.ts b/packages/sdk/src/ledger.ts index f334b77a17..5b26c55bcf 100644 --- a/packages/sdk/src/ledger.ts +++ b/packages/sdk/src/ledger.ts @@ -12,14 +12,19 @@ import { ResponseVersion, ResponseViewKey, } from "@zondax/ledger-namada"; -import { ExtendedViewingKey, ProofGenerationKey } from "../../shared/src"; -import { makeBip44Path, makeSaplingPath } from "./utils"; +import { + ExtendedViewingKey, + ProofGenerationKey, + PseudoExtendedKey, +} from "../../shared/src"; +import { makeBip44Path } from "./utils"; const { coinType } = chains.namada.bip44; export type LedgerAddressAndPublicKey = { address: string; publicKey: string }; export type LedgerShieldedKeys = { xfvk?: string; + pex?: string; proofGenerationKey: { ak?: string; nsk?: string; @@ -55,12 +60,7 @@ export const DEFAULT_LEDGER_BIP44_PATH = makeBip44Path(coinType, { index: 0, }); -export const DEFAULT_LEDGER_ZIP32_PATH = makeSaplingPath(coinType, { - account: 0, - change: 0, - index: 0, -}); - +export const DEFAULT_LEDGER_ZIP32_PATH = "m/32'/877'/0'"; /** * Functionality for interacting with NamadaApp for Ledger Hardware Wallets */ @@ -175,7 +175,9 @@ export class Ledger { NamadaKeys.ViewKey, promptUser ); + console.log("path", path); const asd = new ExtendedViewingKey(xfvk!); + const xvk = new ExtendedViewingKey(xfvk!); const www: ResponseProofGenKey = await this.namadaApp.retrieveKeys( path, @@ -184,9 +186,16 @@ export class Ledger { ); const xxx = ProofGenerationKey.from_bytes(www.ak!, www.nsk!); + console.log("xxx", xxx); + + const pex = PseudoExtendedKey.from(asd, xxx); + + console.log(pex.encode()); + console.log("XVK2", xvk.encode()); return { - xfvk: xfvk?.toString(), + xfvk: xvk.encode(), + pex: pex.encode(), proofGenerationKey: { ak: www?.ak?.toString(), nsk: www?.nsk?.toString(), diff --git a/packages/shared/lib/src/types/masp.rs b/packages/shared/lib/src/types/masp.rs index ec55fa7758..e3088f0432 100644 --- a/packages/shared/lib/src/types/masp.rs +++ b/packages/shared/lib/src/types/masp.rs @@ -93,6 +93,17 @@ impl PseudoExtendedKey { .expect("Deserializing ProofGenerationKey should not fail!"), ) } + + pub fn from(xvk: ExtendedViewingKey, pgk: ProofGenerationKey) -> Self { + web_sys::console::log_1(&"PseudoExtendedKey::from".into()); + let mut pxk = zip32::PseudoExtendedKey::from(zip32::ExtendedFullViewingKey::from(xvk.0)); + web_sys::console::log_1(&"PseudoExtendedKey::from: after from".into()); + // TODO: expect + pxk.augment_proof_generation_key(pgk.0) + .expect("Augmenting proof generation key should not fail!"); + + Self(pxk) + } } /// Wrap ExtendedSpendingKey