@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,888 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fallback Proof Strategies
|
|
3
|
+
*
|
|
4
|
+
* Implements fallback strategies for when primary proof providers fail or are unavailable.
|
|
5
|
+
* Ensures proof composition remains reliable even when individual ZK systems have issues.
|
|
6
|
+
*
|
|
7
|
+
* Key features:
|
|
8
|
+
* - Provider failover logic
|
|
9
|
+
* - Graceful degradation (simpler proofs when complex fails)
|
|
10
|
+
* - Circuit breaker pattern for failing providers
|
|
11
|
+
* - Mock/stub fallback for development
|
|
12
|
+
* - Custom fallback configuration
|
|
13
|
+
* - Fallback logging and alerting
|
|
14
|
+
*
|
|
15
|
+
* @packageDocumentation
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type {
|
|
19
|
+
ProofSystem,
|
|
20
|
+
SingleProof,
|
|
21
|
+
HexString,
|
|
22
|
+
} from '@sip-protocol/types'
|
|
23
|
+
|
|
24
|
+
import type { ComposableProofProvider } from './composer/interface'
|
|
25
|
+
import type {
|
|
26
|
+
ProofGenerationRequest,
|
|
27
|
+
ProofGenerationResult,
|
|
28
|
+
FallbackConfig,
|
|
29
|
+
} from './composer/types'
|
|
30
|
+
|
|
31
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Fallback strategy interface
|
|
35
|
+
*/
|
|
36
|
+
export interface FallbackStrategy {
|
|
37
|
+
/** Strategy name */
|
|
38
|
+
readonly name: string
|
|
39
|
+
|
|
40
|
+
/** Get next provider in fallback chain */
|
|
41
|
+
getNextProvider(
|
|
42
|
+
currentProvider: ProofSystem | null,
|
|
43
|
+
failedProviders: Set<ProofSystem>,
|
|
44
|
+
): ProofSystem | null
|
|
45
|
+
|
|
46
|
+
/** Check if fallback should be attempted */
|
|
47
|
+
shouldAttemptFallback(
|
|
48
|
+
error: Error,
|
|
49
|
+
attemptCount: number,
|
|
50
|
+
maxAttempts: number,
|
|
51
|
+
): boolean
|
|
52
|
+
|
|
53
|
+
/** Get fallback delay (for retry backoff) */
|
|
54
|
+
getRetryDelay(attemptCount: number): number
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Circuit breaker state
|
|
59
|
+
*/
|
|
60
|
+
export interface CircuitBreakerState {
|
|
61
|
+
/** Current state */
|
|
62
|
+
state: 'closed' | 'open' | 'half-open'
|
|
63
|
+
/** Number of consecutive failures */
|
|
64
|
+
failureCount: number
|
|
65
|
+
/** Last failure timestamp */
|
|
66
|
+
lastFailureAt: number
|
|
67
|
+
/** Time when circuit opened */
|
|
68
|
+
openedAt?: number
|
|
69
|
+
/** Success count in half-open state */
|
|
70
|
+
halfOpenSuccessCount: number
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Circuit breaker configuration
|
|
75
|
+
*/
|
|
76
|
+
export interface CircuitBreakerConfig {
|
|
77
|
+
/** Failures before opening circuit */
|
|
78
|
+
failureThreshold: number
|
|
79
|
+
/** Time to wait before half-open (ms) */
|
|
80
|
+
resetTimeoutMs: number
|
|
81
|
+
/** Successes needed in half-open to close */
|
|
82
|
+
halfOpenSuccessThreshold: number
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Provider health status
|
|
87
|
+
*/
|
|
88
|
+
export interface ProviderHealth {
|
|
89
|
+
/** Provider system */
|
|
90
|
+
system: ProofSystem
|
|
91
|
+
/** Whether provider is healthy */
|
|
92
|
+
isHealthy: boolean
|
|
93
|
+
/** Circuit breaker state */
|
|
94
|
+
circuitBreaker: CircuitBreakerState
|
|
95
|
+
/** Success rate (0-1) */
|
|
96
|
+
successRate: number
|
|
97
|
+
/** Average response time (ms) */
|
|
98
|
+
avgResponseTimeMs: number
|
|
99
|
+
/** Last successful operation timestamp */
|
|
100
|
+
lastSuccessAt?: number
|
|
101
|
+
/** Last error message */
|
|
102
|
+
lastError?: string
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Fallback event types
|
|
107
|
+
*/
|
|
108
|
+
export type FallbackEventType =
|
|
109
|
+
| 'fallback:started'
|
|
110
|
+
| 'fallback:provider_failed'
|
|
111
|
+
| 'fallback:provider_switched'
|
|
112
|
+
| 'fallback:success'
|
|
113
|
+
| 'fallback:exhausted'
|
|
114
|
+
| 'circuit:opened'
|
|
115
|
+
| 'circuit:closed'
|
|
116
|
+
| 'circuit:half_open'
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Fallback event
|
|
120
|
+
*/
|
|
121
|
+
export interface FallbackEvent {
|
|
122
|
+
type: FallbackEventType
|
|
123
|
+
timestamp: number
|
|
124
|
+
system?: ProofSystem
|
|
125
|
+
error?: string
|
|
126
|
+
attemptCount?: number
|
|
127
|
+
details?: Record<string, unknown>
|
|
128
|
+
/** Previous state (for circuit breaker transitions) */
|
|
129
|
+
previousState?: 'closed' | 'open' | 'half-open'
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Fallback event listener
|
|
134
|
+
*/
|
|
135
|
+
export type FallbackEventListener = (event: FallbackEvent) => void
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Fallback executor options
|
|
139
|
+
*/
|
|
140
|
+
export interface FallbackExecutorConfig {
|
|
141
|
+
/** Fallback configuration */
|
|
142
|
+
fallbackConfig: FallbackConfig
|
|
143
|
+
/** Circuit breaker config */
|
|
144
|
+
circuitBreakerConfig?: CircuitBreakerConfig
|
|
145
|
+
/** Enable mock fallback in development */
|
|
146
|
+
enableMockFallback?: boolean
|
|
147
|
+
/** Log all fallback events */
|
|
148
|
+
enableLogging?: boolean
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ─── Default Configurations ──────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
export const DEFAULT_CIRCUIT_BREAKER_CONFIG: CircuitBreakerConfig = {
|
|
154
|
+
failureThreshold: 5,
|
|
155
|
+
resetTimeoutMs: 30000, // 30 seconds
|
|
156
|
+
halfOpenSuccessThreshold: 3,
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export const DEFAULT_FALLBACK_CONFIG: FallbackConfig = {
|
|
160
|
+
primary: 'noir',
|
|
161
|
+
fallbackChain: ['halo2', 'kimchi'],
|
|
162
|
+
retryOnFailure: true,
|
|
163
|
+
maxRetries: 3,
|
|
164
|
+
retryDelayMs: 1000,
|
|
165
|
+
exponentialBackoff: true,
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ─── Fallback Strategies ─────────────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Simple sequential fallback strategy
|
|
172
|
+
*/
|
|
173
|
+
export class SequentialFallbackStrategy implements FallbackStrategy {
|
|
174
|
+
readonly name = 'sequential'
|
|
175
|
+
|
|
176
|
+
private _fallbackChain: ProofSystem[]
|
|
177
|
+
|
|
178
|
+
constructor(fallbackChain: ProofSystem[]) {
|
|
179
|
+
this._fallbackChain = fallbackChain
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
getNextProvider(
|
|
183
|
+
currentProvider: ProofSystem | null,
|
|
184
|
+
failedProviders: Set<ProofSystem>,
|
|
185
|
+
): ProofSystem | null {
|
|
186
|
+
// Find first available provider in chain that hasn't failed
|
|
187
|
+
for (const system of this._fallbackChain) {
|
|
188
|
+
if (!failedProviders.has(system) && system !== currentProvider) {
|
|
189
|
+
return system
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return null
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
shouldAttemptFallback(
|
|
196
|
+
_error: Error,
|
|
197
|
+
attemptCount: number,
|
|
198
|
+
maxAttempts: number,
|
|
199
|
+
): boolean {
|
|
200
|
+
return attemptCount < maxAttempts
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
getRetryDelay(attemptCount: number): number {
|
|
204
|
+
// Linear backoff
|
|
205
|
+
return 1000 * attemptCount
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Exponential backoff fallback strategy
|
|
211
|
+
*/
|
|
212
|
+
export class ExponentialBackoffStrategy implements FallbackStrategy {
|
|
213
|
+
readonly name = 'exponential-backoff'
|
|
214
|
+
|
|
215
|
+
private _fallbackChain: ProofSystem[]
|
|
216
|
+
private _baseDelayMs: number
|
|
217
|
+
private _maxDelayMs: number
|
|
218
|
+
|
|
219
|
+
constructor(
|
|
220
|
+
fallbackChain: ProofSystem[],
|
|
221
|
+
baseDelayMs: number = 1000,
|
|
222
|
+
maxDelayMs: number = 30000,
|
|
223
|
+
) {
|
|
224
|
+
this._fallbackChain = fallbackChain
|
|
225
|
+
this._baseDelayMs = baseDelayMs
|
|
226
|
+
this._maxDelayMs = maxDelayMs
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
getNextProvider(
|
|
230
|
+
currentProvider: ProofSystem | null,
|
|
231
|
+
failedProviders: Set<ProofSystem>,
|
|
232
|
+
): ProofSystem | null {
|
|
233
|
+
for (const system of this._fallbackChain) {
|
|
234
|
+
if (!failedProviders.has(system) && system !== currentProvider) {
|
|
235
|
+
return system
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return null
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
shouldAttemptFallback(
|
|
242
|
+
_error: Error,
|
|
243
|
+
attemptCount: number,
|
|
244
|
+
maxAttempts: number,
|
|
245
|
+
): boolean {
|
|
246
|
+
return attemptCount < maxAttempts
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
getRetryDelay(attemptCount: number): number {
|
|
250
|
+
const exponentialDelay = this._baseDelayMs * Math.pow(2, attemptCount)
|
|
251
|
+
const jitter = Math.random() * 0.3 * exponentialDelay
|
|
252
|
+
return Math.min(exponentialDelay + jitter, this._maxDelayMs)
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Priority-based fallback strategy (health-aware)
|
|
258
|
+
*/
|
|
259
|
+
export class PriorityFallbackStrategy implements FallbackStrategy {
|
|
260
|
+
readonly name = 'priority'
|
|
261
|
+
|
|
262
|
+
private _priorities: Map<ProofSystem, number>
|
|
263
|
+
private _healthProvider: () => Map<ProofSystem, ProviderHealth>
|
|
264
|
+
|
|
265
|
+
constructor(
|
|
266
|
+
priorities: Map<ProofSystem, number>,
|
|
267
|
+
healthProvider: () => Map<ProofSystem, ProviderHealth>,
|
|
268
|
+
) {
|
|
269
|
+
this._priorities = priorities
|
|
270
|
+
this._healthProvider = healthProvider
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
getNextProvider(
|
|
274
|
+
_currentProvider: ProofSystem | null,
|
|
275
|
+
failedProviders: Set<ProofSystem>,
|
|
276
|
+
): ProofSystem | null {
|
|
277
|
+
const health = this._healthProvider()
|
|
278
|
+
|
|
279
|
+
// Sort by priority * health score
|
|
280
|
+
const candidates = Array.from(this._priorities.entries())
|
|
281
|
+
.filter(([system]) => !failedProviders.has(system))
|
|
282
|
+
.map(([system, priority]) => {
|
|
283
|
+
const providerHealth = health.get(system)
|
|
284
|
+
const healthScore = providerHealth?.isHealthy ? providerHealth.successRate : 0
|
|
285
|
+
return { system, score: priority * healthScore }
|
|
286
|
+
})
|
|
287
|
+
.sort((a, b) => b.score - a.score)
|
|
288
|
+
|
|
289
|
+
// Return null if no candidates or all have score 0 (all unhealthy)
|
|
290
|
+
if (candidates.length === 0 || candidates[0].score === 0) {
|
|
291
|
+
return null
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return candidates[0].system
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
shouldAttemptFallback(
|
|
298
|
+
_error: Error,
|
|
299
|
+
attemptCount: number,
|
|
300
|
+
maxAttempts: number,
|
|
301
|
+
): boolean {
|
|
302
|
+
return attemptCount < maxAttempts
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
getRetryDelay(attemptCount: number): number {
|
|
306
|
+
return 1000 * Math.pow(1.5, attemptCount)
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// ─── Circuit Breaker ─────────────────────────────────────────────────────────
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* Circuit breaker for provider health management
|
|
314
|
+
*/
|
|
315
|
+
export class CircuitBreaker {
|
|
316
|
+
private _states: Map<ProofSystem, CircuitBreakerState> = new Map()
|
|
317
|
+
private _config: CircuitBreakerConfig
|
|
318
|
+
private _eventListeners: Set<FallbackEventListener> = new Set()
|
|
319
|
+
|
|
320
|
+
constructor(config: CircuitBreakerConfig = DEFAULT_CIRCUIT_BREAKER_CONFIG) {
|
|
321
|
+
this._config = config
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Get or create circuit breaker state for a provider
|
|
326
|
+
*/
|
|
327
|
+
getState(system: ProofSystem): CircuitBreakerState {
|
|
328
|
+
let state = this._states.get(system)
|
|
329
|
+
if (!state) {
|
|
330
|
+
state = {
|
|
331
|
+
state: 'closed',
|
|
332
|
+
failureCount: 0,
|
|
333
|
+
lastFailureAt: 0,
|
|
334
|
+
halfOpenSuccessCount: 0,
|
|
335
|
+
}
|
|
336
|
+
this._states.set(system, state)
|
|
337
|
+
}
|
|
338
|
+
return state
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Check if requests to a provider are allowed
|
|
343
|
+
*/
|
|
344
|
+
isAllowed(system: ProofSystem): boolean {
|
|
345
|
+
const state = this.getState(system)
|
|
346
|
+
|
|
347
|
+
switch (state.state) {
|
|
348
|
+
case 'closed':
|
|
349
|
+
return true
|
|
350
|
+
|
|
351
|
+
case 'open':
|
|
352
|
+
// Check if reset timeout has passed
|
|
353
|
+
if (Date.now() - (state.openedAt || 0) > this._config.resetTimeoutMs) {
|
|
354
|
+
this._transitionTo(system, 'half-open')
|
|
355
|
+
return true
|
|
356
|
+
}
|
|
357
|
+
return false
|
|
358
|
+
|
|
359
|
+
case 'half-open':
|
|
360
|
+
// Allow limited requests in half-open state
|
|
361
|
+
return true
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Record a successful operation
|
|
367
|
+
*/
|
|
368
|
+
recordSuccess(system: ProofSystem): void {
|
|
369
|
+
const state = this.getState(system)
|
|
370
|
+
|
|
371
|
+
if (state.state === 'half-open') {
|
|
372
|
+
state.halfOpenSuccessCount++
|
|
373
|
+
if (state.halfOpenSuccessCount >= this._config.halfOpenSuccessThreshold) {
|
|
374
|
+
this._transitionTo(system, 'closed')
|
|
375
|
+
}
|
|
376
|
+
} else if (state.state === 'closed') {
|
|
377
|
+
// Reset failure count on success
|
|
378
|
+
state.failureCount = Math.max(0, state.failureCount - 1)
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Record a failed operation
|
|
384
|
+
*/
|
|
385
|
+
recordFailure(system: ProofSystem, error?: string): void {
|
|
386
|
+
const state = this.getState(system)
|
|
387
|
+
|
|
388
|
+
state.failureCount++
|
|
389
|
+
state.lastFailureAt = Date.now()
|
|
390
|
+
|
|
391
|
+
if (state.state === 'half-open') {
|
|
392
|
+
// Immediately open circuit on failure in half-open state
|
|
393
|
+
this._transitionTo(system, 'open')
|
|
394
|
+
} else if (state.state === 'closed') {
|
|
395
|
+
if (state.failureCount >= this._config.failureThreshold) {
|
|
396
|
+
this._transitionTo(system, 'open')
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
this._emit({
|
|
401
|
+
type: 'fallback:provider_failed',
|
|
402
|
+
timestamp: Date.now(),
|
|
403
|
+
system,
|
|
404
|
+
error,
|
|
405
|
+
details: { failureCount: state.failureCount },
|
|
406
|
+
})
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
/**
|
|
410
|
+
* Reset circuit breaker for a provider
|
|
411
|
+
*/
|
|
412
|
+
reset(system: ProofSystem): void {
|
|
413
|
+
this._states.set(system, {
|
|
414
|
+
state: 'closed',
|
|
415
|
+
failureCount: 0,
|
|
416
|
+
lastFailureAt: 0,
|
|
417
|
+
halfOpenSuccessCount: 0,
|
|
418
|
+
})
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Reset all circuit breakers
|
|
423
|
+
*/
|
|
424
|
+
resetAll(): void {
|
|
425
|
+
this._states.clear()
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Add event listener
|
|
430
|
+
*/
|
|
431
|
+
addEventListener(listener: FallbackEventListener): () => void {
|
|
432
|
+
this._eventListeners.add(listener)
|
|
433
|
+
return () => this._eventListeners.delete(listener)
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
private _transitionTo(
|
|
437
|
+
system: ProofSystem,
|
|
438
|
+
newState: 'closed' | 'open' | 'half-open',
|
|
439
|
+
): void {
|
|
440
|
+
const state = this.getState(system)
|
|
441
|
+
const previousState = state.state
|
|
442
|
+
|
|
443
|
+
// Only transition if state actually changes
|
|
444
|
+
if (previousState === newState) return
|
|
445
|
+
|
|
446
|
+
state.state = newState
|
|
447
|
+
|
|
448
|
+
if (newState === 'open') {
|
|
449
|
+
state.openedAt = Date.now()
|
|
450
|
+
this._emit({ type: 'circuit:opened', timestamp: Date.now(), system, previousState })
|
|
451
|
+
} else if (newState === 'closed') {
|
|
452
|
+
state.failureCount = 0
|
|
453
|
+
state.halfOpenSuccessCount = 0
|
|
454
|
+
this._emit({ type: 'circuit:closed', timestamp: Date.now(), system, previousState })
|
|
455
|
+
} else if (newState === 'half-open') {
|
|
456
|
+
state.halfOpenSuccessCount = 0
|
|
457
|
+
this._emit({ type: 'circuit:half_open', timestamp: Date.now(), system, previousState })
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
private _emit(event: FallbackEvent): void {
|
|
462
|
+
for (const listener of this._eventListeners) {
|
|
463
|
+
try {
|
|
464
|
+
listener(event)
|
|
465
|
+
} catch {
|
|
466
|
+
// Ignore listener errors
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
// ─── Fallback Executor ───────────────────────────────────────────────────────
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Executes proof generation with fallback support
|
|
476
|
+
*/
|
|
477
|
+
export class FallbackExecutor {
|
|
478
|
+
private _config: FallbackExecutorConfig
|
|
479
|
+
private _strategy: FallbackStrategy
|
|
480
|
+
private _circuitBreaker: CircuitBreaker
|
|
481
|
+
private _providers: Map<ProofSystem, ComposableProofProvider> = new Map()
|
|
482
|
+
private _health: Map<ProofSystem, ProviderHealth> = new Map()
|
|
483
|
+
private _eventListeners: Set<FallbackEventListener> = new Set()
|
|
484
|
+
private _mockProvider?: ComposableProofProvider
|
|
485
|
+
|
|
486
|
+
constructor(config: FallbackExecutorConfig, customStrategy?: FallbackStrategy) {
|
|
487
|
+
this._config = config
|
|
488
|
+
|
|
489
|
+
// Use custom strategy if provided, otherwise create based on config
|
|
490
|
+
if (customStrategy) {
|
|
491
|
+
this._strategy = customStrategy
|
|
492
|
+
} else if (config.fallbackConfig.exponentialBackoff) {
|
|
493
|
+
this._strategy = new ExponentialBackoffStrategy(
|
|
494
|
+
[config.fallbackConfig.primary, ...config.fallbackConfig.fallbackChain],
|
|
495
|
+
config.fallbackConfig.retryDelayMs,
|
|
496
|
+
)
|
|
497
|
+
} else {
|
|
498
|
+
this._strategy = new SequentialFallbackStrategy(
|
|
499
|
+
[config.fallbackConfig.primary, ...config.fallbackConfig.fallbackChain],
|
|
500
|
+
)
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
this._circuitBreaker = new CircuitBreaker(
|
|
504
|
+
config.circuitBreakerConfig || DEFAULT_CIRCUIT_BREAKER_CONFIG,
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
// Forward circuit breaker events
|
|
508
|
+
this._circuitBreaker.addEventListener(event => this._emit(event))
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* Register a provider
|
|
513
|
+
*/
|
|
514
|
+
registerProvider(provider: ComposableProofProvider): void {
|
|
515
|
+
this._providers.set(provider.system, provider)
|
|
516
|
+
this._health.set(provider.system, this._createInitialHealth(provider.system))
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Set mock provider for development fallback
|
|
521
|
+
*/
|
|
522
|
+
setMockProvider(provider: ComposableProofProvider): void {
|
|
523
|
+
this._mockProvider = provider
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Execute proof generation with fallback
|
|
528
|
+
*/
|
|
529
|
+
async execute(request: ProofGenerationRequest): Promise<ProofGenerationResult> {
|
|
530
|
+
const startTime = Date.now()
|
|
531
|
+
const failedProviders = new Set<ProofSystem>()
|
|
532
|
+
let currentProvider = request.system || this._config.fallbackConfig.primary
|
|
533
|
+
let attemptCount = 0
|
|
534
|
+
const maxAttempts = this._config.fallbackConfig.maxRetries + 1
|
|
535
|
+
|
|
536
|
+
this._emit({
|
|
537
|
+
type: 'fallback:started',
|
|
538
|
+
timestamp: startTime,
|
|
539
|
+
system: currentProvider,
|
|
540
|
+
details: { request: { circuitId: request.circuitId } },
|
|
541
|
+
})
|
|
542
|
+
|
|
543
|
+
while (attemptCount < maxAttempts) {
|
|
544
|
+
// Check circuit breaker
|
|
545
|
+
if (!this._circuitBreaker.isAllowed(currentProvider)) {
|
|
546
|
+
this._log(`Circuit open for ${currentProvider}, finding alternative`)
|
|
547
|
+
failedProviders.add(currentProvider)
|
|
548
|
+
const nextProvider = this._strategy.getNextProvider(currentProvider, failedProviders)
|
|
549
|
+
|
|
550
|
+
if (!nextProvider) {
|
|
551
|
+
break
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
this._emit({
|
|
555
|
+
type: 'fallback:provider_switched',
|
|
556
|
+
timestamp: Date.now(),
|
|
557
|
+
system: nextProvider,
|
|
558
|
+
details: { from: currentProvider, reason: 'circuit_open' },
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
currentProvider = nextProvider
|
|
562
|
+
continue
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// Get provider
|
|
566
|
+
const provider = this._providers.get(currentProvider)
|
|
567
|
+
if (!provider) {
|
|
568
|
+
this._log(`Provider ${currentProvider} not found`)
|
|
569
|
+
failedProviders.add(currentProvider)
|
|
570
|
+
const nextProvider = this._strategy.getNextProvider(currentProvider, failedProviders)
|
|
571
|
+
|
|
572
|
+
if (!nextProvider) {
|
|
573
|
+
break
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
currentProvider = nextProvider
|
|
577
|
+
continue
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
// Attempt proof generation
|
|
581
|
+
try {
|
|
582
|
+
const result = await provider.generateProof({
|
|
583
|
+
...request,
|
|
584
|
+
system: currentProvider,
|
|
585
|
+
})
|
|
586
|
+
|
|
587
|
+
if (result.success) {
|
|
588
|
+
this._circuitBreaker.recordSuccess(currentProvider)
|
|
589
|
+
this._updateHealth(currentProvider, true, Date.now() - startTime)
|
|
590
|
+
|
|
591
|
+
this._emit({
|
|
592
|
+
type: 'fallback:success',
|
|
593
|
+
timestamp: Date.now(),
|
|
594
|
+
system: currentProvider,
|
|
595
|
+
attemptCount,
|
|
596
|
+
})
|
|
597
|
+
|
|
598
|
+
return result
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// Generation returned failure
|
|
602
|
+
throw new Error(result.error || 'Proof generation failed')
|
|
603
|
+
} catch (error) {
|
|
604
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
|
|
605
|
+
this._log(`Provider ${currentProvider} failed: ${errorMessage}`)
|
|
606
|
+
|
|
607
|
+
this._circuitBreaker.recordFailure(currentProvider, errorMessage)
|
|
608
|
+
this._updateHealth(currentProvider, false, Date.now() - startTime, errorMessage)
|
|
609
|
+
failedProviders.add(currentProvider)
|
|
610
|
+
|
|
611
|
+
attemptCount++
|
|
612
|
+
|
|
613
|
+
// Check if we should retry
|
|
614
|
+
if (!this._strategy.shouldAttemptFallback(
|
|
615
|
+
error instanceof Error ? error : new Error(errorMessage),
|
|
616
|
+
attemptCount,
|
|
617
|
+
maxAttempts,
|
|
618
|
+
)) {
|
|
619
|
+
break
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Find next provider
|
|
623
|
+
const nextProvider = this._strategy.getNextProvider(currentProvider, failedProviders)
|
|
624
|
+
|
|
625
|
+
if (nextProvider) {
|
|
626
|
+
this._emit({
|
|
627
|
+
type: 'fallback:provider_switched',
|
|
628
|
+
timestamp: Date.now(),
|
|
629
|
+
system: nextProvider,
|
|
630
|
+
details: { from: currentProvider, reason: 'failure' },
|
|
631
|
+
})
|
|
632
|
+
|
|
633
|
+
currentProvider = nextProvider
|
|
634
|
+
|
|
635
|
+
// Apply retry delay
|
|
636
|
+
const delay = this._strategy.getRetryDelay(attemptCount)
|
|
637
|
+
await this._delay(delay)
|
|
638
|
+
} else {
|
|
639
|
+
break
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// All providers exhausted - try mock fallback if enabled
|
|
645
|
+
if (this._config.enableMockFallback && this._mockProvider) {
|
|
646
|
+
this._log('Using mock fallback provider')
|
|
647
|
+
|
|
648
|
+
try {
|
|
649
|
+
const result = await this._mockProvider.generateProof(request)
|
|
650
|
+
if (result.success) {
|
|
651
|
+
return result
|
|
652
|
+
}
|
|
653
|
+
} catch {
|
|
654
|
+
// Mock also failed
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
this._emit({
|
|
659
|
+
type: 'fallback:exhausted',
|
|
660
|
+
timestamp: Date.now(),
|
|
661
|
+
attemptCount,
|
|
662
|
+
details: { failedProviders: Array.from(failedProviders) },
|
|
663
|
+
})
|
|
664
|
+
|
|
665
|
+
return {
|
|
666
|
+
success: false,
|
|
667
|
+
error: `All providers exhausted after ${attemptCount} attempts`,
|
|
668
|
+
timeMs: Date.now() - startTime,
|
|
669
|
+
providerId: 'fallback',
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Get health status for all providers
|
|
675
|
+
*/
|
|
676
|
+
getHealth(): Map<ProofSystem, ProviderHealth> {
|
|
677
|
+
return new Map(this._health)
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Get health status for a specific provider
|
|
682
|
+
*/
|
|
683
|
+
getProviderHealth(system: ProofSystem): ProviderHealth | undefined {
|
|
684
|
+
return this._health.get(system)
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* Reset circuit breaker for a provider
|
|
689
|
+
*/
|
|
690
|
+
resetCircuitBreaker(system: ProofSystem): void {
|
|
691
|
+
this._circuitBreaker.reset(system)
|
|
692
|
+
const health = this._health.get(system)
|
|
693
|
+
if (health) {
|
|
694
|
+
health.circuitBreaker = this._circuitBreaker.getState(system)
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Add event listener
|
|
700
|
+
*/
|
|
701
|
+
addEventListener(listener: FallbackEventListener): () => void {
|
|
702
|
+
this._eventListeners.add(listener)
|
|
703
|
+
return () => this._eventListeners.delete(listener)
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Get current fallback strategy
|
|
708
|
+
*/
|
|
709
|
+
get strategy(): FallbackStrategy {
|
|
710
|
+
return this._strategy
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
/**
|
|
714
|
+
* Set fallback strategy
|
|
715
|
+
*/
|
|
716
|
+
setStrategy(strategy: FallbackStrategy): void {
|
|
717
|
+
this._strategy = strategy
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
private _createInitialHealth(system: ProofSystem): ProviderHealth {
|
|
721
|
+
return {
|
|
722
|
+
system,
|
|
723
|
+
isHealthy: true,
|
|
724
|
+
circuitBreaker: this._circuitBreaker.getState(system),
|
|
725
|
+
successRate: 1.0,
|
|
726
|
+
avgResponseTimeMs: 0,
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
private _updateHealth(
|
|
731
|
+
system: ProofSystem,
|
|
732
|
+
success: boolean,
|
|
733
|
+
responseTimeMs: number,
|
|
734
|
+
error?: string,
|
|
735
|
+
): void {
|
|
736
|
+
const health = this._health.get(system)
|
|
737
|
+
if (!health) return
|
|
738
|
+
|
|
739
|
+
// Update circuit breaker state
|
|
740
|
+
health.circuitBreaker = this._circuitBreaker.getState(system)
|
|
741
|
+
health.isHealthy = health.circuitBreaker.state === 'closed'
|
|
742
|
+
|
|
743
|
+
// Update metrics (exponential moving average)
|
|
744
|
+
const alpha = 0.2
|
|
745
|
+
health.avgResponseTimeMs = alpha * responseTimeMs + (1 - alpha) * health.avgResponseTimeMs
|
|
746
|
+
|
|
747
|
+
if (success) {
|
|
748
|
+
health.lastSuccessAt = Date.now()
|
|
749
|
+
health.successRate = alpha * 1 + (1 - alpha) * health.successRate
|
|
750
|
+
} else {
|
|
751
|
+
health.lastError = error
|
|
752
|
+
health.successRate = alpha * 0 + (1 - alpha) * health.successRate
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
private _emit(event: FallbackEvent): void {
|
|
757
|
+
if (this._config.enableLogging) {
|
|
758
|
+
this._log(`[${event.type}] ${event.system || ''} ${JSON.stringify(event.details || {})}`)
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
for (const listener of this._eventListeners) {
|
|
762
|
+
try {
|
|
763
|
+
listener(event)
|
|
764
|
+
} catch {
|
|
765
|
+
// Ignore listener errors
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
private _log(message: string): void {
|
|
771
|
+
if (this._config.enableLogging) {
|
|
772
|
+
console.log(`[FallbackExecutor] ${message}`)
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
private _delay(ms: number): Promise<void> {
|
|
777
|
+
return new Promise(resolve => setTimeout(resolve, ms))
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// ─── Mock Fallback Provider ──────────────────────────────────────────────────
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Create a mock provider for development fallback
|
|
785
|
+
*/
|
|
786
|
+
export function createMockFallbackProvider(): ComposableProofProvider {
|
|
787
|
+
let proofCounter = 0
|
|
788
|
+
|
|
789
|
+
return {
|
|
790
|
+
system: 'noir' as ProofSystem,
|
|
791
|
+
capabilities: {
|
|
792
|
+
system: 'noir',
|
|
793
|
+
supportsRecursion: false,
|
|
794
|
+
supportsBatchVerification: false,
|
|
795
|
+
supportsBrowser: true,
|
|
796
|
+
supportsNode: true,
|
|
797
|
+
maxProofSize: 65536,
|
|
798
|
+
supportedStrategies: [],
|
|
799
|
+
availableCircuits: ['*'], // Accepts any circuit
|
|
800
|
+
},
|
|
801
|
+
status: {
|
|
802
|
+
isReady: true,
|
|
803
|
+
isBusy: false,
|
|
804
|
+
queueLength: 0,
|
|
805
|
+
metrics: {
|
|
806
|
+
proofsGenerated: 0,
|
|
807
|
+
proofsVerified: 0,
|
|
808
|
+
avgGenerationTimeMs: 10,
|
|
809
|
+
avgVerificationTimeMs: 5,
|
|
810
|
+
successRate: 1.0,
|
|
811
|
+
memoryUsageBytes: 0,
|
|
812
|
+
},
|
|
813
|
+
},
|
|
814
|
+
|
|
815
|
+
async initialize() {},
|
|
816
|
+
async waitUntilReady() {},
|
|
817
|
+
|
|
818
|
+
async generateProof(request) {
|
|
819
|
+
proofCounter++
|
|
820
|
+
const proof: SingleProof = {
|
|
821
|
+
id: `mock-proof-${proofCounter}`,
|
|
822
|
+
proof: '0xmock_proof_data_for_development' as HexString,
|
|
823
|
+
publicInputs: [] as HexString[],
|
|
824
|
+
metadata: {
|
|
825
|
+
system: 'noir',
|
|
826
|
+
systemVersion: 'mock-1.0.0',
|
|
827
|
+
circuitId: request.circuitId,
|
|
828
|
+
circuitVersion: 'mock',
|
|
829
|
+
generatedAt: Date.now(),
|
|
830
|
+
proofSizeBytes: 32,
|
|
831
|
+
},
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
return {
|
|
835
|
+
success: true,
|
|
836
|
+
proof,
|
|
837
|
+
timeMs: 10,
|
|
838
|
+
providerId: 'mock-fallback',
|
|
839
|
+
}
|
|
840
|
+
},
|
|
841
|
+
|
|
842
|
+
async verifyProof() {
|
|
843
|
+
return true
|
|
844
|
+
},
|
|
845
|
+
|
|
846
|
+
async verifyBatch(proofs) {
|
|
847
|
+
return proofs.map(() => true)
|
|
848
|
+
},
|
|
849
|
+
|
|
850
|
+
getAvailableCircuits() {
|
|
851
|
+
return ['*']
|
|
852
|
+
},
|
|
853
|
+
|
|
854
|
+
hasCircuit() {
|
|
855
|
+
return true // Accepts any circuit
|
|
856
|
+
},
|
|
857
|
+
|
|
858
|
+
async dispose() {},
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
// ─── Factory Functions ───────────────────────────────────────────────────────
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Create a fallback executor with default configuration
|
|
866
|
+
*/
|
|
867
|
+
export function createFallbackExecutor(
|
|
868
|
+
config?: Partial<FallbackExecutorConfig>,
|
|
869
|
+
): FallbackExecutor {
|
|
870
|
+
return new FallbackExecutor({
|
|
871
|
+
fallbackConfig: config?.fallbackConfig || DEFAULT_FALLBACK_CONFIG,
|
|
872
|
+
circuitBreakerConfig: config?.circuitBreakerConfig,
|
|
873
|
+
enableMockFallback: config?.enableMockFallback ?? false,
|
|
874
|
+
enableLogging: config?.enableLogging ?? false,
|
|
875
|
+
})
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
/**
|
|
879
|
+
* Create a circuit breaker with custom configuration
|
|
880
|
+
*/
|
|
881
|
+
export function createCircuitBreaker(
|
|
882
|
+
config?: Partial<CircuitBreakerConfig>,
|
|
883
|
+
): CircuitBreaker {
|
|
884
|
+
return new CircuitBreaker({
|
|
885
|
+
...DEFAULT_CIRCUIT_BREAKER_CONFIG,
|
|
886
|
+
...config,
|
|
887
|
+
})
|
|
888
|
+
}
|