@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
package/src/privacy.ts
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Privacy level handling for SIP Protocol
|
|
3
|
+
*
|
|
4
|
+
* Provides authenticated encryption using XChaCha20-Poly1305 for viewing key
|
|
5
|
+
* selective disclosure. This allows transaction details to be encrypted and
|
|
6
|
+
* later revealed to auditors holding the viewing key.
|
|
7
|
+
*
|
|
8
|
+
* ## Security Properties
|
|
9
|
+
* - **Confidentiality**: Only viewing key holders can decrypt
|
|
10
|
+
* - **Integrity**: Authentication tag prevents tampering
|
|
11
|
+
* - **Nonce-misuse resistance**: XChaCha20 uses 24-byte nonces
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type {
|
|
15
|
+
PrivacyLevel,
|
|
16
|
+
ViewingKey,
|
|
17
|
+
EncryptedTransaction,
|
|
18
|
+
HexString,
|
|
19
|
+
Hash,
|
|
20
|
+
} from '@sip-protocol/types'
|
|
21
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
22
|
+
import { sha512 } from '@noble/hashes/sha512'
|
|
23
|
+
import { hmac } from '@noble/hashes/hmac'
|
|
24
|
+
import { hkdf } from '@noble/hashes/hkdf'
|
|
25
|
+
import { bytesToHex, hexToBytes, randomBytes, utf8ToBytes } from '@noble/hashes/utils'
|
|
26
|
+
import { xchacha20poly1305 } from '@noble/ciphers/chacha.js'
|
|
27
|
+
import { ValidationError, CryptoError, ErrorCode } from './errors'
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Maximum size for decrypted transaction data (1MB)
|
|
31
|
+
* Prevents DoS attacks via large payloads
|
|
32
|
+
*/
|
|
33
|
+
const MAX_TRANSACTION_DATA_SIZE = 1024 * 1024
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Privacy configuration for an intent
|
|
37
|
+
*/
|
|
38
|
+
export interface PrivacyConfig {
|
|
39
|
+
/** The privacy level */
|
|
40
|
+
level: PrivacyLevel
|
|
41
|
+
/** Viewing key (required for compliant mode) */
|
|
42
|
+
viewingKey?: ViewingKey
|
|
43
|
+
/** Whether to use stealth addresses */
|
|
44
|
+
useStealth: boolean
|
|
45
|
+
/** Whether to encrypt transaction data */
|
|
46
|
+
encryptData: boolean
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get privacy configuration for a privacy level
|
|
51
|
+
*/
|
|
52
|
+
export function getPrivacyConfig(
|
|
53
|
+
level: PrivacyLevel,
|
|
54
|
+
viewingKey?: ViewingKey,
|
|
55
|
+
): PrivacyConfig {
|
|
56
|
+
switch (level) {
|
|
57
|
+
case 'transparent':
|
|
58
|
+
return {
|
|
59
|
+
level,
|
|
60
|
+
useStealth: false,
|
|
61
|
+
encryptData: false,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
case 'shielded':
|
|
65
|
+
return {
|
|
66
|
+
level,
|
|
67
|
+
useStealth: true,
|
|
68
|
+
encryptData: true,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
case 'compliant':
|
|
72
|
+
if (!viewingKey) {
|
|
73
|
+
throw new ValidationError(
|
|
74
|
+
'viewingKey is required for compliant mode',
|
|
75
|
+
'viewingKey',
|
|
76
|
+
undefined,
|
|
77
|
+
ErrorCode.MISSING_REQUIRED
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
level,
|
|
82
|
+
viewingKey,
|
|
83
|
+
useStealth: true,
|
|
84
|
+
encryptData: true,
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
default:
|
|
88
|
+
throw new ValidationError(
|
|
89
|
+
`unknown privacy level: ${level}`,
|
|
90
|
+
'level',
|
|
91
|
+
{ received: level },
|
|
92
|
+
ErrorCode.INVALID_PRIVACY_LEVEL
|
|
93
|
+
)
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Generate a new viewing key
|
|
99
|
+
*/
|
|
100
|
+
export function generateViewingKey(path: string = 'm/0'): ViewingKey {
|
|
101
|
+
const keyBytes = randomBytes(32)
|
|
102
|
+
const key = `0x${bytesToHex(keyBytes)}` as HexString
|
|
103
|
+
const hashBytes = sha256(keyBytes)
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
key,
|
|
107
|
+
path,
|
|
108
|
+
hash: `0x${bytesToHex(hashBytes)}` as Hash,
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Derive a child viewing key using BIP32-style hierarchical derivation
|
|
114
|
+
*
|
|
115
|
+
* Uses HMAC-SHA512 for proper key derivation:
|
|
116
|
+
* - childKey = HMAC-SHA512(masterKey, childPath)
|
|
117
|
+
* - Takes first 32 bytes as the derived key
|
|
118
|
+
*
|
|
119
|
+
* This provides:
|
|
120
|
+
* - Cryptographic standard compliance (similar to BIP32)
|
|
121
|
+
* - One-way derivation (cannot derive parent from child)
|
|
122
|
+
* - Non-correlatable keys (different paths produce unrelated keys)
|
|
123
|
+
*/
|
|
124
|
+
export function deriveViewingKey(
|
|
125
|
+
masterKey: ViewingKey,
|
|
126
|
+
childPath: string,
|
|
127
|
+
): ViewingKey {
|
|
128
|
+
// Extract raw master key bytes (remove 0x prefix if present)
|
|
129
|
+
const masterKeyHex = masterKey.key.startsWith('0x')
|
|
130
|
+
? masterKey.key.slice(2)
|
|
131
|
+
: masterKey.key
|
|
132
|
+
const masterKeyBytes = hexToBytes(masterKeyHex)
|
|
133
|
+
|
|
134
|
+
// Encode child path as bytes
|
|
135
|
+
const childPathBytes = utf8ToBytes(childPath)
|
|
136
|
+
|
|
137
|
+
// HMAC-SHA512(key=masterKey, data=childPath)
|
|
138
|
+
// This follows BIP32-style hierarchical derivation
|
|
139
|
+
const derivedFull = hmac(sha512, masterKeyBytes, childPathBytes)
|
|
140
|
+
|
|
141
|
+
// Take first 32 bytes as the derived key (standard practice)
|
|
142
|
+
const derivedBytes = derivedFull.slice(0, 32)
|
|
143
|
+
const derived = `0x${bytesToHex(derivedBytes)}` as HexString
|
|
144
|
+
|
|
145
|
+
// Compute hash of the derived key for identification
|
|
146
|
+
const hashBytes = sha256(derivedBytes)
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
key: derived,
|
|
150
|
+
path: `${masterKey.path}/${childPath}`,
|
|
151
|
+
hash: `0x${bytesToHex(hashBytes)}` as Hash,
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// ─── Encryption Constants ─────────────────────────────────────────────────────
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Domain separation for encryption key derivation
|
|
159
|
+
*/
|
|
160
|
+
const ENCRYPTION_DOMAIN = 'SIP-VIEWING-KEY-ENCRYPTION-V1'
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* XChaCha20-Poly1305 nonce size (24 bytes)
|
|
164
|
+
*/
|
|
165
|
+
const NONCE_SIZE = 24
|
|
166
|
+
|
|
167
|
+
// ─── Key Derivation ───────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Derive an encryption key from a viewing key using HKDF
|
|
171
|
+
*
|
|
172
|
+
* Uses HKDF-SHA256 with domain separation for security.
|
|
173
|
+
*
|
|
174
|
+
* @param viewingKey - The viewing key to derive from
|
|
175
|
+
* @returns 32-byte encryption key
|
|
176
|
+
*/
|
|
177
|
+
function deriveEncryptionKey(viewingKey: ViewingKey): Uint8Array {
|
|
178
|
+
// Extract the raw key bytes (remove 0x prefix)
|
|
179
|
+
const keyHex = viewingKey.key.startsWith('0x')
|
|
180
|
+
? viewingKey.key.slice(2)
|
|
181
|
+
: viewingKey.key
|
|
182
|
+
const keyBytes = hexToBytes(keyHex)
|
|
183
|
+
|
|
184
|
+
// Use HKDF to derive a proper encryption key
|
|
185
|
+
// HKDF(SHA256, ikm=viewingKey, salt=domain, info=path, length=32)
|
|
186
|
+
const salt = utf8ToBytes(ENCRYPTION_DOMAIN)
|
|
187
|
+
const info = utf8ToBytes(viewingKey.path)
|
|
188
|
+
|
|
189
|
+
return hkdf(sha256, keyBytes, salt, info, 32)
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ─── Transaction Data Type ────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Transaction data that can be encrypted for viewing
|
|
196
|
+
*/
|
|
197
|
+
export interface TransactionData {
|
|
198
|
+
sender: string
|
|
199
|
+
recipient: string
|
|
200
|
+
amount: string
|
|
201
|
+
timestamp: number
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ─── Encryption Functions ─────────────────────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Encrypt transaction data for viewing key holders
|
|
208
|
+
*
|
|
209
|
+
* Uses XChaCha20-Poly1305 authenticated encryption with:
|
|
210
|
+
* - 24-byte random nonce (nonce-misuse resistant)
|
|
211
|
+
* - HKDF-derived encryption key
|
|
212
|
+
* - 16-byte authentication tag (included in ciphertext)
|
|
213
|
+
*
|
|
214
|
+
* @param data - Transaction data to encrypt
|
|
215
|
+
* @param viewingKey - Viewing key for encryption
|
|
216
|
+
* @returns Encrypted transaction with nonce and key hash
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```typescript
|
|
220
|
+
* const encrypted = encryptForViewing(
|
|
221
|
+
* { sender: '0x...', recipient: '0x...', amount: '100', timestamp: 123 },
|
|
222
|
+
* viewingKey
|
|
223
|
+
* )
|
|
224
|
+
* // encrypted.ciphertext contains the encrypted data
|
|
225
|
+
* // encrypted.nonce is needed for decryption
|
|
226
|
+
* // encrypted.viewingKeyHash identifies which key can decrypt
|
|
227
|
+
* ```
|
|
228
|
+
*/
|
|
229
|
+
export function encryptForViewing(
|
|
230
|
+
data: TransactionData,
|
|
231
|
+
viewingKey: ViewingKey,
|
|
232
|
+
): EncryptedTransaction {
|
|
233
|
+
// Derive encryption key from viewing key
|
|
234
|
+
const key = deriveEncryptionKey(viewingKey)
|
|
235
|
+
|
|
236
|
+
// Generate random nonce (24 bytes for XChaCha20)
|
|
237
|
+
const nonce = randomBytes(NONCE_SIZE)
|
|
238
|
+
|
|
239
|
+
// Serialize data to JSON
|
|
240
|
+
const plaintext = utf8ToBytes(JSON.stringify(data))
|
|
241
|
+
|
|
242
|
+
// Encrypt with XChaCha20-Poly1305
|
|
243
|
+
const cipher = xchacha20poly1305(key, nonce)
|
|
244
|
+
const ciphertext = cipher.encrypt(plaintext)
|
|
245
|
+
|
|
246
|
+
return {
|
|
247
|
+
ciphertext: `0x${bytesToHex(ciphertext)}` as HexString,
|
|
248
|
+
nonce: `0x${bytesToHex(nonce)}` as HexString,
|
|
249
|
+
viewingKeyHash: viewingKey.hash,
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/**
|
|
254
|
+
* Decrypt transaction data with viewing key
|
|
255
|
+
*
|
|
256
|
+
* Performs authenticated decryption using XChaCha20-Poly1305.
|
|
257
|
+
* The authentication tag is verified before returning data.
|
|
258
|
+
*
|
|
259
|
+
* @param encrypted - Encrypted transaction data
|
|
260
|
+
* @param viewingKey - Viewing key for decryption
|
|
261
|
+
* @returns Decrypted transaction data
|
|
262
|
+
* @throws {Error} If decryption fails (wrong key, tampered data, etc.)
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```typescript
|
|
266
|
+
* try {
|
|
267
|
+
* const data = decryptWithViewing(encrypted, viewingKey)
|
|
268
|
+
* console.log(`Amount: ${data.amount}`)
|
|
269
|
+
* } catch (e) {
|
|
270
|
+
* console.error('Decryption failed - wrong key or tampered data')
|
|
271
|
+
* }
|
|
272
|
+
* ```
|
|
273
|
+
*/
|
|
274
|
+
export function decryptWithViewing(
|
|
275
|
+
encrypted: EncryptedTransaction,
|
|
276
|
+
viewingKey: ViewingKey,
|
|
277
|
+
): TransactionData {
|
|
278
|
+
// Verify viewing key hash matches (optional but helpful error message)
|
|
279
|
+
if (encrypted.viewingKeyHash !== viewingKey.hash) {
|
|
280
|
+
throw new CryptoError(
|
|
281
|
+
'Viewing key hash mismatch - this key cannot decrypt this transaction',
|
|
282
|
+
ErrorCode.DECRYPTION_FAILED,
|
|
283
|
+
{ operation: 'decryptWithViewing' }
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
// Derive encryption key from viewing key
|
|
288
|
+
const key = deriveEncryptionKey(viewingKey)
|
|
289
|
+
|
|
290
|
+
// Parse nonce and ciphertext
|
|
291
|
+
const nonceHex = encrypted.nonce.startsWith('0x')
|
|
292
|
+
? encrypted.nonce.slice(2)
|
|
293
|
+
: encrypted.nonce
|
|
294
|
+
const nonce = hexToBytes(nonceHex)
|
|
295
|
+
|
|
296
|
+
const ciphertextHex = encrypted.ciphertext.startsWith('0x')
|
|
297
|
+
? encrypted.ciphertext.slice(2)
|
|
298
|
+
: encrypted.ciphertext
|
|
299
|
+
const ciphertext = hexToBytes(ciphertextHex)
|
|
300
|
+
|
|
301
|
+
// Decrypt with XChaCha20-Poly1305
|
|
302
|
+
// This will throw if authentication fails (wrong key or tampered data)
|
|
303
|
+
const cipher = xchacha20poly1305(key, nonce)
|
|
304
|
+
let plaintext: Uint8Array
|
|
305
|
+
|
|
306
|
+
try {
|
|
307
|
+
plaintext = cipher.decrypt(ciphertext)
|
|
308
|
+
} catch (e) {
|
|
309
|
+
throw new CryptoError(
|
|
310
|
+
'Decryption failed - authentication tag verification failed. ' +
|
|
311
|
+
'Either the viewing key is incorrect or the data has been tampered with.',
|
|
312
|
+
ErrorCode.DECRYPTION_FAILED,
|
|
313
|
+
{
|
|
314
|
+
cause: e instanceof Error ? e : undefined,
|
|
315
|
+
operation: 'decryptWithViewing',
|
|
316
|
+
}
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Parse JSON
|
|
321
|
+
const textDecoder = new TextDecoder()
|
|
322
|
+
const jsonString = textDecoder.decode(plaintext)
|
|
323
|
+
|
|
324
|
+
// Validate size before parsing to prevent DoS
|
|
325
|
+
if (jsonString.length > MAX_TRANSACTION_DATA_SIZE) {
|
|
326
|
+
throw new ValidationError(
|
|
327
|
+
`decrypted data exceeds maximum size limit (${MAX_TRANSACTION_DATA_SIZE} bytes)`,
|
|
328
|
+
'transactionData',
|
|
329
|
+
{ received: jsonString.length, max: MAX_TRANSACTION_DATA_SIZE },
|
|
330
|
+
ErrorCode.INVALID_INPUT
|
|
331
|
+
)
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
const data = JSON.parse(jsonString) as TransactionData
|
|
336
|
+
// Validate required fields
|
|
337
|
+
if (
|
|
338
|
+
typeof data.sender !== 'string' ||
|
|
339
|
+
typeof data.recipient !== 'string' ||
|
|
340
|
+
typeof data.amount !== 'string' ||
|
|
341
|
+
typeof data.timestamp !== 'number'
|
|
342
|
+
) {
|
|
343
|
+
throw new ValidationError(
|
|
344
|
+
'invalid transaction data format',
|
|
345
|
+
'transactionData',
|
|
346
|
+
{ received: data },
|
|
347
|
+
ErrorCode.INVALID_INPUT
|
|
348
|
+
)
|
|
349
|
+
}
|
|
350
|
+
return data
|
|
351
|
+
} catch (e) {
|
|
352
|
+
if (e instanceof SyntaxError) {
|
|
353
|
+
throw new CryptoError(
|
|
354
|
+
'Decryption succeeded but data is malformed JSON',
|
|
355
|
+
ErrorCode.DECRYPTION_FAILED,
|
|
356
|
+
{ cause: e, operation: 'decryptWithViewing' }
|
|
357
|
+
)
|
|
358
|
+
}
|
|
359
|
+
throw e
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Validate privacy level string
|
|
365
|
+
*/
|
|
366
|
+
export function isValidPrivacyLevel(level: string): level is PrivacyLevel {
|
|
367
|
+
return ['transparent', 'shielded', 'compliant'].includes(level)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Get human-readable description of privacy level
|
|
372
|
+
*/
|
|
373
|
+
export function getPrivacyDescription(level: PrivacyLevel): string {
|
|
374
|
+
const descriptions: Record<PrivacyLevel, string> = {
|
|
375
|
+
transparent: 'Public transaction - all details visible on-chain',
|
|
376
|
+
shielded: 'Private transaction - sender, amount, and recipient hidden',
|
|
377
|
+
compliant: 'Private with audit - hidden but viewable with key',
|
|
378
|
+
}
|
|
379
|
+
return descriptions[level]
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
// hexToBytes removed - was only needed for mocked XOR encryption
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof Providers for SIP Protocol
|
|
3
|
+
*
|
|
4
|
+
* This module provides a pluggable interface for ZK proof generation.
|
|
5
|
+
*
|
|
6
|
+
* ## Available Providers
|
|
7
|
+
*
|
|
8
|
+
* - **MockProofProvider**: For testing only - provides NO cryptographic security
|
|
9
|
+
* - **NoirProofProvider**: Production provider using Noir circuits (coming in #14, #15, #16)
|
|
10
|
+
*
|
|
11
|
+
* ## Usage
|
|
12
|
+
*
|
|
13
|
+
* ```typescript
|
|
14
|
+
* import { MockProofProvider, NoirProofProvider } from '@sip-protocol/sdk'
|
|
15
|
+
*
|
|
16
|
+
* // For testing
|
|
17
|
+
* const mockProvider = new MockProofProvider()
|
|
18
|
+
* await mockProvider.initialize()
|
|
19
|
+
*
|
|
20
|
+
* // For production (when available)
|
|
21
|
+
* const noirProvider = new NoirProofProvider()
|
|
22
|
+
* await noirProvider.initialize()
|
|
23
|
+
*
|
|
24
|
+
* // Use with SIP client
|
|
25
|
+
* const sip = new SIP({
|
|
26
|
+
* network: 'testnet',
|
|
27
|
+
* proofProvider: noirProvider,
|
|
28
|
+
* })
|
|
29
|
+
* ```
|
|
30
|
+
*
|
|
31
|
+
* @module proofs
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
// Interface and types
|
|
35
|
+
export type {
|
|
36
|
+
ProofProvider,
|
|
37
|
+
ProofFramework,
|
|
38
|
+
FundingProofParams,
|
|
39
|
+
ValidityProofParams,
|
|
40
|
+
FulfillmentProofParams,
|
|
41
|
+
OracleAttestation,
|
|
42
|
+
ProofResult,
|
|
43
|
+
} from './interface'
|
|
44
|
+
|
|
45
|
+
export { ProofGenerationError } from './interface'
|
|
46
|
+
|
|
47
|
+
// Mock provider (testing only)
|
|
48
|
+
export { MockProofProvider } from './mock'
|
|
49
|
+
|
|
50
|
+
// Noir provider (production)
|
|
51
|
+
export { NoirProofProvider } from './noir'
|
|
52
|
+
export type { NoirProviderConfig } from './noir'
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof Provider Interface
|
|
3
|
+
*
|
|
4
|
+
* Defines a pluggable interface for ZK proof generation and verification.
|
|
5
|
+
* This allows different backends (Noir, mock for testing) to be swapped.
|
|
6
|
+
*
|
|
7
|
+
* @see docs/specs/ZK-ARCHITECTURE.md for framework decision (Noir)
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { ZKProof, Commitment, HexString } from '@sip-protocol/types'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Supported proof framework types
|
|
14
|
+
*/
|
|
15
|
+
export type ProofFramework = 'noir' | 'mock'
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Parameters for generating a Funding Proof
|
|
19
|
+
*
|
|
20
|
+
* Proves: balance >= minimumRequired without revealing balance
|
|
21
|
+
*
|
|
22
|
+
* @see docs/specs/FUNDING-PROOF.md
|
|
23
|
+
*/
|
|
24
|
+
export interface FundingProofParams {
|
|
25
|
+
/** User's actual balance (private) */
|
|
26
|
+
balance: bigint
|
|
27
|
+
/** Minimum amount required for the intent (public) */
|
|
28
|
+
minimumRequired: bigint
|
|
29
|
+
/** Blinding factor for the commitment (private) */
|
|
30
|
+
blindingFactor: Uint8Array
|
|
31
|
+
/** Asset identifier (public) */
|
|
32
|
+
assetId: string
|
|
33
|
+
/** User's address for ownership proof (private) */
|
|
34
|
+
userAddress: string
|
|
35
|
+
/** Signature proving ownership of the address (private) */
|
|
36
|
+
ownershipSignature: Uint8Array
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Parameters for generating a Validity Proof
|
|
41
|
+
*
|
|
42
|
+
* Proves: intent is authorized by sender without revealing sender
|
|
43
|
+
*
|
|
44
|
+
* @see docs/specs/VALIDITY-PROOF.md
|
|
45
|
+
*/
|
|
46
|
+
export interface ValidityProofParams {
|
|
47
|
+
/** Hash of the intent (public) */
|
|
48
|
+
intentHash: HexString
|
|
49
|
+
/** Sender's address (private) */
|
|
50
|
+
senderAddress: string
|
|
51
|
+
/** Blinding factor for sender commitment (private) */
|
|
52
|
+
senderBlinding: Uint8Array
|
|
53
|
+
/** Sender's secret key (private) */
|
|
54
|
+
senderSecret: Uint8Array
|
|
55
|
+
/** Signature authorizing the intent (private) */
|
|
56
|
+
authorizationSignature: Uint8Array
|
|
57
|
+
/** Nonce for nullifier generation (private) */
|
|
58
|
+
nonce: Uint8Array
|
|
59
|
+
/** Intent timestamp (public) */
|
|
60
|
+
timestamp: number
|
|
61
|
+
/** Intent expiry (public) */
|
|
62
|
+
expiry: number
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Parameters for generating a Fulfillment Proof
|
|
67
|
+
*
|
|
68
|
+
* Proves: solver delivered output >= minimum to correct recipient
|
|
69
|
+
*
|
|
70
|
+
* @see docs/specs/FULFILLMENT-PROOF.md
|
|
71
|
+
*/
|
|
72
|
+
export interface FulfillmentProofParams {
|
|
73
|
+
/** Hash of the original intent (public) */
|
|
74
|
+
intentHash: HexString
|
|
75
|
+
/** Actual output amount delivered (private) */
|
|
76
|
+
outputAmount: bigint
|
|
77
|
+
/** Blinding factor for output commitment (private) */
|
|
78
|
+
outputBlinding: Uint8Array
|
|
79
|
+
/** Minimum required output from intent (public) */
|
|
80
|
+
minOutputAmount: bigint
|
|
81
|
+
/** Recipient's stealth address (public) */
|
|
82
|
+
recipientStealth: HexString
|
|
83
|
+
/** Solver's identifier (public) */
|
|
84
|
+
solverId: string
|
|
85
|
+
/** Solver's secret for authorization (private) */
|
|
86
|
+
solverSecret: Uint8Array
|
|
87
|
+
/** Oracle attestation of delivery (private) */
|
|
88
|
+
oracleAttestation: OracleAttestation
|
|
89
|
+
/** Time of fulfillment (public) */
|
|
90
|
+
fulfillmentTime: number
|
|
91
|
+
/** Intent expiry (public) */
|
|
92
|
+
expiry: number
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Oracle attestation for cross-chain verification
|
|
97
|
+
*/
|
|
98
|
+
export interface OracleAttestation {
|
|
99
|
+
/** Recipient who received funds */
|
|
100
|
+
recipient: HexString
|
|
101
|
+
/** Amount received */
|
|
102
|
+
amount: bigint
|
|
103
|
+
/** Transaction hash on destination chain */
|
|
104
|
+
txHash: HexString
|
|
105
|
+
/** Block number containing the transaction */
|
|
106
|
+
blockNumber: bigint
|
|
107
|
+
/** Oracle signature (threshold signature for multi-oracle) */
|
|
108
|
+
signature: Uint8Array
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Result of proof generation
|
|
113
|
+
*/
|
|
114
|
+
export interface ProofResult {
|
|
115
|
+
/** The generated proof */
|
|
116
|
+
proof: ZKProof
|
|
117
|
+
/** Public inputs used in the proof */
|
|
118
|
+
publicInputs: HexString[]
|
|
119
|
+
/** Commitment (if generated as part of proof) */
|
|
120
|
+
commitment?: Commitment
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Proof Provider Interface
|
|
125
|
+
*
|
|
126
|
+
* Implementations of this interface provide ZK proof generation and verification.
|
|
127
|
+
* The SDK uses this interface to remain agnostic to the underlying ZK framework.
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* // Use mock provider for testing
|
|
132
|
+
* const mockProvider = new MockProofProvider()
|
|
133
|
+
*
|
|
134
|
+
* // Use Noir provider for production
|
|
135
|
+
* const noirProvider = new NoirProofProvider()
|
|
136
|
+
*
|
|
137
|
+
* // Configure SIP client with provider
|
|
138
|
+
* const sip = new SIP({
|
|
139
|
+
* network: 'testnet',
|
|
140
|
+
* proofProvider: noirProvider,
|
|
141
|
+
* })
|
|
142
|
+
* ```
|
|
143
|
+
*/
|
|
144
|
+
export interface ProofProvider {
|
|
145
|
+
/**
|
|
146
|
+
* The ZK framework this provider uses
|
|
147
|
+
*/
|
|
148
|
+
readonly framework: ProofFramework
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Whether the provider is ready to generate proofs
|
|
152
|
+
* (e.g., circuits compiled, keys loaded)
|
|
153
|
+
*/
|
|
154
|
+
readonly isReady: boolean
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Initialize the provider (compile circuits, load keys, etc.)
|
|
158
|
+
*
|
|
159
|
+
* @throws Error if initialization fails
|
|
160
|
+
*/
|
|
161
|
+
initialize(): Promise<void>
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Generate a Funding Proof
|
|
165
|
+
*
|
|
166
|
+
* Proves that the user has sufficient balance without revealing the exact amount.
|
|
167
|
+
*
|
|
168
|
+
* @param params - Funding proof parameters
|
|
169
|
+
* @returns The generated proof with public inputs
|
|
170
|
+
* @throws ProofGenerationError if proof generation fails
|
|
171
|
+
*
|
|
172
|
+
* @see docs/specs/FUNDING-PROOF.md (~22,000 constraints)
|
|
173
|
+
*/
|
|
174
|
+
generateFundingProof(params: FundingProofParams): Promise<ProofResult>
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Generate a Validity Proof
|
|
178
|
+
*
|
|
179
|
+
* Proves that the intent is authorized without revealing the sender.
|
|
180
|
+
*
|
|
181
|
+
* @param params - Validity proof parameters
|
|
182
|
+
* @returns The generated proof with public inputs
|
|
183
|
+
* @throws ProofGenerationError if proof generation fails
|
|
184
|
+
*
|
|
185
|
+
* @see docs/specs/VALIDITY-PROOF.md (~72,000 constraints)
|
|
186
|
+
*/
|
|
187
|
+
generateValidityProof(params: ValidityProofParams): Promise<ProofResult>
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Generate a Fulfillment Proof
|
|
191
|
+
*
|
|
192
|
+
* Proves that the solver correctly delivered the output.
|
|
193
|
+
*
|
|
194
|
+
* @param params - Fulfillment proof parameters
|
|
195
|
+
* @returns The generated proof with public inputs
|
|
196
|
+
* @throws ProofGenerationError if proof generation fails
|
|
197
|
+
*
|
|
198
|
+
* @see docs/specs/FULFILLMENT-PROOF.md (~22,000 constraints)
|
|
199
|
+
*/
|
|
200
|
+
generateFulfillmentProof(params: FulfillmentProofParams): Promise<ProofResult>
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Verify a proof
|
|
204
|
+
*
|
|
205
|
+
* @param proof - The proof to verify
|
|
206
|
+
* @returns true if the proof is valid, false otherwise
|
|
207
|
+
*/
|
|
208
|
+
verifyProof(proof: ZKProof): Promise<boolean>
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Error thrown when proof generation fails
|
|
213
|
+
*/
|
|
214
|
+
export class ProofGenerationError extends Error {
|
|
215
|
+
readonly proofType: 'funding' | 'validity' | 'fulfillment'
|
|
216
|
+
readonly cause?: Error
|
|
217
|
+
|
|
218
|
+
constructor(
|
|
219
|
+
proofType: 'funding' | 'validity' | 'fulfillment',
|
|
220
|
+
message: string,
|
|
221
|
+
cause?: Error,
|
|
222
|
+
) {
|
|
223
|
+
super(`${proofType} proof generation failed: ${message}`)
|
|
224
|
+
this.name = 'ProofGenerationError'
|
|
225
|
+
this.proofType = proofType
|
|
226
|
+
this.cause = cause
|
|
227
|
+
}
|
|
228
|
+
}
|