@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,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Oblivious Sync Module
|
|
3
|
+
*
|
|
4
|
+
* Provides oblivious synchronization where third-party sync services
|
|
5
|
+
* learn NOTHING about user transactions.
|
|
6
|
+
*
|
|
7
|
+
* ## Key Features
|
|
8
|
+
*
|
|
9
|
+
* - **Oblivious Queries**: Service cannot see what you're querying for
|
|
10
|
+
* - **Sync Randomness**: Nullifiers cannot be correlated to notes
|
|
11
|
+
* - **Time-Windowed Disclosure**: Viewing keys limited to specific periods
|
|
12
|
+
* - **Multi-Chain Support**: Sync across multiple chains
|
|
13
|
+
*
|
|
14
|
+
* ## Usage
|
|
15
|
+
*
|
|
16
|
+
* ```typescript
|
|
17
|
+
* import {
|
|
18
|
+
* createMockSyncProvider,
|
|
19
|
+
* createSyncState,
|
|
20
|
+
* deriveObliviousNullifier,
|
|
21
|
+
* generateSyncRandomness,
|
|
22
|
+
* } from '@sip-protocol/sdk'
|
|
23
|
+
*
|
|
24
|
+
* // Initialize sync provider
|
|
25
|
+
* const provider = createMockSyncProvider()
|
|
26
|
+
* await provider.initialize()
|
|
27
|
+
*
|
|
28
|
+
* // Create sync state
|
|
29
|
+
* const state = createSyncState(['ethereum', 'solana'])
|
|
30
|
+
*
|
|
31
|
+
* // Generate oblivious nullifier
|
|
32
|
+
* const randomness = state.syncRandomness.get('ethereum')!
|
|
33
|
+
* const nullifier = deriveObliviousNullifier(
|
|
34
|
+
* noteCommitment,
|
|
35
|
+
* spendingKey,
|
|
36
|
+
* randomness
|
|
37
|
+
* )
|
|
38
|
+
*
|
|
39
|
+
* // Check if spent (service learns nothing)
|
|
40
|
+
* const spent = await provider.checkNullifiers([nullifier])
|
|
41
|
+
* ```
|
|
42
|
+
*
|
|
43
|
+
* @module sync
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
// Core oblivious sync types and functions
|
|
47
|
+
export {
|
|
48
|
+
// Types
|
|
49
|
+
type ChainId,
|
|
50
|
+
type BlockRange,
|
|
51
|
+
type EncryptedNote,
|
|
52
|
+
type MerkleProof,
|
|
53
|
+
type SyncRandomness,
|
|
54
|
+
type ObliviousNullifier,
|
|
55
|
+
type ObliviousSyncQuery,
|
|
56
|
+
type ObliviousSyncResponse,
|
|
57
|
+
type SyncServiceHealth,
|
|
58
|
+
type ObliviousSyncConfig,
|
|
59
|
+
type ObliviousSyncProvider,
|
|
60
|
+
type WalletSyncState,
|
|
61
|
+
type TimeWindowedViewingKey,
|
|
62
|
+
|
|
63
|
+
// Constants
|
|
64
|
+
DEFAULT_SYNC_CONFIG,
|
|
65
|
+
|
|
66
|
+
// Sync randomness functions
|
|
67
|
+
generateSyncRandomness,
|
|
68
|
+
isSyncRandomnessValid,
|
|
69
|
+
getCurrentEpoch,
|
|
70
|
+
|
|
71
|
+
// Nullifier functions
|
|
72
|
+
deriveObliviousNullifier,
|
|
73
|
+
deriveTraditionalNullifier,
|
|
74
|
+
|
|
75
|
+
// Sync state management
|
|
76
|
+
createSyncState,
|
|
77
|
+
updateSyncState,
|
|
78
|
+
|
|
79
|
+
// Viewing key integration
|
|
80
|
+
createTimeWindowedKey,
|
|
81
|
+
isNoteInWindow,
|
|
82
|
+
|
|
83
|
+
// Errors
|
|
84
|
+
ObliviousSyncError,
|
|
85
|
+
ObliviousSyncErrorCode,
|
|
86
|
+
} from './oblivious'
|
|
87
|
+
|
|
88
|
+
// Mock provider for testing
|
|
89
|
+
export {
|
|
90
|
+
MockObliviousSyncProvider,
|
|
91
|
+
createMockSyncProvider,
|
|
92
|
+
DEFAULT_MOCK_CONFIG,
|
|
93
|
+
type MockSyncProviderConfig,
|
|
94
|
+
} from './mock-provider'
|
|
95
|
+
|
|
96
|
+
// Sync manager for orchestration
|
|
97
|
+
export {
|
|
98
|
+
SyncManager,
|
|
99
|
+
createSyncManager,
|
|
100
|
+
DEFAULT_MANAGER_CONFIG,
|
|
101
|
+
type SyncManagerConfig,
|
|
102
|
+
type SyncProgressEvent,
|
|
103
|
+
type SyncCompletionEvent,
|
|
104
|
+
type SyncEventListener,
|
|
105
|
+
type SyncOptions,
|
|
106
|
+
} from './manager'
|
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sync Manager
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates oblivious synchronization across multiple providers and chains.
|
|
5
|
+
* Integrates with SIP client for seamless sync experience.
|
|
6
|
+
*
|
|
7
|
+
* @module sync/manager
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import type { HexString, ViewingKey } from '@sip-protocol/types'
|
|
11
|
+
import type {
|
|
12
|
+
ObliviousSyncProvider,
|
|
13
|
+
ChainId,
|
|
14
|
+
BlockRange,
|
|
15
|
+
EncryptedNote,
|
|
16
|
+
WalletSyncState,
|
|
17
|
+
ObliviousSyncConfig,
|
|
18
|
+
SyncServiceHealth,
|
|
19
|
+
ObliviousNullifier,
|
|
20
|
+
} from './oblivious'
|
|
21
|
+
import {
|
|
22
|
+
createSyncState,
|
|
23
|
+
updateSyncState,
|
|
24
|
+
deriveObliviousNullifier,
|
|
25
|
+
generateSyncRandomness,
|
|
26
|
+
getCurrentEpoch,
|
|
27
|
+
DEFAULT_SYNC_CONFIG,
|
|
28
|
+
ObliviousSyncError,
|
|
29
|
+
ObliviousSyncErrorCode,
|
|
30
|
+
} from './oblivious'
|
|
31
|
+
|
|
32
|
+
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Sync progress event
|
|
36
|
+
*/
|
|
37
|
+
export interface SyncProgressEvent {
|
|
38
|
+
/** Chain being synced */
|
|
39
|
+
chainId: ChainId
|
|
40
|
+
/** Current block being processed */
|
|
41
|
+
currentBlock: bigint
|
|
42
|
+
/** Target block */
|
|
43
|
+
targetBlock: bigint
|
|
44
|
+
/** Percentage complete (0-100) */
|
|
45
|
+
percentComplete: number
|
|
46
|
+
/** Notes found so far */
|
|
47
|
+
notesFound: number
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Sync completion event
|
|
52
|
+
*/
|
|
53
|
+
export interface SyncCompletionEvent {
|
|
54
|
+
/** Chains that were synced */
|
|
55
|
+
chains: ChainId[]
|
|
56
|
+
/** Total notes found */
|
|
57
|
+
totalNotes: number
|
|
58
|
+
/** Time taken (ms) */
|
|
59
|
+
durationMs: number
|
|
60
|
+
/** Final sync heights */
|
|
61
|
+
syncHeights: Map<ChainId, bigint>
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Sync event listener
|
|
66
|
+
*/
|
|
67
|
+
export type SyncEventListener = {
|
|
68
|
+
onProgress?: (event: SyncProgressEvent) => void
|
|
69
|
+
onComplete?: (event: SyncCompletionEvent) => void
|
|
70
|
+
onError?: (error: Error, chainId: ChainId) => void
|
|
71
|
+
onNotesFound?: (notes: EncryptedNote[], chainId: ChainId) => void
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Sync options for a single sync operation
|
|
76
|
+
*/
|
|
77
|
+
export interface SyncOptions {
|
|
78
|
+
/** Chains to sync (default: all) */
|
|
79
|
+
chains?: ChainId[]
|
|
80
|
+
/** Maximum blocks to sync per batch */
|
|
81
|
+
batchSize?: number
|
|
82
|
+
/** Event listener */
|
|
83
|
+
listener?: SyncEventListener
|
|
84
|
+
/** Abort signal for cancellation */
|
|
85
|
+
signal?: AbortSignal
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Sync manager configuration
|
|
90
|
+
*/
|
|
91
|
+
export interface SyncManagerConfig extends ObliviousSyncConfig {
|
|
92
|
+
/** Auto-rotate sync randomness on epoch change */
|
|
93
|
+
autoRotateRandomness: boolean
|
|
94
|
+
/** Enable background sync */
|
|
95
|
+
backgroundSync: boolean
|
|
96
|
+
/** Background sync interval (ms) */
|
|
97
|
+
backgroundSyncIntervalMs: number
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Default sync manager config
|
|
102
|
+
*/
|
|
103
|
+
export const DEFAULT_MANAGER_CONFIG: SyncManagerConfig = {
|
|
104
|
+
...DEFAULT_SYNC_CONFIG,
|
|
105
|
+
autoRotateRandomness: true,
|
|
106
|
+
backgroundSync: false,
|
|
107
|
+
backgroundSyncIntervalMs: 60_000, // 1 minute
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ─── Sync Manager ─────────────────────────────────────────────────────────────
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Sync Manager
|
|
114
|
+
*
|
|
115
|
+
* Coordinates oblivious synchronization across providers and chains.
|
|
116
|
+
*
|
|
117
|
+
* ## Features
|
|
118
|
+
*
|
|
119
|
+
* - Multi-provider support (fallback on failure)
|
|
120
|
+
* - Multi-chain parallel sync
|
|
121
|
+
* - Automatic randomness rotation
|
|
122
|
+
* - Background sync option
|
|
123
|
+
* - Event-driven progress updates
|
|
124
|
+
*
|
|
125
|
+
* ## Usage
|
|
126
|
+
*
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const manager = new SyncManager(provider)
|
|
129
|
+
* await manager.initialize()
|
|
130
|
+
*
|
|
131
|
+
* // Sync all chains
|
|
132
|
+
* const result = await manager.sync(viewingKey, spendingKey, {
|
|
133
|
+
* listener: {
|
|
134
|
+
* onProgress: (e) => console.log(`${e.percentComplete}%`),
|
|
135
|
+
* onNotesFound: (notes) => console.log(`Found ${notes.length} notes`),
|
|
136
|
+
* }
|
|
137
|
+
* })
|
|
138
|
+
* ```
|
|
139
|
+
*/
|
|
140
|
+
export class SyncManager {
|
|
141
|
+
private providers: ObliviousSyncProvider[]
|
|
142
|
+
private state: WalletSyncState | null = null
|
|
143
|
+
private config: SyncManagerConfig
|
|
144
|
+
private backgroundSyncInterval: ReturnType<typeof setInterval> | null = null
|
|
145
|
+
private initialized = false
|
|
146
|
+
|
|
147
|
+
constructor(
|
|
148
|
+
providers: ObliviousSyncProvider | ObliviousSyncProvider[],
|
|
149
|
+
config: Partial<SyncManagerConfig> = {}
|
|
150
|
+
) {
|
|
151
|
+
this.providers = Array.isArray(providers) ? providers : [providers]
|
|
152
|
+
this.config = { ...DEFAULT_MANAGER_CONFIG, ...config }
|
|
153
|
+
|
|
154
|
+
if (this.providers.length === 0) {
|
|
155
|
+
throw new Error('At least one provider is required')
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Initialize the sync manager
|
|
161
|
+
*/
|
|
162
|
+
async initialize(): Promise<void> {
|
|
163
|
+
// Initialize all providers
|
|
164
|
+
await Promise.all(this.providers.map(p => p.initialize()))
|
|
165
|
+
|
|
166
|
+
// Collect all supported chains
|
|
167
|
+
const chains = new Set<ChainId>()
|
|
168
|
+
for (const provider of this.providers) {
|
|
169
|
+
for (const chain of provider.supportedChains) {
|
|
170
|
+
chains.add(chain)
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Initialize sync state
|
|
175
|
+
this.state = createSyncState(Array.from(chains))
|
|
176
|
+
this.initialized = true
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get current sync state
|
|
181
|
+
*/
|
|
182
|
+
getState(): WalletSyncState | null {
|
|
183
|
+
return this.state
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Get supported chains
|
|
188
|
+
*/
|
|
189
|
+
getSupportedChains(): ChainId[] {
|
|
190
|
+
return this.state ? Array.from(this.state.syncHeights.keys()) : []
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Check health of all providers for a chain
|
|
195
|
+
*/
|
|
196
|
+
async getHealth(chainId: ChainId): Promise<SyncServiceHealth[]> {
|
|
197
|
+
this.checkInitialized()
|
|
198
|
+
|
|
199
|
+
const healths: SyncServiceHealth[] = []
|
|
200
|
+
for (const provider of this.providers) {
|
|
201
|
+
if (provider.supportedChains.includes(chainId)) {
|
|
202
|
+
try {
|
|
203
|
+
const health = await provider.getHealth(chainId)
|
|
204
|
+
healths.push(health)
|
|
205
|
+
} catch {
|
|
206
|
+
healths.push({
|
|
207
|
+
available: false,
|
|
208
|
+
currentHeight: 0n,
|
|
209
|
+
chainId,
|
|
210
|
+
latencyMs: 0,
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return healths
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Sync obliviously
|
|
221
|
+
*
|
|
222
|
+
* Main method for oblivious synchronization. The sync service learns
|
|
223
|
+
* nothing about which notes belong to you or your spending patterns.
|
|
224
|
+
*
|
|
225
|
+
* @param viewingKey - Viewing key for note detection
|
|
226
|
+
* @param spendingKey - Spending key for nullifier derivation
|
|
227
|
+
* @param options - Sync options
|
|
228
|
+
* @returns Notes found during sync
|
|
229
|
+
*/
|
|
230
|
+
async sync(
|
|
231
|
+
viewingKey: ViewingKey,
|
|
232
|
+
spendingKey: HexString,
|
|
233
|
+
options: SyncOptions = {}
|
|
234
|
+
): Promise<EncryptedNote[]> {
|
|
235
|
+
this.checkInitialized()
|
|
236
|
+
|
|
237
|
+
const startTime = Date.now()
|
|
238
|
+
const allNotes: EncryptedNote[] = []
|
|
239
|
+
const chains = options.chains ?? this.getSupportedChains()
|
|
240
|
+
|
|
241
|
+
// Check for abort signal
|
|
242
|
+
if (options.signal?.aborted) {
|
|
243
|
+
throw new Error('Sync aborted')
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
try {
|
|
247
|
+
if (this.config.parallelSync) {
|
|
248
|
+
// Parallel sync across chains
|
|
249
|
+
const results = await Promise.all(
|
|
250
|
+
chains.map(chainId =>
|
|
251
|
+
this.syncChain(chainId, viewingKey, spendingKey, options)
|
|
252
|
+
)
|
|
253
|
+
)
|
|
254
|
+
for (const notes of results) {
|
|
255
|
+
// Use concat to avoid stack overflow with large arrays
|
|
256
|
+
for (const note of notes) {
|
|
257
|
+
allNotes.push(note)
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} else {
|
|
261
|
+
// Sequential sync
|
|
262
|
+
for (const chainId of chains) {
|
|
263
|
+
if (options.signal?.aborted) {
|
|
264
|
+
throw new Error('Sync aborted')
|
|
265
|
+
}
|
|
266
|
+
const notes = await this.syncChain(chainId, viewingKey, spendingKey, options)
|
|
267
|
+
// Use loop to avoid stack overflow with large arrays
|
|
268
|
+
for (const note of notes) {
|
|
269
|
+
allNotes.push(note)
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// Emit completion event
|
|
275
|
+
options.listener?.onComplete?.({
|
|
276
|
+
chains,
|
|
277
|
+
totalNotes: allNotes.length,
|
|
278
|
+
durationMs: Date.now() - startTime,
|
|
279
|
+
syncHeights: this.state!.syncHeights,
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
return allNotes
|
|
283
|
+
} catch (error) {
|
|
284
|
+
const err = error instanceof Error ? error : new Error(String(error))
|
|
285
|
+
options.listener?.onError?.(err, chains[0] ?? 'unknown')
|
|
286
|
+
throw error
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Check if nullifiers have been spent (obliviously)
|
|
292
|
+
*
|
|
293
|
+
* @param nullifiers - Oblivious nullifiers to check
|
|
294
|
+
* @returns Map of nullifier → spent status
|
|
295
|
+
*/
|
|
296
|
+
async checkNullifiersSpent(
|
|
297
|
+
nullifiers: ObliviousNullifier[]
|
|
298
|
+
): Promise<Map<HexString, boolean>> {
|
|
299
|
+
this.checkInitialized()
|
|
300
|
+
|
|
301
|
+
// Group nullifiers by chain
|
|
302
|
+
const byChain = new Map<ChainId, ObliviousNullifier[]>()
|
|
303
|
+
for (const nullifier of nullifiers) {
|
|
304
|
+
if (!byChain.has(nullifier.chainId)) {
|
|
305
|
+
byChain.set(nullifier.chainId, [])
|
|
306
|
+
}
|
|
307
|
+
byChain.get(nullifier.chainId)!.push(nullifier)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
// Check each chain with appropriate provider
|
|
311
|
+
const results = new Map<HexString, boolean>()
|
|
312
|
+
|
|
313
|
+
for (const [chainId, chainNullifiers] of byChain) {
|
|
314
|
+
const provider = this.getProviderForChain(chainId)
|
|
315
|
+
if (!provider) {
|
|
316
|
+
throw new ObliviousSyncError(
|
|
317
|
+
`No provider available for chain: ${chainId}`,
|
|
318
|
+
ObliviousSyncErrorCode.CHAIN_NOT_SUPPORTED,
|
|
319
|
+
{ chainId }
|
|
320
|
+
)
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const chainResults = await provider.checkNullifiers(chainNullifiers)
|
|
324
|
+
for (const [nullifier, spent] of chainResults) {
|
|
325
|
+
results.set(nullifier, spent)
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
return results
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
/**
|
|
333
|
+
* Derive oblivious nullifier for a note
|
|
334
|
+
*
|
|
335
|
+
* @param noteCommitment - Note commitment
|
|
336
|
+
* @param spendingKey - Spending key
|
|
337
|
+
* @param chainId - Chain ID
|
|
338
|
+
* @returns Oblivious nullifier
|
|
339
|
+
*/
|
|
340
|
+
deriveNullifier(
|
|
341
|
+
noteCommitment: HexString,
|
|
342
|
+
spendingKey: HexString,
|
|
343
|
+
chainId: ChainId
|
|
344
|
+
): ObliviousNullifier {
|
|
345
|
+
this.checkInitialized()
|
|
346
|
+
|
|
347
|
+
// Get or generate sync randomness for this chain
|
|
348
|
+
let randomness = this.state!.syncRandomness.get(chainId)
|
|
349
|
+
const currentEpoch = getCurrentEpoch(this.config.epochDurationSeconds)
|
|
350
|
+
|
|
351
|
+
if (!randomness || randomness.epoch !== currentEpoch) {
|
|
352
|
+
randomness = generateSyncRandomness(currentEpoch, this.config.epochDurationSeconds)
|
|
353
|
+
this.state!.syncRandomness.set(chainId, randomness)
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
const nullifier = deriveObliviousNullifier(noteCommitment, spendingKey, randomness)
|
|
357
|
+
return { ...nullifier, chainId }
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* Start background sync
|
|
362
|
+
*
|
|
363
|
+
* @param viewingKey - Viewing key
|
|
364
|
+
* @param spendingKey - Spending key
|
|
365
|
+
* @param options - Sync options
|
|
366
|
+
*/
|
|
367
|
+
startBackgroundSync(
|
|
368
|
+
viewingKey: ViewingKey,
|
|
369
|
+
spendingKey: HexString,
|
|
370
|
+
options: SyncOptions = {}
|
|
371
|
+
): void {
|
|
372
|
+
this.checkInitialized()
|
|
373
|
+
|
|
374
|
+
if (this.backgroundSyncInterval) {
|
|
375
|
+
this.stopBackgroundSync()
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
this.backgroundSyncInterval = setInterval(
|
|
379
|
+
() => {
|
|
380
|
+
this.sync(viewingKey, spendingKey, options).catch(err => {
|
|
381
|
+
options.listener?.onError?.(err, 'background')
|
|
382
|
+
})
|
|
383
|
+
},
|
|
384
|
+
this.config.backgroundSyncIntervalMs
|
|
385
|
+
)
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/**
|
|
389
|
+
* Stop background sync
|
|
390
|
+
*/
|
|
391
|
+
stopBackgroundSync(): void {
|
|
392
|
+
if (this.backgroundSyncInterval) {
|
|
393
|
+
clearInterval(this.backgroundSyncInterval)
|
|
394
|
+
this.backgroundSyncInterval = null
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Shutdown the sync manager
|
|
400
|
+
*/
|
|
401
|
+
async shutdown(): Promise<void> {
|
|
402
|
+
this.stopBackgroundSync()
|
|
403
|
+
await Promise.all(this.providers.map(p => p.shutdown()))
|
|
404
|
+
this.initialized = false
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// ─── Private Methods ────────────────────────────────────────────────────────
|
|
408
|
+
|
|
409
|
+
private checkInitialized(): void {
|
|
410
|
+
if (!this.initialized || !this.state) {
|
|
411
|
+
throw new Error('SyncManager not initialized. Call initialize() first.')
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
private getProviderForChain(chainId: ChainId): ObliviousSyncProvider | null {
|
|
416
|
+
// Find first available provider for this chain
|
|
417
|
+
for (const provider of this.providers) {
|
|
418
|
+
if (provider.supportedChains.includes(chainId)) {
|
|
419
|
+
return provider
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
return null
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
private async syncChain(
|
|
426
|
+
chainId: ChainId,
|
|
427
|
+
viewingKey: ViewingKey,
|
|
428
|
+
_spendingKey: HexString,
|
|
429
|
+
options: SyncOptions
|
|
430
|
+
): Promise<EncryptedNote[]> {
|
|
431
|
+
const provider = this.getProviderForChain(chainId)
|
|
432
|
+
if (!provider) {
|
|
433
|
+
throw new ObliviousSyncError(
|
|
434
|
+
`No provider available for chain: ${chainId}`,
|
|
435
|
+
ObliviousSyncErrorCode.CHAIN_NOT_SUPPORTED,
|
|
436
|
+
{ chainId }
|
|
437
|
+
)
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Get current height
|
|
441
|
+
const currentHeight = await provider.getCurrentHeight(chainId)
|
|
442
|
+
const startHeight = this.state!.syncHeights.get(chainId) ?? 0n
|
|
443
|
+
const batchSize = BigInt(options.batchSize ?? this.config.maxBatchSize)
|
|
444
|
+
|
|
445
|
+
const allNotes: EncryptedNote[] = []
|
|
446
|
+
let syncedHeight = startHeight
|
|
447
|
+
|
|
448
|
+
// Sync in batches
|
|
449
|
+
while (syncedHeight < currentHeight) {
|
|
450
|
+
if (options.signal?.aborted) {
|
|
451
|
+
throw new Error('Sync aborted')
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
const endHeight = syncedHeight + batchSize > currentHeight
|
|
455
|
+
? currentHeight
|
|
456
|
+
: syncedHeight + batchSize
|
|
457
|
+
|
|
458
|
+
const blockRange: BlockRange = {
|
|
459
|
+
startBlock: syncedHeight + 1n,
|
|
460
|
+
endBlock: endHeight,
|
|
461
|
+
chainId,
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Scan for notes (obliviously)
|
|
465
|
+
const notes = await provider.scanForNotes(viewingKey.key, blockRange)
|
|
466
|
+
// Use loop to avoid stack overflow with large arrays
|
|
467
|
+
for (const note of notes) {
|
|
468
|
+
allNotes.push(note)
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Emit progress
|
|
472
|
+
const percentComplete = Number((endHeight * 100n) / currentHeight)
|
|
473
|
+
options.listener?.onProgress?.({
|
|
474
|
+
chainId,
|
|
475
|
+
currentBlock: endHeight,
|
|
476
|
+
targetBlock: currentHeight,
|
|
477
|
+
percentComplete,
|
|
478
|
+
notesFound: allNotes.length,
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
// Emit notes found
|
|
482
|
+
if (notes.length > 0) {
|
|
483
|
+
options.listener?.onNotesFound?.(notes, chainId)
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
syncedHeight = endHeight
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Update state
|
|
490
|
+
this.state = updateSyncState(this.state!, chainId, syncedHeight, allNotes)
|
|
491
|
+
|
|
492
|
+
return allNotes
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
/**
|
|
497
|
+
* Create a sync manager with default configuration
|
|
498
|
+
*/
|
|
499
|
+
export function createSyncManager(
|
|
500
|
+
providers: ObliviousSyncProvider | ObliviousSyncProvider[],
|
|
501
|
+
config?: Partial<SyncManagerConfig>
|
|
502
|
+
): SyncManager {
|
|
503
|
+
return new SyncManager(providers, config)
|
|
504
|
+
}
|