@sip-protocol/sdk 0.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/LICENSE +21 -0
- package/dist/index.d.mts +3640 -0
- package/dist/index.d.ts +3640 -0
- package/dist/index.js +5725 -0
- package/dist/index.mjs +5606 -0
- package/package.json +61 -0
- package/src/adapters/index.ts +19 -0
- package/src/adapters/near-intents.ts +475 -0
- package/src/adapters/oneclick-client.ts +367 -0
- package/src/commitment.ts +470 -0
- package/src/crypto.ts +93 -0
- package/src/errors.ts +471 -0
- package/src/index.ts +369 -0
- package/src/intent.ts +488 -0
- package/src/privacy.ts +382 -0
- package/src/proofs/index.ts +52 -0
- package/src/proofs/interface.ts +228 -0
- package/src/proofs/mock.ts +258 -0
- package/src/proofs/noir.ts +233 -0
- package/src/sip.ts +299 -0
- package/src/solver/index.ts +25 -0
- package/src/solver/mock-solver.ts +278 -0
- package/src/stealth.ts +414 -0
- package/src/validation.ts +401 -0
- package/src/wallet/base-adapter.ts +407 -0
- package/src/wallet/errors.ts +106 -0
- package/src/wallet/ethereum/adapter.ts +655 -0
- package/src/wallet/ethereum/index.ts +48 -0
- package/src/wallet/ethereum/mock.ts +505 -0
- package/src/wallet/ethereum/types.ts +364 -0
- package/src/wallet/index.ts +116 -0
- package/src/wallet/registry.ts +207 -0
- package/src/wallet/solana/adapter.ts +533 -0
- package/src/wallet/solana/index.ts +40 -0
- package/src/wallet/solana/mock.ts +522 -0
- package/src/wallet/solana/types.ts +253 -0
- package/src/zcash/index.ts +53 -0
- package/src/zcash/rpc-client.ts +623 -0
- package/src/zcash/shielded-service.ts +641 -0
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pedersen Commitment Implementation
|
|
3
|
+
*
|
|
4
|
+
* Cryptographically secure Pedersen commitments on secp256k1.
|
|
5
|
+
*
|
|
6
|
+
* ## Security Properties
|
|
7
|
+
*
|
|
8
|
+
* - **Hiding (Computational)**: Cannot determine value from commitment
|
|
9
|
+
* - **Binding (Computational)**: Cannot open commitment to different value
|
|
10
|
+
* - **Homomorphic**: C(v1) + C(v2) = C(v1 + v2) when blindings sum
|
|
11
|
+
*
|
|
12
|
+
* ## Generator H Construction
|
|
13
|
+
*
|
|
14
|
+
* H is constructed using "nothing-up-my-sleeve" (NUMS) method:
|
|
15
|
+
* - Take a well-known string "SIP-PEDERSEN-GENERATOR-H"
|
|
16
|
+
* - Hash it to get x-coordinate candidate
|
|
17
|
+
* - Iterate until we find a valid curve point
|
|
18
|
+
* - This ensures nobody knows the discrete log of H w.r.t. G
|
|
19
|
+
*
|
|
20
|
+
* @see docs/specs/SIP-SPEC.md Section 3.3 - Pedersen Commitment
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { secp256k1 } from '@noble/curves/secp256k1'
|
|
24
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
25
|
+
import { bytesToHex, hexToBytes, randomBytes } from '@noble/hashes/utils'
|
|
26
|
+
import type { HexString } from '@sip-protocol/types'
|
|
27
|
+
import { ValidationError, CryptoError, ErrorCode } from './errors'
|
|
28
|
+
import { isValidHex } from './validation'
|
|
29
|
+
|
|
30
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* A Pedersen commitment with associated blinding factor
|
|
34
|
+
*/
|
|
35
|
+
export interface PedersenCommitment {
|
|
36
|
+
/**
|
|
37
|
+
* The commitment point C = v*G + r*H (compressed, 33 bytes)
|
|
38
|
+
*/
|
|
39
|
+
commitment: HexString
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* The blinding factor r (32 bytes, secret)
|
|
43
|
+
* Required to open/verify the commitment
|
|
44
|
+
*/
|
|
45
|
+
blinding: HexString
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* A commitment point without the blinding factor (for public sharing)
|
|
50
|
+
*/
|
|
51
|
+
export interface CommitmentPoint {
|
|
52
|
+
/**
|
|
53
|
+
* The commitment point (compressed, 33 bytes)
|
|
54
|
+
*/
|
|
55
|
+
commitment: HexString
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Domain separation tag for H generation
|
|
62
|
+
*/
|
|
63
|
+
const H_DOMAIN = 'SIP-PEDERSEN-GENERATOR-H-v1'
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The generator G (secp256k1 base point)
|
|
67
|
+
*/
|
|
68
|
+
const G = secp256k1.ProjectivePoint.BASE
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The independent generator H (NUMS point)
|
|
72
|
+
*
|
|
73
|
+
* Constructed via hash-to-curve with nothing-up-my-sleeve string.
|
|
74
|
+
* Nobody knows log_G(H), making the commitment binding.
|
|
75
|
+
*/
|
|
76
|
+
const H = generateH()
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* The curve order (number of points in the group)
|
|
80
|
+
*/
|
|
81
|
+
const CURVE_ORDER = secp256k1.CURVE.n
|
|
82
|
+
|
|
83
|
+
// ─── Generator H Construction ────────────────────────────────────────────────
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Generate the independent generator H using NUMS method
|
|
87
|
+
*
|
|
88
|
+
* This uses a try-and-increment approach:
|
|
89
|
+
* 1. Hash the domain separator to get a candidate x-coordinate
|
|
90
|
+
* 2. Try to lift x to a curve point
|
|
91
|
+
* 3. If it fails, increment counter and retry
|
|
92
|
+
*
|
|
93
|
+
* This is similar to how Zcash generates their Pedersen generators.
|
|
94
|
+
*/
|
|
95
|
+
function generateH(): typeof G {
|
|
96
|
+
let counter = 0
|
|
97
|
+
|
|
98
|
+
while (counter < 256) {
|
|
99
|
+
// Create candidate x-coordinate
|
|
100
|
+
const input = new TextEncoder().encode(`${H_DOMAIN}:${counter}`)
|
|
101
|
+
const hashBytes = sha256(input)
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
// Try to create a point from this x-coordinate (with even y)
|
|
105
|
+
// The '02' prefix indicates compressed point with even y
|
|
106
|
+
const pointBytes = new Uint8Array(33)
|
|
107
|
+
pointBytes[0] = 0x02
|
|
108
|
+
pointBytes.set(hashBytes, 1)
|
|
109
|
+
|
|
110
|
+
// This will throw if not a valid point
|
|
111
|
+
const point = secp256k1.ProjectivePoint.fromHex(pointBytes)
|
|
112
|
+
|
|
113
|
+
// Ensure point is not identity and not G
|
|
114
|
+
if (!point.equals(secp256k1.ProjectivePoint.ZERO) && !point.equals(G)) {
|
|
115
|
+
return point
|
|
116
|
+
}
|
|
117
|
+
} catch {
|
|
118
|
+
// Not a valid point, try next counter
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
counter++
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// This should never happen with a properly chosen domain separator
|
|
125
|
+
throw new CryptoError(
|
|
126
|
+
'Failed to generate H point - this should never happen',
|
|
127
|
+
ErrorCode.CRYPTO_FAILED,
|
|
128
|
+
{ context: { domain: H_DOMAIN } }
|
|
129
|
+
)
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ─── Core Functions ──────────────────────────────────────────────────────────
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Create a Pedersen commitment to a value
|
|
136
|
+
*
|
|
137
|
+
* C = v*G + r*H
|
|
138
|
+
*
|
|
139
|
+
* Where:
|
|
140
|
+
* - v = value (the amount being committed)
|
|
141
|
+
* - r = blinding factor (random, keeps value hidden)
|
|
142
|
+
* - G = base generator
|
|
143
|
+
* - H = independent generator (NUMS)
|
|
144
|
+
*
|
|
145
|
+
* @param value - The value to commit to (must be < curve order)
|
|
146
|
+
* @param blinding - Optional blinding factor (random 32 bytes if not provided)
|
|
147
|
+
* @returns The commitment and blinding factor
|
|
148
|
+
*
|
|
149
|
+
* @example
|
|
150
|
+
* ```typescript
|
|
151
|
+
* // Create a commitment to 100 tokens
|
|
152
|
+
* const { commitment, blinding } = commit(100n)
|
|
153
|
+
*
|
|
154
|
+
* // Later, prove the commitment contains 100
|
|
155
|
+
* const valid = verifyOpening(commitment, 100n, blinding)
|
|
156
|
+
* ```
|
|
157
|
+
*/
|
|
158
|
+
export function commit(
|
|
159
|
+
value: bigint,
|
|
160
|
+
blinding?: Uint8Array,
|
|
161
|
+
): PedersenCommitment {
|
|
162
|
+
// Validate value type
|
|
163
|
+
if (typeof value !== 'bigint') {
|
|
164
|
+
throw new ValidationError('must be a bigint', 'value', { received: typeof value })
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Validate value is in valid range
|
|
168
|
+
if (value < 0n) {
|
|
169
|
+
throw new ValidationError('must be non-negative', 'value')
|
|
170
|
+
}
|
|
171
|
+
if (value >= CURVE_ORDER) {
|
|
172
|
+
throw new ValidationError(
|
|
173
|
+
'must be less than curve order',
|
|
174
|
+
'value',
|
|
175
|
+
{ curveOrder: CURVE_ORDER.toString(16) }
|
|
176
|
+
)
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Generate or use provided blinding factor
|
|
180
|
+
const r = blinding ?? randomBytes(32)
|
|
181
|
+
if (r.length !== 32) {
|
|
182
|
+
throw new ValidationError('must be 32 bytes', 'blinding', { received: r.length })
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Ensure blinding is in valid range (mod n), and non-zero for valid scalar
|
|
186
|
+
let rScalar = bytesToBigInt(r) % CURVE_ORDER
|
|
187
|
+
if (rScalar === 0n) {
|
|
188
|
+
rScalar = 1n // Avoid zero scalar which is invalid
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// C = v*G + r*H
|
|
192
|
+
// Handle edge cases where value or blinding could be zero
|
|
193
|
+
let C: typeof G
|
|
194
|
+
|
|
195
|
+
if (value === 0n && rScalar === 0n) {
|
|
196
|
+
// Both zero - use identity point (edge case, shouldn't happen with above fix)
|
|
197
|
+
C = secp256k1.ProjectivePoint.ZERO
|
|
198
|
+
} else if (value === 0n) {
|
|
199
|
+
// Only blinding contributes: C = r*H
|
|
200
|
+
C = H.multiply(rScalar)
|
|
201
|
+
} else if (rScalar === 0n) {
|
|
202
|
+
// Only value contributes: C = v*G (shouldn't happen with above fix)
|
|
203
|
+
C = G.multiply(value)
|
|
204
|
+
} else {
|
|
205
|
+
// Normal case: C = v*G + r*H
|
|
206
|
+
const vG = G.multiply(value)
|
|
207
|
+
const rH = H.multiply(rScalar)
|
|
208
|
+
C = vG.add(rH)
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return {
|
|
212
|
+
commitment: `0x${bytesToHex(C.toRawBytes(true))}` as HexString,
|
|
213
|
+
blinding: `0x${bytesToHex(r)}` as HexString,
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Verify that a commitment opens to a specific value
|
|
219
|
+
*
|
|
220
|
+
* Recomputes C' = v*G + r*H and checks if C' == C
|
|
221
|
+
*
|
|
222
|
+
* @param commitment - The commitment point to verify
|
|
223
|
+
* @param value - The claimed value
|
|
224
|
+
* @param blinding - The blinding factor used
|
|
225
|
+
* @returns true if the commitment opens correctly
|
|
226
|
+
*/
|
|
227
|
+
export function verifyOpening(
|
|
228
|
+
commitment: HexString,
|
|
229
|
+
value: bigint,
|
|
230
|
+
blinding: HexString,
|
|
231
|
+
): boolean {
|
|
232
|
+
try {
|
|
233
|
+
// Handle special case of zero commitment (point at infinity)
|
|
234
|
+
if (commitment === '0x00') {
|
|
235
|
+
// Zero commitment only opens to (0, 0) - but that's not valid with our blinding adjustment
|
|
236
|
+
// Actually, zero point means C = C, so it should verify for 0, 0 blinding
|
|
237
|
+
return value === 0n && blinding === ('0x' + '0'.repeat(64))
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Parse the commitment point
|
|
241
|
+
const C = secp256k1.ProjectivePoint.fromHex(commitment.slice(2))
|
|
242
|
+
|
|
243
|
+
// Recompute expected commitment
|
|
244
|
+
const blindingBytes = hexToBytes(blinding.slice(2))
|
|
245
|
+
let rScalar = bytesToBigInt(blindingBytes) % CURVE_ORDER
|
|
246
|
+
if (rScalar === 0n) {
|
|
247
|
+
rScalar = 1n // Match the commit() behavior
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Handle edge cases
|
|
251
|
+
let expected: typeof G
|
|
252
|
+
if (value === 0n) {
|
|
253
|
+
expected = H.multiply(rScalar)
|
|
254
|
+
} else if (rScalar === 0n) {
|
|
255
|
+
expected = G.multiply(value)
|
|
256
|
+
} else {
|
|
257
|
+
const vG = G.multiply(value)
|
|
258
|
+
const rH = H.multiply(rScalar)
|
|
259
|
+
expected = vG.add(rH)
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Check equality
|
|
263
|
+
return C.equals(expected)
|
|
264
|
+
} catch {
|
|
265
|
+
return false
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Create a commitment to zero with a specific blinding factor
|
|
271
|
+
*
|
|
272
|
+
* C = 0*G + r*H = r*H
|
|
273
|
+
*
|
|
274
|
+
* Useful for creating balance proofs.
|
|
275
|
+
*
|
|
276
|
+
* @param blinding - The blinding factor
|
|
277
|
+
* @returns Commitment to zero
|
|
278
|
+
*/
|
|
279
|
+
export function commitZero(blinding: Uint8Array): PedersenCommitment {
|
|
280
|
+
return commit(0n, blinding)
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// ─── Homomorphic Operations ──────────────────────────────────────────────────
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Add two commitments homomorphically
|
|
287
|
+
*
|
|
288
|
+
* C1 + C2 = (v1*G + r1*H) + (v2*G + r2*H) = (v1+v2)*G + (r1+r2)*H
|
|
289
|
+
*
|
|
290
|
+
* Note: The blinding factors also add. If you need to verify the sum,
|
|
291
|
+
* you must also sum the blinding factors.
|
|
292
|
+
*
|
|
293
|
+
* @param c1 - First commitment point
|
|
294
|
+
* @param c2 - Second commitment point
|
|
295
|
+
* @returns Sum of commitments
|
|
296
|
+
* @throws {ValidationError} If commitments are invalid hex strings
|
|
297
|
+
*/
|
|
298
|
+
export function addCommitments(
|
|
299
|
+
c1: HexString,
|
|
300
|
+
c2: HexString,
|
|
301
|
+
): CommitmentPoint {
|
|
302
|
+
// Validate inputs
|
|
303
|
+
if (!isValidHex(c1)) {
|
|
304
|
+
throw new ValidationError('must be a valid hex string', 'c1')
|
|
305
|
+
}
|
|
306
|
+
if (!isValidHex(c2)) {
|
|
307
|
+
throw new ValidationError('must be a valid hex string', 'c2')
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
let point1: typeof G
|
|
311
|
+
let point2: typeof G
|
|
312
|
+
try {
|
|
313
|
+
point1 = secp256k1.ProjectivePoint.fromHex(c1.slice(2))
|
|
314
|
+
} catch {
|
|
315
|
+
throw new ValidationError('must be a valid curve point', 'c1')
|
|
316
|
+
}
|
|
317
|
+
try {
|
|
318
|
+
point2 = secp256k1.ProjectivePoint.fromHex(c2.slice(2))
|
|
319
|
+
} catch {
|
|
320
|
+
throw new ValidationError('must be a valid curve point', 'c2')
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const sum = point1.add(point2)
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
commitment: `0x${bytesToHex(sum.toRawBytes(true))}` as HexString,
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Subtract two commitments homomorphically
|
|
332
|
+
*
|
|
333
|
+
* C1 - C2 = (v1-v2)*G + (r1-r2)*H
|
|
334
|
+
*
|
|
335
|
+
* @param c1 - First commitment point
|
|
336
|
+
* @param c2 - Second commitment point (to subtract)
|
|
337
|
+
* @returns Difference of commitments
|
|
338
|
+
* @throws {ValidationError} If commitments are invalid
|
|
339
|
+
*/
|
|
340
|
+
export function subtractCommitments(
|
|
341
|
+
c1: HexString,
|
|
342
|
+
c2: HexString,
|
|
343
|
+
): CommitmentPoint {
|
|
344
|
+
// Validate inputs
|
|
345
|
+
if (!isValidHex(c1)) {
|
|
346
|
+
throw new ValidationError('must be a valid hex string', 'c1')
|
|
347
|
+
}
|
|
348
|
+
if (!isValidHex(c2)) {
|
|
349
|
+
throw new ValidationError('must be a valid hex string', 'c2')
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
let point1: typeof G
|
|
353
|
+
let point2: typeof G
|
|
354
|
+
try {
|
|
355
|
+
point1 = secp256k1.ProjectivePoint.fromHex(c1.slice(2))
|
|
356
|
+
} catch {
|
|
357
|
+
throw new ValidationError('must be a valid curve point', 'c1')
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
point2 = secp256k1.ProjectivePoint.fromHex(c2.slice(2))
|
|
361
|
+
} catch {
|
|
362
|
+
throw new ValidationError('must be a valid curve point', 'c2')
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const diff = point1.subtract(point2)
|
|
366
|
+
|
|
367
|
+
// Handle ZERO point (identity element) - can't serialize directly
|
|
368
|
+
if (diff.equals(secp256k1.ProjectivePoint.ZERO)) {
|
|
369
|
+
// Return a special marker for zero commitment
|
|
370
|
+
// This is the point at infinity, represented as all zeros
|
|
371
|
+
return {
|
|
372
|
+
commitment: '0x00' as HexString,
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return {
|
|
377
|
+
commitment: `0x${bytesToHex(diff.toRawBytes(true))}` as HexString,
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Add blinding factors (for use with homomorphic addition)
|
|
383
|
+
*
|
|
384
|
+
* When you add commitments, the result commits to (v1+v2) with
|
|
385
|
+
* blinding (r1+r2). Use this to compute the combined blinding.
|
|
386
|
+
*
|
|
387
|
+
* @param b1 - First blinding factor
|
|
388
|
+
* @param b2 - Second blinding factor
|
|
389
|
+
* @returns Sum of blindings (mod curve order)
|
|
390
|
+
*/
|
|
391
|
+
export function addBlindings(b1: HexString, b2: HexString): HexString {
|
|
392
|
+
const r1 = bytesToBigInt(hexToBytes(b1.slice(2)))
|
|
393
|
+
const r2 = bytesToBigInt(hexToBytes(b2.slice(2)))
|
|
394
|
+
|
|
395
|
+
const sum = (r1 + r2) % CURVE_ORDER
|
|
396
|
+
const sumBytes = bigIntToBytes(sum, 32)
|
|
397
|
+
|
|
398
|
+
return `0x${bytesToHex(sumBytes)}` as HexString
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Subtract blinding factors (for use with homomorphic subtraction)
|
|
403
|
+
*
|
|
404
|
+
* @param b1 - First blinding factor
|
|
405
|
+
* @param b2 - Second blinding factor (to subtract)
|
|
406
|
+
* @returns Difference of blindings (mod curve order)
|
|
407
|
+
*/
|
|
408
|
+
export function subtractBlindings(b1: HexString, b2: HexString): HexString {
|
|
409
|
+
const r1 = bytesToBigInt(hexToBytes(b1.slice(2)))
|
|
410
|
+
const r2 = bytesToBigInt(hexToBytes(b2.slice(2)))
|
|
411
|
+
|
|
412
|
+
// Handle underflow with modular arithmetic
|
|
413
|
+
const diff = (r1 - r2 + CURVE_ORDER) % CURVE_ORDER
|
|
414
|
+
const diffBytes = bigIntToBytes(diff, 32)
|
|
415
|
+
|
|
416
|
+
return `0x${bytesToHex(diffBytes)}` as HexString
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// ─── Range Proof Support ─────────────────────────────────────────────────────
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Get the generators for ZK proof integration
|
|
423
|
+
*
|
|
424
|
+
* Returns the G and H points for use in Noir circuits.
|
|
425
|
+
*/
|
|
426
|
+
export function getGenerators(): {
|
|
427
|
+
G: { x: HexString; y: HexString }
|
|
428
|
+
H: { x: HexString; y: HexString }
|
|
429
|
+
} {
|
|
430
|
+
const gAffine = G.toAffine()
|
|
431
|
+
const hAffine = H.toAffine()
|
|
432
|
+
|
|
433
|
+
return {
|
|
434
|
+
G: {
|
|
435
|
+
x: `0x${gAffine.x.toString(16).padStart(64, '0')}` as HexString,
|
|
436
|
+
y: `0x${gAffine.y.toString(16).padStart(64, '0')}` as HexString,
|
|
437
|
+
},
|
|
438
|
+
H: {
|
|
439
|
+
x: `0x${hAffine.x.toString(16).padStart(64, '0')}` as HexString,
|
|
440
|
+
y: `0x${hAffine.y.toString(16).padStart(64, '0')}` as HexString,
|
|
441
|
+
},
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* Generate a random blinding factor
|
|
447
|
+
*/
|
|
448
|
+
export function generateBlinding(): HexString {
|
|
449
|
+
return `0x${bytesToHex(randomBytes(32))}` as HexString
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// ─── Utility Functions ───────────────────────────────────────────────────────
|
|
453
|
+
|
|
454
|
+
function bytesToBigInt(bytes: Uint8Array): bigint {
|
|
455
|
+
let result = 0n
|
|
456
|
+
for (const byte of bytes) {
|
|
457
|
+
result = (result << 8n) + BigInt(byte)
|
|
458
|
+
}
|
|
459
|
+
return result
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function bigIntToBytes(value: bigint, length: number): Uint8Array {
|
|
463
|
+
const bytes = new Uint8Array(length)
|
|
464
|
+
let v = value
|
|
465
|
+
for (let i = length - 1; i >= 0; i--) {
|
|
466
|
+
bytes[i] = Number(v & 0xffn)
|
|
467
|
+
v >>= 8n
|
|
468
|
+
}
|
|
469
|
+
return bytes
|
|
470
|
+
}
|
package/src/crypto.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cryptographic utilities for SIP Protocol
|
|
3
|
+
*
|
|
4
|
+
* For ZK proofs, use ProofProvider:
|
|
5
|
+
* @see ./proofs/interface.ts for the proof provider interface
|
|
6
|
+
* @see ./proofs/mock.ts for testing
|
|
7
|
+
* @see ./proofs/noir.ts for production (Noir circuits)
|
|
8
|
+
*
|
|
9
|
+
* For Pedersen commitments, use the dedicated commitment module:
|
|
10
|
+
* @see ./commitment.ts for secure Pedersen commitment implementation
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
14
|
+
import { bytesToHex, randomBytes } from '@noble/hashes/utils'
|
|
15
|
+
import type { Commitment, HexString, Hash } from '@sip-protocol/types'
|
|
16
|
+
import { commit, verifyOpening } from './commitment'
|
|
17
|
+
import { ValidationError, ErrorCode } from './errors'
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Create a Pedersen commitment to a value
|
|
21
|
+
*
|
|
22
|
+
* @deprecated Use `commit()` from './commitment' for new code.
|
|
23
|
+
* This wrapper maintains backward compatibility.
|
|
24
|
+
*
|
|
25
|
+
* @param value - The value to commit to
|
|
26
|
+
* @param blindingFactor - Optional blinding factor (random if not provided)
|
|
27
|
+
* @returns Commitment object (legacy format)
|
|
28
|
+
*/
|
|
29
|
+
export function createCommitment(
|
|
30
|
+
value: bigint,
|
|
31
|
+
blindingFactor?: Uint8Array,
|
|
32
|
+
): Commitment {
|
|
33
|
+
console.warn(
|
|
34
|
+
'createCommitment() is deprecated and will be removed in v0.2.0. ' +
|
|
35
|
+
'Use commit() from "./commitment" instead.'
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const { commitment, blinding } = commit(value, blindingFactor)
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
value: commitment,
|
|
42
|
+
blindingFactor: blinding,
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Verify a Pedersen commitment (requires knowing the value and blinding factor)
|
|
48
|
+
*
|
|
49
|
+
* @deprecated Use `verifyOpening()` from './commitment' for new code.
|
|
50
|
+
*/
|
|
51
|
+
export function verifyCommitment(
|
|
52
|
+
commitment: Commitment,
|
|
53
|
+
expectedValue: bigint,
|
|
54
|
+
): boolean {
|
|
55
|
+
console.warn(
|
|
56
|
+
'verifyCommitment() is deprecated and will be removed in v0.2.0. ' +
|
|
57
|
+
'Use verifyOpening() from "./commitment" instead.'
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if (!commitment.blindingFactor) {
|
|
61
|
+
throw new ValidationError(
|
|
62
|
+
'cannot verify commitment without blinding factor',
|
|
63
|
+
'commitment.blindingFactor',
|
|
64
|
+
undefined,
|
|
65
|
+
ErrorCode.MISSING_REQUIRED
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return verifyOpening(commitment.value, expectedValue, commitment.blindingFactor)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generate a random intent ID
|
|
74
|
+
*/
|
|
75
|
+
export function generateIntentId(): string {
|
|
76
|
+
const bytes = randomBytes(16)
|
|
77
|
+
return `sip-${bytesToHex(bytes)}`
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Hash data using SHA256
|
|
82
|
+
*/
|
|
83
|
+
export function hash(data: string | Uint8Array): Hash {
|
|
84
|
+
const input = typeof data === 'string' ? new TextEncoder().encode(data) : data
|
|
85
|
+
return `0x${bytesToHex(sha256(input))}` as Hash
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Generate random bytes
|
|
90
|
+
*/
|
|
91
|
+
export function generateRandomBytes(length: number): HexString {
|
|
92
|
+
return `0x${bytesToHex(randomBytes(length))}` as HexString
|
|
93
|
+
}
|