diff --git a/apps/extension/src/Setup/Ledger/LedgerConnect.tsx b/apps/extension/src/Setup/Ledger/LedgerConnect.tsx index 538984b8e..03c4fbbe3 100644 --- a/apps/extension/src/Setup/Ledger/LedgerConnect.tsx +++ b/apps/extension/src/Setup/Ledger/LedgerConnect.tsx @@ -15,10 +15,16 @@ type Props = { setPath: (path: Bip44Path) => void; }; +enum LedgerReviewPrompt { + AddressAndPublicKey = "address and public key", + ViewingKey = "viewing key", + ProofGenerationKeys = "proof generation keys", +} + export const LedgerConnect: React.FC = ({ path, setPath }) => { const navigate = useNavigate(); const [error, setError] = useState(); - const [isLedgerConnecting, setIsLedgerConnecting] = useState(false); + const [reviewPrompt, setReviewPrompt] = useState(); const [ledger, setLedger] = useState(); const queryLedger = async (ledger: LedgerApp): Promise => { @@ -32,11 +38,23 @@ export const LedgerConnect: React.FC = ({ path, setPath }) => { throw new Error(errorMessage); } - setIsLedgerConnecting(true); - const { address, publicKey } = await ledger.showAddressAndPublicKey( - makeBip44Path(chains.namada.bip44.coinType, path) - ); - setIsLedgerConnecting(false); + const bip44Path = makeBip44Path(chains.namada.bip44.coinType, path); + + setReviewPrompt(LedgerReviewPrompt.AddressAndPublicKey); + const { address, publicKey } = + await ledger.showAddressAndPublicKey(bip44Path); + + setReviewPrompt(LedgerReviewPrompt.ViewingKey); + const viewingKey = await ledger.getViewingKey(bip44Path, true); + console.log({ viewingKey }); + + setReviewPrompt(LedgerReviewPrompt.ProofGenerationKeys); + const proofGenerationKeys = + await ledger.getProofGenerationKeys(bip44Path); + + console.log({ proofGenerationKeys }); + setReviewPrompt(undefined); + navigate(routes.ledgerImport(), { state: { address, @@ -44,7 +62,7 @@ export const LedgerConnect: React.FC = ({ path, setPath }) => { }, }); } catch (e) { - setIsLedgerConnecting(false); + setReviewPrompt(undefined); handleError(e); } finally { await ledger.closeTransport(); @@ -94,8 +112,8 @@ export const LedgerConnect: React.FC = ({ path, setPath }) => { )} - {isLedgerConnecting && ( - Review on your Ledger + {reviewPrompt && ( + Review {reviewPrompt} on your Ledger )} = ({ path, setPath }) => { active={!!ledger} complete={false} onClick={() => connectNamadaApp()} - buttonDisabled={!ledger || isLedgerConnecting} + buttonDisabled={!ledger || Boolean(reviewPrompt)} image={ } diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index dd9b9c5a6..188dc09b8 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -6,8 +6,8 @@ export { } from "./ledger"; export type { LedgerAddressAndPublicKey, - LedgerShieldedKeys, LedgerStatus, + LedgerViewingKey, } from "./ledger"; // Export types diff --git a/packages/sdk/src/ledger.ts b/packages/sdk/src/ledger.ts index 030b6be31..820132824 100644 --- a/packages/sdk/src/ledger.ts +++ b/packages/sdk/src/ledger.ts @@ -1,3 +1,4 @@ +import { toHex } from "@cosmjs/encoding"; import Transport from "@ledgerhq/hw-transport"; import TransportHID from "@ledgerhq/hw-transport-webhid"; import TransportUSB from "@ledgerhq/hw-transport-webusb"; @@ -17,16 +18,15 @@ import { makeBip44Path } from "./utils"; const { coinType } = chains.namada.bip44; export type LedgerAddressAndPublicKey = { address: string; publicKey: string }; -export type LedgerShieldedKeys = { - viewingKey: { - viewKey?: string; - ivk?: string; - ovk?: string; - }; - proofGenerationKey: { - ak?: string; - nsk?: string; - }; +// TODO: This should be xfvk, awaiting an updated version! +export type LedgerViewingKey = { + viewKey?: string; + ivk?: string; + ovk?: string; +}; +export type LedgerProofGenerationKey = { + ak?: string; + nsk?: string; }; export type LedgerStatus = { @@ -147,41 +147,56 @@ export class Ledger { } /** - * Prompt user to get viewing and proof gen key associated with optional path, otherwise, use default path. + * Prompt user to get viewing key associated with optional path, otherwise, use default path. * Throw exception if app is not initialized. * @async * @param [path] Bip44 path for deriving key * @param [promptUser] boolean to determine whether to display on Ledger device and require approval - * @returns ShieldedKeys + * @returns LedgerViewingKey */ - public async getShieldedKeys( + public async getViewingKey( path: string = DEFAULT_LEDGER_BIP44_PATH, promptUser = true - ): Promise { + ): Promise { try { const { viewKey, ivk, ovk }: ResponseViewKey = await this.namadaApp.retrieveKeys(path, NamadaKeys.ViewKey, promptUser); + return { + viewKey: viewKey ? toHex(new Uint8Array(viewKey)) : undefined, + ivk: ivk ? toHex(new Uint8Array(ivk)) : undefined, + ovk: ovk ? toHex(new Uint8Array(ovk)) : undefined, + }; + } catch (e) { + throw new Error(`Could not retrieve Viewing Key: ${e}`); + } + } + + /** + * Prompt user to get proof generation keys associated with optional path, otherwise, use default path. + * Throw exception if app is not initialized. + * @async + * @param [path] Bip44 path for deriving key + * @returns LedgerProofGenerationKey + */ + public async getProofGenerationKeys( + path: string = DEFAULT_LEDGER_BIP44_PATH + ): Promise { + try { const { ak, nsk }: ResponseProofGenKey = await this.namadaApp.retrieveKeys( path, NamadaKeys.ProofGenerationKey, - promptUser + // NOTE: Setting this to false will result in undefined values + true ); return { - viewingKey: { - viewKey: viewKey?.toString(), - ivk: ivk?.toString(), - ovk: ovk?.toString(), - }, - proofGenerationKey: { - ak: ak?.toString(), - nsk: nsk?.toString(), - }, + ak: ak ? toHex(new Uint8Array(ak)) : undefined, + nsk: nsk ? toHex(new Uint8Array(nsk)) : undefined, }; } catch (e) { - throw new Error(`Could not retrieve Viewing Key`); + throw new Error(`Could not retrive Proof Generation Keys: ${e}`); } } diff --git a/packages/shared/lib/src/types/masp.rs b/packages/shared/lib/src/types/masp.rs index 3274b0305..2be675e03 100644 --- a/packages/shared/lib/src/types/masp.rs +++ b/packages/shared/lib/src/types/masp.rs @@ -1,8 +1,11 @@ //! PaymentAddress - Provide wasm_bindgen bindings for shielded addresses //! See @namada/crypto for zip32 HD wallet functionality. use namada_sdk::borsh::BorshDeserialize; -use namada_sdk::{ExtendedViewingKey as NamadaExtendedViewingKey, ExtendedSpendingKey as NamadaExtendedSpendingKey, PaymentAddress as NamadaPaymentAddress}; use namada_sdk::masp_primitives::{sapling, zip32}; +use namada_sdk::{ + ExtendedSpendingKey as NamadaExtendedSpendingKey, + ExtendedViewingKey as NamadaExtendedViewingKey, PaymentAddress as NamadaPaymentAddress, +}; use thiserror::Error; use wasm_bindgen::prelude::*; @@ -23,13 +26,13 @@ pub struct ExtendedViewingKey(pub(crate) NamadaExtendedViewingKey); impl ExtendedViewingKey { /// Instantiate ExtendedViewingKey from serialized vector #[wasm_bindgen(constructor)] - pub fn new(key: &[u8]) -> Result { - let xfvk: zip32::ExtendedFullViewingKey = BorshDeserialize::try_from_slice(key) + pub fn new(xfvk_bytes: &[u8]) -> Result { + let xfvk: zip32::ExtendedFullViewingKey = BorshDeserialize::try_from_slice(xfvk_bytes) .map_err(|err| format!("{}: {:?}", MaspError::BorshDeserialize, err))?; - let vk = NamadaExtendedViewingKey::from(xfvk); + let xvk = NamadaExtendedViewingKey::from(xfvk); - Ok(ExtendedViewingKey(vk)) + Ok(ExtendedViewingKey(xvk)) } /// Return ExtendedViewingKey as Bech32-encoded String @@ -47,8 +50,8 @@ pub struct ExtendedSpendingKey(pub(crate) NamadaExtendedSpendingKey); impl ExtendedSpendingKey { /// Instantiate ExtendedSpendingKey from serialized vector #[wasm_bindgen(constructor)] - pub fn new(key: &[u8]) -> Result { - let xsk: zip32::ExtendedSpendingKey = BorshDeserialize::try_from_slice(key) + pub fn new(xsk_bytes: &[u8]) -> Result { + let xsk: zip32::ExtendedSpendingKey = BorshDeserialize::try_from_slice(xsk_bytes) .map_err(|err| format!("{}: {:?}", MaspError::BorshDeserialize, err))?; let xsk = NamadaExtendedSpendingKey::from(xsk);