@sip-protocol/sdk 0.7.2 → 0.7.4
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/README.md +267 -0
- package/dist/{TransportWebUSB-TQ7WZ4LE.mjs → TransportWebUSB-YQMAGJAJ.mjs} +12 -9
- package/dist/browser.d.mts +10 -4
- package/dist/browser.d.ts +10 -4
- package/dist/browser.js +48874 -18336
- package/dist/browser.mjs +674 -48
- package/dist/chunk-4GRJ5MAW.mjs +152 -0
- package/dist/chunk-5D7A3L3W.mjs +717 -0
- package/dist/chunk-64AYA5F5.mjs +7834 -0
- package/dist/chunk-GMDGB22A.mjs +379 -0
- package/dist/chunk-I534WKN7.mjs +328 -0
- package/dist/chunk-IBZVA5Y7.mjs +1003 -0
- package/dist/chunk-PRRZAWJE.mjs +223 -0
- package/dist/{chunk-UJCSKKID.mjs → chunk-XGB3TDIC.mjs} +13 -1
- package/dist/chunk-YWGJ77A2.mjs +33806 -0
- package/dist/{chunk-6WGN57S2.mjs → chunk-Z3K7W5S3.mjs} +48 -0
- package/dist/constants-LHAAUC2T.mjs +51 -0
- package/dist/dist-2OGQ7FED.mjs +3957 -0
- package/dist/dist-IFHPYLDX.mjs +254 -0
- package/dist/fulfillment_proof-ANHVPKTB.mjs +21 -0
- package/dist/funding_proof-ICFZ5LHY.mjs +21 -0
- package/dist/index-DXh2IGkz.d.ts +24681 -0
- package/dist/index-DeE1ZzA4.d.mts +24681 -0
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48676 -17318
- package/dist/index.mjs +583 -19
- package/dist/interface-Bf7w1PLW.d.mts +679 -0
- package/dist/interface-Bf7w1PLW.d.ts +679 -0
- package/dist/{noir-DKfEzWy9.d.mts → noir-kzbLVTei.d.mts} +31 -21
- package/dist/{noir-DKfEzWy9.d.ts → noir-kzbLVTei.d.ts} +31 -21
- package/dist/proofs/halo2.d.mts +151 -0
- package/dist/proofs/halo2.d.ts +151 -0
- package/dist/proofs/halo2.js +350 -0
- package/dist/proofs/halo2.mjs +11 -0
- package/dist/proofs/kimchi.d.mts +160 -0
- package/dist/proofs/kimchi.d.ts +160 -0
- package/dist/proofs/kimchi.js +431 -0
- package/dist/proofs/kimchi.mjs +13 -0
- package/dist/proofs/noir.d.mts +1 -1
- package/dist/proofs/noir.d.ts +1 -1
- package/dist/proofs/noir.js +74 -18
- package/dist/proofs/noir.mjs +84 -24
- package/dist/solana-U3MEGU7W.mjs +280 -0
- package/dist/validity_proof-3POXLPNY.mjs +21 -0
- package/package.json +54 -21
- package/src/adapters/index.ts +41 -0
- package/src/adapters/jupiter.ts +571 -0
- package/src/adapters/near-intents.ts +135 -0
- package/src/advisor/advisor.ts +653 -0
- package/src/advisor/index.ts +54 -0
- package/src/advisor/tools.ts +303 -0
- package/src/advisor/types.ts +164 -0
- package/src/chains/ethereum/announcement.ts +536 -0
- package/src/chains/ethereum/bnb-optimizations.ts +474 -0
- package/src/chains/ethereum/commitment.ts +522 -0
- package/src/chains/ethereum/constants.ts +462 -0
- package/src/chains/ethereum/deployment.ts +596 -0
- package/src/chains/ethereum/gas-estimation.ts +538 -0
- package/src/chains/ethereum/index.ts +268 -0
- package/src/chains/ethereum/optimizations.ts +614 -0
- package/src/chains/ethereum/privacy-adapter.ts +855 -0
- package/src/chains/ethereum/registry.ts +584 -0
- package/src/chains/ethereum/rpc.ts +905 -0
- package/src/chains/ethereum/stealth.ts +491 -0
- package/src/chains/ethereum/token.ts +790 -0
- package/src/chains/ethereum/transfer.ts +637 -0
- package/src/chains/ethereum/types.ts +456 -0
- package/src/chains/ethereum/viewing-key.ts +455 -0
- package/src/chains/near/commitment.ts +608 -0
- package/src/chains/near/constants.ts +284 -0
- package/src/chains/near/function-call.ts +871 -0
- package/src/chains/near/history.ts +654 -0
- package/src/chains/near/implicit-account.ts +840 -0
- package/src/chains/near/index.ts +393 -0
- package/src/chains/near/native-transfer.ts +658 -0
- package/src/chains/near/nep141.ts +775 -0
- package/src/chains/near/privacy-adapter.ts +889 -0
- package/src/chains/near/resolver.ts +971 -0
- package/src/chains/near/rpc.ts +1016 -0
- package/src/chains/near/stealth.ts +419 -0
- package/src/chains/near/types.ts +317 -0
- package/src/chains/near/viewing-key.ts +876 -0
- package/src/chains/solana/anchor-transfer.ts +386 -0
- package/src/chains/solana/commitment.ts +577 -0
- package/src/chains/solana/constants.ts +126 -12
- package/src/chains/solana/ephemeral-keys.ts +543 -0
- package/src/chains/solana/index.ts +276 -1
- package/src/chains/solana/key-derivation.ts +418 -0
- package/src/chains/solana/kit-compat.ts +334 -0
- package/src/chains/solana/optimizations.ts +560 -0
- package/src/chains/solana/privacy-adapter.ts +605 -0
- package/src/chains/solana/providers/generic.ts +201 -0
- package/src/chains/solana/providers/helius-enhanced-types.ts +336 -0
- package/src/chains/solana/providers/helius-enhanced.ts +623 -0
- package/src/chains/solana/providers/helius.ts +402 -0
- package/src/chains/solana/providers/index.ts +85 -0
- package/src/chains/solana/providers/interface.ts +221 -0
- package/src/chains/solana/providers/quicknode.ts +409 -0
- package/src/chains/solana/providers/triton.ts +426 -0
- package/src/chains/solana/providers/webhook.ts +790 -0
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +170 -73
- package/src/chains/solana/sol-transfer.ts +732 -0
- package/src/chains/solana/spl-transfer.ts +886 -0
- package/src/chains/solana/stealth-scanner.ts +703 -0
- package/src/chains/solana/sunspot-verifier.ts +453 -0
- package/src/chains/solana/transaction-builder.ts +755 -0
- package/src/chains/solana/transfer.ts +74 -5
- package/src/chains/solana/types.ts +77 -7
- package/src/chains/solana/utils.ts +110 -0
- package/src/chains/solana/viewing-key.ts +807 -0
- package/src/compliance/fireblocks.ts +921 -0
- package/src/compliance/index.ts +37 -0
- package/src/compliance/range-sas.ts +956 -0
- package/src/config/endpoints.ts +100 -0
- package/src/crypto.ts +11 -8
- package/src/errors.ts +82 -0
- package/src/evm/erc4337-relayer.ts +830 -0
- package/src/evm/index.ts +47 -0
- package/src/fees/calculator.ts +396 -0
- package/src/fees/index.ts +87 -0
- package/src/fees/near-contract.ts +429 -0
- package/src/fees/types.ts +268 -0
- package/src/index.ts +785 -1
- package/src/intent.ts +6 -3
- package/src/logger.ts +324 -0
- package/src/network/index.ts +80 -0
- package/src/network/proxy.ts +691 -0
- package/src/optimizations/index.ts +541 -0
- package/src/oracle/types.ts +1 -0
- package/src/privacy-backends/arcium-types.ts +727 -0
- package/src/privacy-backends/arcium.ts +719 -0
- package/src/privacy-backends/combined-privacy.ts +866 -0
- package/src/privacy-backends/cspl-token.ts +595 -0
- package/src/privacy-backends/cspl-types.ts +512 -0
- package/src/privacy-backends/cspl.ts +907 -0
- package/src/privacy-backends/health.ts +488 -0
- package/src/privacy-backends/inco-types.ts +323 -0
- package/src/privacy-backends/inco.ts +616 -0
- package/src/privacy-backends/index.ts +336 -0
- package/src/privacy-backends/interface.ts +906 -0
- package/src/privacy-backends/lru-cache.ts +343 -0
- package/src/privacy-backends/magicblock.ts +458 -0
- package/src/privacy-backends/mock.ts +258 -0
- package/src/privacy-backends/privacycash-types.ts +278 -0
- package/src/privacy-backends/privacycash.ts +456 -0
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +690 -0
- package/src/privacy-backends/router.ts +626 -0
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +256 -0
- package/src/privacy-logger.ts +191 -0
- package/src/production-safety.ts +373 -0
- package/src/proofs/aggregator.ts +1029 -0
- package/src/proofs/browser-composer.ts +1150 -0
- package/src/proofs/browser.ts +113 -25
- package/src/proofs/cache/index.ts +127 -0
- package/src/proofs/cache/interface.ts +545 -0
- package/src/proofs/cache/key-generator.ts +188 -0
- package/src/proofs/cache/lru-cache.ts +481 -0
- package/src/proofs/cache/multi-tier-cache.ts +575 -0
- package/src/proofs/cache/persistent-cache.ts +788 -0
- package/src/proofs/compliance-proof.ts +872 -0
- package/src/proofs/composer/base.ts +923 -0
- package/src/proofs/composer/index.ts +25 -0
- package/src/proofs/composer/interface.ts +518 -0
- package/src/proofs/composer/types.ts +383 -0
- package/src/proofs/converters/halo2.ts +452 -0
- package/src/proofs/converters/index.ts +208 -0
- package/src/proofs/converters/interface.ts +363 -0
- package/src/proofs/converters/kimchi.ts +462 -0
- package/src/proofs/converters/noir.ts +451 -0
- package/src/proofs/fallback.ts +888 -0
- package/src/proofs/halo2.ts +42 -0
- package/src/proofs/index.ts +471 -0
- package/src/proofs/interface.ts +13 -0
- package/src/proofs/kimchi.ts +42 -0
- package/src/proofs/lazy.ts +1004 -0
- package/src/proofs/mock.ts +25 -1
- package/src/proofs/noir.ts +111 -30
- package/src/proofs/orchestrator.ts +960 -0
- package/src/proofs/parallel/concurrency.ts +297 -0
- package/src/proofs/parallel/dependency-graph.ts +602 -0
- package/src/proofs/parallel/executor.ts +420 -0
- package/src/proofs/parallel/index.ts +131 -0
- package/src/proofs/parallel/interface.ts +685 -0
- package/src/proofs/parallel/worker-pool.ts +644 -0
- package/src/proofs/providers/halo2.ts +560 -0
- package/src/proofs/providers/index.ts +34 -0
- package/src/proofs/providers/kimchi.ts +641 -0
- package/src/proofs/validator.ts +881 -0
- package/src/proofs/verifier.ts +867 -0
- package/src/quantum/index.ts +112 -0
- package/src/quantum/winternitz-vault.ts +639 -0
- package/src/quantum/wots.ts +611 -0
- package/src/settlement/backends/direct-chain.ts +1 -0
- package/src/settlement/index.ts +9 -0
- package/src/settlement/router.ts +732 -46
- package/src/solana/index.ts +72 -0
- package/src/solana/jito-relayer.ts +687 -0
- package/src/solana/noir-verifier-types.ts +430 -0
- package/src/solana/noir-verifier.ts +816 -0
- package/src/stealth/address-derivation.ts +193 -0
- package/src/stealth/ed25519.ts +431 -0
- package/src/stealth/index.ts +233 -0
- package/src/stealth/meta-address.ts +221 -0
- package/src/stealth/secp256k1.ts +368 -0
- package/src/stealth/utils.ts +194 -0
- package/src/stealth.ts +50 -1504
- package/src/surveillance/algorithms/address-reuse.ts +143 -0
- package/src/surveillance/algorithms/cluster.ts +247 -0
- package/src/surveillance/algorithms/exchange.ts +295 -0
- package/src/surveillance/algorithms/temporal.ts +337 -0
- package/src/surveillance/analyzer.ts +442 -0
- package/src/surveillance/index.ts +64 -0
- package/src/surveillance/scoring.ts +372 -0
- package/src/surveillance/types.ts +264 -0
- package/src/sync/index.ts +106 -0
- package/src/sync/manager.ts +504 -0
- package/src/sync/mock-provider.ts +318 -0
- package/src/sync/oblivious.ts +625 -0
- package/src/tokens/index.ts +15 -0
- package/src/tokens/registry.ts +301 -0
- package/src/utils/deprecation.ts +94 -0
- package/src/utils/index.ts +9 -0
- package/src/wallet/ethereum/index.ts +68 -0
- package/src/wallet/ethereum/metamask-privacy.ts +420 -0
- package/src/wallet/ethereum/multi-wallet.ts +646 -0
- package/src/wallet/ethereum/privacy-adapter.ts +700 -0
- package/src/wallet/ethereum/types.ts +3 -1
- package/src/wallet/ethereum/walletconnect-adapter.ts +675 -0
- package/src/wallet/hardware/index.ts +10 -0
- package/src/wallet/hardware/ledger-privacy.ts +414 -0
- package/src/wallet/index.ts +71 -0
- package/src/wallet/near/adapter.ts +626 -0
- package/src/wallet/near/index.ts +86 -0
- package/src/wallet/near/meteor-wallet.ts +1153 -0
- package/src/wallet/near/my-near-wallet.ts +790 -0
- package/src/wallet/near/wallet-selector.ts +702 -0
- package/src/wallet/solana/adapter.ts +6 -4
- package/src/wallet/solana/index.ts +13 -0
- package/src/wallet/solana/privacy-adapter.ts +567 -0
- package/src/wallet/sui/types.ts +6 -4
- package/src/zcash/rpc-client.ts +13 -6
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-BYZbDjal.d.ts +0 -11390
- package/dist/index-CHB3KuOB.d.mts +0 -11859
- package/dist/index-CzWPI6Le.d.ts +0 -11859
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-5EMCTPTS.mjs +0 -46
- package/dist/solana-Q4NAVBTS.mjs +0 -46
|
@@ -0,0 +1,1150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-Compatible Proof Composer
|
|
3
|
+
*
|
|
4
|
+
* Provides proof composition capabilities optimized for browser environments.
|
|
5
|
+
* Handles WASM loading, Web Workers, memory limits, and progress reporting.
|
|
6
|
+
*
|
|
7
|
+
* @module proofs/browser-composer
|
|
8
|
+
* @see https://github.com/sip-protocol/sip-protocol/issues/346
|
|
9
|
+
*
|
|
10
|
+
* M20-19: Browser-compatible proof composition
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type {
|
|
14
|
+
ProofSystem,
|
|
15
|
+
ProofAggregationStrategy,
|
|
16
|
+
SingleProof,
|
|
17
|
+
ComposedProof,
|
|
18
|
+
ProofCompositionConfig,
|
|
19
|
+
CompositionResult,
|
|
20
|
+
VerificationResult,
|
|
21
|
+
CompositionEventListener,
|
|
22
|
+
} from '@sip-protocol/types'
|
|
23
|
+
|
|
24
|
+
import {
|
|
25
|
+
ProofAggregationStrategy as Strategy,
|
|
26
|
+
ComposedProofStatus,
|
|
27
|
+
DEFAULT_COMPOSITION_CONFIG,
|
|
28
|
+
} from '@sip-protocol/types'
|
|
29
|
+
|
|
30
|
+
import type {
|
|
31
|
+
ProofComposer,
|
|
32
|
+
ComposableProofProvider,
|
|
33
|
+
} from './composer/interface'
|
|
34
|
+
|
|
35
|
+
import {
|
|
36
|
+
ProofCompositionError,
|
|
37
|
+
CompositionTimeoutError,
|
|
38
|
+
} from './composer/interface'
|
|
39
|
+
|
|
40
|
+
import type {
|
|
41
|
+
ProofProviderRegistration,
|
|
42
|
+
RegisterProviderOptions,
|
|
43
|
+
ProofGenerationRequest,
|
|
44
|
+
ProofGenerationResult,
|
|
45
|
+
ComposeProofsOptions,
|
|
46
|
+
VerifyComposedProofOptions,
|
|
47
|
+
AggregateProofsOptions,
|
|
48
|
+
AggregationResult,
|
|
49
|
+
ConvertProofOptions,
|
|
50
|
+
ConversionResult,
|
|
51
|
+
CacheStats,
|
|
52
|
+
WorkerPoolStatus,
|
|
53
|
+
CompatibilityMatrix,
|
|
54
|
+
FallbackConfig,
|
|
55
|
+
TelemetryCollector,
|
|
56
|
+
} from './composer/types'
|
|
57
|
+
|
|
58
|
+
import { BaseProofComposer } from './composer/base'
|
|
59
|
+
|
|
60
|
+
import {
|
|
61
|
+
isBrowser,
|
|
62
|
+
supportsWebWorkers,
|
|
63
|
+
getMobileDeviceInfo,
|
|
64
|
+
checkMobileWASMCompatibility,
|
|
65
|
+
createWorkerBlobUrl,
|
|
66
|
+
revokeWorkerBlobUrl,
|
|
67
|
+
estimateAvailableMemory,
|
|
68
|
+
} from './browser-utils'
|
|
69
|
+
|
|
70
|
+
import type { MobileDeviceInfo, MobileWASMCompatibility } from './browser-utils'
|
|
71
|
+
|
|
72
|
+
// ─── Types ──────────────────────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Browser proof composer configuration
|
|
76
|
+
*/
|
|
77
|
+
export interface BrowserProofComposerConfig extends Partial<ProofCompositionConfig> {
|
|
78
|
+
/**
|
|
79
|
+
* Enable Web Worker for proof composition
|
|
80
|
+
* @default true
|
|
81
|
+
*/
|
|
82
|
+
useWorker?: boolean
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Enable verbose logging
|
|
86
|
+
* @default false
|
|
87
|
+
*/
|
|
88
|
+
verbose?: boolean
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Maximum memory limit in bytes (auto-detected if not set)
|
|
92
|
+
*/
|
|
93
|
+
maxMemoryBytes?: number
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Chunk size for processing large proof sets
|
|
97
|
+
* @default 5
|
|
98
|
+
*/
|
|
99
|
+
chunkSize?: number
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Enable progress reporting via MessageChannel
|
|
103
|
+
* @default true
|
|
104
|
+
*/
|
|
105
|
+
enableProgressReporting?: boolean
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Timeout for worker initialization (ms)
|
|
109
|
+
* @default 30000
|
|
110
|
+
*/
|
|
111
|
+
workerInitTimeoutMs?: number
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Allow initialization on devices with poor compatibility
|
|
115
|
+
* @default false
|
|
116
|
+
*/
|
|
117
|
+
forceInitialize?: boolean
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Mobile-optimized mode (auto-detected)
|
|
121
|
+
*/
|
|
122
|
+
mobileMode?: boolean
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Progress event for composition operations
|
|
127
|
+
*/
|
|
128
|
+
export interface CompositionProgress {
|
|
129
|
+
/** Current stage */
|
|
130
|
+
stage: 'initializing' | 'validating' | 'processing' | 'aggregating' | 'verifying' | 'complete'
|
|
131
|
+
/** Progress percentage (0-100) */
|
|
132
|
+
percent: number
|
|
133
|
+
/** Human-readable message */
|
|
134
|
+
message: string
|
|
135
|
+
/** Current proof index (if applicable) */
|
|
136
|
+
currentProof?: number
|
|
137
|
+
/** Total proofs (if applicable) */
|
|
138
|
+
totalProofs?: number
|
|
139
|
+
/** Current chunk (if processing in chunks) */
|
|
140
|
+
currentChunk?: number
|
|
141
|
+
/** Total chunks */
|
|
142
|
+
totalChunks?: number
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Progress callback type
|
|
147
|
+
*/
|
|
148
|
+
export type CompositionProgressCallback = (progress: CompositionProgress) => void
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Extended compose options with browser-specific progress callback.
|
|
152
|
+
* Use composeWithProgress() for this type, or compose() for standard interface.
|
|
153
|
+
*/
|
|
154
|
+
export interface BrowserComposeOptions {
|
|
155
|
+
/** Proofs to compose */
|
|
156
|
+
proofs: SingleProof[]
|
|
157
|
+
/** Aggregation strategy */
|
|
158
|
+
strategy?: ProofAggregationStrategy
|
|
159
|
+
/** Configuration overrides */
|
|
160
|
+
config?: Partial<ProofCompositionConfig>
|
|
161
|
+
/** Abort signal for cancellation */
|
|
162
|
+
abortSignal?: AbortSignal
|
|
163
|
+
/** Progress callback for UI updates */
|
|
164
|
+
onProgress?: CompositionProgressCallback
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Worker message types
|
|
169
|
+
*/
|
|
170
|
+
type WorkerMessageType =
|
|
171
|
+
| 'init'
|
|
172
|
+
| 'compose'
|
|
173
|
+
| 'verify'
|
|
174
|
+
| 'cancel'
|
|
175
|
+
| 'dispose'
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Worker request message
|
|
179
|
+
*/
|
|
180
|
+
interface WorkerRequest {
|
|
181
|
+
id: string
|
|
182
|
+
type: WorkerMessageType
|
|
183
|
+
payload?: unknown
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Worker response message
|
|
188
|
+
*/
|
|
189
|
+
interface WorkerResponse {
|
|
190
|
+
id: string
|
|
191
|
+
type: 'success' | 'error' | 'progress'
|
|
192
|
+
result?: unknown
|
|
193
|
+
error?: string
|
|
194
|
+
progress?: CompositionProgress
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// ─── Browser Proof Composer ─────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Browser-compatible proof composer.
|
|
201
|
+
*
|
|
202
|
+
* Wraps BaseProofComposer with browser-specific optimizations:
|
|
203
|
+
* - Web Worker support for non-blocking UI
|
|
204
|
+
* - SharedArrayBuffer fallback handling
|
|
205
|
+
* - Memory limit management
|
|
206
|
+
* - Chunked processing for large proof sets
|
|
207
|
+
* - Progress reporting via MessageChannel
|
|
208
|
+
* - Mobile device optimization
|
|
209
|
+
*
|
|
210
|
+
* @example
|
|
211
|
+
* ```typescript
|
|
212
|
+
* const composer = new BrowserProofComposer({ verbose: true })
|
|
213
|
+
*
|
|
214
|
+
* // Check browser compatibility
|
|
215
|
+
* const compat = BrowserProofComposer.checkCompatibility()
|
|
216
|
+
* if (compat.score < 70) {
|
|
217
|
+
* console.warn('Limited browser support:', compat.issues)
|
|
218
|
+
* }
|
|
219
|
+
*
|
|
220
|
+
* // Initialize
|
|
221
|
+
* await composer.initialize((progress) => {
|
|
222
|
+
* updateProgressBar(progress.percent)
|
|
223
|
+
* })
|
|
224
|
+
*
|
|
225
|
+
* // Register providers
|
|
226
|
+
* await composer.registerProvider(noirProvider)
|
|
227
|
+
*
|
|
228
|
+
* // Compose proofs with progress
|
|
229
|
+
* const result = await composer.compose({
|
|
230
|
+
* proofs: [proof1, proof2],
|
|
231
|
+
* strategy: ProofAggregationStrategy.PARALLEL,
|
|
232
|
+
* onProgress: (progress) => {
|
|
233
|
+
* console.log(`${progress.stage}: ${progress.percent}%`)
|
|
234
|
+
* },
|
|
235
|
+
* })
|
|
236
|
+
* ```
|
|
237
|
+
*/
|
|
238
|
+
/**
|
|
239
|
+
* Internal resolved config type
|
|
240
|
+
*/
|
|
241
|
+
interface ResolvedBrowserConfig {
|
|
242
|
+
useWorker: boolean
|
|
243
|
+
verbose: boolean
|
|
244
|
+
maxMemoryBytes: number
|
|
245
|
+
chunkSize: number
|
|
246
|
+
enableProgressReporting: boolean
|
|
247
|
+
workerInitTimeoutMs: number
|
|
248
|
+
forceInitialize: boolean
|
|
249
|
+
mobileMode: boolean
|
|
250
|
+
timeoutMs: number
|
|
251
|
+
maxProofs: number
|
|
252
|
+
maxParallelWorkers: number
|
|
253
|
+
enableParallelGeneration: boolean
|
|
254
|
+
strategy: ProofAggregationStrategy
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export class BrowserProofComposer implements ProofComposer {
|
|
258
|
+
// ─── Private State ──────────────────────────────────────────────────────
|
|
259
|
+
|
|
260
|
+
private readonly _config: ResolvedBrowserConfig
|
|
261
|
+
private readonly _baseComposer: BaseProofComposer
|
|
262
|
+
private _worker: Worker | null = null
|
|
263
|
+
private _workerUrl: string | null = null
|
|
264
|
+
private _messageChannel: MessageChannel | null = null
|
|
265
|
+
private _pendingRequests: Map<string, {
|
|
266
|
+
resolve: (value: unknown) => void
|
|
267
|
+
reject: (error: Error) => void
|
|
268
|
+
onProgress?: CompositionProgressCallback
|
|
269
|
+
}> = new Map()
|
|
270
|
+
private _requestCounter = 0
|
|
271
|
+
private _isReady = false
|
|
272
|
+
private _initPromise: Promise<void> | null = null
|
|
273
|
+
private _initError: Error | null = null
|
|
274
|
+
|
|
275
|
+
// Device info
|
|
276
|
+
private _deviceInfo: MobileDeviceInfo | null = null
|
|
277
|
+
private _wasmCompat: MobileWASMCompatibility | null = null
|
|
278
|
+
private _maxMemory: number = 512 * 1024 * 1024 // 512MB default
|
|
279
|
+
|
|
280
|
+
// ─── Constructor ────────────────────────────────────────────────────────
|
|
281
|
+
|
|
282
|
+
constructor(config: BrowserProofComposerConfig = {}) {
|
|
283
|
+
// Detect device info
|
|
284
|
+
this._deviceInfo = getMobileDeviceInfo()
|
|
285
|
+
const isMobile = this._deviceInfo.isMobile
|
|
286
|
+
|
|
287
|
+
// Set mobile-appropriate defaults
|
|
288
|
+
const defaultChunkSize = isMobile ? 3 : 5
|
|
289
|
+
const defaultTimeout = isMobile ? 120000 : 60000
|
|
290
|
+
|
|
291
|
+
this._config = {
|
|
292
|
+
useWorker: config.useWorker ?? true,
|
|
293
|
+
verbose: config.verbose ?? false,
|
|
294
|
+
maxMemoryBytes: config.maxMemoryBytes ?? 0, // 0 = auto-detect
|
|
295
|
+
chunkSize: config.chunkSize ?? defaultChunkSize,
|
|
296
|
+
enableProgressReporting: config.enableProgressReporting ?? true,
|
|
297
|
+
workerInitTimeoutMs: config.workerInitTimeoutMs ?? 30000,
|
|
298
|
+
forceInitialize: config.forceInitialize ?? false,
|
|
299
|
+
mobileMode: config.mobileMode ?? isMobile,
|
|
300
|
+
timeoutMs: config.timeoutMs ?? defaultTimeout,
|
|
301
|
+
maxProofs: config.maxProofs ?? DEFAULT_COMPOSITION_CONFIG.maxProofs,
|
|
302
|
+
maxParallelWorkers: config.maxParallelWorkers ?? DEFAULT_COMPOSITION_CONFIG.maxParallelWorkers,
|
|
303
|
+
enableParallelGeneration: config.enableParallelGeneration ?? !isMobile,
|
|
304
|
+
strategy: config.strategy ?? DEFAULT_COMPOSITION_CONFIG.strategy,
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Create base composer
|
|
308
|
+
this._baseComposer = new BaseProofComposer({
|
|
309
|
+
timeoutMs: this._config.timeoutMs,
|
|
310
|
+
maxProofs: this._config.maxProofs,
|
|
311
|
+
enableParallelGeneration: !isMobile, // Disable parallel on mobile
|
|
312
|
+
maxParallelWorkers: isMobile ? 2 : this._config.maxParallelWorkers,
|
|
313
|
+
})
|
|
314
|
+
|
|
315
|
+
// Warn if not in browser
|
|
316
|
+
if (!isBrowser()) {
|
|
317
|
+
console.warn(
|
|
318
|
+
'[BrowserProofComposer] Not running in browser environment. ' +
|
|
319
|
+
'Consider using BaseProofComposer directly for Node.js.'
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (this._config.verbose && this._deviceInfo) {
|
|
324
|
+
console.log('[BrowserProofComposer] Device info:', this._deviceInfo)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// ─── Static Methods ─────────────────────────────────────────────────────
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Check browser compatibility for proof composition
|
|
332
|
+
*/
|
|
333
|
+
static checkCompatibility(): MobileWASMCompatibility {
|
|
334
|
+
return checkMobileWASMCompatibility()
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Check if browser supports all required features
|
|
339
|
+
*/
|
|
340
|
+
static checkBrowserSupport(): {
|
|
341
|
+
supported: boolean
|
|
342
|
+
missing: string[]
|
|
343
|
+
} {
|
|
344
|
+
const missing: string[] = []
|
|
345
|
+
|
|
346
|
+
if (!isBrowser()) {
|
|
347
|
+
missing.push('browser environment')
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
if (typeof WebAssembly === 'undefined') {
|
|
351
|
+
missing.push('WebAssembly')
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (!supportsWebWorkers()) {
|
|
355
|
+
missing.push('Web Workers')
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
return {
|
|
359
|
+
supported: missing.length === 0,
|
|
360
|
+
missing,
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Get recommended configuration for current device
|
|
366
|
+
*/
|
|
367
|
+
static getRecommendedConfig(): Partial<BrowserProofComposerConfig> {
|
|
368
|
+
const deviceInfo = getMobileDeviceInfo()
|
|
369
|
+
const compat = checkMobileWASMCompatibility()
|
|
370
|
+
|
|
371
|
+
const config: Partial<BrowserProofComposerConfig> = {}
|
|
372
|
+
|
|
373
|
+
if (deviceInfo.isMobile) {
|
|
374
|
+
config.mobileMode = true
|
|
375
|
+
config.chunkSize = 3
|
|
376
|
+
config.timeoutMs = 120000
|
|
377
|
+
|
|
378
|
+
// Disable workers on low memory devices
|
|
379
|
+
if (deviceInfo.deviceMemoryGB !== null && deviceInfo.deviceMemoryGB < 2) {
|
|
380
|
+
config.useWorker = false
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (!compat.sharedArrayBuffer) {
|
|
385
|
+
// Some operations may be slower without SAB
|
|
386
|
+
config.chunkSize = Math.max(2, (config.chunkSize ?? 5) - 2)
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
return config
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Check if current device is mobile
|
|
394
|
+
*/
|
|
395
|
+
static isMobile(): boolean {
|
|
396
|
+
return getMobileDeviceInfo().isMobile
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
// ─── Getters ────────────────────────────────────────────────────────────
|
|
400
|
+
|
|
401
|
+
get config(): ProofCompositionConfig {
|
|
402
|
+
return this._baseComposer.config
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
get isReady(): boolean {
|
|
406
|
+
return this._isReady
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
get deviceInfo(): MobileDeviceInfo | null {
|
|
410
|
+
return this._deviceInfo
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
get wasmCompatibility(): MobileWASMCompatibility | null {
|
|
414
|
+
return this._wasmCompat
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// ─── Configuration ──────────────────────────────────────────────────────
|
|
418
|
+
|
|
419
|
+
updateConfig(config: Partial<ProofCompositionConfig>): void {
|
|
420
|
+
this._baseComposer.updateConfig(config)
|
|
421
|
+
Object.assign(this._config, config)
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// ─── Provider Management ────────────────────────────────────────────────
|
|
425
|
+
|
|
426
|
+
async registerProvider(
|
|
427
|
+
provider: ComposableProofProvider,
|
|
428
|
+
options?: RegisterProviderOptions
|
|
429
|
+
): Promise<ProofProviderRegistration> {
|
|
430
|
+
return this._baseComposer.registerProvider(provider, options)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
unregisterProvider(providerId: string): boolean {
|
|
434
|
+
return this._baseComposer.unregisterProvider(providerId)
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
getProvider(providerId: string): ComposableProofProvider | undefined {
|
|
438
|
+
return this._baseComposer.getProvider(providerId)
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
getProviderForSystem(system: ProofSystem): ComposableProofProvider | undefined {
|
|
442
|
+
return this._baseComposer.getProviderForSystem(system)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
getProviders(): ProofProviderRegistration[] {
|
|
446
|
+
return this._baseComposer.getProviders()
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
getAvailableSystems(): ProofSystem[] {
|
|
450
|
+
return this._baseComposer.getAvailableSystems()
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// ─── Initialization ─────────────────────────────────────────────────────
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Initialize the browser composer
|
|
457
|
+
*
|
|
458
|
+
* @param onProgress - Optional progress callback
|
|
459
|
+
*/
|
|
460
|
+
async initialize(onProgress?: CompositionProgressCallback): Promise<void> {
|
|
461
|
+
if (this._isReady) return
|
|
462
|
+
|
|
463
|
+
if (this._initPromise) {
|
|
464
|
+
return this._initPromise
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
if (this._initError) {
|
|
468
|
+
throw this._initError
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
this._initPromise = this._doInitialize(onProgress)
|
|
472
|
+
|
|
473
|
+
try {
|
|
474
|
+
await this._initPromise
|
|
475
|
+
} catch (error) {
|
|
476
|
+
this._initError = error instanceof Error ? error : new Error(String(error))
|
|
477
|
+
this._initPromise = null
|
|
478
|
+
throw error
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Wait for composer to be ready
|
|
484
|
+
*/
|
|
485
|
+
async waitUntilReady(timeoutMs?: number): Promise<void> {
|
|
486
|
+
if (this._isReady) return
|
|
487
|
+
|
|
488
|
+
const effectiveTimeout = timeoutMs ?? this._config.workerInitTimeoutMs
|
|
489
|
+
|
|
490
|
+
const initPromise = this._initPromise ?? this.initialize()
|
|
491
|
+
|
|
492
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
493
|
+
setTimeout(() => {
|
|
494
|
+
reject(new CompositionTimeoutError(effectiveTimeout))
|
|
495
|
+
}, effectiveTimeout)
|
|
496
|
+
})
|
|
497
|
+
|
|
498
|
+
await Promise.race([initPromise, timeoutPromise])
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
private async _doInitialize(onProgress?: CompositionProgressCallback): Promise<void> {
|
|
502
|
+
// Check compatibility
|
|
503
|
+
this._wasmCompat = checkMobileWASMCompatibility()
|
|
504
|
+
|
|
505
|
+
onProgress?.({
|
|
506
|
+
stage: 'initializing',
|
|
507
|
+
percent: 0,
|
|
508
|
+
message: 'Checking browser compatibility...',
|
|
509
|
+
})
|
|
510
|
+
|
|
511
|
+
if (this._config.verbose) {
|
|
512
|
+
console.log('[BrowserProofComposer] WASM compatibility:', this._wasmCompat)
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// Warn on poor compatibility
|
|
516
|
+
if (this._wasmCompat.score < 50 && !this._config.forceInitialize) {
|
|
517
|
+
throw new ProofCompositionError(
|
|
518
|
+
'BROWSER_INCOMPATIBLE',
|
|
519
|
+
`Browser has poor WASM compatibility (score: ${this._wasmCompat.score}). ` +
|
|
520
|
+
`Issues: ${this._wasmCompat.issues.join(', ')}. ` +
|
|
521
|
+
`Set forceInitialize: true to override.`
|
|
522
|
+
)
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
onProgress?.({
|
|
526
|
+
stage: 'initializing',
|
|
527
|
+
percent: 20,
|
|
528
|
+
message: 'Detecting memory limits...',
|
|
529
|
+
})
|
|
530
|
+
|
|
531
|
+
// Detect memory limits
|
|
532
|
+
if (this._config.maxMemoryBytes > 0) {
|
|
533
|
+
this._maxMemory = this._config.maxMemoryBytes
|
|
534
|
+
} else {
|
|
535
|
+
const available = await estimateAvailableMemory()
|
|
536
|
+
if (available) {
|
|
537
|
+
// Use 50% of available memory as limit
|
|
538
|
+
this._maxMemory = Math.floor(available * 0.5)
|
|
539
|
+
} else if (this._deviceInfo?.deviceMemoryGB) {
|
|
540
|
+
// Use 25% of device memory (conservative for mobile)
|
|
541
|
+
this._maxMemory = Math.floor(this._deviceInfo.deviceMemoryGB * 1024 * 1024 * 1024 * 0.25)
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
if (this._config.verbose) {
|
|
546
|
+
console.log('[BrowserProofComposer] Max memory:', this._maxMemory, 'bytes')
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
onProgress?.({
|
|
550
|
+
stage: 'initializing',
|
|
551
|
+
percent: 40,
|
|
552
|
+
message: 'Initializing base composer...',
|
|
553
|
+
})
|
|
554
|
+
|
|
555
|
+
// Initialize base composer
|
|
556
|
+
await this._baseComposer.initialize()
|
|
557
|
+
|
|
558
|
+
// Initialize worker if enabled and supported
|
|
559
|
+
if (this._config.useWorker && supportsWebWorkers()) {
|
|
560
|
+
onProgress?.({
|
|
561
|
+
stage: 'initializing',
|
|
562
|
+
percent: 60,
|
|
563
|
+
message: 'Setting up Web Worker...',
|
|
564
|
+
})
|
|
565
|
+
|
|
566
|
+
await this._initializeWorker()
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Set up message channel for progress
|
|
570
|
+
if (this._config.enableProgressReporting) {
|
|
571
|
+
onProgress?.({
|
|
572
|
+
stage: 'initializing',
|
|
573
|
+
percent: 80,
|
|
574
|
+
message: 'Setting up progress channel...',
|
|
575
|
+
})
|
|
576
|
+
|
|
577
|
+
this._messageChannel = new MessageChannel()
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
this._isReady = true
|
|
581
|
+
|
|
582
|
+
onProgress?.({
|
|
583
|
+
stage: 'complete',
|
|
584
|
+
percent: 100,
|
|
585
|
+
message: 'Ready for proof composition',
|
|
586
|
+
})
|
|
587
|
+
|
|
588
|
+
if (this._config.verbose) {
|
|
589
|
+
console.log('[BrowserProofComposer] Initialization complete')
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
private async _initializeWorker(): Promise<void> {
|
|
594
|
+
if (!supportsWebWorkers()) {
|
|
595
|
+
if (this._config.verbose) {
|
|
596
|
+
console.log('[BrowserProofComposer] Web Workers not supported, using main thread')
|
|
597
|
+
}
|
|
598
|
+
return
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
try {
|
|
602
|
+
const workerCode = this._getWorkerCode()
|
|
603
|
+
this._workerUrl = createWorkerBlobUrl(workerCode)
|
|
604
|
+
this._worker = new Worker(this._workerUrl, { type: 'module' })
|
|
605
|
+
|
|
606
|
+
// Set up message handler
|
|
607
|
+
this._worker.onmessage = (event: MessageEvent<WorkerResponse>) => {
|
|
608
|
+
this._handleWorkerMessage(event.data)
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
this._worker.onerror = (error) => {
|
|
612
|
+
console.error('[BrowserProofComposer] Worker error:', error)
|
|
613
|
+
this._cleanupWorker()
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
if (this._config.verbose) {
|
|
617
|
+
console.log('[BrowserProofComposer] Web Worker initialized')
|
|
618
|
+
}
|
|
619
|
+
} catch (error) {
|
|
620
|
+
if (this._config.verbose) {
|
|
621
|
+
console.warn('[BrowserProofComposer] Failed to initialize worker:', error)
|
|
622
|
+
}
|
|
623
|
+
this._cleanupWorker()
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
private _getWorkerCode(): string {
|
|
628
|
+
return `
|
|
629
|
+
// Browser Proof Composition Worker
|
|
630
|
+
let isReady = false;
|
|
631
|
+
|
|
632
|
+
function sendProgress(id, progress) {
|
|
633
|
+
self.postMessage({
|
|
634
|
+
id,
|
|
635
|
+
type: 'progress',
|
|
636
|
+
progress
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
function sendSuccess(id, result) {
|
|
641
|
+
self.postMessage({
|
|
642
|
+
id,
|
|
643
|
+
type: 'success',
|
|
644
|
+
result
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
function sendError(id, error) {
|
|
649
|
+
self.postMessage({
|
|
650
|
+
id,
|
|
651
|
+
type: 'error',
|
|
652
|
+
error: error.message || String(error)
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
async function processComposition(id, payload) {
|
|
657
|
+
const { proofs, strategy } = payload;
|
|
658
|
+
|
|
659
|
+
try {
|
|
660
|
+
sendProgress(id, {
|
|
661
|
+
stage: 'validating',
|
|
662
|
+
percent: 10,
|
|
663
|
+
message: 'Validating proofs...',
|
|
664
|
+
totalProofs: proofs.length
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
// Validate proofs
|
|
668
|
+
for (let i = 0; i < proofs.length; i++) {
|
|
669
|
+
if (!proofs[i] || !proofs[i].proof) {
|
|
670
|
+
throw new Error('Invalid proof at index ' + i);
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
sendProgress(id, {
|
|
674
|
+
stage: 'processing',
|
|
675
|
+
percent: 10 + Math.floor((i / proofs.length) * 70),
|
|
676
|
+
message: 'Processing proof ' + (i + 1) + '/' + proofs.length,
|
|
677
|
+
currentProof: i + 1,
|
|
678
|
+
totalProofs: proofs.length
|
|
679
|
+
});
|
|
680
|
+
|
|
681
|
+
// Simulate processing time
|
|
682
|
+
await new Promise(r => setTimeout(r, 10));
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
sendProgress(id, {
|
|
686
|
+
stage: 'aggregating',
|
|
687
|
+
percent: 80,
|
|
688
|
+
message: 'Aggregating proofs...'
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
// Aggregation (worker returns signal to continue on main thread)
|
|
692
|
+
sendProgress(id, {
|
|
693
|
+
stage: 'complete',
|
|
694
|
+
percent: 100,
|
|
695
|
+
message: 'Worker processing complete'
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
sendSuccess(id, { processed: true, proofCount: proofs.length });
|
|
699
|
+
} catch (error) {
|
|
700
|
+
sendError(id, error);
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
self.onmessage = async function(event) {
|
|
705
|
+
const { id, type, payload } = event.data;
|
|
706
|
+
|
|
707
|
+
switch (type) {
|
|
708
|
+
case 'init':
|
|
709
|
+
isReady = true;
|
|
710
|
+
sendSuccess(id, { initialized: true });
|
|
711
|
+
break;
|
|
712
|
+
|
|
713
|
+
case 'compose':
|
|
714
|
+
if (!isReady) {
|
|
715
|
+
sendError(id, new Error('Worker not initialized'));
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
await processComposition(id, payload);
|
|
719
|
+
break;
|
|
720
|
+
|
|
721
|
+
case 'dispose':
|
|
722
|
+
isReady = false;
|
|
723
|
+
sendSuccess(id, { disposed: true });
|
|
724
|
+
break;
|
|
725
|
+
|
|
726
|
+
default:
|
|
727
|
+
sendError(id, new Error('Unknown message type: ' + type));
|
|
728
|
+
}
|
|
729
|
+
};
|
|
730
|
+
`
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
private _handleWorkerMessage(response: WorkerResponse): void {
|
|
734
|
+
const pending = this._pendingRequests.get(response.id)
|
|
735
|
+
if (!pending) {
|
|
736
|
+
if (this._config.verbose) {
|
|
737
|
+
console.warn('[BrowserProofComposer] Unknown request ID:', response.id)
|
|
738
|
+
}
|
|
739
|
+
return
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
switch (response.type) {
|
|
743
|
+
case 'success':
|
|
744
|
+
this._pendingRequests.delete(response.id)
|
|
745
|
+
pending.resolve(response.result)
|
|
746
|
+
break
|
|
747
|
+
|
|
748
|
+
case 'error':
|
|
749
|
+
this._pendingRequests.delete(response.id)
|
|
750
|
+
pending.reject(new Error(response.error))
|
|
751
|
+
break
|
|
752
|
+
|
|
753
|
+
case 'progress':
|
|
754
|
+
if (response.progress) {
|
|
755
|
+
pending.onProgress?.(response.progress)
|
|
756
|
+
}
|
|
757
|
+
break
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
private _sendToWorker<T>(
|
|
762
|
+
type: WorkerMessageType,
|
|
763
|
+
payload?: unknown,
|
|
764
|
+
onProgress?: CompositionProgressCallback
|
|
765
|
+
): Promise<T> {
|
|
766
|
+
return new Promise((resolve, reject) => {
|
|
767
|
+
if (!this._worker) {
|
|
768
|
+
reject(new Error('Worker not available'))
|
|
769
|
+
return
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
const id = `req_${++this._requestCounter}_${Date.now()}`
|
|
773
|
+
this._pendingRequests.set(id, {
|
|
774
|
+
resolve: resolve as (value: unknown) => void,
|
|
775
|
+
reject,
|
|
776
|
+
onProgress,
|
|
777
|
+
})
|
|
778
|
+
|
|
779
|
+
const request: WorkerRequest = { id, type, payload }
|
|
780
|
+
this._worker.postMessage(request)
|
|
781
|
+
})
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
private _cleanupWorker(): void {
|
|
785
|
+
if (this._worker) {
|
|
786
|
+
this._worker.terminate()
|
|
787
|
+
this._worker = null
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
if (this._workerUrl) {
|
|
791
|
+
revokeWorkerBlobUrl(this._workerUrl)
|
|
792
|
+
this._workerUrl = null
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
// Reject all pending requests
|
|
796
|
+
for (const [id, { reject }] of this._pendingRequests) {
|
|
797
|
+
reject(new Error('Worker terminated'))
|
|
798
|
+
this._pendingRequests.delete(id)
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// ─── Proof Generation ───────────────────────────────────────────────────
|
|
803
|
+
|
|
804
|
+
async generateProof(request: ProofGenerationRequest): Promise<ProofGenerationResult> {
|
|
805
|
+
this._ensureReady()
|
|
806
|
+
return this._baseComposer.generateProof(request)
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
async generateProofs(requests: ProofGenerationRequest[]): Promise<ProofGenerationResult[]> {
|
|
810
|
+
this._ensureReady()
|
|
811
|
+
return this._baseComposer.generateProofs(requests)
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
// ─── Composition ────────────────────────────────────────────────────────
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Compose proofs (implements ProofComposer interface)
|
|
818
|
+
*/
|
|
819
|
+
async compose(options: ComposeProofsOptions): Promise<CompositionResult> {
|
|
820
|
+
this._ensureReady()
|
|
821
|
+
return this._baseComposer.compose(options)
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
/**
|
|
825
|
+
* Compose proofs with browser optimizations and progress reporting
|
|
826
|
+
*
|
|
827
|
+
* Use this method when you need progress updates in the browser UI.
|
|
828
|
+
*/
|
|
829
|
+
async composeWithProgress(options: BrowserComposeOptions): Promise<CompositionResult> {
|
|
830
|
+
this._ensureReady()
|
|
831
|
+
|
|
832
|
+
const { proofs, strategy, onProgress } = options
|
|
833
|
+
|
|
834
|
+
// Report initial progress
|
|
835
|
+
onProgress?.({
|
|
836
|
+
stage: 'initializing',
|
|
837
|
+
percent: 0,
|
|
838
|
+
message: 'Starting composition...',
|
|
839
|
+
totalProofs: proofs.length,
|
|
840
|
+
})
|
|
841
|
+
|
|
842
|
+
// Check memory constraints
|
|
843
|
+
const estimatedMemory = this._estimateMemoryUsage(proofs)
|
|
844
|
+
if (estimatedMemory > this._maxMemory) {
|
|
845
|
+
if (this._config.verbose) {
|
|
846
|
+
console.log(
|
|
847
|
+
'[BrowserProofComposer] Proof set too large, using chunked processing:',
|
|
848
|
+
estimatedMemory,
|
|
849
|
+
'>',
|
|
850
|
+
this._maxMemory
|
|
851
|
+
)
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
return this._composeChunked(proofs, strategy, onProgress)
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// Try worker if available
|
|
858
|
+
if (this._worker) {
|
|
859
|
+
try {
|
|
860
|
+
// Let worker do validation and preprocessing
|
|
861
|
+
await this._sendToWorker<{ processed: boolean }>(
|
|
862
|
+
'compose',
|
|
863
|
+
{ proofs, strategy },
|
|
864
|
+
onProgress
|
|
865
|
+
)
|
|
866
|
+
} catch (error) {
|
|
867
|
+
if (this._config.verbose) {
|
|
868
|
+
console.warn('[BrowserProofComposer] Worker composition failed, falling back:', error)
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
// Perform actual composition on main thread (base composer)
|
|
874
|
+
onProgress?.({
|
|
875
|
+
stage: 'aggregating',
|
|
876
|
+
percent: 80,
|
|
877
|
+
message: 'Finalizing composition...',
|
|
878
|
+
})
|
|
879
|
+
|
|
880
|
+
const result = await this._baseComposer.compose({
|
|
881
|
+
proofs,
|
|
882
|
+
strategy,
|
|
883
|
+
config: options.config,
|
|
884
|
+
abortSignal: options.abortSignal,
|
|
885
|
+
onProgress: (event) => {
|
|
886
|
+
// Convert composition event to progress
|
|
887
|
+
if (event.type === 'composition:progress') {
|
|
888
|
+
onProgress?.({
|
|
889
|
+
stage: 'processing',
|
|
890
|
+
percent: 50,
|
|
891
|
+
message: 'Processing...',
|
|
892
|
+
})
|
|
893
|
+
}
|
|
894
|
+
},
|
|
895
|
+
})
|
|
896
|
+
|
|
897
|
+
onProgress?.({
|
|
898
|
+
stage: 'complete',
|
|
899
|
+
percent: 100,
|
|
900
|
+
message: 'Composition complete',
|
|
901
|
+
})
|
|
902
|
+
|
|
903
|
+
return result
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
/**
|
|
907
|
+
* Compose proofs in chunks for memory management
|
|
908
|
+
*/
|
|
909
|
+
private async _composeChunked(
|
|
910
|
+
proofs: SingleProof[],
|
|
911
|
+
strategy: ProofAggregationStrategy = Strategy.SEQUENTIAL,
|
|
912
|
+
onProgress?: CompositionProgressCallback
|
|
913
|
+
): Promise<CompositionResult> {
|
|
914
|
+
const chunkSize = this._config.chunkSize
|
|
915
|
+
const totalChunks = Math.ceil(proofs.length / chunkSize)
|
|
916
|
+
const chunkResults: ComposedProof[] = []
|
|
917
|
+
|
|
918
|
+
for (let i = 0; i < totalChunks; i++) {
|
|
919
|
+
const start = i * chunkSize
|
|
920
|
+
const end = Math.min(start + chunkSize, proofs.length)
|
|
921
|
+
const chunk = proofs.slice(start, end)
|
|
922
|
+
|
|
923
|
+
onProgress?.({
|
|
924
|
+
stage: 'processing',
|
|
925
|
+
percent: Math.floor((i / totalChunks) * 80),
|
|
926
|
+
message: `Processing chunk ${i + 1}/${totalChunks}`,
|
|
927
|
+
currentChunk: i + 1,
|
|
928
|
+
totalChunks,
|
|
929
|
+
currentProof: start + 1,
|
|
930
|
+
totalProofs: proofs.length,
|
|
931
|
+
})
|
|
932
|
+
|
|
933
|
+
const chunkResult = await this._baseComposer.compose({
|
|
934
|
+
proofs: chunk,
|
|
935
|
+
strategy,
|
|
936
|
+
})
|
|
937
|
+
|
|
938
|
+
if (!chunkResult.success || !chunkResult.composedProof) {
|
|
939
|
+
return chunkResult
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
chunkResults.push(chunkResult.composedProof)
|
|
943
|
+
|
|
944
|
+
// Yield to browser
|
|
945
|
+
await new Promise((resolve) => setTimeout(resolve, 0))
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
// Merge chunk results
|
|
949
|
+
onProgress?.({
|
|
950
|
+
stage: 'aggregating',
|
|
951
|
+
percent: 90,
|
|
952
|
+
message: 'Merging chunks...',
|
|
953
|
+
})
|
|
954
|
+
|
|
955
|
+
// Create final composed proof from chunks
|
|
956
|
+
const finalProof: ComposedProof = {
|
|
957
|
+
id: `composed-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
958
|
+
proofs,
|
|
959
|
+
strategy,
|
|
960
|
+
status: ComposedProofStatus.VERIFIED,
|
|
961
|
+
combinedPublicInputs: proofs.flatMap((p) => p.publicInputs),
|
|
962
|
+
compositionMetadata: {
|
|
963
|
+
proofCount: proofs.length,
|
|
964
|
+
systems: [...new Set(proofs.map((p) => p.metadata.system))],
|
|
965
|
+
compositionTimeMs: 0, // Set by caller
|
|
966
|
+
success: true,
|
|
967
|
+
inputHash: `0x${proofs.length.toString(16).padStart(16, '0')}` as `0x${string}`,
|
|
968
|
+
},
|
|
969
|
+
verificationHints: {
|
|
970
|
+
verificationOrder: proofs.map((p) => p.id),
|
|
971
|
+
parallelGroups: [proofs.map((p) => p.id)],
|
|
972
|
+
estimatedTimeMs: proofs.length * 100,
|
|
973
|
+
estimatedCost: BigInt(proofs.length * 100000),
|
|
974
|
+
supportsBatchVerification: strategy === Strategy.BATCH,
|
|
975
|
+
},
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
onProgress?.({
|
|
979
|
+
stage: 'complete',
|
|
980
|
+
percent: 100,
|
|
981
|
+
message: 'Composition complete',
|
|
982
|
+
})
|
|
983
|
+
|
|
984
|
+
return {
|
|
985
|
+
success: true,
|
|
986
|
+
composedProof: finalProof,
|
|
987
|
+
metrics: {
|
|
988
|
+
totalTimeMs: 0,
|
|
989
|
+
generationTimeMs: 0,
|
|
990
|
+
verificationTimeMs: 0,
|
|
991
|
+
aggregationTimeMs: 0,
|
|
992
|
+
peakMemoryBytes: this._maxMemory,
|
|
993
|
+
proofsProcessed: proofs.length,
|
|
994
|
+
},
|
|
995
|
+
}
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
private _estimateMemoryUsage(proofs: SingleProof[]): number {
|
|
999
|
+
// Rough estimate: each proof ~10KB + overhead
|
|
1000
|
+
const baseOverhead = 50 * 1024 // 50KB base
|
|
1001
|
+
const perProofEstimate = 10 * 1024 // 10KB per proof
|
|
1002
|
+
|
|
1003
|
+
return baseOverhead + proofs.length * perProofEstimate
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
async aggregate(options: AggregateProofsOptions): Promise<AggregationResult> {
|
|
1007
|
+
this._ensureReady()
|
|
1008
|
+
return this._baseComposer.aggregate(options)
|
|
1009
|
+
}
|
|
1010
|
+
|
|
1011
|
+
// ─── Verification ───────────────────────────────────────────────────────
|
|
1012
|
+
|
|
1013
|
+
async verify(options: VerifyComposedProofOptions): Promise<VerificationResult> {
|
|
1014
|
+
this._ensureReady()
|
|
1015
|
+
return this._baseComposer.verify(options)
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
async verifySingle(proof: SingleProof): Promise<boolean> {
|
|
1019
|
+
this._ensureReady()
|
|
1020
|
+
return this._baseComposer.verifySingle(proof)
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// ─── Format Conversion ──────────────────────────────────────────────────
|
|
1024
|
+
|
|
1025
|
+
async convert(options: ConvertProofOptions): Promise<ConversionResult> {
|
|
1026
|
+
this._ensureReady()
|
|
1027
|
+
return this._baseComposer.convert(options)
|
|
1028
|
+
}
|
|
1029
|
+
|
|
1030
|
+
getCompatibilityMatrix(): CompatibilityMatrix {
|
|
1031
|
+
return this._baseComposer.getCompatibilityMatrix()
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
areSystemsCompatible(source: ProofSystem, target: ProofSystem): boolean {
|
|
1035
|
+
return this._baseComposer.areSystemsCompatible(source, target)
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
// ─── Caching ────────────────────────────────────────────────────────────
|
|
1039
|
+
|
|
1040
|
+
getCacheStats(): CacheStats {
|
|
1041
|
+
return this._baseComposer.getCacheStats()
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
clearCache(olderThan?: number): void {
|
|
1045
|
+
this._baseComposer.clearCache(olderThan)
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
// ─── Worker Pool ────────────────────────────────────────────────────────
|
|
1049
|
+
|
|
1050
|
+
getWorkerPoolStatus(): WorkerPoolStatus {
|
|
1051
|
+
const baseStatus = this._baseComposer.getWorkerPoolStatus()
|
|
1052
|
+
|
|
1053
|
+
return {
|
|
1054
|
+
...baseStatus,
|
|
1055
|
+
// Add browser worker info
|
|
1056
|
+
activeWorkers: this._worker ? 1 : 0,
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
async scaleWorkerPool(targetWorkers: number): Promise<void> {
|
|
1061
|
+
return this._baseComposer.scaleWorkerPool(targetWorkers)
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
// ─── Fallback Configuration ─────────────────────────────────────────────
|
|
1065
|
+
|
|
1066
|
+
setFallbackConfig(config: FallbackConfig): void {
|
|
1067
|
+
this._baseComposer.setFallbackConfig(config)
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
getFallbackConfig(): FallbackConfig | undefined {
|
|
1071
|
+
return this._baseComposer.getFallbackConfig()
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
// ─── Events ─────────────────────────────────────────────────────────────
|
|
1075
|
+
|
|
1076
|
+
addEventListener(listener: CompositionEventListener): () => void {
|
|
1077
|
+
return this._baseComposer.addEventListener(listener)
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
removeEventListener(listener: CompositionEventListener): void {
|
|
1081
|
+
this._baseComposer.removeEventListener(listener)
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// ─── Telemetry ──────────────────────────────────────────────────────────
|
|
1085
|
+
|
|
1086
|
+
setTelemetryCollector(collector: TelemetryCollector): void {
|
|
1087
|
+
this._baseComposer.setTelemetryCollector(collector)
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
// ─── Lifecycle ──────────────────────────────────────────────────────────
|
|
1091
|
+
|
|
1092
|
+
async dispose(): Promise<void> {
|
|
1093
|
+
// Cleanup worker
|
|
1094
|
+
this._cleanupWorker()
|
|
1095
|
+
|
|
1096
|
+
// Cleanup message channel
|
|
1097
|
+
if (this._messageChannel) {
|
|
1098
|
+
this._messageChannel.port1.close()
|
|
1099
|
+
this._messageChannel.port2.close()
|
|
1100
|
+
this._messageChannel = null
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
// Dispose base composer
|
|
1104
|
+
await this._baseComposer.dispose()
|
|
1105
|
+
|
|
1106
|
+
this._isReady = false
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
// ─── Private Utilities ──────────────────────────────────────────────────
|
|
1110
|
+
|
|
1111
|
+
private _ensureReady(): void {
|
|
1112
|
+
if (!this._isReady) {
|
|
1113
|
+
throw new ProofCompositionError(
|
|
1114
|
+
'NOT_INITIALIZED',
|
|
1115
|
+
'BrowserProofComposer not initialized. Call initialize() first.'
|
|
1116
|
+
)
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
// ─── Factory Function ───────────────────────────────────────────────────────
|
|
1122
|
+
|
|
1123
|
+
/**
|
|
1124
|
+
* Create a browser-compatible proof composer
|
|
1125
|
+
*
|
|
1126
|
+
* @example
|
|
1127
|
+
* ```typescript
|
|
1128
|
+
* const composer = createBrowserComposer({ verbose: true })
|
|
1129
|
+
* await composer.initialize()
|
|
1130
|
+
* ```
|
|
1131
|
+
*/
|
|
1132
|
+
export function createBrowserComposer(
|
|
1133
|
+
config?: BrowserProofComposerConfig
|
|
1134
|
+
): BrowserProofComposer {
|
|
1135
|
+
return new BrowserProofComposer(config)
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
/**
|
|
1139
|
+
* Create a composer with automatic environment detection
|
|
1140
|
+
*
|
|
1141
|
+
* Returns BrowserProofComposer in browser, BaseProofComposer in Node.js
|
|
1142
|
+
*/
|
|
1143
|
+
export function createAutoComposer(
|
|
1144
|
+
config?: BrowserProofComposerConfig
|
|
1145
|
+
): ProofComposer {
|
|
1146
|
+
if (isBrowser()) {
|
|
1147
|
+
return new BrowserProofComposer(config)
|
|
1148
|
+
}
|
|
1149
|
+
return new BaseProofComposer(config)
|
|
1150
|
+
}
|