Skip to content

Commit 3f6f30a

Browse files
author
tilo-14
committed
refactor: extract self-contained createNullifierInstruction wrapper
1 parent f7c17b2 commit 3f6f30a

1 file changed

Lines changed: 65 additions & 81 deletions

File tree

Lines changed: 65 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,73 @@
11
import { web3, Program, AnchorProvider, setProvider } from "@coral-xyz/anchor";
2-
import {
3-
bn,
4-
createRpc,
5-
deriveAddressSeedV2,
6-
deriveAddressV2,
7-
batchAddressTree,
8-
PackedAccounts,
9-
Rpc,
10-
sleep,
11-
SystemAccountMetaConfig,
12-
defaultTestStateTreeAccounts,
13-
featureFlags,
14-
VERSION,
15-
confirmTx,
16-
} from "@lightprotocol/stateless.js";
2+
import { createRpc, Rpc, sleep, confirmTx } from "@lightprotocol/stateless.js";
173
import * as assert from "assert";
184
import * as path from "path";
195
import * as fs from "fs";
206

21-
// Force V2 mode
22-
(featureFlags as any).version = VERSION.V2;
23-
24-
// Load IDL
257
const IDL = JSON.parse(
268
fs.readFileSync(path.join(process.cwd(), "target/idl/zk_nullifier.json"), "utf8")
279
);
2810

2911
const PROGRAM_ID = new web3.PublicKey(IDL.address);
30-
const NULLIFIER_PREFIX = Buffer.from("nullifier");
12+
13+
async function createNullifierInstruction(
14+
rpc: Rpc,
15+
program: Program,
16+
signer: web3.PublicKey,
17+
nullifiers: Uint8Array[]
18+
): Promise<web3.TransactionInstruction> {
19+
const {
20+
bn,
21+
deriveAddressSeedV2,
22+
deriveAddressV2,
23+
batchAddressTree,
24+
PackedAccounts,
25+
SystemAccountMetaConfig,
26+
defaultTestStateTreeAccounts,
27+
featureFlags,
28+
VERSION,
29+
} = await import("@lightprotocol/stateless.js");
30+
31+
(featureFlags as any).version = VERSION.V2;
32+
33+
const NULLIFIER_PREFIX = Buffer.from("nullifier");
34+
const addressTree = new web3.PublicKey(batchAddressTree);
35+
const outputStateTree = defaultTestStateTreeAccounts().merkleTree;
36+
37+
const addressesWithTree = nullifiers.map((nullifier) => {
38+
const seed = deriveAddressSeedV2([NULLIFIER_PREFIX, nullifier]);
39+
const address = deriveAddressV2(seed, addressTree, program.programId);
40+
return { tree: addressTree, queue: addressTree, address: bn(address.toBytes()) };
41+
});
42+
43+
const proofResult = await rpc.getValidityProofV0([], addressesWithTree);
44+
45+
const remainingAccounts = new PackedAccounts();
46+
remainingAccounts.addPreAccountsSigner(signer);
47+
remainingAccounts.addSystemAccountsV2(SystemAccountMetaConfig.new(program.programId));
48+
49+
const addressMerkleTreeIndex = remainingAccounts.insertOrGet(addressTree);
50+
const outputStateTreeIndex = remainingAccounts.insertOrGet(outputStateTree);
51+
52+
const { remainingAccounts: accountMetas, systemStart } = remainingAccounts.toAccountMetas();
53+
54+
const data = {
55+
proof: { 0: proofResult.compressedProof },
56+
addressTreeInfo: {
57+
addressMerkleTreePubkeyIndex: addressMerkleTreeIndex,
58+
addressQueuePubkeyIndex: addressMerkleTreeIndex,
59+
rootIndex: proofResult.rootIndices[0],
60+
},
61+
outputStateTreeIndex,
62+
systemAccountsOffset: systemStart,
63+
};
64+
65+
return program.methods
66+
.createNullifier(data, nullifiers.map((n) => Array.from(n)))
67+
.accounts({ signer })
68+
.remainingAccounts(accountMetas)
69+
.instruction();
70+
}
3171

