From 0c280d59438f73a8a518ab61ec31898649ec0795 Mon Sep 17 00:00:00 2001 From: nigiri <168690269+0xnigir1@users.noreply.github.com> Date: Fri, 25 Apr 2025 13:43:07 -0300 Subject: [PATCH] feat(sdk): support AccountCommitment type in proveWithdrawal (#78) --- packages/sdk/src/core/sdk.ts | 3 +- packages/sdk/src/core/withdrawal.service.ts | 34 ++++++++--- packages/sdk/test/unit/sdk.spec.ts | 68 +++++++++++++++++++-- 3 files changed, 90 insertions(+), 15 deletions(-) diff --git a/packages/sdk/src/core/sdk.ts b/packages/sdk/src/core/sdk.ts index d97e1c8..d6f1197 100644 --- a/packages/sdk/src/core/sdk.ts +++ b/packages/sdk/src/core/sdk.ts @@ -5,6 +5,7 @@ import { Commitment, CommitmentProof } from "../types/commitment.js"; import { WithdrawalProof, WithdrawalProofInput } from "../types/withdrawal.js"; import { ContractInteractionsService } from "./contracts.service.js"; import { Hex, Address, Chain } from "viem"; +import { AccountCommitment } from "../types/account.js"; /** * Main SDK class providing access to all privacy pool functionality. @@ -72,7 +73,7 @@ export class PrivacyPoolSDK { * @param withdrawal - Withdrawal details */ public async proveWithdrawal( - commitment: Commitment, + commitment: Commitment | AccountCommitment , input: WithdrawalProofInput, ): Promise { return await this.withdrawalService.proveWithdrawal(commitment, input); diff --git a/packages/sdk/src/core/withdrawal.service.ts b/packages/sdk/src/core/withdrawal.service.ts index 74e5b84..3537c6f 100644 --- a/packages/sdk/src/core/withdrawal.service.ts +++ b/packages/sdk/src/core/withdrawal.service.ts @@ -4,8 +4,8 @@ import { CircuitName, CircuitsInterface, } from "../interfaces/circuits.interface.js"; -import { Commitment } from "../types/commitment.js"; import { WithdrawalProof, WithdrawalProofInput } from "../types/withdrawal.js"; +import { AccountCommitment, Commitment } from "../index.js"; /** * Service responsible for handling withdrawal-related operations. @@ -23,8 +23,8 @@ export class WithdrawalService { * @throws {ProofError} If proof generation fails */ public async proveWithdrawal( - commitment: Commitment, - input: WithdrawalProofInput, + commitment: Commitment | AccountCommitment, + input: WithdrawalProofInput ): Promise { try { const inputSignals = this.prepareInputSignals(commitment, input); @@ -80,9 +80,25 @@ export class WithdrawalService { * Prepares input signals for the withdrawal circuit. */ private prepareInputSignals( - commitment: Commitment, - input: WithdrawalProofInput, + commitment: Commitment | AccountCommitment, + input: WithdrawalProofInput ): Record { + let existingValue: bigint; + let existingNullifier: bigint; + let existingSecret: bigint; + let label: bigint; + if ("preimage" in commitment) { + existingValue = commitment.preimage.value; + existingNullifier = commitment.preimage.precommitment.nullifier; + existingSecret = commitment.preimage.precommitment.secret; + label = commitment.preimage.label; + } else { + existingValue = commitment.value; + existingNullifier = commitment.nullifier; + existingSecret = commitment.secret; + label = commitment.label; + } + return { // Public signals withdrawnValue: input.withdrawalAmount, @@ -93,10 +109,10 @@ export class WithdrawalService { context: input.context, // Private signals - label: commitment.preimage.label, - existingValue: commitment.preimage.value, - existingNullifier: commitment.preimage.precommitment.nullifier, - existingSecret: commitment.preimage.precommitment.secret, + label, + existingValue, + existingNullifier, + existingSecret, newNullifier: input.newNullifier, newSecret: input.newSecret, diff --git a/packages/sdk/test/unit/sdk.spec.ts b/packages/sdk/test/unit/sdk.spec.ts index a932a31..77ab838 100644 --- a/packages/sdk/test/unit/sdk.spec.ts +++ b/packages/sdk/test/unit/sdk.spec.ts @@ -5,6 +5,7 @@ import * as snarkjs from "snarkjs"; import { Commitment, Hash, Secret } from "../../src/types/commitment.js"; import { LeanIMTMerkleProof } from "@zk-kit/lean-imt"; import { ProofError } from "../../src/errors/base.error.js"; +import { AccountCommitment } from "../../src/types/account.js"; vi.mock("snarkjs"); vi.mock("viem", async (importOriginal) => { @@ -55,7 +56,7 @@ describe("PrivacyPoolSDK", () => { BigInt(1), BigInt(2), BigInt(3) as Secret, - BigInt(4) as Secret, + BigInt(4) as Secret ); expect(result).toStrictEqual({ proof: "PROOF", @@ -65,7 +66,7 @@ describe("PrivacyPoolSDK", () => { expect(snarkjs.groth16.fullProve).toHaveBeenCalledWith( inputSignals, binariesMock.commitment.wasm, - binariesMock.commitment.zkey, + binariesMock.commitment.zkey ); }); @@ -81,7 +82,7 @@ describe("PrivacyPoolSDK", () => { sdk.verifyCommitment({ proof: {} as snarkjs.Groth16Proof, publicSignals: [], - }), + }) ).rejects.toThrowError(ProofError); }); @@ -158,6 +159,63 @@ describe("PrivacyPoolSDK", () => { expect(downloadArtifactsSpy).toHaveBeenCalledOnce(); }); + it("can prove withdrawal with account commitment", async () => { + const mockAccountCommitment: AccountCommitment = { + hash: BigInt(1) as Hash, + value: BigInt(1000), + label: BigInt(3) as Hash, + nullifier: BigInt(2) as Secret, + secret: BigInt(4) as Secret, + blockNumber: BigInt(5), + txHash: "0x1234", + }; + + snarkjs.groth16.fullProve = vi.fn().mockResolvedValue({ + proof: "mockProof", + publicSignals: "mockPublicSignals", + }); + + const stateMerkleProof: LeanIMTMerkleProof = { + root: BigInt(5), + leaf: mockCommitment.hash, + index: 1, + siblings: [BigInt(6), BigInt(7)], + }; + + const aspMerkleProof: LeanIMTMerkleProof = { + root: BigInt(8), + leaf: BigInt(3), + index: 2, + siblings: [BigInt(9), BigInt(10)], + }; + + const withdrawalInput = { + withdrawalAmount: BigInt(500), + stateMerkleProof, + aspMerkleProof, + stateRoot: BigInt(5) as Hash, + aspRoot: BigInt(8) as Hash, + newNullifier: BigInt(12) as Secret, + newSecret: BigInt(13) as Secret, + context: BigInt(1), + stateTreeDepth: BigInt(32), + aspTreeDepth: BigInt(32), + }; + + const downloadArtifactsSpy = vi + .spyOn(circuits, "downloadArtifacts") + .mockResolvedValue(binariesMock); + + const result = await sdk.proveWithdrawal( + mockAccountCommitment, + withdrawalInput + ); + + expect(result).toHaveProperty("proof", "mockProof"); + expect(result).toHaveProperty("publicSignals", "mockPublicSignals"); + expect(downloadArtifactsSpy).toHaveBeenCalledOnce(); + }); + it("should throw error on proof generation failure", async () => { snarkjs.groth16.fullProve = vi .fn() @@ -191,7 +249,7 @@ describe("PrivacyPoolSDK", () => { }; await expect( - sdk.proveWithdrawal(mockCommitment, withdrawalInput), + sdk.proveWithdrawal(mockCommitment, withdrawalInput) ).rejects.toThrow(ProofError); }); @@ -207,7 +265,7 @@ describe("PrivacyPoolSDK", () => { sdk.verifyWithdrawal({ proof: {} as snarkjs.Groth16Proof, publicSignals: [], - }), + }) ).rejects.toThrow(ProofError); });