@sip-protocol/sdk 0.7.3 → 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 +47556 -19603
- package/dist/browser.mjs +628 -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-3M3HNQCW.mjs → chunk-YWGJ77A2.mjs} +28656 -13103
- 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-DIBZHOOQ.d.ts → index-DXh2IGkz.d.ts} +21239 -10304
- package/dist/{index-8MQz13eJ.d.mts → index-DeE1ZzA4.d.mts} +21239 -10304
- package/dist/index.d.mts +9 -3
- package/dist/index.d.ts +9 -3
- package/dist/index.js +48396 -19623
- package/dist/index.mjs +537 -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 +252 -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 +47 -6
- 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 +186 -33
- package/src/chains/solana/providers/index.ts +31 -0
- package/src/chains/solana/providers/interface.ts +61 -18
- 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 +338 -67
- package/src/chains/solana/rpc-client.ts +1150 -0
- package/src/chains/solana/scan.ts +83 -66
- 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 +57 -6
- 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 +23 -0
- package/src/compliance/range-sas.ts +398 -33
- 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 +686 -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 +254 -4
- package/src/privacy-backends/interface.ts +649 -6
- 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.ts +13 -17
- package/src/privacy-backends/private-swap.ts +570 -0
- package/src/privacy-backends/rate-limiter.ts +683 -0
- package/src/privacy-backends/registry.ts +414 -2
- package/src/privacy-backends/router.ts +283 -3
- package/src/privacy-backends/shadowwire.ts +449 -0
- package/src/privacy-backends/sip-native.ts +3 -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 +110 -29
- 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/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-2XIVXWHA.mjs +0 -1930
- package/dist/chunk-3INS3PR5.mjs +0 -884
- package/dist/chunk-3OVABDRH.mjs +0 -17096
- package/dist/chunk-7RFRWDCW.mjs +0 -1504
- package/dist/chunk-DLDWZFYC.mjs +0 -1495
- package/dist/chunk-E6SZWREQ.mjs +0 -57
- package/dist/chunk-F6F73W35.mjs +0 -16166
- package/dist/chunk-G33LB27A.mjs +0 -16166
- package/dist/chunk-HGU6HZRC.mjs +0 -231
- package/dist/chunk-L2K34JCU.mjs +0 -1496
- package/dist/chunk-OFDBEIEK.mjs +0 -16166
- package/dist/chunk-SF7YSLF5.mjs +0 -1515
- package/dist/chunk-SN4ZDTVW.mjs +0 -16166
- package/dist/chunk-WWUSGOXE.mjs +0 -17129
- package/dist/constants-VOI7BSLK.mjs +0 -27
- package/dist/index-B71aXVzk.d.ts +0 -13264
- 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-pOIIuwfV.d.mts +0 -13264
- package/dist/index-xbWjohNq.d.mts +0 -11390
- package/dist/solana-4O4K45VU.mjs +0 -46
- package/dist/solana-5EMCTPTS.mjs +0 -46
- package/dist/solana-NDABAZ6P.mjs +0 -56
- package/dist/solana-Q4NAVBTS.mjs +0 -46
- package/dist/solana-ZYO63LY5.mjs +0 -46
|
@@ -0,0 +1,816 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solana Noir Verifier
|
|
3
|
+
*
|
|
4
|
+
* Verifies Noir ZK proofs on Solana blockchain.
|
|
5
|
+
*
|
|
6
|
+
* ## Overview
|
|
7
|
+
*
|
|
8
|
+
* This module enables verification of SIP Protocol's Noir proofs on Solana:
|
|
9
|
+
* - Funding Proofs: Prove sufficient balance without revealing amount
|
|
10
|
+
* - Validity Proofs: Prove intent authorization without revealing sender
|
|
11
|
+
* - Fulfillment Proofs: Prove correct execution without revealing path
|
|
12
|
+
*
|
|
13
|
+
* ## Usage
|
|
14
|
+
*
|
|
15
|
+
* ```typescript
|
|
16
|
+
* import { SolanaNoirVerifier } from '@sip-protocol/sdk'
|
|
17
|
+
* import { NoirProofProvider } from '@sip-protocol/sdk'
|
|
18
|
+
*
|
|
19
|
+
* // Generate proof
|
|
20
|
+
* const provider = new NoirProofProvider()
|
|
21
|
+
* await provider.initialize()
|
|
22
|
+
* const { proof } = await provider.generateFundingProof(params)
|
|
23
|
+
*
|
|
24
|
+
* // Verify on Solana
|
|
25
|
+
* const verifier = new SolanaNoirVerifier({ network: 'devnet' })
|
|
26
|
+
* await verifier.initialize()
|
|
27
|
+
*
|
|
28
|
+
* // Off-chain verification (fast, no transaction)
|
|
29
|
+
* const isValid = await verifier.verifyOffChain(proof)
|
|
30
|
+
*
|
|
31
|
+
* // On-chain verification (submits transaction)
|
|
32
|
+
* const result = await verifier.verifyOnChain(proof, wallet)
|
|
33
|
+
* ```
|
|
34
|
+
*
|
|
35
|
+
* ## Architecture
|
|
36
|
+
*
|
|
37
|
+
* ```
|
|
38
|
+
* ┌─────────────────────────────────────────────────────────────┐
|
|
39
|
+
* │ CLIENT SIDE │
|
|
40
|
+
* │ ┌─────────────┐ ┌──────────────────┐ │
|
|
41
|
+
* │ │ Noir Proof │───►│ SolanaNoirVerifier│ │
|
|
42
|
+
* │ │ Provider │ │ - serialize() │ │
|
|
43
|
+
* │ └─────────────┘ │ - verifyOffChain()│ │
|
|
44
|
+
* │ │ - verifyOnChain() │ │
|
|
45
|
+
* │ └────────┬─────────┘ │
|
|
46
|
+
* └──────────────────────────────┼─────────────────────────────┘
|
|
47
|
+
* │
|
|
48
|
+
* ▼
|
|
49
|
+
* ┌─────────────────────────────────────────────────────────────┐
|
|
50
|
+
* │ SOLANA BLOCKCHAIN │
|
|
51
|
+
* │ ┌──────────────────────────────────────────────────────┐ │
|
|
52
|
+
* │ │ ZK Proof Verification Program │ │
|
|
53
|
+
* │ │ - Deserialize proof │ │
|
|
54
|
+
* │ │ - Load verification key │ │
|
|
55
|
+
* │ │ - Execute pairing checks │ │
|
|
56
|
+
* │ │ - Return verification result │ │
|
|
57
|
+
* │ └──────────────────────────────────────────────────────┘ │
|
|
58
|
+
* └─────────────────────────────────────────────────────────────┘
|
|
59
|
+
* ```
|
|
60
|
+
*
|
|
61
|
+
* @module solana/noir-verifier
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
import type { ZKProof } from '@sip-protocol/types'
|
|
65
|
+
import {
|
|
66
|
+
Connection,
|
|
67
|
+
PublicKey,
|
|
68
|
+
Transaction,
|
|
69
|
+
TransactionInstruction,
|
|
70
|
+
ComputeBudgetProgram,
|
|
71
|
+
} from '@solana/web3.js'
|
|
72
|
+
import {
|
|
73
|
+
type NoirCircuitType,
|
|
74
|
+
type SolanaNoirVerifierConfig,
|
|
75
|
+
type SolanaVerificationKey,
|
|
76
|
+
type SolanaSerializedProof,
|
|
77
|
+
type SolanaVerificationResult,
|
|
78
|
+
type SolanaVerifyInstruction,
|
|
79
|
+
type ProofStatistics,
|
|
80
|
+
type BatchVerificationRequest,
|
|
81
|
+
type BatchVerificationResult,
|
|
82
|
+
CIRCUIT_METADATA,
|
|
83
|
+
DEFAULT_RPC_URLS,
|
|
84
|
+
SOLANA_ZK_PROGRAM_IDS,
|
|
85
|
+
SolanaNoirError,
|
|
86
|
+
SolanaNoirErrorCode,
|
|
87
|
+
isNoirCircuitType,
|
|
88
|
+
isValidSolanaProof,
|
|
89
|
+
estimateComputeUnits,
|
|
90
|
+
getSunspotVerifierProgramId,
|
|
91
|
+
} from './noir-verifier-types'
|
|
92
|
+
|
|
93
|
+
// Re-export types for convenience
|
|
94
|
+
export * from './noir-verifier-types'
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Solana Noir Verifier
|
|
98
|
+
*
|
|
99
|
+
* Enables verification of Noir ZK proofs on Solana blockchain.
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* const verifier = new SolanaNoirVerifier({
|
|
104
|
+
* network: 'devnet',
|
|
105
|
+
* verbose: true,
|
|
106
|
+
* })
|
|
107
|
+
*
|
|
108
|
+
* await verifier.initialize()
|
|
109
|
+
*
|
|
110
|
+
* // Verify proof off-chain (no transaction)
|
|
111
|
+
* const valid = await verifier.verifyOffChain(proof)
|
|
112
|
+
*
|
|
113
|
+
* // Or verify on-chain (submits transaction)
|
|
114
|
+
* const result = await verifier.verifyOnChain(proof, wallet)
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export class SolanaNoirVerifier {
|
|
118
|
+
private config: Required<SolanaNoirVerifierConfig>
|
|
119
|
+
private _isReady = false
|
|
120
|
+
private verificationKeys: Map<NoirCircuitType, SolanaVerificationKey> = new Map()
|
|
121
|
+
private connection: Connection
|
|
122
|
+
|
|
123
|
+
constructor(config: SolanaNoirVerifierConfig = {}) {
|
|
124
|
+
const network = config.network ?? 'devnet'
|
|
125
|
+
|
|
126
|
+
this.config = {
|
|
127
|
+
network,
|
|
128
|
+
rpcUrl: config.rpcUrl ?? DEFAULT_RPC_URLS[network],
|
|
129
|
+
programId: config.programId ?? SOLANA_ZK_PROGRAM_IDS.SIP_NOIR_VERIFIER,
|
|
130
|
+
verbose: config.verbose ?? false,
|
|
131
|
+
commitment: config.commitment ?? 'confirmed',
|
|
132
|
+
maxComputeUnits: config.maxComputeUnits ?? 400000,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
this.connection = new Connection(this.config.rpcUrl, this.config.commitment)
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Get the Solana connection
|
|
140
|
+
*/
|
|
141
|
+
getConnection(): Connection {
|
|
142
|
+
return this.connection
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Check if verifier is initialized
|
|
147
|
+
*/
|
|
148
|
+
get isReady(): boolean {
|
|
149
|
+
return this._isReady
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Get current configuration
|
|
154
|
+
*/
|
|
155
|
+
getConfig(): Readonly<SolanaNoirVerifierConfig> {
|
|
156
|
+
return { ...this.config }
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Get RPC URL being used
|
|
161
|
+
*/
|
|
162
|
+
getRpcUrl(): string {
|
|
163
|
+
return this.config.rpcUrl
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Initialize the verifier
|
|
168
|
+
*
|
|
169
|
+
* Loads verification keys for all circuit types.
|
|
170
|
+
*/
|
|
171
|
+
async initialize(): Promise<void> {
|
|
172
|
+
if (this._isReady) {
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (this.config.verbose) {
|
|
177
|
+
console.log('[SolanaNoirVerifier] Initializing...')
|
|
178
|
+
console.log(`[SolanaNoirVerifier] Network: ${this.config.network}`)
|
|
179
|
+
console.log(`[SolanaNoirVerifier] RPC: ${this.config.rpcUrl}`)
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
try {
|
|
183
|
+
// Generate verification keys for each circuit type
|
|
184
|
+
for (const circuitType of ['funding', 'validity', 'fulfillment'] as NoirCircuitType[]) {
|
|
185
|
+
const vkey = await this.generateVerificationKey(circuitType)
|
|
186
|
+
this.verificationKeys.set(circuitType, vkey)
|
|
187
|
+
|
|
188
|
+
if (this.config.verbose) {
|
|
189
|
+
console.log(`[SolanaNoirVerifier] Loaded ${circuitType} verification key`)
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
this._isReady = true
|
|
194
|
+
|
|
195
|
+
if (this.config.verbose) {
|
|
196
|
+
console.log('[SolanaNoirVerifier] Initialization complete')
|
|
197
|
+
}
|
|
198
|
+
} catch (error) {
|
|
199
|
+
throw new SolanaNoirError(
|
|
200
|
+
`Failed to initialize verifier: ${error instanceof Error ? error.message : String(error)}`,
|
|
201
|
+
SolanaNoirErrorCode.NETWORK_ERROR,
|
|
202
|
+
{ error }
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Generate verification key for a circuit type
|
|
209
|
+
*
|
|
210
|
+
* Creates the cryptographic verification key needed to verify proofs.
|
|
211
|
+
*/
|
|
212
|
+
async generateVerificationKey(circuitType: NoirCircuitType): Promise<SolanaVerificationKey> {
|
|
213
|
+
if (!isNoirCircuitType(circuitType)) {
|
|
214
|
+
throw new SolanaNoirError(
|
|
215
|
+
`Unsupported circuit type: ${circuitType}`,
|
|
216
|
+
SolanaNoirErrorCode.UNSUPPORTED_CIRCUIT
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const metadata = CIRCUIT_METADATA[circuitType]
|
|
221
|
+
|
|
222
|
+
// In production, this would load the actual verification key from
|
|
223
|
+
// the compiled circuit artifacts. For now, we generate a deterministic
|
|
224
|
+
// placeholder that matches the circuit structure.
|
|
225
|
+
const keyBytes = this.generateVKeyBytes(circuitType)
|
|
226
|
+
const keyHash = await this.hashBytes(keyBytes)
|
|
227
|
+
|
|
228
|
+
return {
|
|
229
|
+
circuitType,
|
|
230
|
+
keyBytes,
|
|
231
|
+
keyHash,
|
|
232
|
+
publicInputCount: metadata.publicInputCount,
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Serialize a proof for Solana
|
|
238
|
+
*
|
|
239
|
+
* Converts a Noir proof into a format suitable for Solana transaction.
|
|
240
|
+
*/
|
|
241
|
+
serializeProof(proof: ZKProof): SolanaSerializedProof {
|
|
242
|
+
if (!isValidSolanaProof(proof)) {
|
|
243
|
+
throw new SolanaNoirError(
|
|
244
|
+
'Invalid proof structure',
|
|
245
|
+
SolanaNoirErrorCode.INVALID_PROOF_FORMAT,
|
|
246
|
+
{ proof }
|
|
247
|
+
)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const circuitType = proof.type as NoirCircuitType
|
|
251
|
+
|
|
252
|
+
// Convert hex proof to bytes
|
|
253
|
+
const proofHex = proof.proof.startsWith('0x') ? proof.proof.slice(2) : proof.proof
|
|
254
|
+
const proofBytes = this.hexToBytes(proofHex)
|
|
255
|
+
|
|
256
|
+
// Convert public inputs to bytes
|
|
257
|
+
const publicInputs = proof.publicInputs.map((input) => {
|
|
258
|
+
const inputHex = input.startsWith('0x') ? input.slice(2) : input
|
|
259
|
+
return this.hexToBytes(inputHex.padStart(64, '0'))
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
const totalSize = proofBytes.length + publicInputs.reduce((sum, pi) => sum + pi.length, 0)
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
circuitType,
|
|
266
|
+
proofBytes,
|
|
267
|
+
publicInputs,
|
|
268
|
+
totalSize,
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Verify a proof off-chain
|
|
274
|
+
*
|
|
275
|
+
* Fast verification without submitting a transaction.
|
|
276
|
+
* Uses the local Noir backend for verification if @aztec/bb.js is available,
|
|
277
|
+
* otherwise falls back to mock verification.
|
|
278
|
+
*
|
|
279
|
+
* @param proof - The proof to verify
|
|
280
|
+
* @returns true if valid, false otherwise
|
|
281
|
+
*
|
|
282
|
+
* @remarks
|
|
283
|
+
* For production use, install @aztec/bb.js as a dependency:
|
|
284
|
+
* ```bash
|
|
285
|
+
* npm install @aztec/bb.js
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
async verifyOffChain(proof: ZKProof): Promise<boolean> {
|
|
289
|
+
this.ensureReady()
|
|
290
|
+
|
|
291
|
+
if (!isValidSolanaProof(proof)) {
|
|
292
|
+
return false
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const circuitType = proof.type as NoirCircuitType
|
|
296
|
+
const vkey = this.verificationKeys.get(circuitType)
|
|
297
|
+
|
|
298
|
+
if (!vkey) {
|
|
299
|
+
throw new SolanaNoirError(
|
|
300
|
+
`Verification key not found for circuit: ${circuitType}`,
|
|
301
|
+
SolanaNoirErrorCode.VKEY_NOT_FOUND
|
|
302
|
+
)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
try {
|
|
306
|
+
// Try to import the Noir backend for real verification
|
|
307
|
+
const bbjs = await this.tryImportBBJS()
|
|
308
|
+
|
|
309
|
+
if (bbjs) {
|
|
310
|
+
return await this.verifyWithBackend(bbjs, proof, circuitType)
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Fallback to mock verification if @aztec/bb.js not available
|
|
314
|
+
if (this.config.verbose) {
|
|
315
|
+
console.log('[SolanaNoirVerifier] @aztec/bb.js not available, using mock verification')
|
|
316
|
+
}
|
|
317
|
+
return this.mockVerify(proof)
|
|
318
|
+
} catch (error) {
|
|
319
|
+
if (this.config.verbose) {
|
|
320
|
+
console.error('[SolanaNoirVerifier] Off-chain verification error:', error)
|
|
321
|
+
}
|
|
322
|
+
return false
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Try to import @aztec/bb.js dynamically
|
|
328
|
+
* Returns null if not available (allows graceful fallback)
|
|
329
|
+
*/
|
|
330
|
+
private async tryImportBBJS(): Promise<{ UltraHonkBackend: unknown } | null> {
|
|
331
|
+
try {
|
|
332
|
+
// Dynamic import - will fail if package not installed
|
|
333
|
+
const bbjs = await import('@aztec/bb.js')
|
|
334
|
+
return bbjs
|
|
335
|
+
} catch {
|
|
336
|
+
// Package not installed - this is expected in many environments
|
|
337
|
+
return null
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Verify proof using the real Noir backend
|
|
343
|
+
*/
|
|
344
|
+
private async verifyWithBackend(
|
|
345
|
+
bbjs: { UltraHonkBackend: unknown },
|
|
346
|
+
proof: ZKProof,
|
|
347
|
+
circuitType: NoirCircuitType
|
|
348
|
+
): Promise<boolean> {
|
|
349
|
+
// Load the appropriate circuit artifact
|
|
350
|
+
const circuit = await this.loadCircuitArtifact(circuitType)
|
|
351
|
+
|
|
352
|
+
// Create backend and verify
|
|
353
|
+
// Cast required: bbjs is dynamically imported with unknown types
|
|
354
|
+
const Backend = bbjs.UltraHonkBackend as new (bytecode: string) => {
|
|
355
|
+
verifyProof(args: { proof: Uint8Array; publicInputs: string[] }): Promise<boolean>
|
|
356
|
+
destroy(): Promise<void>
|
|
357
|
+
}
|
|
358
|
+
const backend = new Backend(circuit.bytecode)
|
|
359
|
+
|
|
360
|
+
try {
|
|
361
|
+
// Convert proof to bytes
|
|
362
|
+
const proofHex = proof.proof.startsWith('0x') ? proof.proof.slice(2) : proof.proof
|
|
363
|
+
const proofBytes = this.hexToBytes(proofHex)
|
|
364
|
+
|
|
365
|
+
// Strip 0x prefix from public inputs
|
|
366
|
+
const publicInputs = proof.publicInputs.map((input) =>
|
|
367
|
+
input.startsWith('0x') ? input.slice(2) : input
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
const isValid = await backend.verifyProof({
|
|
371
|
+
proof: proofBytes,
|
|
372
|
+
publicInputs,
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
if (this.config.verbose) {
|
|
376
|
+
console.log(`[SolanaNoirVerifier] Off-chain verification: ${isValid ? 'VALID' : 'INVALID'}`)
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return isValid
|
|
380
|
+
} finally {
|
|
381
|
+
await backend.destroy()
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Mock verification for testing and development
|
|
387
|
+
*
|
|
388
|
+
* WARNING: This does NOT provide cryptographic security.
|
|
389
|
+
* Only used when @aztec/bb.js is not available.
|
|
390
|
+
*/
|
|
391
|
+
private mockVerify(proof: ZKProof): boolean {
|
|
392
|
+
// Basic structural validation only
|
|
393
|
+
const proofHex = proof.proof.startsWith('0x') ? proof.proof.slice(2) : proof.proof
|
|
394
|
+
|
|
395
|
+
// Check proof has reasonable size (real proofs are ~2KB)
|
|
396
|
+
if (proofHex.length < 128) {
|
|
397
|
+
return false
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Check public inputs exist
|
|
401
|
+
if (proof.publicInputs.length === 0) {
|
|
402
|
+
return false
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
if (this.config.verbose) {
|
|
406
|
+
console.log('[SolanaNoirVerifier] Mock verification: VALID (not cryptographically verified)')
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return true
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Verify a proof on-chain
|
|
414
|
+
*
|
|
415
|
+
* Submits a transaction to Solana to verify the proof using the Sunspot verifier.
|
|
416
|
+
*
|
|
417
|
+
* @param proof - The proof to verify
|
|
418
|
+
* @param wallet - Wallet interface with signTransaction method
|
|
419
|
+
* @returns Verification result with transaction signature
|
|
420
|
+
*/
|
|
421
|
+
async verifyOnChain(
|
|
422
|
+
proof: ZKProof,
|
|
423
|
+
wallet: {
|
|
424
|
+
publicKey: { toBase58(): string }
|
|
425
|
+
signTransaction: <T extends Transaction>(tx: T) => Promise<T>
|
|
426
|
+
}
|
|
427
|
+
): Promise<SolanaVerificationResult> {
|
|
428
|
+
this.ensureReady()
|
|
429
|
+
|
|
430
|
+
if (!isValidSolanaProof(proof)) {
|
|
431
|
+
return {
|
|
432
|
+
valid: false,
|
|
433
|
+
error: 'Invalid proof structure',
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const circuitType = proof.type as NoirCircuitType
|
|
438
|
+
|
|
439
|
+
// Get the Sunspot verifier program ID for this circuit type
|
|
440
|
+
const verifierProgramId = getSunspotVerifierProgramId(circuitType, this.config.network)
|
|
441
|
+
if (!verifierProgramId) {
|
|
442
|
+
return {
|
|
443
|
+
valid: false,
|
|
444
|
+
error: `No Sunspot verifier deployed for circuit type: ${circuitType} on ${this.config.network}`,
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
try {
|
|
449
|
+
// First verify off-chain (fast fail for invalid proofs)
|
|
450
|
+
const offChainValid = await this.verifyOffChain(proof)
|
|
451
|
+
if (!offChainValid) {
|
|
452
|
+
return {
|
|
453
|
+
valid: false,
|
|
454
|
+
error: 'Proof failed off-chain verification',
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Create Sunspot verify instruction
|
|
459
|
+
const instruction = this.createSunspotVerifyInstruction(proof, verifierProgramId)
|
|
460
|
+
|
|
461
|
+
// Estimate compute units
|
|
462
|
+
const computeUnits = estimateComputeUnits(circuitType)
|
|
463
|
+
|
|
464
|
+
if (computeUnits > this.config.maxComputeUnits) {
|
|
465
|
+
return {
|
|
466
|
+
valid: false,
|
|
467
|
+
error: `Proof requires ${computeUnits} CU, max is ${this.config.maxComputeUnits}`,
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
// Create transaction with compute budget
|
|
472
|
+
const transaction = new Transaction()
|
|
473
|
+
|
|
474
|
+
// Add compute budget instruction
|
|
475
|
+
transaction.add(
|
|
476
|
+
ComputeBudgetProgram.setComputeUnitLimit({ units: this.config.maxComputeUnits })
|
|
477
|
+
)
|
|
478
|
+
|
|
479
|
+
// Add verify instruction
|
|
480
|
+
transaction.add(instruction)
|
|
481
|
+
|
|
482
|
+
// Get recent blockhash
|
|
483
|
+
const { blockhash, lastValidBlockHeight } = await this.connection.getLatestBlockhash(
|
|
484
|
+
this.config.commitment
|
|
485
|
+
)
|
|
486
|
+
transaction.recentBlockhash = blockhash
|
|
487
|
+
transaction.feePayer = new PublicKey(wallet.publicKey.toBase58())
|
|
488
|
+
|
|
489
|
+
// Sign transaction
|
|
490
|
+
const signedTransaction = await wallet.signTransaction(transaction)
|
|
491
|
+
|
|
492
|
+
// Send transaction
|
|
493
|
+
const signature = await this.connection.sendRawTransaction(signedTransaction.serialize())
|
|
494
|
+
|
|
495
|
+
// Wait for confirmation
|
|
496
|
+
const confirmation = await this.connection.confirmTransaction(
|
|
497
|
+
{
|
|
498
|
+
signature,
|
|
499
|
+
blockhash,
|
|
500
|
+
lastValidBlockHeight,
|
|
501
|
+
},
|
|
502
|
+
this.config.commitment
|
|
503
|
+
)
|
|
504
|
+
|
|
505
|
+
if (confirmation.value.err) {
|
|
506
|
+
return {
|
|
507
|
+
valid: false,
|
|
508
|
+
error: `Transaction failed: ${JSON.stringify(confirmation.value.err)}`,
|
|
509
|
+
signature,
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (this.config.verbose) {
|
|
514
|
+
console.log(`[SolanaNoirVerifier] On-chain verification: VALID`)
|
|
515
|
+
console.log(`[SolanaNoirVerifier] Signature: ${signature}`)
|
|
516
|
+
console.log(`[SolanaNoirVerifier] Compute units: ${computeUnits}`)
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
return {
|
|
520
|
+
valid: true,
|
|
521
|
+
signature,
|
|
522
|
+
computeUnits,
|
|
523
|
+
}
|
|
524
|
+
} catch (error) {
|
|
525
|
+
return {
|
|
526
|
+
valid: false,
|
|
527
|
+
error: error instanceof Error ? error.message : String(error),
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Create a Sunspot verifier instruction
|
|
534
|
+
*
|
|
535
|
+
* Sunspot verifiers expect instruction data in format: proof_bytes || public_witness_bytes
|
|
536
|
+
*/
|
|
537
|
+
createSunspotVerifyInstruction(proof: ZKProof, programId: string): TransactionInstruction {
|
|
538
|
+
const serialized = this.serializeProofForSunspot(proof)
|
|
539
|
+
|
|
540
|
+
return new TransactionInstruction({
|
|
541
|
+
programId: new PublicKey(programId),
|
|
542
|
+
keys: [], // Sunspot verifiers don't require any accounts
|
|
543
|
+
data: serialized,
|
|
544
|
+
})
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* Serialize proof for Sunspot verifier
|
|
549
|
+
*
|
|
550
|
+
* Sunspot expects: proof_bytes || public_witness_bytes
|
|
551
|
+
* where public_witness contains the number of public inputs followed by the values
|
|
552
|
+
*/
|
|
553
|
+
serializeProofForSunspot(proof: ZKProof): Buffer {
|
|
554
|
+
// Convert hex proof to bytes
|
|
555
|
+
const proofHex = proof.proof.startsWith('0x') ? proof.proof.slice(2) : proof.proof
|
|
556
|
+
const proofBytes = Buffer.from(proofHex, 'hex')
|
|
557
|
+
|
|
558
|
+
// Convert public inputs to public witness format
|
|
559
|
+
// Format: [num_inputs (4 bytes LE)] [input_1 (32 bytes)] [input_2 (32 bytes)] ...
|
|
560
|
+
const numInputs = proof.publicInputs.length
|
|
561
|
+
const publicWitnessSize = 4 + numInputs * 32
|
|
562
|
+
|
|
563
|
+
const publicWitness = Buffer.alloc(publicWitnessSize)
|
|
564
|
+
publicWitness.writeUInt32LE(numInputs, 0)
|
|
565
|
+
|
|
566
|
+
for (let i = 0; i < numInputs; i++) {
|
|
567
|
+
const inputHex = proof.publicInputs[i].startsWith('0x')
|
|
568
|
+
? proof.publicInputs[i].slice(2)
|
|
569
|
+
: proof.publicInputs[i]
|
|
570
|
+
const inputBytes = Buffer.from(inputHex.padStart(64, '0'), 'hex')
|
|
571
|
+
inputBytes.copy(publicWitness, 4 + i * 32)
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// Concatenate proof and public witness
|
|
575
|
+
return Buffer.concat([proofBytes, publicWitness])
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
/**
|
|
579
|
+
* Create a verify instruction for Solana
|
|
580
|
+
*
|
|
581
|
+
* Builds the instruction data and account metas needed for verification.
|
|
582
|
+
*/
|
|
583
|
+
createVerifyInstruction(proof: ZKProof): SolanaVerifyInstruction {
|
|
584
|
+
const serialized = this.serializeProof(proof)
|
|
585
|
+
const circuitType = proof.type as NoirCircuitType
|
|
586
|
+
|
|
587
|
+
// Build instruction data
|
|
588
|
+
// Format: [discriminator (1 byte)] [circuit_type (1 byte)] [proof_len (4 bytes)] [proof] [inputs]
|
|
589
|
+
const discriminator = 0x01 // Verify instruction
|
|
590
|
+
const circuitTypeId = this.circuitTypeToId(circuitType)
|
|
591
|
+
|
|
592
|
+
const proofLen = serialized.proofBytes.length
|
|
593
|
+
const data = new Uint8Array(
|
|
594
|
+
1 + 1 + 4 + proofLen + serialized.publicInputs.length * 32
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
let offset = 0
|
|
598
|
+
data[offset++] = discriminator
|
|
599
|
+
data[offset++] = circuitTypeId
|
|
600
|
+
|
|
601
|
+
// Write proof length (little-endian u32)
|
|
602
|
+
data[offset++] = proofLen & 0xff
|
|
603
|
+
data[offset++] = (proofLen >> 8) & 0xff
|
|
604
|
+
data[offset++] = (proofLen >> 16) & 0xff
|
|
605
|
+
data[offset++] = (proofLen >> 24) & 0xff
|
|
606
|
+
|
|
607
|
+
// Write proof bytes
|
|
608
|
+
data.set(serialized.proofBytes, offset)
|
|
609
|
+
offset += proofLen
|
|
610
|
+
|
|
611
|
+
// Write public inputs (each 32 bytes)
|
|
612
|
+
for (const input of serialized.publicInputs) {
|
|
613
|
+
data.set(input, offset)
|
|
614
|
+
offset += 32
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
return {
|
|
618
|
+
programId: this.config.programId,
|
|
619
|
+
data,
|
|
620
|
+
keys: [
|
|
621
|
+
{
|
|
622
|
+
pubkey: this.config.programId, // Verification key account (PDA)
|
|
623
|
+
isSigner: false,
|
|
624
|
+
isWritable: false,
|
|
625
|
+
},
|
|
626
|
+
],
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Get proof statistics
|
|
632
|
+
*
|
|
633
|
+
* Returns size and compute unit estimates for a proof.
|
|
634
|
+
*/
|
|
635
|
+
getProofStatistics(proof: ZKProof): ProofStatistics {
|
|
636
|
+
const serialized = this.serializeProof(proof)
|
|
637
|
+
const circuitType = proof.type as NoirCircuitType
|
|
638
|
+
|
|
639
|
+
return {
|
|
640
|
+
circuitType,
|
|
641
|
+
proofSize: serialized.proofBytes.length,
|
|
642
|
+
publicInputsSize: serialized.publicInputs.length * 32,
|
|
643
|
+
totalSize: serialized.totalSize,
|
|
644
|
+
estimatedComputeUnits: estimateComputeUnits(circuitType),
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Batch verify multiple proofs
|
|
650
|
+
*
|
|
651
|
+
* Verifies multiple proofs, optionally failing fast on first invalid.
|
|
652
|
+
*/
|
|
653
|
+
async batchVerify(request: BatchVerificationRequest): Promise<BatchVerificationResult> {
|
|
654
|
+
this.ensureReady()
|
|
655
|
+
|
|
656
|
+
const results: SolanaVerificationResult[] = []
|
|
657
|
+
let validCount = 0
|
|
658
|
+
let totalComputeUnits = 0
|
|
659
|
+
|
|
660
|
+
for (const proof of request.proofs) {
|
|
661
|
+
const valid = await this.verifyOffChain(proof)
|
|
662
|
+
const computeUnits = isValidSolanaProof(proof)
|
|
663
|
+
? estimateComputeUnits(proof.type as NoirCircuitType)
|
|
664
|
+
: 0
|
|
665
|
+
|
|
666
|
+
results.push({
|
|
667
|
+
valid,
|
|
668
|
+
computeUnits,
|
|
669
|
+
})
|
|
670
|
+
|
|
671
|
+
if (valid) {
|
|
672
|
+
validCount++
|
|
673
|
+
}
|
|
674
|
+
totalComputeUnits += computeUnits
|
|
675
|
+
|
|
676
|
+
// Fail fast if requested and we found an invalid proof
|
|
677
|
+
if (request.failFast && !valid) {
|
|
678
|
+
break
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return {
|
|
683
|
+
success: validCount === request.proofs.length,
|
|
684
|
+
results,
|
|
685
|
+
totalVerified: results.length,
|
|
686
|
+
validCount,
|
|
687
|
+
totalComputeUnits,
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/**
|
|
692
|
+
* Get verification key for a circuit type
|
|
693
|
+
*/
|
|
694
|
+
getVerificationKey(circuitType: NoirCircuitType): SolanaVerificationKey | undefined {
|
|
695
|
+
return this.verificationKeys.get(circuitType)
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Destroy the verifier and free resources
|
|
700
|
+
*/
|
|
701
|
+
async destroy(): Promise<void> {
|
|
702
|
+
this.verificationKeys.clear()
|
|
703
|
+
this._isReady = false
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// ─── Private Methods ───────────────────────────────────────────────────────
|
|
707
|
+
|
|
708
|
+
private ensureReady(): void {
|
|
709
|
+
if (!this._isReady) {
|
|
710
|
+
throw new SolanaNoirError(
|
|
711
|
+
'Verifier not initialized. Call initialize() first.',
|
|
712
|
+
SolanaNoirErrorCode.NETWORK_ERROR
|
|
713
|
+
)
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
private async loadCircuitArtifact(circuitType: NoirCircuitType): Promise<{ bytecode: string }> {
|
|
718
|
+
try {
|
|
719
|
+
// Dynamically import circuit artifacts
|
|
720
|
+
switch (circuitType) {
|
|
721
|
+
case 'funding':
|
|
722
|
+
return await import('../proofs/circuits/funding_proof.json') as { bytecode: string }
|
|
723
|
+
case 'validity':
|
|
724
|
+
return await import('../proofs/circuits/validity_proof.json') as { bytecode: string }
|
|
725
|
+
case 'fulfillment':
|
|
726
|
+
return await import('../proofs/circuits/fulfillment_proof.json') as { bytecode: string }
|
|
727
|
+
default:
|
|
728
|
+
throw new SolanaNoirError(
|
|
729
|
+
`Unknown circuit type: ${circuitType}`,
|
|
730
|
+
SolanaNoirErrorCode.UNSUPPORTED_CIRCUIT
|
|
731
|
+
)
|
|
732
|
+
}
|
|
733
|
+
} catch (error) {
|
|
734
|
+
// Circuit artifacts may not be compiled yet
|
|
735
|
+
if (error instanceof SolanaNoirError) {
|
|
736
|
+
throw error
|
|
737
|
+
}
|
|
738
|
+
throw new SolanaNoirError(
|
|
739
|
+
`Circuit artifact not found for ${circuitType}. ` +
|
|
740
|
+
`Ensure circuit is compiled: cd circuits && nargo compile`,
|
|
741
|
+
SolanaNoirErrorCode.VKEY_NOT_FOUND,
|
|
742
|
+
{ circuitType, originalError: error instanceof Error ? error.message : String(error) }
|
|
743
|
+
)
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
private generateVKeyBytes(circuitType: NoirCircuitType): Uint8Array {
|
|
748
|
+
// Generate deterministic verification key bytes
|
|
749
|
+
// In production, these would come from the compiled circuit
|
|
750
|
+
const metadata = CIRCUIT_METADATA[circuitType]
|
|
751
|
+
const seed = `sip-noir-vkey-${circuitType}-${metadata.versionHash}`
|
|
752
|
+
|
|
753
|
+
// Create deterministic bytes from seed
|
|
754
|
+
const encoder = new TextEncoder()
|
|
755
|
+
const seedBytes = encoder.encode(seed)
|
|
756
|
+
|
|
757
|
+
// Pad to 256 bytes (typical vkey size)
|
|
758
|
+
const vkeyBytes = new Uint8Array(256)
|
|
759
|
+
vkeyBytes.set(seedBytes.slice(0, Math.min(seedBytes.length, 256)))
|
|
760
|
+
|
|
761
|
+
return vkeyBytes
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
private async hashBytes(bytes: Uint8Array): Promise<string> {
|
|
765
|
+
const { sha256 } = await import('@noble/hashes/sha256')
|
|
766
|
+
const { bytesToHex } = await import('@noble/hashes/utils')
|
|
767
|
+
return bytesToHex(sha256(bytes))
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
private hexToBytes(hex: string): Uint8Array {
|
|
771
|
+
const h = hex.startsWith('0x') ? hex.slice(2) : hex
|
|
772
|
+
const bytes = new Uint8Array(h.length / 2)
|
|
773
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
774
|
+
bytes[i] = parseInt(h.slice(i * 2, i * 2 + 2), 16)
|
|
775
|
+
}
|
|
776
|
+
return bytes
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
private circuitTypeToId(circuitType: NoirCircuitType): number {
|
|
780
|
+
switch (circuitType) {
|
|
781
|
+
case 'funding':
|
|
782
|
+
return 0
|
|
783
|
+
case 'validity':
|
|
784
|
+
return 1
|
|
785
|
+
case 'fulfillment':
|
|
786
|
+
return 2
|
|
787
|
+
default:
|
|
788
|
+
return 255
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
/**
|
|
795
|
+
* Create a Solana Noir Verifier with default devnet configuration
|
|
796
|
+
*/
|
|
797
|
+
export function createDevnetVerifier(
|
|
798
|
+
config: Omit<SolanaNoirVerifierConfig, 'network'> = {}
|
|
799
|
+
): SolanaNoirVerifier {
|
|
800
|
+
return new SolanaNoirVerifier({
|
|
801
|
+
...config,
|
|
802
|
+
network: 'devnet',
|
|
803
|
+
})
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Create a Solana Noir Verifier with mainnet configuration
|
|
808
|
+
*/
|
|
809
|
+
export function createMainnetVerifier(
|
|
810
|
+
config: Omit<SolanaNoirVerifierConfig, 'network'> = {}
|
|
811
|
+
): SolanaNoirVerifier {
|
|
812
|
+
return new SolanaNoirVerifier({
|
|
813
|
+
...config,
|
|
814
|
+
network: 'mainnet-beta',
|
|
815
|
+
})
|
|
816
|
+
}
|