From 6e00ea3b9319e12485bab3f3838eac5f0aeafdda Mon Sep 17 00:00:00 2001
From: Mateusz Jasiuk <mateusz.jasiuk@gmail.com>
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<Props> = ({ 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<AccountStore> {
-    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<AddLedgerAccountMsg> = (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<UtilityStore>,
     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<AccountStore | false> {
     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<AccountStore | false> {
     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<AccountStore | false> {
     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<AccountStore | false> {
     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