@interest-protocol/vortex-sdk 11.3.0 → 12.0.3
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/dist/constants.d.ts +6 -9
- package/dist/constants.d.ts.map +1 -1
- package/dist/crypto/ff/f1field.d.ts.map +1 -1
- package/dist/crypto/ff/index.d.ts +1 -2
- package/dist/crypto/ff/index.d.ts.map +1 -1
- package/dist/crypto/ff/scalar.d.ts.map +1 -1
- package/dist/crypto/ff/utils.d.ts.map +1 -1
- package/dist/entities/keypair.d.ts.map +1 -1
- package/dist/entities/utxo.d.ts.map +1 -1
- package/dist/index.js +3768 -3584
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3769 -3584
- package/dist/index.mjs.map +1 -1
- package/dist/utils/decrypt.d.ts +5 -5
- package/dist/utils/decrypt.d.ts.map +1 -1
- package/dist/vortex-api.d.ts.map +1 -1
- package/package.json +10 -10
- package/src/__tests__/entities/keypair.spec.ts +159 -2
- package/src/constants.ts +6 -13
- package/src/crypto/ff/f1field.ts +3 -2
- package/src/crypto/ff/index.ts +2 -2
- package/src/crypto/ff/scalar.ts +1 -0
- package/src/crypto/ff/utils.ts +1 -0
- package/src/entities/keypair.ts +63 -13
- package/src/entities/utxo.ts +7 -2
- package/src/utils/decrypt.ts +13 -16
- package/src/vortex-api.ts +5 -1
- package/dist/crypto/ff/random.d.ts +0 -2
- package/dist/crypto/ff/random.d.ts.map +0 -1
- package/src/crypto/ff/random.ts +0 -32
package/dist/utils/decrypt.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { PaginatedEvents } from '@mysten/sui/client';
|
|
2
|
-
import { Commitment } from '../vortex-api.types';
|
|
3
|
-
import { VortexKeypair } from '../entities/keypair';
|
|
1
|
+
import type { PaginatedEvents } from '@mysten/sui/client';
|
|
2
|
+
import type { Commitment } from '../vortex-api.types';
|
|
3
|
+
import type { VortexKeypair } from '../entities/keypair';
|
|
4
|
+
import type { Vortex } from '../vortex';
|
|
5
|
+
import type { VortexPool } from '../vortex.types';
|
|
4
6
|
import { Utxo } from '../entities/utxo';
|
|
5
|
-
import { Vortex } from '../vortex';
|
|
6
|
-
import { VortexPool } from '../vortex.types';
|
|
7
7
|
interface GetUnspentUtxosArgs {
|
|
8
8
|
commitmentEvents: PaginatedEvents;
|
|
9
9
|
vortexKeypair: VortexKeypair;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decrypt.d.ts","sourceRoot":"","sources":["../../src/utils/decrypt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"decrypt.d.ts","sourceRoot":"","sources":["../../src/utils/decrypt.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAe,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGlD,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAIxC,UAAU,mBAAmB;IAC3B,gBAAgB,EAAE,eAAe,CAAC;IAClC,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,UAAU,CAAC;CACjC;AAED,eAAO,MAAM,eAAe,GAAU,6DAKnC,mBAAmB,oBAkCrB,CAAC;AAEF,UAAU,0BAA0B;IAClC,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,UAAU,CAAC;CACjC;AAED,UAAU,wCAAwC;IAChD,WAAW,EAAE,IAAI,CAAC,UAAU,EAAE,UAAU,GAAG,iBAAiB,CAAC,EAAE,CAAC;IAChE,aAAa,EAAE,aAAa,CAAC;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,UAAU,CAAC;CACjC;AAED,eAAO,MAAM,sBAAsB,GAAU,wDAK1C,0BAA0B,oBA2C5B,CAAC;AAEF,eAAO,MAAM,oCAAoC,GAAU,wDAKxD,wCAAwC;;;EAgD1C,CAAC"}
|
package/dist/vortex-api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vortex-api.d.ts","sourceRoot":"","sources":["../src/vortex-api.ts"],"names":[],"mappings":"AACA,OAAO,EACL,wBAAwB,EAExB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,EACzB,mBAAmB,EACnB,eAAe,EAGhB,MAAM,oBAAoB,CAAC;AAE5B,qBAAa,SAAS;;gBAGR,EAAE,MAAuB,EAAE,GAAE,wBAA6B;IAIhE,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC;IAQjC,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAiB7D,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAenE,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAsBtE,QAAQ,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,aAAa,CAAC;IAyBzD,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA4BtE,iBAAiB,CACrB,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5D,OAAO,CAAC,UAAU,EAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"vortex-api.d.ts","sourceRoot":"","sources":["../src/vortex-api.ts"],"names":[],"mappings":"AACA,OAAO,EACL,wBAAwB,EAExB,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,oBAAoB,EACpB,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,EACb,YAAY,EACZ,UAAU,EACV,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,EACzB,mBAAmB,EACnB,eAAe,EAGhB,MAAM,oBAAoB,CAAC;AAE5B,qBAAa,SAAS;;gBAGR,EAAE,MAAuB,EAAE,GAAE,wBAA6B;IAIhE,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC;IAQjC,WAAW,CAAC,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAiB7D,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC;IAenE,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,oBAAoB,CAAC;IAsBtE,QAAQ,CAAC,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,aAAa,CAAC;IAyBzD,cAAc,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IA4BtE,iBAAiB,CACrB,IAAI,EAAE,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,GAAG;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5D,OAAO,CAAC,UAAU,EAAE,CAAC;IAwBlB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAmBnE,kBAAkB,CACtB,IAAI,EAAE,yBAAyB,GAC9B,OAAO,CAAC,mBAAmB,CAAC;IAczB,UAAU,IAAI,OAAO,CAAC,eAAe,CAAC;CAuD7C;AAED,eAAO,MAAM,SAAS,WAAkB,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@interest-protocol/vortex-sdk",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
"author": "",
|
|
18
18
|
"license": "ISC",
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@mysten/sui": "^1.
|
|
21
|
-
"@noble/ciphers": "^2.
|
|
20
|
+
"@mysten/sui": "^1.45.2",
|
|
21
|
+
"@noble/ciphers": "^2.1.1",
|
|
22
22
|
"@noble/curves": "^2.0.1",
|
|
23
23
|
"@noble/hashes": "^2.0.1",
|
|
24
24
|
"@polymedia/suitcase-core": "^0.0.67",
|
|
@@ -29,16 +29,16 @@
|
|
|
29
29
|
"@interest-protocol/sui-core-sdk": "1.0.0"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
|
-
"@types/bn.js": "^5.
|
|
33
|
-
"@types/jest": "^29.5.
|
|
32
|
+
"@types/bn.js": "^5.2.0",
|
|
33
|
+
"@types/jest": "^29.5.14",
|
|
34
34
|
"@types/ramda": "^0.30.2",
|
|
35
35
|
"jest": "^29.7.0",
|
|
36
36
|
"jest-environment-node": "^29.7.0",
|
|
37
|
-
"rimraf": "^6.
|
|
38
|
-
"ts-jest": "^29.
|
|
39
|
-
"tsup": "^8.
|
|
40
|
-
"@interest-protocol/
|
|
41
|
-
"@interest-protocol/
|
|
37
|
+
"rimraf": "^6.1.2",
|
|
38
|
+
"ts-jest": "^29.4.6",
|
|
39
|
+
"tsup": "^8.5.1",
|
|
40
|
+
"@interest-protocol/typescript-config": "1.0.0",
|
|
41
|
+
"@interest-protocol/prettier-config": "1.0.0"
|
|
42
42
|
},
|
|
43
43
|
"publishConfig": {
|
|
44
44
|
"access": "public",
|
|
@@ -108,8 +108,165 @@ describe(VortexKeypair.name, () => {
|
|
|
108
108
|
keypair1.encryptionKey
|
|
109
109
|
);
|
|
110
110
|
|
|
111
|
-
// Attempting to decrypt with wrong keypair should throw
|
|
112
|
-
expect(() => keypair2.decryptUtxo(encrypted)).toThrow(
|
|
111
|
+
// Attempting to decrypt with wrong keypair should throw
|
|
112
|
+
expect(() => keypair2.decryptUtxo(encrypted)).toThrow(
|
|
113
|
+
'Decryption failed: HMAC verification failed'
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
it('should ALWAYS fail with wrong keypair - 100 iterations', () => {
|
|
118
|
+
// This test verifies the key-committing property of HMAC
|
|
119
|
+
// With the old Poly1305, this could sometimes succeed with garbage data
|
|
120
|
+
// With HMAC-SHA256, it should ALWAYS fail
|
|
121
|
+
|
|
122
|
+
const correctKeypair = VortexKeypair.generate();
|
|
123
|
+
const utxo = {
|
|
124
|
+
amount: 1000000n,
|
|
125
|
+
blinding: 123456789n,
|
|
126
|
+
index: 42n,
|
|
127
|
+
vortexPool:
|
|
128
|
+
'0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef',
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const encrypted = VortexKeypair.encryptUtxoFor(
|
|
132
|
+
utxo,
|
|
133
|
+
correctKeypair.encryptionKey
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
let failureCount = 0;
|
|
137
|
+
const iterations = 100;
|
|
138
|
+
|
|
139
|
+
for (let i = 0; i < iterations; i++) {
|
|
140
|
+
const wrongKeypair = VortexKeypair.generate();
|
|
141
|
+
try {
|
|
142
|
+
wrongKeypair.decryptUtxo(encrypted);
|
|
143
|
+
// If we get here, decryption "succeeded" (BUG!)
|
|
144
|
+
} catch {
|
|
145
|
+
failureCount++;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// ALL attempts must fail - this proves the key-committing property
|
|
150
|
+
expect(failureCount).toBe(iterations);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('should throw specific HMAC error message on wrong key', () => {
|
|
154
|
+
const keypair1 = VortexKeypair.generate();
|
|
155
|
+
const keypair2 = VortexKeypair.generate();
|
|
156
|
+
|
|
157
|
+
const utxo = {
|
|
158
|
+
amount: 500n,
|
|
159
|
+
blinding: 12345n,
|
|
160
|
+
index: 1n,
|
|
161
|
+
vortexPool: '0xabc',
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
const encrypted = VortexKeypair.encryptUtxoFor(
|
|
165
|
+
utxo,
|
|
166
|
+
keypair1.encryptionKey
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
expect(() => keypair2.decryptUtxo(encrypted)).toThrow(
|
|
170
|
+
'HMAC verification failed'
|
|
171
|
+
);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
it('should decrypt correctly with the right keypair after failed attempts', () => {
|
|
175
|
+
const correctKeypair = VortexKeypair.generate();
|
|
176
|
+
const utxo = {
|
|
177
|
+
amount: 9999n,
|
|
178
|
+
blinding: 8888n,
|
|
179
|
+
index: 7n,
|
|
180
|
+
vortexPool: '0xdef',
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const encrypted = VortexKeypair.encryptUtxoFor(
|
|
184
|
+
utxo,
|
|
185
|
+
correctKeypair.encryptionKey
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
// Try 10 wrong keypairs first
|
|
189
|
+
for (let i = 0; i < 10; i++) {
|
|
190
|
+
const wrongKeypair = VortexKeypair.generate();
|
|
191
|
+
expect(() => wrongKeypair.decryptUtxo(encrypted)).toThrow();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Now decrypt with correct keypair - should still work
|
|
195
|
+
const decrypted = correctKeypair.decryptUtxo(encrypted);
|
|
196
|
+
expect(decrypted.amount).toBe(utxo.amount);
|
|
197
|
+
expect(decrypted.blinding).toBe(utxo.blinding);
|
|
198
|
+
expect(decrypted.index).toBe(utxo.index);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('should handle large amounts correctly', () => {
|
|
202
|
+
const keypair = VortexKeypair.generate();
|
|
203
|
+
const utxo = {
|
|
204
|
+
amount: BigInt('999999999999999999999999999'),
|
|
205
|
+
blinding: BigInt('888888888888888888888888888'),
|
|
206
|
+
index: BigInt('77777777777'),
|
|
207
|
+
vortexPool:
|
|
208
|
+
'0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff',
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const encrypted = VortexKeypair.encryptUtxoFor(
|
|
212
|
+
utxo,
|
|
213
|
+
keypair.encryptionKey
|
|
214
|
+
);
|
|
215
|
+
const decrypted = keypair.decryptUtxo(encrypted);
|
|
216
|
+
|
|
217
|
+
expect(decrypted.amount).toBe(utxo.amount);
|
|
218
|
+
expect(decrypted.blinding).toBe(utxo.blinding);
|
|
219
|
+
expect(decrypted.index).toBe(utxo.index);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('should handle zero values correctly', () => {
|
|
223
|
+
const keypair = VortexKeypair.generate();
|
|
224
|
+
const utxo = {
|
|
225
|
+
amount: 0n,
|
|
226
|
+
blinding: 0n,
|
|
227
|
+
index: 0n,
|
|
228
|
+
vortexPool: '0x0',
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
const encrypted = VortexKeypair.encryptUtxoFor(
|
|
232
|
+
utxo,
|
|
233
|
+
keypair.encryptionKey
|
|
234
|
+
);
|
|
235
|
+
const decrypted = keypair.decryptUtxo(encrypted);
|
|
236
|
+
|
|
237
|
+
expect(decrypted.amount).toBe(utxo.amount);
|
|
238
|
+
expect(decrypted.blinding).toBe(utxo.blinding);
|
|
239
|
+
expect(decrypted.index).toBe(utxo.index);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
it('should produce different ciphertexts for same data (random nonce)', () => {
|
|
243
|
+
const keypair = VortexKeypair.generate();
|
|
244
|
+
const utxo = {
|
|
245
|
+
amount: 1000n,
|
|
246
|
+
blinding: 999n,
|
|
247
|
+
index: 5n,
|
|
248
|
+
vortexPool: '0x123',
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
const encrypted1 = VortexKeypair.encryptUtxoFor(
|
|
252
|
+
utxo,
|
|
253
|
+
keypair.encryptionKey
|
|
254
|
+
);
|
|
255
|
+
const encrypted2 = VortexKeypair.encryptUtxoFor(
|
|
256
|
+
utxo,
|
|
257
|
+
keypair.encryptionKey
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
// Different ciphertexts due to random nonce
|
|
261
|
+
expect(encrypted1).not.toBe(encrypted2);
|
|
262
|
+
|
|
263
|
+
// But both decrypt to same data
|
|
264
|
+
const decrypted1 = keypair.decryptUtxo(encrypted1);
|
|
265
|
+
const decrypted2 = keypair.decryptUtxo(encrypted2);
|
|
266
|
+
|
|
267
|
+
expect(decrypted1.amount).toBe(decrypted2.amount);
|
|
268
|
+
expect(decrypted1.blinding).toBe(decrypted2.blinding);
|
|
269
|
+
expect(decrypted1.index).toBe(decrypted2.index);
|
|
113
270
|
});
|
|
114
271
|
});
|
|
115
272
|
|
package/src/constants.ts
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { SUI_TYPE_ARG } from '@mysten/sui/utils';
|
|
2
|
-
|
|
3
1
|
export const BN254_FIELD_MODULUS =
|
|
4
2
|
21888242871839275222246405745257275088548364400416034343698204186575808495617n;
|
|
5
3
|
|
|
@@ -73,24 +71,24 @@ export const ERROR_CODES = {
|
|
|
73
71
|
};
|
|
74
72
|
|
|
75
73
|
export const VORTEX_PACKAGE_ID =
|
|
76
|
-
'
|
|
74
|
+
'0xd9d3b65c318e7d7dd208050a28e113a45256765b4c45acd119626d8a228d7555';
|
|
77
75
|
|
|
78
76
|
export const VORTEX_UPGRADE_CAP =
|
|
79
|
-
'
|
|
77
|
+
'0xa0d5c1dec2ad7c732cf8d01378eba8fc1451841b051fa0292d3ad07ce92aad80';
|
|
80
78
|
|
|
81
79
|
export const REGISTRY_OBJECT_ID =
|
|
82
|
-
'
|
|
80
|
+
'0x6cec550fb43435462f34b15aee9f8b8030e0433e16c8104e966013f0443b2e36';
|
|
83
81
|
|
|
84
82
|
export const VORTEX_SWAP_PACKAGE_ID =
|
|
85
|
-
'
|
|
83
|
+
'0x325610fc0c15c91682f8bf263ef0991c28fe7ff77fd98266821c3e696e459cdf';
|
|
86
84
|
|
|
87
85
|
export const VORTEX_SWAP_UPGRADE_CAP =
|
|
88
|
-
'
|
|
86
|
+
'0x3f535b61b6baa2174dc301179bd2bc392563086ddca23b8eec69da505d54e3c1';
|
|
89
87
|
|
|
90
88
|
export const SECRET_PACKAGE_ID =
|
|
91
89
|
'0x2d57ed0dd0d5f44d91f865fee3bc33d13ef3ce97c7daf88ad5f5fbb32468ccd6';
|
|
92
90
|
|
|
93
|
-
export const INITIAL_SHARED_VERSION = '
|
|
91
|
+
export const INITIAL_SHARED_VERSION = '738926997';
|
|
94
92
|
|
|
95
93
|
export const LSK_FETCH_OFFSET = 'fetch_offset';
|
|
96
94
|
|
|
@@ -106,8 +104,3 @@ export const TREASURY_ADDRESS =
|
|
|
106
104
|
export const DEPOSIT_FEE_IN_BASIS_POINTS = 50n;
|
|
107
105
|
|
|
108
106
|
export const BASIS_POINTS = 10_000n;
|
|
109
|
-
|
|
110
|
-
export const VORTEX_POOL_IDS = {
|
|
111
|
-
[SUI_TYPE_ARG]:
|
|
112
|
-
'0x1e3672f35853fccded923505434b5138543829231f025120d57fda95b86b504c',
|
|
113
|
-
};
|
package/src/crypto/ff/f1field.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
1
2
|
import * as Scalar from './scalar';
|
|
2
|
-
import {
|
|
3
|
+
import { randomBytes } from '@noble/ciphers/utils.js';
|
|
3
4
|
|
|
4
5
|
export class F1Field {
|
|
5
6
|
type: string;
|
|
@@ -288,7 +289,7 @@ export class F1Field {
|
|
|
288
289
|
const nBytes = (this.bitLength * 2) / 8;
|
|
289
290
|
let res = this.zero;
|
|
290
291
|
for (let i = 0; i < nBytes; i++) {
|
|
291
|
-
res = (res << BigInt(8)) + BigInt(
|
|
292
|
+
res = (res << BigInt(8)) + BigInt(randomBytes(1)[0]!);
|
|
292
293
|
}
|
|
293
294
|
return res % this.p;
|
|
294
295
|
}
|
package/src/crypto/ff/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
/* eslint-disable */
|
|
1
2
|
import * as utils from './utils';
|
|
2
3
|
import * as Scalar from './scalar';
|
|
3
4
|
import { F1Field } from './f1field';
|
|
4
|
-
import { getRandomBytes } from './random';
|
|
5
5
|
|
|
6
|
-
export { utils, Scalar, F1Field
|
|
6
|
+
export { utils, Scalar, F1Field };
|
package/src/crypto/ff/scalar.ts
CHANGED
package/src/crypto/ff/utils.ts
CHANGED
package/src/entities/keypair.ts
CHANGED
|
@@ -6,10 +6,22 @@ import { Ed25519Keypair } from '@mysten/sui/keypairs/ed25519';
|
|
|
6
6
|
import invariant from 'tiny-invariant';
|
|
7
7
|
import { randomBytes } from '@noble/ciphers/utils.js';
|
|
8
8
|
import { x25519 } from '@noble/curves/ed25519.js';
|
|
9
|
-
import {
|
|
9
|
+
import { xchacha20 } from '@noble/ciphers/chacha.js';
|
|
10
|
+
import { hmac } from '@noble/hashes/hmac.js';
|
|
11
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
10
12
|
import { blake2b } from '@noble/hashes/blake2.js';
|
|
11
13
|
import { normalizeSuiAddress } from '@mysten/sui/utils';
|
|
12
14
|
|
|
15
|
+
// Constant-time comparison to prevent timing attacks
|
|
16
|
+
function constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean {
|
|
17
|
+
if (a.length !== b.length) return false;
|
|
18
|
+
let diff = 0;
|
|
19
|
+
for (let i = 0; i < a.length; i++) {
|
|
20
|
+
diff |= a[i] ^ b[i];
|
|
21
|
+
}
|
|
22
|
+
return diff === 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
13
25
|
export interface UtxoPayload {
|
|
14
26
|
amount: bigint;
|
|
15
27
|
blinding: bigint;
|
|
@@ -21,6 +33,7 @@ interface EncryptedMessage {
|
|
|
21
33
|
version: string;
|
|
22
34
|
nonce: string; // base64
|
|
23
35
|
ephemPublicKey: string; // base64
|
|
36
|
+
hmacTag: string; // base64 - HMAC-SHA256 tag for key-committing authentication
|
|
24
37
|
ciphertext: string; // base64
|
|
25
38
|
}
|
|
26
39
|
|
|
@@ -30,12 +43,14 @@ type SignMessageFn = (message: Uint8Array) => Promise<{
|
|
|
30
43
|
bytes: string;
|
|
31
44
|
}>;
|
|
32
45
|
|
|
46
|
+
// Message format: nonce (24) | ephemPublicKey (32) | hmacTag (32) | ciphertext (variable)
|
|
33
47
|
function packEncryptedMessage(encryptedMessage: EncryptedMessage): string {
|
|
34
48
|
const nonceBuf = Buffer.from(encryptedMessage.nonce, 'base64');
|
|
35
49
|
const ephemPublicKeyBuf = Buffer.from(
|
|
36
50
|
encryptedMessage.ephemPublicKey,
|
|
37
51
|
'base64'
|
|
38
52
|
);
|
|
53
|
+
const hmacTagBuf = Buffer.from(encryptedMessage.hmacTag, 'base64');
|
|
39
54
|
const ciphertextBuf = Buffer.from(encryptedMessage.ciphertext, 'base64');
|
|
40
55
|
|
|
41
56
|
const messageBuff = Buffer.concat([
|
|
@@ -43,6 +58,8 @@ function packEncryptedMessage(encryptedMessage: EncryptedMessage): string {
|
|
|
43
58
|
nonceBuf,
|
|
44
59
|
Buffer.alloc(32 - ephemPublicKeyBuf.length),
|
|
45
60
|
ephemPublicKeyBuf,
|
|
61
|
+
Buffer.alloc(32 - hmacTagBuf.length),
|
|
62
|
+
hmacTagBuf,
|
|
46
63
|
ciphertextBuf,
|
|
47
64
|
]);
|
|
48
65
|
|
|
@@ -58,12 +75,14 @@ function unpackEncryptedMessage(encryptedMessage: string): EncryptedMessage {
|
|
|
58
75
|
|
|
59
76
|
const nonceBuf = messageBuff.subarray(0, 24);
|
|
60
77
|
const ephemPublicKeyBuf = messageBuff.subarray(24, 56);
|
|
61
|
-
const
|
|
78
|
+
const hmacTagBuf = messageBuff.subarray(56, 88);
|
|
79
|
+
const ciphertextBuf = messageBuff.subarray(88);
|
|
62
80
|
|
|
63
81
|
return {
|
|
64
|
-
version: 'x25519-
|
|
82
|
+
version: 'x25519-xchacha20-hmac-sha256',
|
|
65
83
|
nonce: nonceBuf.toString('base64'),
|
|
66
84
|
ephemPublicKey: ephemPublicKeyBuf.toString('base64'),
|
|
85
|
+
hmacTag: hmacTagBuf.toString('base64'),
|
|
67
86
|
ciphertext: ciphertextBuf.toString('base64'),
|
|
68
87
|
};
|
|
69
88
|
}
|
|
@@ -159,14 +178,19 @@ export class VortexKeypair {
|
|
|
159
178
|
recipientPublicKey
|
|
160
179
|
);
|
|
161
180
|
|
|
181
|
+
// Encrypt with XChaCha20
|
|
162
182
|
const nonce = randomBytes(24);
|
|
163
|
-
const
|
|
164
|
-
|
|
183
|
+
const ciphertext = xchacha20(sharedSecret, nonce, bytes);
|
|
184
|
+
|
|
185
|
+
// Compute HMAC-SHA256 over ciphertext (Encrypt-then-MAC)
|
|
186
|
+
// This provides key-committing authentication
|
|
187
|
+
const hmacTag = hmac(sha256, sharedSecret, ciphertext);
|
|
165
188
|
|
|
166
189
|
const encryptedMessage: EncryptedMessage = {
|
|
167
|
-
version: 'x25519-
|
|
190
|
+
version: 'x25519-xchacha20-hmac-sha256',
|
|
168
191
|
nonce: Buffer.from(nonce).toString('base64'),
|
|
169
192
|
ephemPublicKey: Buffer.from(ephemeralPublicKey).toString('base64'),
|
|
193
|
+
hmacTag: Buffer.from(hmacTag).toString('base64'),
|
|
170
194
|
ciphertext: Buffer.from(ciphertext).toString('base64'),
|
|
171
195
|
};
|
|
172
196
|
|
|
@@ -189,10 +213,28 @@ export class VortexKeypair {
|
|
|
189
213
|
|
|
190
214
|
invariant(parts.length === 4, 'Invalid UTXO format after decryption');
|
|
191
215
|
|
|
216
|
+
const amount = BigInt(parts[0]);
|
|
217
|
+
const blinding = BigInt(parts[1]);
|
|
218
|
+
const index = BigInt(parts[2]);
|
|
219
|
+
|
|
220
|
+
// Validate values are within BN254 field to prevent proof failures
|
|
221
|
+
invariant(
|
|
222
|
+
amount >= 0n && amount < BN254_FIELD_MODULUS,
|
|
223
|
+
'Amount exceeds field modulus'
|
|
224
|
+
);
|
|
225
|
+
invariant(
|
|
226
|
+
blinding >= 0n && blinding < BN254_FIELD_MODULUS,
|
|
227
|
+
'Blinding exceeds field modulus'
|
|
228
|
+
);
|
|
229
|
+
invariant(
|
|
230
|
+
index >= 0n && index < BN254_FIELD_MODULUS,
|
|
231
|
+
'Index exceeds field modulus'
|
|
232
|
+
);
|
|
233
|
+
|
|
192
234
|
return {
|
|
193
|
-
amount
|
|
194
|
-
blinding
|
|
195
|
-
index
|
|
235
|
+
amount,
|
|
236
|
+
blinding,
|
|
237
|
+
index,
|
|
196
238
|
vortexPool: normalizeSuiAddress(parts[3]),
|
|
197
239
|
};
|
|
198
240
|
}
|
|
@@ -254,12 +296,20 @@ export class VortexKeypair {
|
|
|
254
296
|
ephemeralPublicKey
|
|
255
297
|
);
|
|
256
298
|
|
|
257
|
-
// Decrypt using XSalsa20-Poly1305
|
|
258
|
-
const nonce = Buffer.from(encryptedMessage.nonce, 'base64');
|
|
259
299
|
const ciphertext = Buffer.from(encryptedMessage.ciphertext, 'base64');
|
|
300
|
+
const receivedHmacTag = Buffer.from(encryptedMessage.hmacTag, 'base64');
|
|
260
301
|
|
|
261
|
-
|
|
262
|
-
|
|
302
|
+
// Verify HMAC-SHA256 first (Encrypt-then-MAC verification)
|
|
303
|
+
// This is key-committing: wrong key = wrong HMAC = guaranteed failure
|
|
304
|
+
const expectedHmacTag = hmac(sha256, sharedSecret, ciphertext);
|
|
305
|
+
|
|
306
|
+
if (!constantTimeEqual(receivedHmacTag, expectedHmacTag)) {
|
|
307
|
+
throw new Error('Decryption failed: HMAC verification failed');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Decrypt using XChaCha20
|
|
311
|
+
const nonce = Buffer.from(encryptedMessage.nonce, 'base64');
|
|
312
|
+
const decrypted = xchacha20(sharedSecret, nonce, ciphertext);
|
|
263
313
|
|
|
264
314
|
return Buffer.from(decrypted);
|
|
265
315
|
}
|
package/src/entities/utxo.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { VortexKeypair } from './keypair';
|
|
2
2
|
import { poseidon3, poseidon4 } from '../crypto';
|
|
3
|
-
import { normalizeSuiAddress } from '@mysten/sui/utils';
|
|
3
|
+
import { normalizeSuiAddress, toHex } from '@mysten/sui/utils';
|
|
4
|
+
import { randomBytes } from '@noble/ciphers/utils.js';
|
|
5
|
+
import { BN254_FIELD_MODULUS } from '../constants';
|
|
4
6
|
|
|
5
7
|
interface UtxoConstructorArgs {
|
|
6
8
|
amount: bigint;
|
|
@@ -53,7 +55,10 @@ export class Utxo {
|
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
static blinding() {
|
|
56
|
-
|
|
58
|
+
// Use cryptographically secure randomness (works on web + Node.js)
|
|
59
|
+
// 31 bytes = 248 bits, safely below BN254 field modulus (~254 bits)
|
|
60
|
+
const bytes = randomBytes(31);
|
|
61
|
+
return BigInt('0x' + toHex(bytes)) % BN254_FIELD_MODULUS;
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
commitment() {
|
package/src/utils/decrypt.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import { PaginatedEvents } from '@mysten/sui/client';
|
|
1
|
+
import type { PaginatedEvents } from '@mysten/sui/client';
|
|
2
|
+
import type { Commitment } from '../vortex-api.types';
|
|
3
|
+
import type { UtxoPayload, VortexKeypair } from '../entities/keypair';
|
|
4
|
+
import type { Vortex } from '../vortex';
|
|
5
|
+
import type { VortexPool } from '../vortex.types';
|
|
6
|
+
|
|
2
7
|
import { parseNewCommitmentEvent } from './events';
|
|
3
|
-
import { Commitment } from '../vortex-api.types';
|
|
4
|
-
import { UtxoPayload } from '../entities/keypair';
|
|
5
|
-
import { VortexKeypair } from '../entities/keypair';
|
|
6
8
|
import { Utxo } from '../entities/utxo';
|
|
7
9
|
import { normalizeStructTag, toHex } from '@mysten/sui/utils';
|
|
8
|
-
import { Vortex } from '../vortex';
|
|
9
|
-
import { VortexPool } from '../vortex.types';
|
|
10
10
|
import invariant from 'tiny-invariant';
|
|
11
11
|
|
|
12
12
|
interface GetUnspentUtxosArgs {
|
|
@@ -26,18 +26,18 @@ export const getUnspentUtxos = async ({
|
|
|
26
26
|
|
|
27
27
|
const allUtxos = [] as UtxoPayload[];
|
|
28
28
|
|
|
29
|
+
const vortexObjectId =
|
|
30
|
+
typeof vortexPool === 'string' ? vortexPool : vortexPool.objectId;
|
|
31
|
+
|
|
29
32
|
commitments.forEach((commitment) => {
|
|
30
33
|
try {
|
|
31
34
|
const utxo = vortexKeypair.decryptUtxo(commitment.encryptedOutput);
|
|
32
35
|
allUtxos.push(utxo);
|
|
33
36
|
} catch {
|
|
34
|
-
//
|
|
37
|
+
// HMAC verification failed - wrong keypair
|
|
35
38
|
}
|
|
36
39
|
});
|
|
37
40
|
|
|
38
|
-
const vortexObjectId =
|
|
39
|
-
typeof vortexPool === 'string' ? vortexPool : vortexPool.objectId;
|
|
40
|
-
|
|
41
41
|
const utxos = allUtxos.map(
|
|
42
42
|
(utxo) =>
|
|
43
43
|
new Utxo({ ...utxo, keypair: vortexKeypair, vortexPool: vortexObjectId })
|
|
@@ -94,7 +94,7 @@ export const getUnspentUtxosWithApi = async ({
|
|
|
94
94
|
const utxo = vortexKeypair.decryptUtxo(encryptedOutputHex);
|
|
95
95
|
allUtxos.push(utxo);
|
|
96
96
|
} catch {
|
|
97
|
-
//
|
|
97
|
+
// HMAC verification failed - wrong keypair
|
|
98
98
|
}
|
|
99
99
|
});
|
|
100
100
|
|
|
@@ -128,10 +128,7 @@ export const getUnspentUtxosWithApiAndCommitments = async ({
|
|
|
128
128
|
vortexPool,
|
|
129
129
|
}: GetUnspentUtxosWithApiAndCommitmentsArgs) => {
|
|
130
130
|
const allUtxos = [] as UtxoPayload[];
|
|
131
|
-
const userCommitments = [] as Pick<
|
|
132
|
-
Commitment,
|
|
133
|
-
'coinType' | 'encryptedOutput'
|
|
134
|
-
>[];
|
|
131
|
+
const userCommitments = [] as Pick<Commitment, 'coinType' | 'encryptedOutput'>[];
|
|
135
132
|
|
|
136
133
|
const vortexObject = await vortexSdk.resolveVortexPool(vortexPool);
|
|
137
134
|
|
|
@@ -152,7 +149,7 @@ export const getUnspentUtxosWithApiAndCommitments = async ({
|
|
|
152
149
|
});
|
|
153
150
|
allUtxos.push(utxo);
|
|
154
151
|
} catch {
|
|
155
|
-
//
|
|
152
|
+
// HMAC verification failed - wrong keypair
|
|
156
153
|
}
|
|
157
154
|
});
|
|
158
155
|
|
package/src/vortex-api.ts
CHANGED
|
@@ -154,7 +154,11 @@ export class VortexAPI {
|
|
|
154
154
|
let hasNext = true;
|
|
155
155
|
|
|
156
156
|
while (hasNext) {
|
|
157
|
-
const response = await this.getCommitments({
|
|
157
|
+
const response = await this.getCommitments({
|
|
158
|
+
...args,
|
|
159
|
+
page,
|
|
160
|
+
apiKey: args.apiKey,
|
|
161
|
+
});
|
|
158
162
|
allCommitments.push(...response.data.items);
|
|
159
163
|
hasNext = response.data.pagination.hasNext;
|
|
160
164
|
page++;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"random.d.ts","sourceRoot":"","sources":["../../../src/crypto/ff/random.ts"],"names":[],"mappings":"AAUA,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,GAAG,UAAU,CAqBzD"}
|
package/src/crypto/ff/random.ts
DELETED
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Type definition for the browser crypto object.
|
|
3
|
-
*/
|
|
4
|
-
type BrowserCrypto = {
|
|
5
|
-
crypto?: { getRandomValues?: (arg0: Uint8Array) => void };
|
|
6
|
-
};
|
|
7
|
-
|
|
8
|
-
// Type declaration for Node.js require (used in Node.js environment fallback)
|
|
9
|
-
declare const require: (module: string) => any;
|
|
10
|
-
|
|
11
|
-
export function getRandomBytes(length: number): Uint8Array {
|
|
12
|
-
if (length <= 0) {
|
|
13
|
-
throw new Error('Length must be greater than 0');
|
|
14
|
-
}
|
|
15
|
-
const global = globalThis as BrowserCrypto;
|
|
16
|
-
if (global.crypto?.getRandomValues) {
|
|
17
|
-
const randomValues = new Uint8Array(length);
|
|
18
|
-
global.crypto.getRandomValues(randomValues);
|
|
19
|
-
return randomValues;
|
|
20
|
-
}
|
|
21
|
-
// eslint-disable-next-line no-unused-labels
|
|
22
|
-
NODE: {
|
|
23
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
24
|
-
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
25
|
-
const crypto = require('crypto');
|
|
26
|
-
return crypto.randomBytes(length);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
throw new Error(
|
|
30
|
-
'Random byte generation is not supported in this environment'
|
|
31
|
-
);
|
|
32
|
-
}
|