3272
describe("zk-nullifier", () => {
3373
let rpc: Rpc;
@@ -46,7 +86,6 @@ describe("zk-nullifier", () => {
4686
await rpc.requestAirdrop(signer.publicKey, web3.LAMPORTS_PER_SOL);
4787
await sleep(2000);
4888

49-
// Setup Anchor provider and program
5089
const connection = new web3.Connection("http://127.0.0.1:8899", "confirmed");
5190
const wallet = {
5291
publicKey: signer.publicKey,
@@ -64,71 +103,17 @@ describe("zk-nullifier", () => {
64103
program = new Program(IDL, provider);
65104
});
66105

67-
/** Generate a random 32-byte value */
68106
function randomBytes32(): Uint8Array {
69107
return web3.Keypair.generate().publicKey.toBytes();
70108
}
71109

72-
async function buildCreateNullifierInstruction(
73-
nullifiers: Uint8Array[]
74-
): Promise<web3.TransactionInstruction> {
75-
const addressTree = new web3.PublicKey(batchAddressTree);
76-
const outputStateTree = defaultTestStateTreeAccounts().merkleTree;
77-
78-
// Derive addresses for all nullifiers
79-
const addressesWithTree = nullifiers.map((nullifier) => {
80-
const seed = deriveAddressSeedV2([NULLIFIER_PREFIX, nullifier]);
81-
const address = deriveAddressV2(seed, addressTree, PROGRAM_ID);
82-
return { tree: addressTree, queue: addressTree, address: bn(address.toBytes()) };
83-
});
84-
85-
const proofResult = await rpc.getValidityProofV0([], addressesWithTree);
86-
87-
// Use V2 accounts layout
88-
const remainingAccounts = new PackedAccounts();
89-
remainingAccounts.addPreAccountsSigner(signer.publicKey);
90-
remainingAccounts.addSystemAccountsV2(SystemAccountMetaConfig.new(PROGRAM_ID));
91-
92-
const addressMerkleTreeIndex = remainingAccounts.insertOrGet(addressTree);
93-
const outputStateTreeIndex = remainingAccounts.insertOrGet(outputStateTree);
94-
95-
const { remainingAccounts: accountMetas, systemStart } = remainingAccounts.toAccountMetas();
96-
97-
// ValidityProof struct
98-
const proof = {
99-
0: proofResult.compressedProof,
100-
};
101-
102-
// NullifierInstructionData struct
103-
const data = {
104-
proof,
105-
addressTreeInfo: {
106-
addressMerkleTreePubkeyIndex: addressMerkleTreeIndex,
107-
addressQueuePubkeyIndex: addressMerkleTreeIndex,
108-
rootIndex: proofResult.rootIndices[0],
109-
},
110-
outputStateTreeIndex,
111-
systemAccountsOffset: systemStart,
112-
};
113-
114-
const ix = await program.methods
115-
.createNullifier(data, nullifiers.map((n) => Array.from(n)))
116-
.accounts({
117-
signer: signer.publicKey,
118-
})
119-
.remainingAccounts(accountMetas)
120-
.instruction();
121-
122-
return ix;
123-
}
124-
125110
describe("Single nullifier", () => {
126111
it("should create a nullifier", async () => {
127112
const nullifier = randomBytes32();
128113

129114
console.log("Nullifier:", Buffer.from(nullifier).toString("hex").slice(0, 16) + "...");
130115

131-
const ix = await buildCreateNullifierInstruction([nullifier]);
116+
const ix = await createNullifierInstruction(rpc, program, signer.publicKey, [nullifier]);
132117
const computeIx = web3.ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 });
133118

134119
const tx = new web3.Transaction().add(computeIx, ix);
@@ -152,7 +137,7 @@ describe("zk-nullifier", () => {
152137
it("should reject duplicate nullifier", async () => {
153138
const nullifier = randomBytes32();
154139

155-
const ix1 = await buildCreateNullifierInstruction([nullifier]);
140+
const ix1 = await createNullifierInstruction(rpc, program, signer.publicKey, [nullifier]);
156141
const computeIx = web3.ComputeBudgetProgram.setComputeUnitLimit({ units: 200_000 });
157142

158143
const tx1 = new web3.Transaction().add(computeIx, ix1);
@@ -163,9 +148,8 @@ describe("zk-nullifier", () => {
163148
await rpc.sendTransaction(tx1, [signer]);
164149
await sleep(2000);
165150

166-
// Attempt to create duplicate - should fail when getting validity proof
167151
try {
168-
await buildCreateNullifierInstruction([nullifier]);
152+
await createNullifierInstruction(rpc, program, signer.publicKey, [nullifier]);
169153
assert.fail("Should have rejected duplicate nullifier");
170154
} catch (err: any) {
171155
assert.ok(
@@ -183,7 +167,7 @@ describe("zk-nullifier", () => {
183167

184168
console.log("Creating 3 nullifiers in one transaction...");
185169

186-
const ix = await buildCreateNullifierInstruction(nullifiers);
170+
const ix = await createNullifierInstruction(rpc, program, signer.publicKey, nullifiers);
187171
const computeIx = web3.ComputeBudgetProgram.setComputeUnitLimit({ units: 300_000 });
188172

189173
const tx = new web3.Transaction().add(computeIx, ix);
@@ -203,4 +187,4 @@ describe("zk-nullifier", () => {
203187
console.log("Total nullifier accounts:", accounts.items.length);
204188
});
205189
});
206-
});
190+
});

0 commit comments

Comments
 (0)