From 694ba67596ecf2b7c28736ce63b6f624e00d809b Mon Sep 17 00:00:00 2001 From: Mateusz Jasiuk Date: Wed, 8 Jan 2025 14:15:37 +0100 Subject: [PATCH] feat: unshielding working --- .../src/Approvals/ConfirmSignLedgerTx.tsx | 50 +++++++++++++----- .../src/background/approvals/handler.ts | 14 +++++ .../src/background/approvals/init.ts | 2 + .../src/background/approvals/messages.ts | 33 +++++++++++- .../src/background/approvals/service.ts | 52 ++++++++++++++----- apps/namadillo/public/config.toml | 2 +- apps/namadillo/src/atoms/transfer/services.ts | 13 ++++- apps/namadillo/src/workers/MaspTxWorker.ts | 2 +- packages/shared/lib/src/sdk/mod.rs | 26 ++++++---- 9 files changed, 155 insertions(+), 39 deletions(-) diff --git a/apps/extension/src/Approvals/ConfirmSignLedgerTx.tsx b/apps/extension/src/Approvals/ConfirmSignLedgerTx.tsx index 96cd2be4d2..e98003c097 100644 --- a/apps/extension/src/Approvals/ConfirmSignLedgerTx.tsx +++ b/apps/extension/src/Approvals/ConfirmSignLedgerTx.tsx @@ -2,15 +2,16 @@ import clsx from "clsx"; import { ReactNode, useCallback, useEffect, useState } from "react"; import { ActionButton, Stack } from "@namada/components"; -import { Ledger, makeBip44Path } from "@namada/sdk/web"; +import { Ledger, makeBip44Path, makeSaplingPath } from "@namada/sdk/web"; import { LedgerError, ResponseSign } from "@zondax/ledger-namada"; -import { fromBase64 } from "@cosmjs/encoding"; +import { fromBase64, toBase64 } from "@cosmjs/encoding"; import { chains } from "@namada/chains"; import { PageHeader } from "App/Common"; import { ApprovalDetails, Status } from "Approvals/Approvals"; import { QueryPendingTxBytesMsg, + ReplaceMaspSignatureMsg, SubmitApprovedSignLedgerTxMsg, } from "background/approvals"; import { QueryAccountDetailsMsg } from "background/keyring"; @@ -75,15 +76,27 @@ export const ConfirmSignLedgerTx: React.FC = ({ details }) => { const signMaspTx = async ( ledger: Ledger, bytes: Uint8Array, - _path: string + path: string ): Promise<{ sbar: Uint8Array; rbar: Uint8Array }> => { - const _response = await ledger.namadaApp.signMaspSpends( - // TODO: - "m/32'/877'/0'", + const signMaspSpendsResponse = await ledger.namadaApp.signMaspSpends( + path, Buffer.from(bytes) ); - // TODO - return await ledger.namadaApp.getSpendSignature(); + + if (signMaspSpendsResponse.returnCode !== LedgerError.NoErrors) { + throw new Error( + `Signing masp spends error encountered: ${signMaspSpendsResponse.errorMessage}` + ); + } + + const spendSignatureResponse = await ledger.namadaApp.getSpendSignature(); + if (spendSignatureResponse.returnCode !== LedgerError.NoErrors) { + throw new Error( + `Getting spends signature error encountered: ${signMaspSpendsResponse.errorMessage}` + ); + } + + return spendSignatureResponse; }; const signLedgerTx = async ( @@ -150,6 +163,9 @@ export const ConfirmSignLedgerTx: React.FC = ({ details }) => { index: accountDetails.path.index || 0, }; const bip44Path = makeBip44Path(chains.namada.bip44.coinType, path); + const zip32Path = makeSaplingPath(chains.namada.bip44.coinType, { + account: path.account, + }); const pendingTxs = await requester.sendMessage( Ports.Background, new QueryPendingTxBytesMsg(msgId) @@ -162,7 +178,7 @@ export const ConfirmSignLedgerTx: React.FC = ({ details }) => { } const signatures: ResponseSign[] = []; - const maspSignatures: number[][] = []; + const maspSignatures: string[] = []; let txIndex = 0; const txCount = pendingTxs.length; @@ -182,13 +198,23 @@ export const ConfirmSignLedgerTx: React.FC = ({ details }) => {

); } - const asd = await signMaspTx(ledger, fromBase64(tx), bip44Path); - const maspSignature = [...asd.rbar, ...asd.sbar]; + const { sbar, rbar } = await signMaspTx( + ledger, + fromBase64(tx), + zip32Path + ); + const maspSignature = toBase64(new Uint8Array([...rbar, ...sbar])); maspSignatures.push(maspSignature); + const txWithMaspSection = await requester.sendMessage( + Ports.Background, + new ReplaceMaspSignatureMsg(msgId, maspSignature, txIndex) + ); + console.log("txWithMaspSection", txWithMaspSection); + const signature = await signLedgerTx( ledger, - fromBase64(tx), + fromBase64(txWithMaspSection), bip44Path ); signatures.push(signature); diff --git a/apps/extension/src/background/approvals/handler.ts b/apps/extension/src/background/approvals/handler.ts index 65f880333e..5e38c02ce4 100644 --- a/apps/extension/src/background/approvals/handler.ts +++ b/apps/extension/src/background/approvals/handler.ts @@ -16,6 +16,7 @@ import { QueryTxDetailsMsg, RejectSignArbitraryMsg, RejectSignTxMsg, + ReplaceMaspSignatureMsg, RevokeConnectionMsg, SubmitApprovedSignArbitraryMsg, SubmitApprovedSignLedgerTxMsg, @@ -113,6 +114,11 @@ export const getHandler: (service: ApprovalsService) => Handler = (service) => { env, msg as SubmitApprovedSignLedgerTxMsg ); + case ReplaceMaspSignatureMsg: + return handleReplaceMaspSignatureMsg(service)( + env, + msg as ReplaceMaspSignatureMsg + ); default: throw new Error("Unknown msg type"); @@ -287,6 +293,14 @@ const handleSubmitApprovedSignLedgerTxMsg: ( }; }; +const handleReplaceMaspSignatureMsg: ( + service: ApprovalsService +) => InternalHandler = (service) => { + return async (_, { msgId, signature, txIndex }) => { + return await service.replaceMaspSignature(msgId, signature, txIndex); + }; +}; + const handleCheckIsApprovedSite: ( service: ApprovalsService ) => InternalHandler = (service) => { diff --git a/apps/extension/src/background/approvals/init.ts b/apps/extension/src/background/approvals/init.ts index 37e95732a1..bb2976235c 100644 --- a/apps/extension/src/background/approvals/init.ts +++ b/apps/extension/src/background/approvals/init.ts @@ -16,6 +16,7 @@ import { QueryTxDetailsMsg, RejectSignArbitraryMsg, RejectSignTxMsg, + ReplaceMaspSignatureMsg, RevokeConnectionMsg, SubmitApprovedSignArbitraryMsg, SubmitApprovedSignLedgerTxMsg, @@ -36,6 +37,7 @@ export function init(router: Router, service: ApprovalsService): void { router.registerMessage(SubmitApprovedSignTxMsg); router.registerMessage(SubmitApprovedSignArbitraryMsg); router.registerMessage(SubmitApprovedSignLedgerTxMsg); + router.registerMessage(ReplaceMaspSignatureMsg); router.registerMessage(IsConnectionApprovedMsg); router.registerMessage(ApproveConnectInterfaceMsg); router.registerMessage(ConnectInterfaceResponseMsg); diff --git a/apps/extension/src/background/approvals/messages.ts b/apps/extension/src/background/approvals/messages.ts index 80dbe70f69..bbb1ee6dcd 100644 --- a/apps/extension/src/background/approvals/messages.ts +++ b/apps/extension/src/background/approvals/messages.ts @@ -10,6 +10,7 @@ export enum MessageType { SubmitApprovedSignTx = "submit-approved-sign-tx", SubmitApprovedSignArbitrary = "submit-approved-sign-arbitrary", SubmitApprovedSignLedgerTx = "submit-approved-sign-ledger-tx", + ReplaceMaspSignature = "replace-masp-signature", RejectSignArbitrary = "reject-sign-arbitrary", ConnectInterfaceResponse = "connect-interface-response", DisconnectInterfaceResponse = "disconnect-interface-response", @@ -54,7 +55,8 @@ export class SubmitApprovedSignLedgerTxMsg extends Message { constructor( public readonly msgId: string, public readonly responseSign: ResponseSign[], - public readonly maspSignatures: number[][] + //base64 encoded masp signatures + public readonly maspSignatures: string[] ) { super(); } @@ -72,6 +74,35 @@ export class SubmitApprovedSignLedgerTxMsg extends Message { } } +// returns base64 encoded tx +export class ReplaceMaspSignatureMsg extends Message { + public static type(): MessageType { + return MessageType.ReplaceMaspSignature; + } + + constructor( + public readonly msgId: string, + // base64 encoded + public readonly signature: string, + // pending tx index + public readonly txIndex: number + ) { + super(); + } + + validate(): void { + validateProps(this, ["msgId", "signature", "txIndex"]); + } + + route(): string { + return ROUTE; + } + + type(): string { + return ReplaceMaspSignatureMsg.type(); + } +} + export class RejectSignTxMsg extends Message { public static type(): MessageType { return MessageType.RejectSignTx; diff --git a/apps/extension/src/background/approvals/service.ts b/apps/extension/src/background/approvals/service.ts index c24efb074b..678b646648 100644 --- a/apps/extension/src/background/approvals/service.ts +++ b/apps/extension/src/background/approvals/service.ts @@ -1,4 +1,4 @@ -import { toBase64 } from "@cosmjs/encoding"; +import { fromBase64, toBase64 } from "@cosmjs/encoding"; import { v4 as uuid } from "uuid"; import browser, { Windows } from "webextension-polyfill"; @@ -137,7 +137,7 @@ export class ApprovalsService { popupTabId: number, msgId: string, responseSign: ResponseSign[], - maspSignatures: number[][] + maspSignatures: string[] ): Promise { const pendingTx = await this.txStore.get(msgId); const resolvers = this.getResolver(popupTabId); @@ -152,19 +152,22 @@ export class ApprovalsService { const { tx } = this.sdkService.getSdk(); - console.log("maspSignatures", maspSignatures); try { - const signedTxs = pendingTx.txs.map(({ bytes, signingData }, i) => { - const sd = signingData.map((sd) => - new Message().encode(new SigningDataMsgValue(sd)) + const signedTxs = pendingTx.txs.map((pendingTx, i) => { + const signingData = pendingTx.signingData.map((signingData) => + new Message().encode(new SigningDataMsgValue(signingData)) + ); + const maspSignature = fromBase64(maspSignatures[i]); + // TODO: consider improving this, explanation below: + // Because we read tx from the store we have to append masp signatures twice + // First time before ledger computes tx wrapper signature and second time now to submit proper tx + const txWithMasp = tx.appendMaspSignature( + pendingTx.bytes, + signingData, + maspSignature ); - const wwww = new Uint8Array(maspSignatures[i]); - console.log("sd", sd, maspSignatures[i], i); - const asd = tx.appendMaspSignature(bytes, sd, wwww); - console.log("asd", asd); - // return tx.appendSignature(asd, responseSign[i]); - return asd; + return tx.appendSignature(txWithMasp, responseSign[i]); }); resolvers.resolve(signedTxs); } catch (e) { @@ -174,6 +177,31 @@ export class ApprovalsService { await this.clearPendingSignature(msgId); } + async replaceMaspSignature( + msgId: string, + signature: string, + txIndex: number + ): Promise { + const pendingTx = (await this.txStore.get(msgId))?.txs.at(txIndex); + + if (!pendingTx) { + throw new Error(ApprovalErrors.TransactionDataNotFound(msgId)); + } + + const { tx } = this.sdkService.getSdk(); + + const signingData = pendingTx.signingData.map((signingData) => + new Message().encode(new SigningDataMsgValue(signingData)) + ); + const txWithMasp = tx.appendMaspSignature( + pendingTx.bytes, + signingData, + fromBase64(signature) + ); + + return toBase64(txWithMasp); + } + async submitSignArbitrary( popupTabId: number, msgId: string, diff --git a/apps/namadillo/public/config.toml b/apps/namadillo/public/config.toml index 29d5b71031..7bd0c7210c 100644 --- a/apps/namadillo/public/config.toml +++ b/apps/namadillo/public/config.toml @@ -2,4 +2,4 @@ #indexer_url = "" #rpc_url = "" #masp_indexer_url = "" -#localnet_enabled = false +localnet_enabled = true diff --git a/apps/namadillo/src/atoms/transfer/services.ts b/apps/namadillo/src/atoms/transfer/services.ts index 7e6e16471b..c9e3408a13 100644 --- a/apps/namadillo/src/atoms/transfer/services.ts +++ b/apps/namadillo/src/atoms/transfer/services.ts @@ -188,22 +188,31 @@ export const createUnshieldingTransferTx = async ( const token = props[0]?.data[0]?.token; const amount = props[0]?.data[0]?.amount; + const sdk = await getSdkInstance(); + const ledger = await sdk.initLedger(); + + const bparams = await ledger.getBparams(); + ledger.closeTransport(); + console.log("bparams closed", bparams); + return await workerBuildTxPair({ rpcUrl, token, - signerAddress: disposableSigner.address, + signerAddress: account.address, + // signerAddress: disposableSigner.address, buildTxFn: async (workerLink) => { const msgValue = new UnshieldingTransferMsgValue({ source, gasSpendingKey: source, data: [{ target: destination, token, amount }], + bparams, }); const msg: Unshield = { type: "unshield", payload: { account: { ...account, - publicKey: disposableSigner.publicKey, + // publicKey: disposableSigner.publicKey, }, gasConfig, props: [msgValue], diff --git a/apps/namadillo/src/workers/MaspTxWorker.ts b/apps/namadillo/src/workers/MaspTxWorker.ts index 644da1a446..c5f4562fb7 100644 --- a/apps/namadillo/src/workers/MaspTxWorker.ts +++ b/apps/namadillo/src/workers/MaspTxWorker.ts @@ -115,7 +115,6 @@ async function unshield( await sdk.masp.loadMaspParams("", chain.chainId); - console.log("Unshielding props", unshieldingProps); const encodedTxData = await buildTx( sdk, account, @@ -125,6 +124,7 @@ async function unshield( sdk.tx.buildUnshieldingTransfer, true ); + console.log("encodedTxData", encodedTxData); return encodedTxData; } diff --git a/packages/shared/lib/src/sdk/mod.rs b/packages/shared/lib/src/sdk/mod.rs index 117c54cc06..4f92f1ea4f 100644 --- a/packages/shared/lib/src/sdk/mod.rs +++ b/packages/shared/lib/src/sdk/mod.rs @@ -596,22 +596,28 @@ impl Sdk { (tx, masp_signing_data) } BuildParams::StoredBuildParams(mut bparams) => { + web_sys::console::log_1(&"StoredBuildParams".into()); let tx = build_unshielding_transfer(&self.namada, &mut args, &mut bparams).await?; let masp_signing_data = MaspSigningData::new(bparams, xfvks); (tx, masp_signing_data) } }; - - if let Some(shielded_hash) = signing_data.shielded_hash { - web_sys::console::log_1( - &format!( - "tx: {:?}", - borsh::to_vec(tx.get_masp_section(&shielded_hash).unwrap()) - ) - .into(), - ); - } + web_sys::console::log_1(&format!("signing_data owner: {:?}", signing_data.owner).into()); + web_sys::console::log_1( + &format!("signing_data public_keys: {:?}", signing_data.public_keys).into(), + ); + web_sys::console::log_1(&format!("signing_data fee: {:?}", signing_data.fee_payer).into()); + + // if let Some(shielded_hash) = signing_data.shielded_hash { + // web_sys::console::log_1( + // &format!( + // "tx: {:?}", + // borsh::to_vec(tx.get_masp_section(&shielded_hash).unwrap()) + // ) + // .into(), + // ); + // } self.serialize_tx_result(tx, wrapper_tx_msg, signing_data, Some(masp_signing_data)) }