@interest-protocol/vortex-sdk 0.0.1-alpha.0 → 1.1.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/.eslingignore +1 -0
- package/dist/__tests__/entities/keypair.spec.d.ts +2 -0
- package/dist/__tests__/entities/keypair.spec.d.ts.map +1 -0
- package/dist/__tests__/test-utils.d.ts +25 -0
- package/dist/__tests__/test-utils.d.ts.map +1 -0
- package/dist/__tests__/types.d.ts +3 -0
- package/dist/__tests__/types.d.ts.map +1 -0
- package/dist/__tests__/vortex.test.d.ts +2 -0
- package/dist/__tests__/vortex.test.d.ts.map +1 -0
- package/dist/constants.d.ts +44 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/crypto/ff/f1field.d.ts +76 -0
- package/dist/crypto/ff/f1field.d.ts.map +1 -0
- package/dist/crypto/ff/index.d.ts +6 -0
- package/dist/crypto/ff/index.d.ts.map +1 -0
- package/dist/crypto/ff/random.d.ts +2 -0
- package/dist/crypto/ff/random.d.ts.map +1 -0
- package/dist/crypto/ff/scalar.d.ts +45 -0
- package/dist/crypto/ff/scalar.d.ts.map +1 -0
- package/dist/crypto/ff/utils.d.ts +6 -0
- package/dist/crypto/ff/utils.d.ts.map +1 -0
- package/dist/crypto/index.d.ts +6 -0
- package/dist/crypto/index.d.ts.map +1 -0
- package/dist/crypto/poseidon/index.d.ts +2 -0
- package/dist/crypto/poseidon/index.d.ts.map +1 -0
- package/dist/crypto/poseidon/poseidon-constants-opt.d.ts +7 -0
- package/dist/crypto/poseidon/poseidon-constants-opt.d.ts.map +1 -0
- package/dist/crypto/poseidon/poseidon-opt.d.ts +16 -0
- package/dist/crypto/poseidon/poseidon-opt.d.ts.map +1 -0
- package/dist/deposit.d.ts +4 -0
- package/dist/deposit.d.ts.map +1 -0
- package/dist/entities/index.d.ts +4 -0
- package/dist/entities/index.d.ts.map +1 -0
- package/dist/entities/keypair.d.ts +29 -0
- package/dist/entities/keypair.d.ts.map +1 -0
- package/dist/entities/merkle-tree.d.ts +81 -0
- package/dist/entities/merkle-tree.d.ts.map +1 -0
- package/dist/entities/utxo.d.ts +24 -0
- package/dist/entities/utxo.d.ts.map +1 -0
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +38280 -4459
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +38244 -4453
- package/dist/index.mjs.map +1 -1
- package/dist/jest-setup.d.ts +2 -0
- package/dist/jest-setup.d.ts.map +1 -0
- package/dist/keys/index.d.ts +3 -0
- package/dist/keys/index.d.ts.map +1 -0
- package/dist/pkg/nodejs/vortex.d.ts +11 -0
- package/dist/pkg/nodejs/vortex.d.ts.map +1 -0
- package/dist/pkg/web/vortex.d.ts +44 -0
- package/dist/pkg/web/vortex.d.ts.map +1 -0
- package/dist/utils/decrypt.d.ts +12 -0
- package/dist/utils/decrypt.d.ts.map +1 -0
- package/dist/utils/env.d.ts +2 -0
- package/dist/utils/env.d.ts.map +1 -0
- package/dist/utils/events.d.ts +7 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/utils/ext-data.d.ts +3 -0
- package/dist/utils/ext-data.d.ts.map +1 -0
- package/dist/utils/index.d.ts +50 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/prove.d.ts +3 -0
- package/dist/utils/prove.d.ts.map +1 -0
- package/dist/vortex.d.ts +51 -21
- package/dist/vortex.d.ts.map +1 -1
- package/dist/vortex.types.d.ts +74 -50
- package/dist/vortex.types.d.ts.map +1 -1
- package/dist/vortex_bg.wasm +0 -0
- package/dist/withdraw.d.ts +4 -0
- package/dist/withdraw.d.ts.map +1 -0
- package/jest.config.js +31 -0
- package/package.json +22 -7
- package/src/__tests__/entities/keypair.spec.ts +191 -0
- package/src/__tests__/test-utils.ts +76 -0
- package/src/__tests__/types.ts +3 -0
- package/src/__tests__/vortex.test.ts +25 -0
- package/src/constants.ts +104 -0
- package/src/crypto/ff/f1field.ts +464 -0
- package/src/crypto/ff/index.ts +6 -0
- package/src/crypto/ff/random.ts +32 -0
- package/src/crypto/ff/readme.md +8 -0
- package/src/crypto/ff/scalar.ts +264 -0
- package/src/crypto/ff/utils.ts +121 -0
- package/src/crypto/index.ts +8 -0
- package/src/crypto/poseidon/index.ts +1 -0
- package/src/crypto/poseidon/poseidon-constants-opt.ts +24806 -0
- package/src/crypto/poseidon/poseidon-opt.ts +184 -0
- package/src/deposit.ts +168 -0
- package/src/entities/index.ts +3 -0
- package/src/entities/keypair.ts +262 -0
- package/src/entities/merkle-tree.ts +256 -0
- package/src/entities/utxo.ts +52 -0
- package/src/index.ts +6 -2
- package/src/jest-setup.ts +2 -0
- package/src/keys/index.ts +5 -0
- package/src/pkg/nodejs/vortex.d.ts +36 -0
- package/src/pkg/nodejs/vortex.js +332 -0
- package/src/pkg/nodejs/vortex_bg.wasm +0 -0
- package/src/pkg/nodejs/vortex_bg.wasm.d.ts +12 -0
- package/src/pkg/web/vortex.d.ts +72 -0
- package/src/pkg/web/vortex.js +442 -0
- package/src/pkg/web/vortex_bg.wasm +0 -0
- package/src/pkg/web/vortex_bg.wasm.d.ts +12 -0
- package/src/utils/decrypt.ts +46 -0
- package/src/utils/env.ts +18 -0
- package/src/utils/events.ts +16 -0
- package/src/utils/ext-data.ts +43 -0
- package/src/utils/index.ts +152 -0
- package/src/utils/prove.ts +18 -0
- package/src/vortex.ts +235 -111
- package/src/vortex.types.ts +74 -54
- package/src/withdraw.ts +159 -0
- package/tsconfig.json +4 -2
- package/dist/admin.d.ts +0 -17
- package/dist/admin.d.ts.map +0 -1
- package/dist/utils.d.ts +0 -11
- package/dist/utils.d.ts.map +0 -1
- package/src/admin.ts +0 -124
- package/src/utils.ts +0 -66
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// packages/vortex/src/entities/merkle-tree.ts
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ZERO_VALUE,
|
|
5
|
+
EMPTY_SUBTREE_HASHES,
|
|
6
|
+
MERKLE_TREE_HEIGHT,
|
|
7
|
+
} from '../constants';
|
|
8
|
+
import { poseidon2 } from '../crypto';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Sparse Merkle tree using Tornado Cash Nova's paired insertion strategy.
|
|
12
|
+
* Inserts two leaves at once for better efficiency and privacy.
|
|
13
|
+
*/
|
|
14
|
+
export class MerkleTree {
|
|
15
|
+
levels: number;
|
|
16
|
+
capacity: number;
|
|
17
|
+
zeroElement: bigint;
|
|
18
|
+
|
|
19
|
+
private zeros: bigint[];
|
|
20
|
+
private subtrees: bigint[]; // filledSubtrees in Tornado Nova (indices 0..levels-1)
|
|
21
|
+
private _nextIndex: number;
|
|
22
|
+
private leaves: bigint[];
|
|
23
|
+
private _root: bigint;
|
|
24
|
+
|
|
25
|
+
constructor(levels: number) {
|
|
26
|
+
this.levels = levels;
|
|
27
|
+
this.capacity = 2 ** levels;
|
|
28
|
+
this.zeroElement = ZERO_VALUE;
|
|
29
|
+
this.zeros = [];
|
|
30
|
+
this.subtrees = [];
|
|
31
|
+
this._nextIndex = 0;
|
|
32
|
+
this.leaves = [];
|
|
33
|
+
|
|
34
|
+
// Initialize zero hashes (0 to levels, inclusive)
|
|
35
|
+
// We need levels+1 elements: zeros[0] through zeros[levels]
|
|
36
|
+
for (let i = 0; i <= levels; i++) {
|
|
37
|
+
this.zeros[i] = EMPTY_SUBTREE_HASHES[i];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Initialize subtrees[0..levels-1] (matching Nova's filledSubtrees)
|
|
41
|
+
// subtrees[0] is initialized but never used (Nova quirk)
|
|
42
|
+
for (let i = 0; i < levels; i++) {
|
|
43
|
+
this.subtrees[i] = this.zeros[i];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Empty root is zeros[levels]
|
|
47
|
+
this._root = this.zeros[levels];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Returns the current Merkle root
|
|
52
|
+
*/
|
|
53
|
+
root(): bigint {
|
|
54
|
+
return this._root;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Insert elements in bulk (must be even number for pairing)
|
|
59
|
+
*/
|
|
60
|
+
bulkInsert(elements: bigint[]) {
|
|
61
|
+
if (elements.length % 2 !== 0) {
|
|
62
|
+
throw new Error('Must insert even number of elements (pairs)');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
for (let i = 0; i < elements.length; i += 2) {
|
|
66
|
+
this.insertPair(elements[i], elements[i + 1]);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Insert a pair of leaves (Nova style)
|
|
72
|
+
* This is the primary insertion method
|
|
73
|
+
*/
|
|
74
|
+
insertPair(leaf1: bigint, leaf2: bigint) {
|
|
75
|
+
if (this._nextIndex >= this.capacity) {
|
|
76
|
+
throw new Error('Merkle tree is full. No more leaves can be added');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Store both leaves
|
|
80
|
+
this.leaves.push(leaf1, leaf2);
|
|
81
|
+
|
|
82
|
+
// Start by hashing the pair (level 0)
|
|
83
|
+
let currentIndex = Math.floor(this._nextIndex / 2);
|
|
84
|
+
let currentLevelHash = poseidon2(leaf1, leaf2);
|
|
85
|
+
|
|
86
|
+
// Increment by 2 since we're inserting a pair
|
|
87
|
+
this._nextIndex += 2;
|
|
88
|
+
|
|
89
|
+
// Process levels 1 to levels-1 (matching Nova: for i = 1; i < levels)
|
|
90
|
+
for (let i = 1; i < this.levels; i++) {
|
|
91
|
+
let left: bigint;
|
|
92
|
+
let right: bigint;
|
|
93
|
+
|
|
94
|
+
if (currentIndex % 2 === 0) {
|
|
95
|
+
// Current is left child
|
|
96
|
+
left = currentLevelHash;
|
|
97
|
+
right = this.zeros[i];
|
|
98
|
+
this.subtrees[i] = currentLevelHash; // Cache left subtree
|
|
99
|
+
} else {
|
|
100
|
+
// Current is right child
|
|
101
|
+
left = this.subtrees[i]; // Get cached left subtree
|
|
102
|
+
right = currentLevelHash;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
currentLevelHash = poseidon2(left, right);
|
|
106
|
+
currentIndex = Math.floor(currentIndex / 2);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Update root
|
|
110
|
+
this._root = currentLevelHash;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Insert a single leaf (for backward compatibility)
|
|
115
|
+
* Pairs it with a zero leaf
|
|
116
|
+
*/
|
|
117
|
+
insert(leaf: bigint) {
|
|
118
|
+
this.insertPair(leaf, this.zeros[0]);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Generate Merkle path for a leaf at given index
|
|
123
|
+
* Returns sibling hashes and indices for verification
|
|
124
|
+
*/
|
|
125
|
+
path(index: number): { pathElements: bigint[]; pathIndices: number[] } {
|
|
126
|
+
if (index < 0 || index >= this._nextIndex) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Index out of bounds: ${index} (tree has ${this._nextIndex} leaves)`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const pathElements: bigint[] = [];
|
|
133
|
+
const pathIndices: number[] = [];
|
|
134
|
+
|
|
135
|
+
// Level 0: Get sibling leaf
|
|
136
|
+
const isLeftAtLevel0 = index % 2 === 0;
|
|
137
|
+
pathIndices.push(isLeftAtLevel0 ? 0 : 1);
|
|
138
|
+
|
|
139
|
+
const siblingIndex = isLeftAtLevel0 ? index + 1 : index - 1;
|
|
140
|
+
const sibling =
|
|
141
|
+
siblingIndex < this._nextIndex
|
|
142
|
+
? this.leaves[siblingIndex]
|
|
143
|
+
: this.zeros[0];
|
|
144
|
+
pathElements.push(sibling);
|
|
145
|
+
|
|
146
|
+
// Levels 1 to levels-1 (matching Nova's loop structure)
|
|
147
|
+
let currentIndex = Math.floor(index / 2);
|
|
148
|
+
for (let i = 1; i < this.levels; i++) {
|
|
149
|
+
const isLeft = currentIndex % 2 === 0;
|
|
150
|
+
pathIndices.push(isLeft ? 0 : 1);
|
|
151
|
+
|
|
152
|
+
if (isLeft) {
|
|
153
|
+
// We're left child, sibling is right (zero hash)
|
|
154
|
+
pathElements.push(this.zeros[i]);
|
|
155
|
+
} else {
|
|
156
|
+
// We're right child, sibling is the cached left subtree
|
|
157
|
+
pathElements.push(this.subtrees[i]);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
currentIndex = Math.floor(currentIndex / 2);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
return { pathElements, pathIndices };
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get all leaves in the tree
|
|
168
|
+
*/
|
|
169
|
+
elements(): bigint[] {
|
|
170
|
+
return this.leaves;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Find the index of a specific leaf
|
|
175
|
+
*/
|
|
176
|
+
indexOf(element: bigint, comparator: Function | null = null): number {
|
|
177
|
+
if (comparator) {
|
|
178
|
+
return this.leaves.findIndex((leaf) => comparator(element, leaf));
|
|
179
|
+
}
|
|
180
|
+
return this.leaves.findIndex((leaf) => leaf === element);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Serialize tree state
|
|
185
|
+
*/
|
|
186
|
+
serialize() {
|
|
187
|
+
return {
|
|
188
|
+
levels: this.levels,
|
|
189
|
+
zeros: this.zeros,
|
|
190
|
+
subtrees: this.subtrees,
|
|
191
|
+
nextIndex: this._nextIndex,
|
|
192
|
+
leaves: this.leaves,
|
|
193
|
+
root: this._root,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Verify a Merkle path is valid for a given leaf and root
|
|
199
|
+
*/
|
|
200
|
+
verify(
|
|
201
|
+
leaf: bigint,
|
|
202
|
+
pathElements: bigint[],
|
|
203
|
+
pathIndices: number[],
|
|
204
|
+
root: bigint
|
|
205
|
+
): boolean {
|
|
206
|
+
if (
|
|
207
|
+
pathElements.length !== this.levels ||
|
|
208
|
+
pathIndices.length !== this.levels
|
|
209
|
+
) {
|
|
210
|
+
return false;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
let currentHash = leaf;
|
|
214
|
+
|
|
215
|
+
for (let i = 0; i < this.levels; i++) {
|
|
216
|
+
const sibling = pathElements[i];
|
|
217
|
+
const isLeft = pathIndices[i] === 0;
|
|
218
|
+
|
|
219
|
+
if (isLeft) {
|
|
220
|
+
currentHash = poseidon2(currentHash, sibling);
|
|
221
|
+
} else {
|
|
222
|
+
currentHash = poseidon2(sibling, currentHash);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return currentHash === root;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Get the current number of leaves in the tree
|
|
231
|
+
*/
|
|
232
|
+
get nextIndex(): number {
|
|
233
|
+
return this._nextIndex;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Check if the tree is full
|
|
238
|
+
*/
|
|
239
|
+
isFull(): boolean {
|
|
240
|
+
return this._nextIndex >= this.capacity;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Get the maximum capacity of the tree
|
|
245
|
+
*/
|
|
246
|
+
getCapacity(): number {
|
|
247
|
+
return this.capacity;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get the current fill percentage
|
|
252
|
+
*/
|
|
253
|
+
getFillPercentage(): number {
|
|
254
|
+
return (this._nextIndex / this.capacity) * 100;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { VortexKeypair } from './keypair';
|
|
2
|
+
import { poseidon3 } from '../crypto';
|
|
3
|
+
|
|
4
|
+
interface UtxoConstructorArgs {
|
|
5
|
+
amount: bigint;
|
|
6
|
+
blinding?: bigint;
|
|
7
|
+
keypair?: VortexKeypair;
|
|
8
|
+
index?: bigint;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class Utxo {
|
|
12
|
+
amount: bigint;
|
|
13
|
+
blinding: bigint;
|
|
14
|
+
keypair: VortexKeypair;
|
|
15
|
+
index: bigint;
|
|
16
|
+
|
|
17
|
+
constructor({ amount, blinding, keypair, index }: UtxoConstructorArgs) {
|
|
18
|
+
this.amount = amount;
|
|
19
|
+
this.blinding = blinding ?? Utxo.blinding();
|
|
20
|
+
this.keypair = keypair ?? VortexKeypair.generate();
|
|
21
|
+
this.index = index ?? 0n;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static blinding() {
|
|
25
|
+
return BigInt(Math.floor(Math.random() * 1_000_000_000));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
commitment() {
|
|
29
|
+
return poseidon3(
|
|
30
|
+
this.amount,
|
|
31
|
+
BigInt(this.keypair.publicKey),
|
|
32
|
+
this.blinding
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
nullifier() {
|
|
37
|
+
const commitment = this.commitment();
|
|
38
|
+
return poseidon3(
|
|
39
|
+
commitment,
|
|
40
|
+
this.index,
|
|
41
|
+
this.keypair.sign(commitment, this.index)
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
payload() {
|
|
46
|
+
return {
|
|
47
|
+
amount: this.amount,
|
|
48
|
+
blinding: this.blinding,
|
|
49
|
+
index: this.index,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
export * from './
|
|
1
|
+
export * from './constants';
|
|
2
2
|
export * from './vortex';
|
|
3
|
-
export * from './
|
|
3
|
+
export * from './entities';
|
|
4
4
|
export * from './vortex.types';
|
|
5
|
+
export * from './crypto';
|
|
6
|
+
export * from './utils';
|
|
7
|
+
export * from './deposit';
|
|
8
|
+
export * from './withdraw';
|