Skip to content

Commit

Permalink
feat: ledger masp with disposable signer
Browse files Browse the repository at this point in the history
  • Loading branch information
mateuszjasiuk committed Jan 15, 2025
1 parent cc9625c commit 22f8929
Show file tree
Hide file tree
Showing 12 changed files with 235 additions and 128 deletions.
97 changes: 68 additions & 29 deletions apps/extension/src/Approvals/ConfirmSignLedgerTx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import { PageHeader } from "App/Common";
import { ApprovalDetails, Status } from "Approvals/Approvals";
import {
QueryPendingTxBytesMsg,
ReplaceMaspSignatureMsg,
ReplaceMaspSignaturesMsg,
SubmitApprovedSignLedgerTxMsg,
SubmitApprovedSignTxMsg,
} from "background/approvals";
import { QueryAccountDetailsMsg } from "background/keyring";
import { useRequester } from "hooks/useRequester";
Expand Down Expand Up @@ -117,6 +118,37 @@ export const ConfirmSignLedgerTx: React.FC<Props> = ({ details }) => {
return signature;
};

const handleMaspSignTx = useCallback(
async (
ledger: Ledger,
tx: string,
zip32Path: string,
signatures: string[]
) => {
const { sbar, rbar } = await signMaspTx(
ledger,
fromBase64(tx),
zip32Path
);
const signature = toBase64(new Uint8Array([...rbar, ...sbar]));
signatures.push(signature);
},
[]
);

const handleSignTx = useCallback(
async (
ledger: Ledger,
tx: string,
bip44Path: string,
signatures: ResponseSign[]
) => {
const signature = await signLedgerTx(ledger, fromBase64(tx), bip44Path);
signatures.push(signature);
},
[]
);

