@veilo/sdk-core 0.3.3 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -3
- package/SHIELD_INTEGRATION.md +143 -0
- package/config.d.ts +2 -0
- package/config.js +4 -0
- package/dist/cjs/client.d.ts +5 -5
- package/dist/cjs/client.js +79 -42
- package/dist/cjs/compactNote.d.ts +107 -0
- package/dist/cjs/compactNote.js +167 -0
- package/dist/cjs/events.d.ts +1 -1
- package/dist/cjs/events.js +2 -2
- package/dist/cjs/idl/privacy_pool.d.ts +5 -0
- package/dist/cjs/idl/privacy_pool.js +15218 -0
- package/dist/cjs/index.d.ts +16 -13
- package/dist/cjs/index.js +40 -30
- package/dist/cjs/merkle.d.ts +13 -0
- package/dist/cjs/merkle.js +31 -8
- package/dist/cjs/program.d.ts +11 -0
- package/dist/cjs/program.js +26 -3
- package/dist/cjs/proof.d.ts +1 -1
- package/dist/cjs/proof.js +37 -37
- package/dist/cjs/prover.d.ts +1 -1
- package/dist/cjs/prover.js +2 -2
- package/dist/cjs/random.d.ts +16 -0
- package/dist/cjs/random.js +28 -0
- package/dist/cjs/relayer.d.ts +27 -4
- package/dist/cjs/relayer.js +16 -5
- package/dist/cjs/shield/alt.d.ts +87 -0
- package/dist/cjs/shield/alt.js +194 -0
- package/dist/cjs/shield/computeBudget.d.ts +61 -0
- package/dist/cjs/shield/computeBudget.js +64 -0
- package/dist/cjs/shield/errors.d.ts +58 -0
- package/dist/cjs/shield/errors.js +121 -0
- package/dist/cjs/shield/finalize.d.ts +45 -0
- package/dist/cjs/shield/finalize.js +119 -0
- package/dist/cjs/shield/index.d.ts +35 -0
- package/dist/cjs/shield/index.js +68 -0
- package/dist/cjs/shield/ix.d.ts +54 -0
- package/dist/cjs/shield/ix.js +119 -0
- package/dist/cjs/shield/owner.d.ts +36 -0
- package/dist/cjs/shield/owner.js +126 -0
- package/dist/cjs/shield/ports.d.ts +43 -0
- package/dist/cjs/shield/ports.js +153 -0
- package/dist/cjs/shield/preflight.d.ts +30 -0
- package/dist/cjs/shield/preflight.js +154 -0
- package/dist/cjs/shield/shield.d.ts +68 -0
- package/dist/cjs/shield/shield.js +499 -0
- package/dist/cjs/shield/types.d.ts +202 -0
- package/dist/cjs/shield/types.js +2 -0
- package/dist/cjs/utxo.d.ts +20 -0
- package/dist/cjs/utxo.js +41 -28
- package/dist/esm/client.d.ts +407 -0
- package/dist/esm/client.js +9 -5
- package/dist/esm/compactNote.d.ts +107 -0
- package/dist/esm/compactNote.js +156 -0
- package/dist/esm/config.d.ts +82 -0
- package/dist/esm/events.d.ts +77 -0
- package/dist/esm/events.js +1 -1
- package/dist/esm/idl/privacy_pool.d.ts +5 -0
- package/dist/esm/idl/privacy_pool.js +15216 -0
- package/dist/esm/index.d.ts +16 -0
- package/dist/esm/index.js +18 -11
- package/dist/esm/merkle.d.ts +77 -0
- package/dist/esm/merkle.js +23 -1
- package/dist/esm/package.json +1 -0
- package/dist/esm/poseidon.d.ts +29 -0
- package/dist/esm/program.d.ts +37 -0
- package/dist/esm/program.js +23 -1
- package/dist/esm/proof.d.ts +183 -0
- package/dist/esm/proof.js +2 -2
- package/dist/esm/prover.d.ts +54 -0
- package/dist/esm/prover.js +1 -1
- package/dist/esm/random.d.ts +16 -0
- package/dist/esm/random.js +21 -0
- package/dist/esm/relayer.d.ts +318 -0
- package/dist/esm/relayer.js +16 -5
- package/dist/esm/retry.d.ts +32 -0
- package/dist/esm/shield/alt.d.ts +87 -0
- package/dist/esm/shield/alt.js +186 -0
- package/dist/esm/shield/computeBudget.d.ts +61 -0
- package/dist/esm/shield/computeBudget.js +61 -0
- package/dist/esm/shield/errors.d.ts +58 -0
- package/dist/esm/shield/errors.js +115 -0
- package/dist/esm/shield/finalize.d.ts +45 -0
- package/dist/esm/shield/finalize.js +83 -0
- package/dist/esm/shield/index.d.ts +35 -0
- package/dist/esm/shield/index.js +32 -0
- package/dist/esm/shield/ix.d.ts +54 -0
- package/dist/esm/shield/ix.js +82 -0
- package/dist/esm/shield/owner.d.ts +36 -0
- package/dist/esm/shield/owner.js +122 -0
- package/dist/esm/shield/ports.d.ts +43 -0
- package/dist/esm/shield/ports.js +147 -0
- package/dist/esm/shield/preflight.d.ts +30 -0
- package/dist/esm/shield/preflight.js +151 -0
- package/dist/esm/shield/shield.d.ts +68 -0
- package/dist/esm/shield/shield.js +492 -0
- package/dist/esm/shield/types.d.ts +202 -0
- package/dist/esm/shield/types.js +1 -0
- package/dist/esm/utxo.d.ts +235 -0
- package/dist/esm/utxo.js +18 -8
- package/dist/src/client.d.ts +407 -0
- package/dist/src/client.js +951 -0
- package/dist/src/compactNote.d.ts +107 -0
- package/dist/src/compactNote.js +167 -0
- package/dist/src/config.d.ts +82 -0
- package/dist/src/config.js +57 -0
- package/dist/src/events.d.ts +77 -0
- package/dist/src/events.js +167 -0
- package/dist/src/idl/privacy_pool.d.ts +5 -0
- package/dist/src/idl/privacy_pool.js +15218 -0
- package/dist/src/index.d.ts +16 -0
- package/dist/src/index.js +67 -0
- package/dist/src/merkle.d.ts +77 -0
- package/dist/src/merkle.js +156 -0
- package/dist/src/poseidon.d.ts +29 -0
- package/dist/src/poseidon.js +100 -0
- package/dist/src/program.d.ts +37 -0
- package/dist/src/program.js +61 -0
- package/dist/src/proof.d.ts +183 -0
- package/dist/src/proof.js +292 -0
- package/dist/src/prover.d.ts +54 -0
- package/dist/src/prover.js +112 -0
- package/dist/src/random.d.ts +16 -0
- package/dist/src/random.js +28 -0
- package/dist/src/relayer.d.ts +318 -0
- package/dist/src/relayer.js +257 -0
- package/dist/src/retry.d.ts +32 -0
- package/dist/src/retry.js +75 -0
- package/dist/src/shield/alt.d.ts +87 -0
- package/dist/src/shield/alt.js +194 -0
- package/dist/src/shield/computeBudget.d.ts +61 -0
- package/dist/src/shield/computeBudget.js +64 -0
- package/dist/src/shield/errors.d.ts +58 -0
- package/dist/src/shield/errors.js +121 -0
- package/dist/src/shield/finalize.d.ts +45 -0
- package/dist/src/shield/finalize.js +119 -0
- package/dist/src/shield/index.d.ts +35 -0
- package/dist/src/shield/index.js +68 -0
- package/dist/src/shield/ix.d.ts +54 -0
- package/dist/src/shield/ix.js +119 -0
- package/dist/src/shield/owner.d.ts +36 -0
- package/dist/src/shield/owner.js +126 -0
- package/dist/src/shield/ports.d.ts +43 -0
- package/dist/src/shield/ports.js +153 -0
- package/dist/src/shield/preflight.d.ts +30 -0
- package/dist/src/shield/preflight.js +154 -0
- package/dist/src/shield/shield.d.ts +68 -0
- package/dist/src/shield/shield.js +499 -0
- package/dist/src/shield/types.d.ts +202 -0
- package/dist/src/shield/types.js +2 -0
- package/dist/src/utxo.d.ts +235 -0
- package/dist/src/utxo.js +407 -0
- package/dist/tests/compact-note.test.d.ts +1 -0
- package/dist/tests/compact-note.test.js +173 -0
- package/dist/tests/config.test.d.ts +1 -0
- package/dist/tests/config.test.js +102 -0
- package/dist/tests/edge-cases.test.d.ts +1 -0
- package/dist/tests/edge-cases.test.js +220 -0
- package/dist/tests/encryption.test.d.ts +1 -0
- package/dist/tests/encryption.test.js +215 -0
- package/dist/tests/events.test.d.ts +1 -0
- package/dist/tests/events.test.js +78 -0
- package/dist/tests/multi-tree.test.d.ts +1 -0
- package/dist/tests/multi-tree.test.js +405 -0
- package/dist/tests/pda.test.d.ts +1 -0
- package/dist/tests/pda.test.js +229 -0
- package/dist/tests/poseidon-builder-parity.test.d.ts +1 -0
- package/dist/tests/poseidon-builder-parity.test.js +72 -0
- package/dist/tests/poseidon.test.d.ts +1 -0
- package/dist/tests/poseidon.test.js +142 -0
- package/dist/tests/proof.test.d.ts +1 -0
- package/dist/tests/proof.test.js +296 -0
- package/dist/tests/relayer.test.d.ts +1 -0
- package/dist/tests/relayer.test.js +271 -0
- package/dist/tests/sdk.integration.test.d.ts +1 -0
- package/dist/tests/sdk.integration.test.js +330 -0
- package/dist/tests/shield-owner.test.d.ts +1 -0
- package/dist/tests/shield-owner.test.js +89 -0
- package/dist/tests/shield-preflight.test.d.ts +1 -0
- package/dist/tests/shield-preflight.test.js +87 -0
- package/dist/tests/shield-realproof.test.d.ts +1 -0
- package/dist/tests/shield-realproof.test.js +272 -0
- package/dist/tests/shield.test.d.ts +1 -0
- package/dist/tests/shield.test.js +403 -0
- package/dist/tests/utxo.test.d.ts +1 -0
- package/dist/tests/utxo.test.js +140 -0
- package/package.json +69 -11
- package/poseidon.d.ts +2 -0
- package/poseidon.js +4 -0
- package/proof.d.ts +2 -0
- package/proof.js +4 -0
- package/prover.d.ts +2 -0
- package/prover.js +4 -0
- package/shield.d.ts +2 -0
- package/shield.js +4 -0
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
require("mocha");
|
|
7
|
+
const assert_1 = require("assert");
|
|
8
|
+
const http_1 = __importDefault(require("http"));
|
|
9
|
+
const src_1 = require("../src");
|
|
10
|
+
const tweetnacl_1 = __importDefault(require("tweetnacl"));
|
|
11
|
+
/**
|
|
12
|
+
* Relayer client tests using a local HTTP mock server.
|
|
13
|
+
*/
|
|
14
|
+
// Generate a real NaCl keypair for the "relayer"
|
|
15
|
+
const relayerKeyPair = tweetnacl_1.default.box.keyPair();
|
|
16
|
+
const relayerPublicKeyB64 = typeof Buffer !== "undefined"
|
|
17
|
+
? Buffer.from(relayerKeyPair.publicKey).toString("base64")
|
|
18
|
+
: btoa(String.fromCharCode(...relayerKeyPair.publicKey));
|
|
19
|
+
let server;
|
|
20
|
+
let baseUrl;
|
|
21
|
+
let lastRequest;
|
|
22
|
+
function createMockServer(handler) {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
server = http_1.default.createServer((req, res) => {
|
|
25
|
+
let body = "";
|
|
26
|
+
req.on("data", (c) => (body += c));
|
|
27
|
+
req.on("end", () => {
|
|
28
|
+
lastRequest = {
|
|
29
|
+
method: req.method,
|
|
30
|
+
url: req.url,
|
|
31
|
+
body: body ? JSON.parse(body) : null,
|
|
32
|
+
headers: req.headers,
|
|
33
|
+
};
|
|
34
|
+
if (handler) {
|
|
35
|
+
handler(req, res);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
39
|
+
res.end(JSON.stringify({ success: true }));
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
server.listen(0, () => {
|
|
44
|
+
const addr = server.address();
|
|
45
|
+
resolve(`http://127.0.0.1:${addr.port}`);
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function makeConfig(url) {
|
|
50
|
+
return {
|
|
51
|
+
relayerUrl: url,
|
|
52
|
+
apiKey: "test-api-key",
|
|
53
|
+
relayerPublicKey: relayerPublicKeyB64,
|
|
54
|
+
authToken: "test-token",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
describe("VeiloRelayerClient", function () {
|
|
58
|
+
this.timeout(15000);
|
|
59
|
+
afterEach((done) => {
|
|
60
|
+
if (server)
|
|
61
|
+
server.close(done);
|
|
62
|
+
else
|
|
63
|
+
done();
|
|
64
|
+
});
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
// Basic request mechanics
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
it("sends x-api-key header on all requests", async () => {
|
|
69
|
+
baseUrl = await createMockServer((req, res) => {
|
|
70
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
71
|
+
res.end(JSON.stringify({ success: true, available: true }));
|
|
72
|
+
});
|
|
73
|
+
const client = new src_1.VeiloRelayerClient(makeConfig(baseUrl));
|
|
74
|
+
await client.checkUsername("alice");
|
|
75
|
+
assert_1.strict.equal(lastRequest.headers["x-api-key"], "test-api-key");
|
|
76
|
+
});
|
|
77
|
+
it("sends Authorization header for authenticated endpoints", async () => {
|
|
78
|
+
baseUrl = await createMockServer((req, res) => {
|
|
79
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
80
|
+
res.end(JSON.stringify({ success: true, notes: [], total: 0, limit: 50, offset: 0 }));
|
|
81
|
+
});
|
|
82
|
+
const client = new src_1.VeiloRelayerClient(makeConfig(baseUrl));
|
|
83
|
+
await client.queryEncryptedNotes();
|
|
84
|
+
assert_1.strict.equal(lastRequest.headers["authorization"], "Bearer test-token");
|
|
85
|
+
});
|
|
86
|
+
it("does not send Authorization when no authToken", async () => {
|
|
87
|
+
baseUrl = await createMockServer((req, res) => {
|
|
88
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
89
|
+
res.end(JSON.stringify({ success: true, notes: [], total: 0, limit: 50, offset: 0 }));
|
|
90
|
+
});
|
|
91
|
+
const cfg = makeConfig(baseUrl);
|
|
92
|
+
delete cfg.authToken;
|
|
93
|
+
const client = new src_1.VeiloRelayerClient(cfg);
|
|
94
|
+
await client.queryEncryptedNotes();
|
|
95
|
+
assert_1.strict.equal(lastRequest.headers["authorization"], undefined);
|
|
96
|
+
});
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
// Error handling
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
it("throws VeiloApiError on non-2xx responses", async () => {
|
|
101
|
+
baseUrl = await createMockServer((req, res) => {
|
|
102
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
103
|
+
res.end(JSON.stringify({ success: false, error: "BAD_REQUEST", message: "Invalid input" }));
|
|
104
|
+
});
|
|
105
|
+
const client = new src_1.VeiloRelayerClient(makeConfig(baseUrl));
|
|
106
|
+
try {
|
|
107
|
+
await client.checkUsername("");
|
|
108
|
+
assert_1.strict.fail("should have thrown");
|
|
109
|
+
}
|
|
110
|
+
catch (err) {
|
|
111
|
+
assert_1.strict.ok(err instanceof src_1.VeiloApiError);
|
|
112
|
+
assert_1.strict.equal(err.code, "BAD_REQUEST");
|
|
113
|
+
assert_1.strict.equal(err.status, 400);
|
|
114
|
+
assert_1.strict.equal(err.message, "Invalid input");
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
it("throws VeiloApiError when success=false on 200", async () => {
|
|
118
|
+
baseUrl = await createMockServer((req, res) => {
|
|
119
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
120
|
+
res.end(JSON.stringify({ success: false, error: "DUPLICATE_COMMITMENT" }));
|
|
121
|
+
});
|
|
122
|
+
const client = new src_1.VeiloRelayerClient(makeConfig(baseUrl));
|
|
123
|
+
try {
|
|
124
|
+
await client.saveEncryptedNote({
|
|
125
|
+
commitment: "abc",
|
|
126
|
+
ephemeralPublicKey: "def",
|
|
127
|
+
encryptedBlob: "ghi",
|
|
128
|
+
timestamp: Date.now(),
|
|
129
|
+
});
|
|
130
|
+
assert_1.strict.fail("should have thrown");
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
assert_1.strict.ok(err instanceof src_1.VeiloApiError);
|
|
134
|
+
assert_1.strict.equal(err.code, "DUPLICATE_COMMITMENT");
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Retry behavior
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
it("retries on 503 and succeeds", async () => {
|
|
141
|
+
let callCount = 0;
|
|
142
|
+
baseUrl = await createMockServer((req, res) => {
|
|
143
|
+
callCount++;
|
|
144
|
+
if (callCount < 3) {
|
|
145
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
146
|
+
res.end(JSON.stringify({ success: false, error: "SERVICE_UNAVAILABLE" }));
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
150
|
+
res.end(JSON.stringify({ success: true, available: true }));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
const client = new src_1.VeiloRelayerClient(makeConfig(baseUrl), {
|
|
154
|
+
maxRetries: 3,
|
|
155
|
+
baseDelayMs: 50,
|
|
156
|
+
});
|
|
157
|
+
const result = await client.checkUsername("bob");
|
|
158
|
+
assert_1.strict.equal(result.success, true);
|
|
159
|
+
assert_1.strict.equal(callCount, 3);
|
|
160
|
+
});
|
|
161
|
+
it("does not retry on 400", async () => {
|
|
162
|
+
let callCount = 0;
|
|
163
|
+
baseUrl = await createMockServer((req, res) => {
|
|
164
|
+
callCount++;
|
|
165
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
166
|
+
res.end(JSON.stringify({ success: false, error: "INVALID" }));
|
|
167
|
+
});
|
|
168
|
+
const client = new src_1.VeiloRelayerClient(makeConfig(baseUrl), {
|
|
169
|
+
maxRetries: 3,
|
|
170
|
+
baseDelayMs: 50,
|
|
171
|
+
});
|
|
172
|
+
try {
|
|
173
|
+
await client.checkUsername("bad");
|
|
174
|
+
assert_1.strict.fail("should have thrown");
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
assert_1.strict.equal(callCount, 1, "should not retry on 400");
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
it("does not retry transact endpoints", async () => {
|
|
181
|
+
let callCount = 0;
|
|
182
|
+
baseUrl = await createMockServer((req, res) => {
|
|
183
|
+
callCount++;
|
|
184
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
185
|
+
res.end(JSON.stringify({ success: false, error: "SERVICE_UNAVAILABLE" }));
|
|
186
|
+
});
|
|
187
|
+
const client = new src_1.VeiloRelayerClient(makeConfig(baseUrl), {
|
|
188
|
+
maxRetries: 3,
|
|
189
|
+
baseDelayMs: 50,
|
|
190
|
+
});
|
|
191
|
+
try {
|
|
192
|
+
await client.submitWithdraw({
|
|
193
|
+
notes: [],
|
|
194
|
+
recipient: "11111111111111111111111111111111",
|
|
195
|
+
amount: "1000000",
|
|
196
|
+
userPublicKey: "11111111111111111111111111111111",
|
|
197
|
+
mintAddress: "11111111111111111111111111111111",
|
|
198
|
+
});
|
|
199
|
+
assert_1.strict.fail("should have thrown");
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
assert_1.strict.equal(callCount, 1, "transact endpoints should not retry");
|
|
203
|
+
}
|
|
204
|
+
});
|
|
205
|
+
// ---------------------------------------------------------------------------
|
|
206
|
+
// Encryption
|
|
207
|
+
// ---------------------------------------------------------------------------
|
|
208
|
+
it("encrypts withdraw payload (body contains encryptedPayload)", async () => {
|
|
209
|
+
baseUrl = await createMockServer((req, res) => {
|
|
210
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
211
|
+
res.end(JSON.stringify({ success: true, message: "ok" }));
|
|
212
|
+
});
|
|
213
|
+
const client = new src_1.VeiloRelayerClient(makeConfig(baseUrl));
|
|
214
|
+
await client.submitWithdraw({
|
|
215
|
+
notes: [],
|
|
216
|
+
recipient: "11111111111111111111111111111111",
|
|
217
|
+
amount: "1000000",
|
|
218
|
+
userPublicKey: "11111111111111111111111111111111",
|
|
219
|
+
mintAddress: "11111111111111111111111111111111",
|
|
220
|
+
});
|
|
221
|
+
assert_1.strict.ok(lastRequest.body.encryptedPayload, "body should contain encryptedPayload");
|
|
222
|
+
assert_1.strict.equal(typeof lastRequest.body.encryptedPayload, "string");
|
|
223
|
+
// Decrypt and verify the payload structure
|
|
224
|
+
const raw = Buffer.from(lastRequest.body.encryptedPayload, "base64");
|
|
225
|
+
const ephPub = raw.subarray(0, 32);
|
|
226
|
+
const nonce = raw.subarray(32, 56);
|
|
227
|
+
const ciphertext = raw.subarray(56);
|
|
228
|
+
const decrypted = tweetnacl_1.default.box.open(new Uint8Array(ciphertext), new Uint8Array(nonce), new Uint8Array(ephPub), relayerKeyPair.secretKey);
|
|
229
|
+
assert_1.strict.ok(decrypted, "relayer should be able to decrypt the payload");
|
|
230
|
+
const payload = JSON.parse(new TextDecoder().decode(decrypted));
|
|
231
|
+
assert_1.strict.equal(payload.recipient, "11111111111111111111111111111111");
|
|
232
|
+
assert_1.strict.equal(payload.amount, "1000000");
|
|
233
|
+
assert_1.strict.ok(payload.timestamp, "should include timestamp");
|
|
234
|
+
assert_1.strict.ok(payload.nonce, "should include nonce");
|
|
235
|
+
});
|
|
236
|
+
// ---------------------------------------------------------------------------
|
|
237
|
+
// Auth endpoints
|
|
238
|
+
// ---------------------------------------------------------------------------
|
|
239
|
+
it("setAuthToken updates subsequent requests", async () => {
|
|
240
|
+
baseUrl = await createMockServer((req, res) => {
|
|
241
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
242
|
+
res.end(JSON.stringify({ success: true, notes: [], total: 0, limit: 50, offset: 0 }));
|
|
243
|
+
});
|
|
244
|
+
const cfg = makeConfig(baseUrl);
|
|
245
|
+
cfg.authToken = undefined;
|
|
246
|
+
const client = new src_1.VeiloRelayerClient(cfg);
|
|
247
|
+
// First call — no auth
|
|
248
|
+
await client.queryEncryptedNotes();
|
|
249
|
+
assert_1.strict.equal(lastRequest.headers["authorization"], undefined);
|
|
250
|
+
// Set token and call again
|
|
251
|
+
client.setAuthToken("new-token");
|
|
252
|
+
await client.queryEncryptedNotes();
|
|
253
|
+
assert_1.strict.equal(lastRequest.headers["authorization"], "Bearer new-token");
|
|
254
|
+
});
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Merkle endpoints
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
it("getMerkleRoot passes query params", async () => {
|
|
259
|
+
baseUrl = await createMockServer((req, res) => {
|
|
260
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
261
|
+
res.end(JSON.stringify({
|
|
262
|
+
success: true,
|
|
263
|
+
data: { root: "abc", nextIndex: 5, treeId: 0, onChainTreeId: 0, mintAddress: "So11111111111111111111111111111111111111112" },
|
|
264
|
+
}));
|
|
265
|
+
});
|
|
266
|
+
const client = new src_1.VeiloRelayerClient(makeConfig(baseUrl));
|
|
267
|
+
await client.getMerkleRoot("So11111111111111111111111111111111111111112", 0);
|
|
268
|
+
assert_1.strict.ok(lastRequest.url.includes("mintAddress=So11111111111111111111111111111111111111112"));
|
|
269
|
+
assert_1.strict.ok(lastRequest.url.includes("treeId=0"));
|
|
270
|
+
});
|
|
271
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "mocha";
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
require("mocha");
|
|
40
|
+
const assert_1 = require("assert");
|
|
41
|
+
const anchor = __importStar(require("@coral-xyz/anchor"));
|
|
42
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
43
|
+
const fs_1 = __importDefault(require("fs"));
|
|
44
|
+
const os_1 = __importDefault(require("os"));
|
|
45
|
+
const path_1 = __importDefault(require("path"));
|
|
46
|
+
const src_1 = require("../src");
|
|
47
|
+
// ---- IDL + Provider helpers ----
|
|
48
|
+
function loadIdl() {
|
|
49
|
+
// NOTE: this path is relative to dist/tests at runtime
|
|
50
|
+
const idlPath = path_1.default.join(__dirname, "../../idl/idl/privacy_pool.json");
|
|
51
|
+
if (!fs_1.default.existsSync(idlPath))
|
|
52
|
+
return null;
|
|
53
|
+
const raw = fs_1.default.readFileSync(idlPath, "utf8");
|
|
54
|
+
return JSON.parse(raw);
|
|
55
|
+
}
|
|
56
|
+
function makeProvider() {
|
|
57
|
+
try {
|
|
58
|
+
const url = process.env.ANCHOR_PROVIDER_URL ?? "http://127.0.0.1:8899";
|
|
59
|
+
const connection = new anchor.web3.Connection(url, "confirmed");
|
|
60
|
+
const keypairPath = process.env.ANCHOR_WALLET ??
|
|
61
|
+
path_1.default.join(os_1.default.homedir(), ".config", "solana", "id.json");
|
|
62
|
+
if (!fs_1.default.existsSync(keypairPath))
|
|
63
|
+
return null;
|
|
64
|
+
const secret = JSON.parse(fs_1.default.readFileSync(keypairPath, "utf8"));
|
|
65
|
+
const kp = web3_js_1.Keypair.fromSecretKey(Uint8Array.from(secret));
|
|
66
|
+
const wallet = new anchor.Wallet(kp);
|
|
67
|
+
return new anchor.AnchorProvider(connection, wallet, {
|
|
68
|
+
commitment: "confirmed",
|
|
69
|
+
preflightCommitment: "confirmed",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
describe("privacy-pool SDK integration (UTXO model)", function () {
|
|
77
|
+
this.timeout(60000);
|
|
78
|
+
const provider = makeProvider();
|
|
79
|
+
const idl = loadIdl();
|
|
80
|
+
// Skip on-chain tests when no provider or IDL is available, or program not deployed
|
|
81
|
+
before(async function () {
|
|
82
|
+
if (!provider || !idl) {
|
|
83
|
+
console.log("Skipping on-chain integration tests: no provider or IDL available");
|
|
84
|
+
this.skip();
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
const programId = new web3_js_1.PublicKey(idl.address);
|
|
89
|
+
const info = await provider.connection.getAccountInfo(programId);
|
|
90
|
+
if (!info) {
|
|
91
|
+
console.log(`Skipping on-chain integration tests: program ${programId.toBase58()} not deployed on this cluster`);
|
|
92
|
+
this.skip();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
console.log("Skipping on-chain integration tests: could not reach cluster");
|
|
97
|
+
this.skip();
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
// These bindings are populated only when the suite runs
|
|
101
|
+
let program;
|
|
102
|
+
let wallet;
|
|
103
|
+
let connection;
|
|
104
|
+
before(async function () {
|
|
105
|
+
if (!provider || !idl)
|
|
106
|
+
return;
|
|
107
|
+
await (0, src_1.initPoseidon)();
|
|
108
|
+
anchor.setProvider(provider);
|
|
109
|
+
program = new anchor.Program(idl, provider);
|
|
110
|
+
wallet = provider.wallet;
|
|
111
|
+
connection = provider.connection;
|
|
112
|
+
});
|
|
113
|
+
const FEE_BPS = 50; // 0.5%
|
|
114
|
+
const MIN_DEPOSIT = (0, src_1.sol)(0.1);
|
|
115
|
+
const MAX_DEPOSIT = (0, src_1.sol)(100);
|
|
116
|
+
const MIN_WITHDRAW = (0, src_1.sol)(0.1);
|
|
117
|
+
const MAX_WITHDRAW = (0, src_1.sol)(100);
|
|
118
|
+
async function airdropAndConfirm(pubkey, lamports) {
|
|
119
|
+
const sig = await connection.requestAirdrop(pubkey, lamports);
|
|
120
|
+
await connection.confirmTransaction(sig, "confirmed");
|
|
121
|
+
}
|
|
122
|
+
it("initializes global config", async () => {
|
|
123
|
+
const globalConfig = (0, src_1.getGlobalConfigPda)(program.programId);
|
|
124
|
+
// Check if already initialized
|
|
125
|
+
const existingConfig = await connection.getAccountInfo(globalConfig);
|
|
126
|
+
if (!existingConfig) {
|
|
127
|
+
await airdropAndConfirm(wallet.publicKey, 2 * web3_js_1.LAMPORTS_PER_SOL);
|
|
128
|
+
await (0, src_1.initializeGlobalConfig)({
|
|
129
|
+
program,
|
|
130
|
+
admin: wallet,
|
|
131
|
+
payer: wallet.payer,
|
|
132
|
+
});
|
|
133
|
+
console.log("Global config initialized");
|
|
134
|
+
}
|
|
135
|
+
else {
|
|
136
|
+
console.log("Global config already exists, skipping initialization");
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
it("initializes pool with new parameters", async () => {
|
|
140
|
+
const { config } = (0, src_1.getPoolPdas)(program.programId, src_1.NATIVE_SOL_MINT);
|
|
141
|
+
// Check if already initialized
|
|
142
|
+
const existingConfig = await connection.getAccountInfo(config);
|
|
143
|
+
if (!existingConfig) {
|
|
144
|
+
await airdropAndConfirm(wallet.publicKey, 5 * web3_js_1.LAMPORTS_PER_SOL);
|
|
145
|
+
await (0, src_1.initializePool)({
|
|
146
|
+
program,
|
|
147
|
+
admin: wallet,
|
|
148
|
+
payer: wallet.payer,
|
|
149
|
+
feeBps: FEE_BPS,
|
|
150
|
+
mintAddress: src_1.NATIVE_SOL_MINT,
|
|
151
|
+
minDepositAmount: MIN_DEPOSIT,
|
|
152
|
+
maxDepositAmount: MAX_DEPOSIT,
|
|
153
|
+
minWithdrawAmount: MIN_WITHDRAW,
|
|
154
|
+
maxWithdrawAmount: MAX_WITHDRAW,
|
|
155
|
+
});
|
|
156
|
+
console.log("Pool initialized for native SOL");
|
|
157
|
+
}
|
|
158
|
+
else {
|
|
159
|
+
console.log("Pool already exists, skipping initialization");
|
|
160
|
+
}
|
|
161
|
+
// Verify the PDAs exist
|
|
162
|
+
const configAcc = await connection.getAccountInfo(config);
|
|
163
|
+
(0, assert_1.strict)(configAcc !== null, "Config account should exist");
|
|
164
|
+
});
|
|
165
|
+
it("adds a relayer to the pool", async () => {
|
|
166
|
+
const relayer = web3_js_1.Keypair.generate();
|
|
167
|
+
await airdropAndConfirm(relayer.publicKey, 1 * web3_js_1.LAMPORTS_PER_SOL);
|
|
168
|
+
await (0, src_1.addRelayer)({
|
|
169
|
+
program,
|
|
170
|
+
admin: wallet,
|
|
171
|
+
mintAddress: src_1.NATIVE_SOL_MINT,
|
|
172
|
+
newRelayer: relayer.publicKey,
|
|
173
|
+
});
|
|
174
|
+
console.log(`Relayer added: ${relayer.publicKey.toBase58()}`);
|
|
175
|
+
});
|
|
176
|
+
it("creates UTXOs with correct structure", () => {
|
|
177
|
+
const keypair = (0, src_1.generateKeypair)();
|
|
178
|
+
const amount = (0, src_1.sol)(1);
|
|
179
|
+
const utxo = (0, src_1.createUTXO)({
|
|
180
|
+
amount,
|
|
181
|
+
pubkey: keypair.publicKey,
|
|
182
|
+
mintAddress: src_1.NATIVE_SOL_MINT,
|
|
183
|
+
});
|
|
184
|
+
assert_1.strict.equal(utxo.amount, amount);
|
|
185
|
+
assert_1.strict.equal(utxo.pubkey, keypair.publicKey);
|
|
186
|
+
assert_1.strict.equal(utxo.commitment.length, 32);
|
|
187
|
+
assert_1.strict.ok(utxo.blinding > 0n);
|
|
188
|
+
});
|
|
189
|
+
it("derives nullifiers correctly", () => {
|
|
190
|
+
const privateKey = 12345n;
|
|
191
|
+
const utxo = (0, src_1.createOwnedUTXO)({
|
|
192
|
+
amount: (0, src_1.sol)(1),
|
|
193
|
+
privateKey,
|
|
194
|
+
mintAddress: src_1.NATIVE_SOL_MINT,
|
|
195
|
+
});
|
|
196
|
+
const pathIndex = 0;
|
|
197
|
+
const nullifier = (0, src_1.deriveNullifier)(utxo, pathIndex, privateKey);
|
|
198
|
+
assert_1.strict.equal(nullifier.length, 32);
|
|
199
|
+
// Verify determinism
|
|
200
|
+
const nullifier2 = (0, src_1.deriveNullifier)(utxo, pathIndex, privateKey);
|
|
201
|
+
assert_1.strict.equal(Buffer.compare(Buffer.from(nullifier), Buffer.from(nullifier2)), 0);
|
|
202
|
+
});
|
|
203
|
+
it("computes extDataHash correctly", () => {
|
|
204
|
+
const extData = {
|
|
205
|
+
recipient: web3_js_1.Keypair.generate().publicKey,
|
|
206
|
+
relayer: web3_js_1.Keypair.generate().publicKey,
|
|
207
|
+
fee: (0, src_1.sol)(0.01),
|
|
208
|
+
refund: 0n,
|
|
209
|
+
claimant: web3_js_1.PublicKey.default,
|
|
210
|
+
};
|
|
211
|
+
const hash = (0, src_1.computeExtDataHash)(extData);
|
|
212
|
+
assert_1.strict.equal(hash.length, 32);
|
|
213
|
+
// Verify determinism
|
|
214
|
+
const hash2 = (0, src_1.computeExtDataHash)(extData);
|
|
215
|
+
assert_1.strict.equal(Buffer.compare(Buffer.from(hash), Buffer.from(hash2)), 0);
|
|
216
|
+
});
|
|
217
|
+
it("builds Merkle tree with correct depth", () => {
|
|
218
|
+
const tree = new src_1.MerkleTree(16);
|
|
219
|
+
assert_1.strict.equal(tree.depth, 16);
|
|
220
|
+
assert_1.strict.equal(tree.capacity, 65536);
|
|
221
|
+
assert_1.strict.equal(tree.root.length, 32);
|
|
222
|
+
assert_1.strict.equal(tree.nextIndex, 0);
|
|
223
|
+
});
|
|
224
|
+
it("inserts commitments and generates paths", () => {
|
|
225
|
+
const tree = new src_1.MerkleTree(16);
|
|
226
|
+
const keypair = (0, src_1.generateKeypair)();
|
|
227
|
+
// Create and insert a UTXO
|
|
228
|
+
const utxo = (0, src_1.createUTXO)({
|
|
229
|
+
amount: (0, src_1.sol)(1),
|
|
230
|
+
pubkey: keypair.publicKey,
|
|
231
|
+
mintAddress: src_1.NATIVE_SOL_MINT,
|
|
232
|
+
});
|
|
233
|
+
const { index, root } = tree.insert(utxo.commitment);
|
|
234
|
+
assert_1.strict.equal(index, 0);
|
|
235
|
+
assert_1.strict.equal(root.length, 32);
|
|
236
|
+
// Get the path
|
|
237
|
+
const path = tree.getPath(index);
|
|
238
|
+
assert_1.strict.equal(path.leafIndex, 0);
|
|
239
|
+
assert_1.strict.equal(path.path.length, 16);
|
|
240
|
+
assert_1.strict.equal(path.indices.length, 16);
|
|
241
|
+
// All path elements should be 32 bytes
|
|
242
|
+
for (const element of path.path) {
|
|
243
|
+
assert_1.strict.equal(element.length, 32);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
it("generates circuit-compatible paths", () => {
|
|
247
|
+
const tree = new src_1.MerkleTree(16);
|
|
248
|
+
const keypair = (0, src_1.generateKeypair)();
|
|
249
|
+
const utxo = (0, src_1.createUTXO)({
|
|
250
|
+
amount: (0, src_1.sol)(1),
|
|
251
|
+
pubkey: keypair.publicKey,
|
|
252
|
+
mintAddress: src_1.NATIVE_SOL_MINT,
|
|
253
|
+
});
|
|
254
|
+
const { index } = tree.insert(utxo.commitment);
|
|
255
|
+
const circuitPath = tree.getCircuitPath(index);
|
|
256
|
+
assert_1.strict.equal(circuitPath.pathIndex, 0);
|
|
257
|
+
assert_1.strict.equal(circuitPath.pathElements.length, 16);
|
|
258
|
+
// All elements should be bigints
|
|
259
|
+
for (const element of circuitPath.pathElements) {
|
|
260
|
+
assert_1.strict.equal(typeof element, "bigint");
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
// Note: The actual transact instruction test requires a valid ZK proof
|
|
264
|
+
// which would need the full circuit setup. This is a placeholder for
|
|
265
|
+
// when proof generation is available.
|
|
266
|
+
it.skip("executes a deposit transaction", async () => {
|
|
267
|
+
// This test would require:
|
|
268
|
+
// 1. A valid snarkjs proof
|
|
269
|
+
// 2. The circuit WASM and zkey files
|
|
270
|
+
// 3. A running local validator with the contract deployed
|
|
271
|
+
const relayer = web3_js_1.Keypair.generate();
|
|
272
|
+
const recipient = web3_js_1.Keypair.generate();
|
|
273
|
+
await airdropAndConfirm(relayer.publicKey, 2 * web3_js_1.LAMPORTS_PER_SOL);
|
|
274
|
+
const keypair = (0, src_1.generateKeypair)();
|
|
275
|
+
const tree = new src_1.MerkleTree(16);
|
|
276
|
+
// Create zero inputs for deposit
|
|
277
|
+
const zeroInput0 = (0, src_1.createOwnedZeroUTXO)(keypair.privateKey, src_1.NATIVE_SOL_MINT);
|
|
278
|
+
const zeroInput1 = (0, src_1.createOwnedZeroUTXO)(keypair.privateKey, src_1.NATIVE_SOL_MINT);
|
|
279
|
+
tree.insert(zeroInput0.commitment);
|
|
280
|
+
const { root } = tree.insert(zeroInput1.commitment);
|
|
281
|
+
// Create output UTXOs
|
|
282
|
+
const outputUTXO0 = (0, src_1.createUTXO)({
|
|
283
|
+
amount: (0, src_1.sol)(1),
|
|
284
|
+
pubkey: keypair.publicKey,
|
|
285
|
+
mintAddress: src_1.NATIVE_SOL_MINT,
|
|
286
|
+
});
|
|
287
|
+
const outputUTXO1 = (0, src_1.createUTXO)({
|
|
288
|
+
amount: 0n,
|
|
289
|
+
pubkey: keypair.publicKey,
|
|
290
|
+
mintAddress: src_1.NATIVE_SOL_MINT,
|
|
291
|
+
});
|
|
292
|
+
const extData = {
|
|
293
|
+
recipient: recipient.publicKey,
|
|
294
|
+
relayer: relayer.publicKey,
|
|
295
|
+
fee: 0n,
|
|
296
|
+
refund: 0n,
|
|
297
|
+
claimant: web3_js_1.PublicKey.default,
|
|
298
|
+
};
|
|
299
|
+
// Mock proof (would need real proof in production)
|
|
300
|
+
const mockProof = {
|
|
301
|
+
proofA: new Array(64).fill(0),
|
|
302
|
+
proofB: new Array(128).fill(0),
|
|
303
|
+
proofC: new Array(64).fill(0),
|
|
304
|
+
};
|
|
305
|
+
// This would fail without a valid proof, but tests the API
|
|
306
|
+
try {
|
|
307
|
+
await (0, src_1.transact)({
|
|
308
|
+
program,
|
|
309
|
+
relayer,
|
|
310
|
+
recipient: recipient.publicKey,
|
|
311
|
+
mintAddress: src_1.NATIVE_SOL_MINT,
|
|
312
|
+
root,
|
|
313
|
+
inputTreeId: 0,
|
|
314
|
+
outputTreeId: 0,
|
|
315
|
+
publicAmount: (0, src_1.sol)(1),
|
|
316
|
+
inputNullifiers: [
|
|
317
|
+
(0, src_1.deriveNullifier)(zeroInput0, 0, keypair.privateKey),
|
|
318
|
+
(0, src_1.deriveNullifier)(zeroInput1, 1, keypair.privateKey),
|
|
319
|
+
],
|
|
320
|
+
outputCommitments: [outputUTXO0.commitment, outputUTXO1.commitment],
|
|
321
|
+
extData,
|
|
322
|
+
proof: mockProof,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
catch (e) {
|
|
326
|
+
// Expected to fail with VerifyFailed since we're using a mock proof
|
|
327
|
+
console.log("Transaction failed as expected with mock proof:", e.message);
|
|
328
|
+
}
|
|
329
|
+
});
|
|
330
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "mocha";
|