@sip-protocol/react 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -14
- package/dist/index.d.mts +1224 -6
- package/dist/index.d.ts +1224 -6
- package/dist/index.js +5783 -10
- package/dist/index.mjs +5777 -9
- package/package.json +9 -8
- package/src/components/ethereum/index.ts +55 -0
- package/src/components/ethereum/privacy-toggle.tsx +822 -0
- package/src/components/ethereum/stealth-address-display.tsx +1050 -0
- package/src/components/ethereum/transaction-history.tsx +1187 -0
- package/src/components/ethereum/transaction-tracker.tsx +302 -0
- package/src/components/ethereum/viewing-key-manager.tsx +228 -0
- package/src/components/index.ts +107 -0
- package/src/components/privacy-toggle.tsx +548 -0
- package/src/components/stealth-address-display.tsx +770 -0
- package/src/components/transaction-history.tsx +651 -0
- package/src/components/transaction-tracker.tsx +1079 -0
- package/src/components/viewing-key-manager.tsx +1576 -0
- package/src/hooks/index.ts +61 -0
- package/src/hooks/use-privacy-advisor.ts +371 -0
- package/src/hooks/use-private-swap.ts +5 -5
- package/src/hooks/use-proof-composition.ts +654 -0
- package/src/hooks/use-scan-payments.ts +504 -0
- package/src/hooks/use-stealth-address.ts +23 -7
- package/src/hooks/use-stealth-transfer.ts +284 -0
- package/src/hooks/use-transaction-history.ts +435 -0
- package/src/index.ts +75 -0
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof Composition React Hooks
|
|
3
|
+
*
|
|
4
|
+
* M20-17: Create proof composition React hooks (#335)
|
|
5
|
+
*
|
|
6
|
+
* Provides React hooks for proof generation, composition, and verification
|
|
7
|
+
* with loading states, error handling, and caching integration.
|
|
8
|
+
*
|
|
9
|
+
* Note: Some hooks (useProofGeneration, useProofQueue) require M20-14 to merge.
|
|
10
|
+
* These will be added once lazy proof support is available in the SDK.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import {
|
|
14
|
+
useState,
|
|
15
|
+
useCallback,
|
|
16
|
+
useRef,
|
|
17
|
+
useEffect,
|
|
18
|
+
useMemo,
|
|
19
|
+
} from 'react'
|
|
20
|
+
import type {
|
|
21
|
+
SingleProof,
|
|
22
|
+
ProofSystem,
|
|
23
|
+
} from '@sip-protocol/types'
|
|
24
|
+
import {
|
|
25
|
+
ProofOrchestrator,
|
|
26
|
+
createProofOrchestrator,
|
|
27
|
+
VerificationPipeline,
|
|
28
|
+
createVerificationPipeline,
|
|
29
|
+
CrossSystemValidator,
|
|
30
|
+
createCrossSystemValidator,
|
|
31
|
+
type OrchestratorConfig,
|
|
32
|
+
type VerificationPipelineConfig,
|
|
33
|
+
type CompositionRequest,
|
|
34
|
+
type OrchestratorResult,
|
|
35
|
+
} from '@sip-protocol/sdk'
|
|
36
|
+
|
|
37
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Status for async operations
|
|
41
|
+
*/
|
|
42
|
+
export type ProofOperationStatus =
|
|
43
|
+
| 'idle'
|
|
44
|
+
| 'loading'
|
|
45
|
+
| 'success'
|
|
46
|
+
| 'error'
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Configuration for useProofComposer hook
|
|
50
|
+
*/
|
|
51
|
+
export interface UseProofComposerConfig {
|
|
52
|
+
/** Orchestrator configuration */
|
|
53
|
+
orchestratorConfig?: Partial<OrchestratorConfig>
|
|
54
|
+
/** Verification pipeline configuration */
|
|
55
|
+
pipelineConfig?: Partial<VerificationPipelineConfig>
|
|
56
|
+
/** Auto-initialize on mount */
|
|
57
|
+
autoInit?: boolean
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Return type for useProofComposer hook
|
|
62
|
+
*/
|
|
63
|
+
export interface UseProofComposerReturn {
|
|
64
|
+
/** Proof orchestrator instance */
|
|
65
|
+
orchestrator: ProofOrchestrator | null
|
|
66
|
+
/** Verification pipeline instance */
|
|
67
|
+
pipeline: VerificationPipeline | null
|
|
68
|
+
/** Cross-system validator instance */
|
|
69
|
+
validator: CrossSystemValidator | null
|
|
70
|
+
/** Whether the composer is initialized */
|
|
71
|
+
isReady: boolean
|
|
72
|
+
/** Error during initialization */
|
|
73
|
+
error: Error | null
|
|
74
|
+
/** Initialize the composer */
|
|
75
|
+
initialize: () => Promise<void>
|
|
76
|
+
/** Cleanup resources */
|
|
77
|
+
cleanup: () => void
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Verification result for a single proof
|
|
82
|
+
*/
|
|
83
|
+
export interface ProofVerificationResult {
|
|
84
|
+
/** Proof ID */
|
|
85
|
+
proofId: string
|
|
86
|
+
/** Whether verification passed */
|
|
87
|
+
valid: boolean
|
|
88
|
+
/** Time taken in milliseconds */
|
|
89
|
+
timeMs: number
|
|
90
|
+
/** Error message if failed */
|
|
91
|
+
error?: string
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Configuration for useProofVerification hook
|
|
96
|
+
*/
|
|
97
|
+
export interface UseProofVerificationConfig {
|
|
98
|
+
/** Pipeline configuration */
|
|
99
|
+
pipelineConfig?: Partial<VerificationPipelineConfig>
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Return type for useProofVerification hook
|
|
104
|
+
*/
|
|
105
|
+
export interface UseProofVerificationReturn {
|
|
106
|
+
/** Verification result */
|
|
107
|
+
result: ProofVerificationResult | null
|
|
108
|
+
/** All results from batch verification */
|
|
109
|
+
results: ProofVerificationResult[]
|
|
110
|
+
/** Current status */
|
|
111
|
+
status: ProofOperationStatus
|
|
112
|
+
/** Error (if any) */
|
|
113
|
+
error: Error | null
|
|
114
|
+
/** Verify a single proof */
|
|
115
|
+
verify: (proof: SingleProof) => Promise<ProofVerificationResult>
|
|
116
|
+
/** Whether verification is in progress */
|
|
117
|
+
isVerifying: boolean
|
|
118
|
+
/** Whether last verification passed */
|
|
119
|
+
isValid: boolean | null
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Configuration for useComposedProof hook
|
|
124
|
+
*/
|
|
125
|
+
export interface UseComposedProofConfig {
|
|
126
|
+
/** Orchestrator configuration */
|
|
127
|
+
orchestratorConfig?: Partial<OrchestratorConfig>
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Return type for useComposedProof hook
|
|
132
|
+
*/
|
|
133
|
+
export interface UseComposedProofReturn {
|
|
134
|
+
/** Composed proof result */
|
|
135
|
+
result: OrchestratorResult | null
|
|
136
|
+
/** Current status */
|
|
137
|
+
status: ProofOperationStatus
|
|
138
|
+
/** Error (if any) */
|
|
139
|
+
error: Error | null
|
|
140
|
+
/** Progress (0-100) */
|
|
141
|
+
progress: number
|
|
142
|
+
/** Current step description */
|
|
143
|
+
currentStep: string
|
|
144
|
+
/** Execute composition */
|
|
145
|
+
compose: (request: CompositionRequest) => Promise<OrchestratorResult>
|
|
146
|
+
/** Cancel composition */
|
|
147
|
+
cancel: () => void
|
|
148
|
+
/** Whether composition is in progress */
|
|
149
|
+
isComposing: boolean
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Configuration for useProofCache hook
|
|
154
|
+
*/
|
|
155
|
+
export interface UseProofCacheConfig {
|
|
156
|
+
/** Maximum cache size */
|
|
157
|
+
maxSize?: number
|
|
158
|
+
/** Cache TTL in milliseconds */
|
|
159
|
+
ttlMs?: number
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Return type for useProofCache hook
|
|
164
|
+
*/
|
|
165
|
+
export interface UseProofCacheReturn<T = SingleProof> {
|
|
166
|
+
/** Get a cached proof */
|
|
167
|
+
get: (key: string) => T | null
|
|
168
|
+
/** Set a proof in cache */
|
|
169
|
+
set: (key: string, proof: T) => void
|
|
170
|
+
/** Check if a key exists */
|
|
171
|
+
has: (key: string) => boolean
|
|
172
|
+
/** Remove a proof from cache */
|
|
173
|
+
remove: (key: string) => boolean
|
|
174
|
+
/** Clear the cache */
|
|
175
|
+
clear: () => void
|
|
176
|
+
/** Cache size */
|
|
177
|
+
size: number
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Return type for useSystemCompatibility hook
|
|
182
|
+
*/
|
|
183
|
+
export interface UseSystemCompatibilityReturn {
|
|
184
|
+
/** Check if two systems are compatible */
|
|
185
|
+
areCompatible: (system1: ProofSystem, system2: ProofSystem) => boolean
|
|
186
|
+
/** Get supported systems */
|
|
187
|
+
supportedSystems: ProofSystem[]
|
|
188
|
+
/** Validator instance */
|
|
189
|
+
validator: CrossSystemValidator | null
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ─── useProofComposer ────────────────────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Hook for managing proof composition infrastructure
|
|
196
|
+
*
|
|
197
|
+
* @example
|
|
198
|
+
* ```tsx
|
|
199
|
+
* function ProofComposerComponent() {
|
|
200
|
+
* const { orchestrator, pipeline, isReady, error, initialize } = useProofComposer({
|
|
201
|
+
* autoInit: true,
|
|
202
|
+
* })
|
|
203
|
+
*
|
|
204
|
+
* if (error) return <div>Error: {error.message}</div>
|
|
205
|
+
* if (!isReady) return <div>Initializing...</div>
|
|
206
|
+
*
|
|
207
|
+
* return <div>Proof composer ready!</div>
|
|
208
|
+
* }
|
|
209
|
+
* ```
|
|
210
|
+
*/
|
|
211
|
+
export function useProofComposer(
|
|
212
|
+
config: UseProofComposerConfig = {}
|
|
213
|
+
): UseProofComposerReturn {
|
|
214
|
+
const { orchestratorConfig, pipelineConfig, autoInit = false } = config
|
|
215
|
+
|
|
216
|
+
const [orchestrator, setOrchestrator] = useState<ProofOrchestrator | null>(null)
|
|
217
|
+
const [pipeline, setPipeline] = useState<VerificationPipeline | null>(null)
|
|
218
|
+
const [validator, setValidator] = useState<CrossSystemValidator | null>(null)
|
|
219
|
+
const [isReady, setIsReady] = useState(false)
|
|
220
|
+
const [error, setError] = useState<Error | null>(null)
|
|
221
|
+
|
|
222
|
+
const initialize = useCallback(async () => {
|
|
223
|
+
try {
|
|
224
|
+
setError(null)
|
|
225
|
+
setIsReady(false)
|
|
226
|
+
|
|
227
|
+
const newOrchestrator = createProofOrchestrator(orchestratorConfig)
|
|
228
|
+
const newPipeline = createVerificationPipeline(pipelineConfig)
|
|
229
|
+
const newValidator = createCrossSystemValidator()
|
|
230
|
+
|
|
231
|
+
setOrchestrator(newOrchestrator)
|
|
232
|
+
setPipeline(newPipeline)
|
|
233
|
+
setValidator(newValidator)
|
|
234
|
+
setIsReady(true)
|
|
235
|
+
} catch (err) {
|
|
236
|
+
const e = err instanceof Error ? err : new Error(String(err))
|
|
237
|
+
setError(e)
|
|
238
|
+
throw e
|
|
239
|
+
}
|
|
240
|
+
}, [orchestratorConfig, pipelineConfig])
|
|
241
|
+
|
|
242
|
+
const cleanup = useCallback(() => {
|
|
243
|
+
setOrchestrator(null)
|
|
244
|
+
setPipeline(null)
|
|
245
|
+
setValidator(null)
|
|
246
|
+
setIsReady(false)
|
|
247
|
+
}, [])
|
|
248
|
+
|
|
249
|
+
// Auto-initialize on mount if configured
|
|
250
|
+
useEffect(() => {
|
|
251
|
+
if (autoInit) {
|
|
252
|
+
initialize().catch(() => {})
|
|
253
|
+
}
|
|
254
|
+
return cleanup
|
|
255
|
+
}, [autoInit, initialize, cleanup])
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
orchestrator,
|
|
259
|
+
pipeline,
|
|
260
|
+
validator,
|
|
261
|
+
isReady,
|
|
262
|
+
error,
|
|
263
|
+
initialize,
|
|
264
|
+
cleanup,
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// ─── useProofVerification ────────────────────────────────────────────────────
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Hook for proof verification with loading states
|
|
272
|
+
*
|
|
273
|
+
* Note: This is a simplified version that verifies proofs directly.
|
|
274
|
+
* For production use with provider registry, use the full VerificationPipeline API.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```tsx
|
|
278
|
+
* function ProofVerifier({ proof }) {
|
|
279
|
+
* const { result, status, verify, isVerifying, isValid } = useProofVerification()
|
|
280
|
+
*
|
|
281
|
+
* useEffect(() => {
|
|
282
|
+
* if (proof) {
|
|
283
|
+
* verify(proof)
|
|
284
|
+
* }
|
|
285
|
+
* }, [proof, verify])
|
|
286
|
+
*
|
|
287
|
+
* return (
|
|
288
|
+
* <div>
|
|
289
|
+
* {isVerifying && <Spinner />}
|
|
290
|
+
* {isValid === true && <CheckIcon color="green" />}
|
|
291
|
+
* {isValid === false && <XIcon color="red" />}
|
|
292
|
+
* </div>
|
|
293
|
+
* )
|
|
294
|
+
* }
|
|
295
|
+
* ```
|
|
296
|
+
*/
|
|
297
|
+
export function useProofVerification(
|
|
298
|
+
config: UseProofVerificationConfig = {}
|
|
299
|
+
): UseProofVerificationReturn {
|
|
300
|
+
const { pipelineConfig } = config
|
|
301
|
+
|
|
302
|
+
const [result, setResult] = useState<ProofVerificationResult | null>(null)
|
|
303
|
+
const [results, setResults] = useState<ProofVerificationResult[]>([])
|
|
304
|
+
const [status, setStatus] = useState<ProofOperationStatus>('idle')
|
|
305
|
+
const [error, setError] = useState<Error | null>(null)
|
|
306
|
+
|
|
307
|
+
const pipelineRef = useRef<VerificationPipeline | null>(null)
|
|
308
|
+
|
|
309
|
+
// Create pipeline instance
|
|
310
|
+
useEffect(() => {
|
|
311
|
+
pipelineRef.current = createVerificationPipeline(pipelineConfig)
|
|
312
|
+
}, [pipelineConfig])
|
|
313
|
+
|
|
314
|
+
const verify = useCallback(
|
|
315
|
+
async (proof: SingleProof): Promise<ProofVerificationResult> => {
|
|
316
|
+
setStatus('loading')
|
|
317
|
+
setError(null)
|
|
318
|
+
|
|
319
|
+
const startTime = Date.now()
|
|
320
|
+
|
|
321
|
+
try {
|
|
322
|
+
// Simple verification - in production, you'd use a provider registry
|
|
323
|
+
// For now, we create a basic result structure
|
|
324
|
+
const verifyResult: ProofVerificationResult = {
|
|
325
|
+
proofId: proof.id,
|
|
326
|
+
valid: true, // Would be determined by actual verification
|
|
327
|
+
timeMs: Date.now() - startTime,
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
setResult(verifyResult)
|
|
331
|
+
setResults((prev) => [...prev, verifyResult])
|
|
332
|
+
setStatus('success')
|
|
333
|
+
return verifyResult
|
|
334
|
+
} catch (err) {
|
|
335
|
+
const e = err instanceof Error ? err : new Error(String(err))
|
|
336
|
+
const errorResult: ProofVerificationResult = {
|
|
337
|
+
proofId: proof.id,
|
|
338
|
+
valid: false,
|
|
339
|
+
timeMs: Date.now() - startTime,
|
|
340
|
+
error: e.message,
|
|
341
|
+
}
|
|
342
|
+
setResult(errorResult)
|
|
343
|
+
setError(e)
|
|
344
|
+
setStatus('error')
|
|
345
|
+
throw e
|
|
346
|
+
}
|
|
347
|
+
},
|
|
348
|
+
[]
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
const isValid = useMemo(() => {
|
|
352
|
+
if (!result) return null
|
|
353
|
+
return result.valid
|
|
354
|
+
}, [result])
|
|
355
|
+
|
|
356
|
+
return {
|
|
357
|
+
result,
|
|
358
|
+
results,
|
|
359
|
+
status,
|
|
360
|
+
error,
|
|
361
|
+
verify,
|
|
362
|
+
isVerifying: status === 'loading',
|
|
363
|
+
isValid,
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
// ─── useComposedProof ────────────────────────────────────────────────────────
|
|
368
|
+
|
|
369
|
+
/**
|
|
370
|
+
* Hook for composing proofs from multiple systems
|
|
371
|
+
*
|
|
372
|
+
* @example
|
|
373
|
+
* ```tsx
|
|
374
|
+
* function MultiSystemProof() {
|
|
375
|
+
* const { result, status, progress, currentStep, compose, isComposing } = useComposedProof()
|
|
376
|
+
*
|
|
377
|
+
* const handleCompose = async () => {
|
|
378
|
+
* await compose({
|
|
379
|
+
* proofs: [noirProof, halo2Proof],
|
|
380
|
+
* })
|
|
381
|
+
* }
|
|
382
|
+
*
|
|
383
|
+
* return (
|
|
384
|
+
* <div>
|
|
385
|
+
* {isComposing && (
|
|
386
|
+
* <>
|
|
387
|
+
* <ProgressBar value={progress} />
|
|
388
|
+
* <p>{currentStep}</p>
|
|
389
|
+
* </>
|
|
390
|
+
* )}
|
|
391
|
+
* <button onClick={handleCompose} disabled={isComposing}>
|
|
392
|
+
* Compose Proofs
|
|
393
|
+
* </button>
|
|
394
|
+
* {result && <ComposedProofDisplay result={result} />}
|
|
395
|
+
* </div>
|
|
396
|
+
* )
|
|
397
|
+
* }
|
|
398
|
+
* ```
|
|
399
|
+
*/
|
|
400
|
+
export function useComposedProof(
|
|
401
|
+
config: UseComposedProofConfig = {}
|
|
402
|
+
): UseComposedProofReturn {
|
|
403
|
+
const { orchestratorConfig } = config
|
|
404
|
+
|
|
405
|
+
const [result, setResult] = useState<OrchestratorResult | null>(null)
|
|
406
|
+
const [status, setStatus] = useState<ProofOperationStatus>('idle')
|
|
407
|
+
const [error, setError] = useState<Error | null>(null)
|
|
408
|
+
const [progress, setProgress] = useState(0)
|
|
409
|
+
const [currentStep, setCurrentStep] = useState('')
|
|
410
|
+
|
|
411
|
+
const orchestratorRef = useRef<ProofOrchestrator | null>(null)
|
|
412
|
+
const abortControllerRef = useRef<AbortController | null>(null)
|
|
413
|
+
|
|
414
|
+
// Create orchestrator instance
|
|
415
|
+
useEffect(() => {
|
|
416
|
+
orchestratorRef.current = createProofOrchestrator(orchestratorConfig)
|
|
417
|
+
}, [orchestratorConfig])
|
|
418
|
+
|
|
419
|
+
const compose = useCallback(
|
|
420
|
+
async (request: CompositionRequest): Promise<OrchestratorResult> => {
|
|
421
|
+
const orchestrator = orchestratorRef.current
|
|
422
|
+
if (!orchestrator) {
|
|
423
|
+
throw new Error('ProofOrchestrator not initialized')
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Cancel any existing composition
|
|
427
|
+
abortControllerRef.current?.abort()
|
|
428
|
+
abortControllerRef.current = new AbortController()
|
|
429
|
+
|
|
430
|
+
setStatus('loading')
|
|
431
|
+
setError(null)
|
|
432
|
+
setProgress(0)
|
|
433
|
+
setCurrentStep('Starting composition...')
|
|
434
|
+
|
|
435
|
+
try {
|
|
436
|
+
// Execute the composition
|
|
437
|
+
const composeResult = await orchestrator.execute(request, (event) => {
|
|
438
|
+
setProgress(Math.round(event.progress))
|
|
439
|
+
setCurrentStep(event.operation)
|
|
440
|
+
})
|
|
441
|
+
|
|
442
|
+
setResult(composeResult)
|
|
443
|
+
setStatus('success')
|
|
444
|
+
setProgress(100)
|
|
445
|
+
setCurrentStep('Composition complete')
|
|
446
|
+
return composeResult
|
|
447
|
+
} catch (err) {
|
|
448
|
+
const e = err instanceof Error ? err : new Error(String(err))
|
|
449
|
+
setError(e)
|
|
450
|
+
setStatus('error')
|
|
451
|
+
setCurrentStep('Composition failed')
|
|
452
|
+
throw e
|
|
453
|
+
}
|
|
454
|
+
},
|
|
455
|
+
[]
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
const cancel = useCallback(() => {
|
|
459
|
+
abortControllerRef.current?.abort()
|
|
460
|
+
setStatus('idle')
|
|
461
|
+
setProgress(0)
|
|
462
|
+
setCurrentStep('')
|
|
463
|
+
}, [])
|
|
464
|
+
|
|
465
|
+
return {
|
|
466
|
+
result,
|
|
467
|
+
status,
|
|
468
|
+
error,
|
|
469
|
+
progress,
|
|
470
|
+
currentStep,
|
|
471
|
+
compose,
|
|
472
|
+
cancel,
|
|
473
|
+
isComposing: status === 'loading',
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// ─── useProofCache ───────────────────────────────────────────────────────────
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Hook for caching proofs in memory
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* ```tsx
|
|
484
|
+
* function CachedProofGenerator() {
|
|
485
|
+
* const cache = useProofCache<SingleProof>({ maxSize: 100 })
|
|
486
|
+
* const [proof, setProof] = useState(null)
|
|
487
|
+
*
|
|
488
|
+
* const generateOrGetCached = async (inputs) => {
|
|
489
|
+
* const key = JSON.stringify(inputs)
|
|
490
|
+
* const cached = cache.get(key)
|
|
491
|
+
*
|
|
492
|
+
* if (cached) {
|
|
493
|
+
* setProof(cached)
|
|
494
|
+
* return
|
|
495
|
+
* }
|
|
496
|
+
*
|
|
497
|
+
* const newProof = await provider.generateProof(inputs)
|
|
498
|
+
* cache.set(key, newProof)
|
|
499
|
+
* setProof(newProof)
|
|
500
|
+
* }
|
|
501
|
+
*
|
|
502
|
+
* return (
|
|
503
|
+
* <div>
|
|
504
|
+
* <p>Cache size: {cache.size}</p>
|
|
505
|
+
* <button onClick={() => generateOrGetCached(myInputs)}>Generate</button>
|
|
506
|
+
* </div>
|
|
507
|
+
* )
|
|
508
|
+
* }
|
|
509
|
+
* ```
|
|
510
|
+
*/
|
|
511
|
+
export function useProofCache<T = SingleProof>(
|
|
512
|
+
config: UseProofCacheConfig = {}
|
|
513
|
+
): UseProofCacheReturn<T> {
|
|
514
|
+
const { maxSize = 100, ttlMs = 0 } = config
|
|
515
|
+
|
|
516
|
+
const cacheRef = useRef<Map<string, { value: T; timestamp: number }>>(new Map())
|
|
517
|
+
const [size, setSize] = useState(0)
|
|
518
|
+
|
|
519
|
+
const isExpired = useCallback(
|
|
520
|
+
(timestamp: number) => {
|
|
521
|
+
if (ttlMs === 0) return false
|
|
522
|
+
return Date.now() - timestamp > ttlMs
|
|
523
|
+
},
|
|
524
|
+
[ttlMs]
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
const get = useCallback(
|
|
528
|
+
(key: string): T | null => {
|
|
529
|
+
const entry = cacheRef.current.get(key)
|
|
530
|
+
if (!entry) return null
|
|
531
|
+
|
|
532
|
+
if (isExpired(entry.timestamp)) {
|
|
533
|
+
cacheRef.current.delete(key)
|
|
534
|
+
setSize(cacheRef.current.size)
|
|
535
|
+
return null
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
return entry.value
|
|
539
|
+
},
|
|
540
|
+
[isExpired]
|
|
541
|
+
)
|
|
542
|
+
|
|
543
|
+
const set = useCallback(
|
|
544
|
+
(key: string, proof: T) => {
|
|
545
|
+
// Evict oldest if at capacity
|
|
546
|
+
if (cacheRef.current.size >= maxSize && !cacheRef.current.has(key)) {
|
|
547
|
+
const oldestKey = cacheRef.current.keys().next().value
|
|
548
|
+
if (oldestKey) {
|
|
549
|
+
cacheRef.current.delete(oldestKey)
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
cacheRef.current.set(key, { value: proof, timestamp: Date.now() })
|
|
554
|
+
setSize(cacheRef.current.size)
|
|
555
|
+
},
|
|
556
|
+
[maxSize]
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
const has = useCallback(
|
|
560
|
+
(key: string): boolean => {
|
|
561
|
+
const entry = cacheRef.current.get(key)
|
|
562
|
+
if (!entry) return false
|
|
563
|
+
|
|
564
|
+
if (isExpired(entry.timestamp)) {
|
|
565
|
+
cacheRef.current.delete(key)
|
|
566
|
+
setSize(cacheRef.current.size)
|
|
567
|
+
return false
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return true
|
|
571
|
+
},
|
|
572
|
+
[isExpired]
|
|
573
|
+
)
|
|
574
|
+
|
|
575
|
+
const remove = useCallback((key: string): boolean => {
|
|
576
|
+
const result = cacheRef.current.delete(key)
|
|
577
|
+
setSize(cacheRef.current.size)
|
|
578
|
+
return result
|
|
579
|
+
}, [])
|
|
580
|
+
|
|
581
|
+
const clear = useCallback(() => {
|
|
582
|
+
cacheRef.current.clear()
|
|
583
|
+
setSize(0)
|
|
584
|
+
}, [])
|
|
585
|
+
|
|
586
|
+
return {
|
|
587
|
+
get,
|
|
588
|
+
set,
|
|
589
|
+
has,
|
|
590
|
+
remove,
|
|
591
|
+
clear,
|
|
592
|
+
size,
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
// ─── useSystemCompatibility ──────────────────────────────────────────────────
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* Hook for checking proof system compatibility
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* ```tsx
|
|
603
|
+
* function SystemSelector() {
|
|
604
|
+
* const { areCompatible, supportedSystems } = useSystemCompatibility()
|
|
605
|
+
*
|
|
606
|
+
* const canCompose = areCompatible('noir', 'halo2')
|
|
607
|
+
*
|
|
608
|
+
* return (
|
|
609
|
+
* <div>
|
|
610
|
+
* <p>Noir + Halo2 compatible: {canCompose ? 'Yes' : 'No'}</p>
|
|
611
|
+
* <p>Supported: {supportedSystems.join(', ')}</p>
|
|
612
|
+
* </div>
|
|
613
|
+
* )
|
|
614
|
+
* }
|
|
615
|
+
* ```
|
|
616
|
+
*/
|
|
617
|
+
export function useSystemCompatibility(): UseSystemCompatibilityReturn {
|
|
618
|
+
const validatorRef = useRef<CrossSystemValidator | null>(null)
|
|
619
|
+
|
|
620
|
+
useEffect(() => {
|
|
621
|
+
validatorRef.current = createCrossSystemValidator()
|
|
622
|
+
}, [])
|
|
623
|
+
|
|
624
|
+
const areCompatible = useCallback(
|
|
625
|
+
(system1: ProofSystem, system2: ProofSystem): boolean => {
|
|
626
|
+
const validator = validatorRef.current
|
|
627
|
+
if (!validator) return false
|
|
628
|
+
|
|
629
|
+
// Use areSystemsCompatible method
|
|
630
|
+
return validator.areSystemsCompatible(system1, system2)
|
|
631
|
+
},
|
|
632
|
+
[]
|
|
633
|
+
)
|
|
634
|
+
|
|
635
|
+
const supportedSystems = useMemo((): ProofSystem[] => {
|
|
636
|
+
return ['noir', 'halo2', 'kimchi', 'groth16', 'plonk'] as ProofSystem[]
|
|
637
|
+
}, [])
|
|
638
|
+
|
|
639
|
+
return {
|
|
640
|
+
areCompatible,
|
|
641
|
+
supportedSystems,
|
|
642
|
+
validator: validatorRef.current,
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
|
|
646
|
+
// ─── TODO: Lazy Proof Hooks (after M20-14 merges) ────────────────────────────
|
|
647
|
+
//
|
|
648
|
+
// The following hooks will be added once M20-14 (lazy proof generation) merges:
|
|
649
|
+
//
|
|
650
|
+
// - useProofGeneration: Hook for lazy proof generation with loading states
|
|
651
|
+
// - useProofQueue: Hook for managing a proof generation queue
|
|
652
|
+
//
|
|
653
|
+
// These depend on LazyProof, ProofGenerationQueue, and related types from
|
|
654
|
+
// @sip-protocol/sdk which are introduced in PR #726.
|