const handleApproveLedgerSignTx = useCallback(
async (e: React.FormEvent): Promise<void> => {
e.preventDefault();
Expand Down Expand Up @@ -149,6 +181,9 @@ export const ConfirmSignLedgerTx: React.FC<Props> = ({ details }) => {
setStepTwoDescription("Preparing transaction...");

try {
// TODO: we have to check if the signer is disposable or not
const isDisposableSigner = false;

const accountDetails = await requester.sendMessage(
Ports.Background,
new QueryAccountDetailsMsg(signer)
Expand All @@ -161,10 +196,7 @@ export const ConfirmSignLedgerTx: React.FC<Props> = ({ details }) => {
change: accountDetails.path.change || 0,
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)
Expand All @@ -176,9 +208,6 @@ export const ConfirmSignLedgerTx: React.FC<Props> = ({ details }) => {
);
}

const signatures: ResponseSign[] = [];
const maspSignatures: string[] = [];

let txIndex = 0;
const txCount = pendingTxs.length;
const stepTwoText = "Approve on your device";
Expand All @@ -187,6 +216,10 @@ export const ConfirmSignLedgerTx: React.FC<Props> = ({ details }) => {
setStepTwoDescription(<p>{stepTwoText}</p>);
}

// Those collections are being mutated in the loop
const signatures: ResponseSign[] = [];
const maspSignatures: string[] = [];

for await (const tx of pendingTxs) {
if (txCount > 1) {
setStepTwoDescription(
Expand All @@ -197,33 +230,39 @@ export const ConfirmSignLedgerTx: React.FC<Props> = ({ details }) => {
</p>
);
}
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)
);
if (isDisposableSigner) {
const zip32Path = makeSaplingPath(chains.namada.bip44.coinType, {
account: path.account,
});
// Adds new signature to the collection
await handleMaspSignTx(ledger, tx, zip32Path, maspSignatures);
} else {
const bip44Path = makeBip44Path(chains.namada.bip44.coinType, path);
// Adds new signature to the collection
await handleSignTx(ledger, tx, bip44Path, signatures);
}

const signature = await signLedgerTx(
ledger,
fromBase64(txWithMaspSection),
bip44Path
);
signatures.push(signature);
txIndex++;
}

setStepTwoDescription(<p>Submitting...</p>);
await requester.sendMessage(
Ports.Background,
new SubmitApprovedSignLedgerTxMsg(msgId, signatures, maspSignatures)
);

if (isDisposableSigner) {
await requester.sendMessage(
Ports.Background,
new ReplaceMaspSignaturesMsg(msgId, maspSignatures)
);
await requester.sendMessage(
Ports.Background,
new SubmitApprovedSignTxMsg(msgId, signer)
);
} else {
await requester.sendMessage(
Ports.Background,
new SubmitApprovedSignLedgerTxMsg(msgId, signatures)
);
}

setStatus(Status.Completed);
} catch (e) {
Expand Down
9 changes: 8 additions & 1 deletion apps/extension/src/Approvals/ConfirmSignTx.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useNavigate } from "react-router-dom";
import { ActionButton, Input, Stack } from "@namada/components";
import { PageHeader } from "App/Common";
import { ApprovalDetails, Status } from "Approvals/Approvals";
import { SubmitApprovedSignTxMsg } from "background/approvals";
import { SignMaspMsg, SubmitApprovedSignTxMsg } from "background/approvals";
import { UnlockVaultMsg } from "background/vault";
import { useRequester } from "hooks/useRequester";
import { Ports } from "router";
Expand Down Expand Up @@ -41,6 +41,13 @@ export const ConfirmSignTx: React.FC<Props> = ({ details }) => {
throw new Error("Invalid password!");
}

// TODO: ideally we should only calling this for Unshielding and Shielded Transfers,
// it should not break anything it's just unnecessary computation
await requester.sendMessage(
Ports.Background,
new SignMaspMsg(msgId, signer)
);

await requester.sendMessage(
Ports.Background,
new SubmitApprovedSignTxMsg(msgId, signer)
Expand Down
39 changes: 21 additions & 18 deletions apps/extension/src/background/approvals/handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import {
QueryTxDetailsMsg,
RejectSignArbitraryMsg,
RejectSignTxMsg,
ReplaceMaspSignatureMsg,
ReplaceMaspSignaturesMsg,
RevokeConnectionMsg,
SignMaspMsg,
SubmitApprovedSignArbitraryMsg,
SubmitApprovedSignLedgerTxMsg,
SubmitApprovedSignTxMsg,
Expand Down Expand Up @@ -114,11 +115,13 @@ export const getHandler: (service: ApprovalsService) => Handler = (service) => {
env,
msg as SubmitApprovedSignLedgerTxMsg
);
case ReplaceMaspSignatureMsg:
return handleReplaceMaspSignatureMsg(service)(
case ReplaceMaspSignaturesMsg:
return handleReplaceMaspSignaturesMsg(service)(
env,
msg as ReplaceMaspSignatureMsg
msg as ReplaceMaspSignaturesMsg
);
case SignMaspMsg:
return handleSignMaspMsg(service)(env, msg as SignMaspMsg);

default:
throw new Error("Unknown msg type");
Expand Down Expand Up @@ -280,24 +283,24 @@ const handleQuerySignArbitraryData: (
const handleSubmitApprovedSignLedgerTxMsg: (
service: ApprovalsService
) => InternalHandler<SubmitApprovedSignLedgerTxMsg> = (service) => {
return async (
{ senderTabId: popupTabId },
{ msgId, responseSign, maspSignatures }
) => {
return await service.submitSignLedgerTx(
popupTabId,
msgId,
responseSign,
maspSignatures
);
return async ({ senderTabId: popupTabId }, { msgId, responseSign }) => {
return await service.submitSignLedgerTx(popupTabId, msgId, responseSign);
};
};

const handleReplaceMaspSignaturesMsg: (
service: ApprovalsService
) => InternalHandler<ReplaceMaspSignaturesMsg> = (service) => {
return async (_, { msgId, signatures }) => {
return await service.replaceMaspSignatures(msgId, signatures);
};
};

const handleReplaceMaspSignatureMsg: (
const handleSignMaspMsg: (
service: ApprovalsService
) => InternalHandler<ReplaceMaspSignatureMsg> = (service) => {
return async (_, { msgId, signature, txIndex }) => {
return await service.replaceMaspSignature(msgId, signature, txIndex);
) => InternalHandler<SignMaspMsg> = (service) => {
return async (_, { msgId, signer }) => {
return await service.signMasp(msgId, signer);
};
};

Expand Down
6 changes: 4 additions & 2 deletions apps/extension/src/background/approvals/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import {
QueryTxDetailsMsg,
RejectSignArbitraryMsg,
RejectSignTxMsg,
ReplaceMaspSignatureMsg,
ReplaceMaspSignaturesMsg,
RevokeConnectionMsg,
SignMaspMsg,
SubmitApprovedSignArbitraryMsg,
SubmitApprovedSignLedgerTxMsg,
SubmitApprovedSignTxMsg,
Expand All @@ -37,7 +38,7 @@ export function init(router: Router, service: ApprovalsService): void {
router.registerMessage(SubmitApprovedSignTxMsg);
router.registerMessage(SubmitApprovedSignArbitraryMsg);
router.registerMessage(SubmitApprovedSignLedgerTxMsg);
router.registerMessage(ReplaceMaspSignatureMsg);
router.registerMessage(ReplaceMaspSignaturesMsg);
router.registerMessage(IsConnectionApprovedMsg);
router.registerMessage(ApproveConnectInterfaceMsg);
router.registerMessage(ConnectInterfaceResponseMsg);
Expand All @@ -49,6 +50,7 @@ export function init(router: Router, service: ApprovalsService): void {
router.registerMessage(QueryTxDetailsMsg);
router.registerMessage(QuerySignArbitraryDataMsg);
router.registerMessage(QueryPendingTxBytesMsg);
router.registerMessage(SignMaspMsg);

router.addHandler(ROUTE, getHandler(service));
}
46 changes: 34 additions & 12 deletions apps/extension/src/background/approvals/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +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",
ReplaceMaspSignatures = "replace-masp-signatures",
RejectSignArbitrary = "reject-sign-arbitrary",
ConnectInterfaceResponse = "connect-interface-response",
DisconnectInterfaceResponse = "disconnect-interface-response",
Expand All @@ -20,6 +20,7 @@ export enum MessageType {
QuerySignArbitraryData = "query-sign-arbitrary-data",
QueryPendingTxBytes = "query-pending-tx-bytes",
CheckIsApprovedSite = "check-is-approved-site",
SignMaspMsg = "sign-masp",
}

export class SubmitApprovedSignTxMsg extends Message<void> {
Expand Down Expand Up @@ -47,22 +48,45 @@ export class SubmitApprovedSignTxMsg extends Message<void> {
}
}

export class SignMaspMsg extends Message<void> {
public static type(): MessageType {
return MessageType.SignMaspMsg;
}

constructor(
public readonly msgId: string,
public readonly signer: string
) {
super();
}

validate(): void {
validateProps(this, ["msgId", "signer"]);
}

route(): string {
return ROUTE;
}

type(): string {
return SignMaspMsg.type();
}
}

export class SubmitApprovedSignLedgerTxMsg extends Message<void> {
public static type(): MessageType {
return MessageType.SubmitApprovedSignLedgerTx;
}

constructor(
public readonly msgId: string,
public readonly responseSign: ResponseSign[],
//base64 encoded masp signatures
public readonly maspSignatures: string[]
public readonly responseSign: ResponseSign[]
) {
super();
}

validate(): void {
validateProps(this, ["msgId", "responseSign", "maspSignatures"]);
validateProps(this, ["msgId", "responseSign"]);
}

route(): string {
Expand All @@ -75,31 +99,29 @@ export class SubmitApprovedSignLedgerTxMsg extends Message<void> {
}

// returns base64 encoded tx
export class ReplaceMaspSignatureMsg extends Message<string> {
export class ReplaceMaspSignaturesMsg extends Message<void> {
public static type(): MessageType {
return MessageType.ReplaceMaspSignature;
return MessageType.ReplaceMaspSignatures;
}

constructor(
public readonly msgId: string,
// base64 encoded
public readonly signature: string,
// pending tx index
public readonly txIndex: number
public readonly signatures: string[]
) {
super();
}

validate(): void {
validateProps(this, ["msgId", "signature", "txIndex"]);
validateProps(this, ["msgId", "signatures"]);
}

route(): string {
return ROUTE;
}

type(): string {
return ReplaceMaspSignatureMsg.type();
return ReplaceMaspSignaturesMsg.type();
}
}

Expand Down
Loading

0 comments on commit 22f8929

Please sign in to comment.