Skip to content

Commit

Permalink
feat: ledger shielded accounts
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszjasiuk committed Jan 8, 2025
1 parent 2892467 commit 2a07e11
Show file tree
Hide file tree
Showing 17 changed files with 196 additions and 50 deletions.
2 changes: 1 addition & 1 deletion apps/extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"@ledgerhq/hw-transport": "^6.31.4",
"@ledgerhq/hw-transport-webhid": "^6.29.4",
"@ledgerhq/hw-transport-webusb": "^6.29.4",
"@zondax/ledger-namada": "^1.0.0",
"@zondax/ledger-namada": "^2.0.0",
"bignumber.js": "^9.1.1",
"buffer": "^6.0.3",
"dompurify": "^3.0.2",
Expand Down
17 changes: 6 additions & 11 deletions apps/extension/src/App/Accounts/ParentAccounts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";

/**
Expand All @@ -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 };
});
Expand Down
24 changes: 24 additions & 0 deletions apps/extension/src/Setup/Ledger/LedgerConnect.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import { chains } from "@namada/chains";
import { ActionButton, Alert, Image, Stack } from "@namada/components";
import { Ledger as LedgerApp, makeBip44Path } from "@namada/sdk/web";
import sdkInit from "@namada/sdk/web-init";
import {
ExtendedViewingKey,
ProofGenerationKey,
PseudoExtendedKey,
} from "@namada/shared";
import { Bip44Path } from "@namada/types";
import { LedgerError } from "@zondax/ledger-namada";
import { LedgerStep } from "Setup/Common";
Expand Down Expand Up @@ -33,14 +39,32 @@ export const LedgerConnect: React.FC<Props> = ({ path, setPath }) => {
}

setIsLedgerConnecting(true);

await sdkInit();
const { xfvk, pgk } = await ledger.getShieldedKeys();
const { address, publicKey } = await ledger.showAddressAndPublicKey(
makeBip44Path(chains.namada.bip44.coinType, path)
);
const extendedViewingKey = new ExtendedViewingKey(xfvk);
const encodedExtendedViewingKey = extendedViewingKey.encode();
const encodedPaymentAddress = extendedViewingKey
.default_payment_address()
.encode();
const proofGenerationKey = ProofGenerationKey.from_bytes(pgk.ak, pgk.nsk);
const pseudoExtendedKey = PseudoExtendedKey.from(
extendedViewingKey,
proofGenerationKey
);
const encodedPseudoExtendedKey = pseudoExtendedKey.encode();

setIsLedgerConnecting(false);
navigate(routes.ledgerImport(), {
state: {
address,
publicKey,
extendedViewingKey: encodedExtendedViewingKey,
pseudoExtendedKey: encodedPseudoExtendedKey,
paymentAddress: encodedPaymentAddress,
},
});
} catch (e) {
Expand Down
14 changes: 13 additions & 1 deletion apps/extension/src/Setup/Ledger/LedgerImport.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import { useLocation, useNavigate } from "react-router-dom";
type LedgerImportLocationState = {
address: string;
publicKey: string;
extendedViewingKey: string;
pseudoExtendedKey: string;
paymentAddress: string;
};

type LedgerProps = {
Expand Down Expand Up @@ -55,12 +58,21 @@ export const LedgerImport = ({
await accountManager.savePassword(password);
}

const { address, publicKey } = locationState;
const {
address,
publicKey,
extendedViewingKey,
pseudoExtendedKey,
paymentAddress,
} = locationState;
const account = await accountManager.saveLedgerAccount({
alias,
address,
publicKey,
path,
extendedViewingKey,
pseudoExtendedKey,
paymentAddress,
});

navigate(routes.ledgerComplete(), {
Expand Down
4 changes: 2 additions & 2 deletions apps/extension/src/Setup/Setup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
22 changes: 19 additions & 3 deletions apps/extension/src/Setup/query.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -88,10 +88,26 @@ export class AccountManager {
async saveLedgerAccount(
details: LedgerAccountDetails
): Promise<AccountStore> {
const { alias, address, publicKey, path } = details;
const {
alias,
address,
publicKey,
path,
extendedViewingKey,
pseudoExtendedKey,
paymentAddress,
} = details;
return (await this.requester.sendMessage(
Ports.Background,
new AddLedgerAccountMsg(alias, address, publicKey, path)
new AddLedgerAccountMsg(
alias,
address,
publicKey,
path,
extendedViewingKey,
pseudoExtendedKey,
paymentAddress
)
)) as AccountStore;
}
}
3 changes: 3 additions & 0 deletions apps/extension/src/Setup/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,7 @@ export type LedgerAccountDetails = {
path: Bip44Path;
address: string;
publicKey: string;
extendedViewingKey: string;
pseudoExtendedKey: string;
paymentAddress: string;
};
22 changes: 19 additions & 3 deletions apps/extension/src/background/keyring/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,24 @@ 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,
extendedViewingKey,
pseudoExtendedKey,
paymentAddress,
} = msg;
return await service.saveLedger(
alias,
address,
publicKey,
bip44Path,
extendedViewingKey,
pseudoExtendedKey,
paymentAddress
);
};
};

Expand Down Expand Up @@ -179,7 +195,7 @@ const handleQueryAccountsMsg: (
const output =
query && query.accountId ?
await service.queryAccountsByParentId(query.accountId)
: await service.queryAccounts();
: await service.queryAccounts();

return output;
};
Expand Down
29 changes: 28 additions & 1 deletion apps/extension/src/background/keyring/keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,10 @@ export class KeyRing {
alias: string,
address: string,
publicKey: string,
bip44Path: Bip44Path
bip44Path: Bip44Path,
pseudoExtendedKey: string,
extendedViewingKey: string,
paymentAddress: string
): Promise<AccountStore | false> {
const id = generateId(UUID_NAMESPACE, alias, address);
const accountStore: AccountStore = {
Expand All @@ -125,6 +128,30 @@ export class KeyRing {
sensitive,
});

const shieldedId = generateId(UUID_NAMESPACE, alias, address);
const shieldedAccountStore: AccountStore = {
id: shieldedId,
alias,
address: paymentAddress,
publicKey,
owner: extendedViewingKey,
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;
}
Expand Down
16 changes: 15 additions & 1 deletion apps/extension/src/background/keyring/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,9 @@ 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 extendedViewingKey: string,
public readonly pseudoExtendedKey: string,
public readonly paymentAddress: string
) {
super();
}
Expand All @@ -207,6 +209,18 @@ 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.extendedViewingKey) {
throw new Error("Extended viewing key was not provided!");
}

if (!this.paymentAddress) {
throw new Error("Payment address was not provided!");
}
}

route(): string {
Expand Down
10 changes: 8 additions & 2 deletions apps/extension/src/background/keyring/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,10 @@ export class KeyRingService {
alias: string,
address: string,
publicKey: string,
bip44Path: Bip44Path
bip44Path: Bip44Path,
extendedViewingKey: string,
pseudoExtendedKey: string,
paymentAddress: string
): Promise<AccountStore | false> {
const account = await this._keyRing.queryAccountByAddress(address);
if (account) {
Expand All @@ -94,7 +97,10 @@ export class KeyRingService {
alias,
address,
publicKey,
bip44Path
bip44Path,
pseudoExtendedKey,
extendedViewingKey,
paymentAddress
);

await this.broadcaster.updateAccounts();
Expand Down
1 change: 1 addition & 0 deletions apps/namadillo/src/App/Ibc/IbcTransfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const IbcTransfer = (): JSX.Element => {
walletAddress: sourceAddress,
})
);
console.log("userAssets", userAssets);

// Local State
const [shielded, setShielded] = useState<boolean>(true);
Expand Down
2 changes: 1 addition & 1 deletion apps/namadillo/src/App/NamadaTransfer/NamadaTransfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export const NamadaTransfer: React.FC = () => {
const account = defaultAccounts.data?.find((account) =>
shielded ?
account.type === AccountType.ShieldedKeys
: account.type !== AccountType.ShieldedKeys
: account.type !== AccountType.ShieldedKeys
);
const sourceAddress = account?.address;
const selectedAssetAddress = searchParams.get(params.asset) || undefined;
Expand Down
2 changes: 1 addition & 1 deletion packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"@ledgerhq/hw-transport": "^6.31.4",
"@ledgerhq/hw-transport-webhid": "^6.29.4",
"@ledgerhq/hw-transport-webusb": "^6.29.4",
"@zondax/ledger-namada": "^1.0.0",
"@zondax/ledger-namada": "^2.0.0",
"bignumber.js": "^9.1.1",
"buffer": "^6.0.3",
"slip44": "^3.0.18"
Expand Down
Loading

0 comments on commit 2a07e11

Please sign in to comment.