@sip-protocol/sdk 0.4.0 → 0.5.0
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/dist/browser.d.mts +1 -1
- package/dist/browser.d.ts +1 -1
- package/dist/browser.js +1866 -153
- package/dist/browser.mjs +14 -2
- package/dist/chunk-DMHBKRWV.mjs +14712 -0
- package/dist/chunk-HGU6HZRC.mjs +231 -0
- package/dist/chunk-J4Q4NJ2U.mjs +13544 -0
- package/dist/chunk-W2B7T6WU.mjs +14714 -0
- package/dist/index-5jAdWMA-.d.ts +8973 -0
- package/dist/index-B9Vkpaao.d.mts +8973 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1851 -138
- package/dist/index.mjs +14 -2
- package/dist/proofs/noir.mjs +1 -1
- package/package.json +1 -1
- package/src/compliance/compliance-manager.ts +87 -0
- package/src/compliance/conditional-threshold.ts +379 -0
- package/src/compliance/conditional.ts +382 -0
- package/src/compliance/derivation.ts +489 -0
- package/src/compliance/index.ts +50 -8
- package/src/compliance/pdf.ts +365 -0
- package/src/compliance/reports.ts +644 -0
- package/src/compliance/threshold.ts +529 -0
- package/src/compliance/types.ts +223 -0
- package/src/errors.ts +8 -0
- package/src/index.ts +29 -1
package/dist/index.mjs
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ATTESTATION_VERSION,
|
|
3
3
|
AptosStealthService,
|
|
4
|
+
AuditorKeyDerivation,
|
|
5
|
+
AuditorType,
|
|
4
6
|
BaseWalletAdapter,
|
|
5
7
|
CHAIN_NUMERIC_IDS,
|
|
6
8
|
CHAIN_PREFIXES,
|
|
7
9
|
ComplianceManager,
|
|
10
|
+
ComplianceReporter,
|
|
11
|
+
ConditionalDisclosure,
|
|
8
12
|
CosmosStealthService,
|
|
9
13
|
DEFAULT_THRESHOLD,
|
|
10
14
|
DEFAULT_TOTAL_ORACLES,
|
|
@@ -47,6 +51,7 @@ import {
|
|
|
47
51
|
SmartRouter,
|
|
48
52
|
SolanaWalletAdapter,
|
|
49
53
|
SwapStatus,
|
|
54
|
+
ThresholdViewingKey,
|
|
50
55
|
Treasury,
|
|
51
56
|
TrezorWalletAdapter,
|
|
52
57
|
WalletError,
|
|
@@ -129,6 +134,7 @@ import {
|
|
|
129
134
|
generateEd25519StealthAddress,
|
|
130
135
|
generateEd25519StealthMetaAddress,
|
|
131
136
|
generateIntentId,
|
|
137
|
+
generatePdfReport,
|
|
132
138
|
generateRandomBytes,
|
|
133
139
|
generateStealthAddress,
|
|
134
140
|
generateStealthMetaAddress,
|
|
@@ -232,7 +238,7 @@ import {
|
|
|
232
238
|
walletRegistry,
|
|
233
239
|
withSecureBuffer,
|
|
234
240
|
withSecureBufferSync
|
|
235
|
-
} from "./chunk-
|
|
241
|
+
} from "./chunk-DMHBKRWV.mjs";
|
|
236
242
|
import {
|
|
237
243
|
CryptoError,
|
|
238
244
|
EncryptionNotImplementedError,
|
|
@@ -248,14 +254,18 @@ import {
|
|
|
248
254
|
hasErrorCode,
|
|
249
255
|
isSIPError,
|
|
250
256
|
wrapError
|
|
251
|
-
} from "./chunk-
|
|
257
|
+
} from "./chunk-HGU6HZRC.mjs";
|
|
252
258
|
export {
|
|
253
259
|
ATTESTATION_VERSION,
|
|
254
260
|
AptosStealthService,
|
|
261
|
+
AuditorKeyDerivation,
|
|
262
|
+
AuditorType,
|
|
255
263
|
BaseWalletAdapter,
|
|
256
264
|
CHAIN_NUMERIC_IDS,
|
|
257
265
|
CHAIN_PREFIXES as COSMOS_CHAIN_PREFIXES,
|
|
258
266
|
ComplianceManager,
|
|
267
|
+
ComplianceReporter,
|
|
268
|
+
ConditionalDisclosure,
|
|
259
269
|
CosmosStealthService,
|
|
260
270
|
CryptoError,
|
|
261
271
|
DEFAULT_THRESHOLD,
|
|
@@ -307,6 +317,7 @@ export {
|
|
|
307
317
|
SmartRouter,
|
|
308
318
|
SolanaWalletAdapter,
|
|
309
319
|
SwapStatus,
|
|
320
|
+
ThresholdViewingKey,
|
|
310
321
|
Treasury,
|
|
311
322
|
TrezorWalletAdapter,
|
|
312
323
|
ValidationError,
|
|
@@ -391,6 +402,7 @@ export {
|
|
|
391
402
|
generateEd25519StealthAddress,
|
|
392
403
|
generateEd25519StealthMetaAddress,
|
|
393
404
|
generateIntentId,
|
|
405
|
+
generatePdfReport,
|
|
394
406
|
generateRandomBytes,
|
|
395
407
|
generateStealthAddress,
|
|
396
408
|
generateStealthMetaAddress,
|
package/dist/proofs/noir.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sip-protocol/sdk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Core SDK for Shielded Intents Protocol - Privacy layer for cross-chain transactions",
|
|
5
5
|
"author": "SIP Protocol <hello@sip-protocol.org>",
|
|
6
6
|
"homepage": "https://sip-protocol.org",
|
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
type GenerateReportParams,
|
|
57
57
|
type DisclosureRequest,
|
|
58
58
|
type AuditLogEntry,
|
|
59
|
+
type ComplianceMetrics,
|
|
59
60
|
type ViewingKey,
|
|
60
61
|
type HexString,
|
|
61
62
|
type ChainId,
|
|
@@ -610,6 +611,92 @@ export class ComplianceManager {
|
|
|
610
611
|
return Array.from(this.reports.values())
|
|
611
612
|
}
|
|
612
613
|
|
|
614
|
+
// ─── Dashboard Data API ──────────────────────────────────────────────────────
|
|
615
|
+
|
|
616
|
+
/**
|
|
617
|
+
* Get list of auditors for dashboard UI
|
|
618
|
+
*
|
|
619
|
+
* Returns a simplified view of auditors with essential info.
|
|
620
|
+
* Alias for getAllAuditors() but returns AuditorRegistration directly.
|
|
621
|
+
*
|
|
622
|
+
* @returns Array of auditor registrations
|
|
623
|
+
*/
|
|
624
|
+
getAuditorList(): AuditorRegistration[] {
|
|
625
|
+
return this.getAllAuditors()
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/**
|
|
629
|
+
* Get pending disclosure requests for dashboard
|
|
630
|
+
*
|
|
631
|
+
* Returns disclosure requests waiting for approval/denial.
|
|
632
|
+
* This is for disclosure REQUESTS, not disclosed transactions.
|
|
633
|
+
*
|
|
634
|
+
* @returns Array of pending disclosure requests
|
|
635
|
+
*/
|
|
636
|
+
getPendingDisclosures(): DisclosureRequest[] {
|
|
637
|
+
return this.getPendingRequests()
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* Get disclosure history for a specific auditor
|
|
642
|
+
*
|
|
643
|
+
* Returns all disclosed transactions that were shared with this auditor,
|
|
644
|
+
* sorted by disclosure date (most recent first).
|
|
645
|
+
*
|
|
646
|
+
* @param auditorId - Auditor ID to get history for
|
|
647
|
+
* @returns Array of disclosed transactions for this auditor
|
|
648
|
+
*/
|
|
649
|
+
getDisclosureHistory(auditorId: string): DisclosedTransaction[] {
|
|
650
|
+
return this.getDisclosedTransactions(auditorId)
|
|
651
|
+
.sort((a, b) => b.disclosedAt - a.disclosedAt)
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
/**
|
|
655
|
+
* Get compliance metrics for dashboard
|
|
656
|
+
*
|
|
657
|
+
* Calculates key compliance metrics including:
|
|
658
|
+
* - Total auditors (active + inactive)
|
|
659
|
+
* - Total disclosures made
|
|
660
|
+
* - Pending disclosure requests
|
|
661
|
+
* - Approval rate (approved / total resolved requests)
|
|
662
|
+
* - Average processing time for disclosure requests
|
|
663
|
+
*
|
|
664
|
+
* @returns Compliance metrics object
|
|
665
|
+
*/
|
|
666
|
+
getComplianceMetrics(): ComplianceMetrics {
|
|
667
|
+
const allAuditors = this.getAllAuditors()
|
|
668
|
+
const allDisclosures = this.getDisclosedTransactions()
|
|
669
|
+
const pendingRequests = this.getPendingRequests()
|
|
670
|
+
const allRequests = Array.from(this.disclosureRequests.values())
|
|
671
|
+
|
|
672
|
+
// Calculate approval rate
|
|
673
|
+
const resolvedRequests = allRequests.filter(r => r.status !== 'pending')
|
|
674
|
+
const approvedRequests = resolvedRequests.filter(r => r.status === 'approved')
|
|
675
|
+
const approvalRate = resolvedRequests.length > 0
|
|
676
|
+
? approvedRequests.length / resolvedRequests.length
|
|
677
|
+
: 0
|
|
678
|
+
|
|
679
|
+
// Calculate average processing time (in seconds)
|
|
680
|
+
let averageProcessingTime: number | undefined
|
|
681
|
+
if (resolvedRequests.length > 0) {
|
|
682
|
+
const totalProcessingTime = resolvedRequests.reduce((sum, req) => {
|
|
683
|
+
if (req.resolvedAt) {
|
|
684
|
+
return sum + (req.resolvedAt - req.requestedAt)
|
|
685
|
+
}
|
|
686
|
+
return sum
|
|
687
|
+
}, 0)
|
|
688
|
+
averageProcessingTime = totalProcessingTime / resolvedRequests.length
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
return {
|
|
692
|
+
totalAuditors: allAuditors.length,
|
|
693
|
+
totalDisclosures: allDisclosures.length,
|
|
694
|
+
pendingDisclosures: pendingRequests.length,
|
|
695
|
+
approvalRate,
|
|
696
|
+
averageProcessingTime,
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
|
|
613
700
|
// ─── Audit Log ───────────────────────────────────────────────────────────────
|
|
614
701
|
|
|
615
702
|
/**
|
|
@@ -0,0 +1,379 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conditional Amount Threshold Disclosure for SIP Protocol
|
|
3
|
+
*
|
|
4
|
+
* Implements automatic disclosure mechanisms based on transaction amount thresholds.
|
|
5
|
+
* Use Case: Regulatory requirement to report transactions over $10,000.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { createAmountThreshold, proveExceedsThreshold } from '@sip-protocol/sdk'
|
|
10
|
+
*
|
|
11
|
+
* // Create amount threshold (e.g., $10,000 USDC)
|
|
12
|
+
* const threshold = createAmountThreshold({
|
|
13
|
+
* viewingKey: auditorViewingKey,
|
|
14
|
+
* threshold: 10000_000000n, // $10,000 with 6 decimals
|
|
15
|
+
* commitment: amountCommitment,
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* // Prove amount exceeds threshold without revealing exact amount
|
|
19
|
+
* const proof = proveExceedsThreshold(
|
|
20
|
+
* actualAmount,
|
|
21
|
+
* threshold.threshold
|
|
22
|
+
* )
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { secp256k1 } from '@noble/curves/secp256k1'
|
|
27
|
+
import { sha256 } from '@noble/hashes/sha256'
|
|
28
|
+
import { bytesToHex, hexToBytes } from '@noble/hashes/utils'
|
|
29
|
+
import type { HexString } from '@sip-protocol/types'
|
|
30
|
+
import { commit, generateBlinding } from '../commitment'
|
|
31
|
+
import { ValidationError, ErrorCode } from '../errors'
|
|
32
|
+
|
|
33
|
+
// ─── Types ───────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Threshold disclosure configuration
|
|
37
|
+
*/
|
|
38
|
+
export interface ThresholdDisclosure {
|
|
39
|
+
/** The viewing key for disclosure */
|
|
40
|
+
viewingKey: string
|
|
41
|
+
/** The threshold amount */
|
|
42
|
+
threshold: bigint
|
|
43
|
+
/** The commitment to the amount */
|
|
44
|
+
commitment: string
|
|
45
|
+
/** Threshold configuration ID */
|
|
46
|
+
thresholdId: string
|
|
47
|
+
/** Creation timestamp */
|
|
48
|
+
createdAt: number
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Range proof that amount >= threshold
|
|
53
|
+
*
|
|
54
|
+
* This is a simplified commitment-based range proof.
|
|
55
|
+
* For production, this would use bulletproofs or similar ZK range proofs.
|
|
56
|
+
*/
|
|
57
|
+
export interface RangeProof {
|
|
58
|
+
/** Commitment to (amount - threshold) */
|
|
59
|
+
differenceCommitment: HexString
|
|
60
|
+
/** Blinding factor for the difference commitment */
|
|
61
|
+
differenceBlinding: HexString
|
|
62
|
+
/** The threshold being compared against */
|
|
63
|
+
threshold: bigint
|
|
64
|
+
/** Bit decomposition commitments (proves non-negativity) */
|
|
65
|
+
bitCommitments: HexString[]
|
|
66
|
+
/** Proof metadata */
|
|
67
|
+
metadata: {
|
|
68
|
+
/** Number of bits in the range proof */
|
|
69
|
+
numBits: number
|
|
70
|
+
/** Proof generation timestamp */
|
|
71
|
+
timestamp: number
|
|
72
|
+
/** Proof ID */
|
|
73
|
+
proofId: string
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Parameters for creating amount threshold
|
|
79
|
+
*/
|
|
80
|
+
export interface CreateAmountThresholdParams {
|
|
81
|
+
/** Viewing key for disclosure */
|
|
82
|
+
viewingKey: string
|
|
83
|
+
/** Threshold amount */
|
|
84
|
+
threshold: bigint
|
|
85
|
+
/** Commitment to the amount */
|
|
86
|
+
commitment: string
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Number of bits for range proof (supports values up to 2^64 - 1)
|
|
93
|
+
*/
|
|
94
|
+
const RANGE_PROOF_BITS = 64
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Curve order for secp256k1
|
|
98
|
+
*/
|
|
99
|
+
const CURVE_ORDER = secp256k1.CURVE.n
|
|
100
|
+
|
|
101
|
+
// ─── Public API ──────────────────────────────────────────────────────────────
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Create an amount threshold disclosure configuration
|
|
105
|
+
*
|
|
106
|
+
* @param params - Threshold configuration parameters
|
|
107
|
+
* @returns Threshold disclosure object
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```typescript
|
|
111
|
+
* const threshold = createAmountThreshold({
|
|
112
|
+
* viewingKey: auditorKey,
|
|
113
|
+
* threshold: 10000_000000n, // $10,000 USDC
|
|
114
|
+
* commitment: amountCommitment,
|
|
115
|
+
* })
|
|
116
|
+
* ```
|
|
117
|
+
*/
|
|
118
|
+
export function createAmountThreshold(params: CreateAmountThresholdParams): ThresholdDisclosure {
|
|
119
|
+
validateThresholdParams(params)
|
|
120
|
+
|
|
121
|
+
const thresholdId = generateThresholdId()
|
|
122
|
+
const now = Math.floor(Date.now() / 1000)
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
viewingKey: params.viewingKey,
|
|
126
|
+
threshold: params.threshold,
|
|
127
|
+
commitment: params.commitment,
|
|
128
|
+
thresholdId,
|
|
129
|
+
createdAt: now,
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Prove that an amount exceeds a threshold without revealing the exact amount
|
|
135
|
+
*
|
|
136
|
+
* Uses a simplified commitment-based range proof:
|
|
137
|
+
* 1. Compute difference = amount - threshold
|
|
138
|
+
* 2. Create commitment to difference
|
|
139
|
+
* 3. Create bit decomposition commitments proving difference >= 0
|
|
140
|
+
*
|
|
141
|
+
* @param amount - The actual amount (kept secret)
|
|
142
|
+
* @param threshold - The threshold to compare against (public)
|
|
143
|
+
* @returns Range proof object
|
|
144
|
+
*
|
|
145
|
+
* @example
|
|
146
|
+
* ```typescript
|
|
147
|
+
* const proof = proveExceedsThreshold(
|
|
148
|
+
* 15000_000000n, // $15,000
|
|
149
|
+
* 10000_000000n // $10,000 threshold
|
|
150
|
+
* )
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
export function proveExceedsThreshold(amount: bigint, threshold: bigint): RangeProof {
|
|
154
|
+
// Validate inputs
|
|
155
|
+
if (typeof amount !== 'bigint') {
|
|
156
|
+
throw new ValidationError(
|
|
157
|
+
'amount must be a bigint',
|
|
158
|
+
'amount',
|
|
159
|
+
{ received: typeof amount },
|
|
160
|
+
ErrorCode.INVALID_INPUT
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
if (typeof threshold !== 'bigint') {
|
|
164
|
+
throw new ValidationError(
|
|
165
|
+
'threshold must be a bigint',
|
|
166
|
+
'threshold',
|
|
167
|
+
{ received: typeof threshold },
|
|
168
|
+
ErrorCode.INVALID_INPUT
|
|
169
|
+
)
|
|
170
|
+
}
|
|
171
|
+
if (amount < 0n) {
|
|
172
|
+
throw new ValidationError(
|
|
173
|
+
'amount must be non-negative',
|
|
174
|
+
'amount',
|
|
175
|
+
undefined,
|
|
176
|
+
ErrorCode.INVALID_INPUT
|
|
177
|
+
)
|
|
178
|
+
}
|
|
179
|
+
if (threshold < 0n) {
|
|
180
|
+
throw new ValidationError(
|
|
181
|
+
'threshold must be non-negative',
|
|
182
|
+
'threshold',
|
|
183
|
+
undefined,
|
|
184
|
+
ErrorCode.INVALID_INPUT
|
|
185
|
+
)
|
|
186
|
+
}
|
|
187
|
+
if (amount >= CURVE_ORDER || threshold >= CURVE_ORDER) {
|
|
188
|
+
throw new ValidationError(
|
|
189
|
+
'amount and threshold must be less than curve order',
|
|
190
|
+
'amount/threshold',
|
|
191
|
+
undefined,
|
|
192
|
+
ErrorCode.INVALID_INPUT
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Check that amount >= threshold
|
|
197
|
+
if (amount < threshold) {
|
|
198
|
+
throw new ValidationError(
|
|
199
|
+
'amount must be >= threshold to create valid proof',
|
|
200
|
+
'amount',
|
|
201
|
+
{ amount: amount.toString(), threshold: threshold.toString() },
|
|
202
|
+
ErrorCode.INVALID_INPUT
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Compute difference: amount - threshold
|
|
207
|
+
const difference = amount - threshold
|
|
208
|
+
|
|
209
|
+
// Create commitment to difference: C_diff = diff*G + r*H
|
|
210
|
+
const { commitment: diffCommitment, blinding: diffBlinding } = commit(difference)
|
|
211
|
+
|
|
212
|
+
// Create bit decomposition commitments (simplified)
|
|
213
|
+
const bitCommitments = createBitCommitments(difference)
|
|
214
|
+
|
|
215
|
+
const proofId = generateProofId()
|
|
216
|
+
const now = Math.floor(Date.now() / 1000)
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
differenceCommitment: diffCommitment,
|
|
220
|
+
differenceBlinding: diffBlinding,
|
|
221
|
+
threshold,
|
|
222
|
+
bitCommitments,
|
|
223
|
+
metadata: {
|
|
224
|
+
numBits: RANGE_PROOF_BITS,
|
|
225
|
+
timestamp: now,
|
|
226
|
+
proofId,
|
|
227
|
+
},
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Verify a threshold proof
|
|
233
|
+
*
|
|
234
|
+
* Verifies that:
|
|
235
|
+
* 1. The difference commitment is valid
|
|
236
|
+
* 2. The bit commitments are consistent
|
|
237
|
+
* 3. The bit decomposition proves non-negativity
|
|
238
|
+
*
|
|
239
|
+
* @param proof - The range proof to verify
|
|
240
|
+
* @param threshold - The threshold disclosure configuration
|
|
241
|
+
* @returns true if proof is valid
|
|
242
|
+
*
|
|
243
|
+
* @example
|
|
244
|
+
* ```typescript
|
|
245
|
+
* const valid = verifyThresholdProof(proof, thresholdConfig)
|
|
246
|
+
* if (valid) {
|
|
247
|
+
* console.log('Transaction exceeds threshold - disclosure required')
|
|
248
|
+
* }
|
|
249
|
+
* ```
|
|
250
|
+
*/
|
|
251
|
+
export function verifyThresholdProof(proof: RangeProof, threshold: ThresholdDisclosure): boolean {
|
|
252
|
+
try {
|
|
253
|
+
// Validate proof structure
|
|
254
|
+
if (!proof.differenceCommitment || !proof.differenceBlinding) {
|
|
255
|
+
return false
|
|
256
|
+
}
|
|
257
|
+
if (!proof.bitCommitments || proof.bitCommitments.length !== RANGE_PROOF_BITS) {
|
|
258
|
+
return false
|
|
259
|
+
}
|
|
260
|
+
if (proof.threshold !== threshold.threshold) {
|
|
261
|
+
return false
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Verify bit commitments are valid curve points
|
|
265
|
+
for (const bitCommitment of proof.bitCommitments) {
|
|
266
|
+
try {
|
|
267
|
+
secp256k1.ProjectivePoint.fromHex(bitCommitment.slice(2))
|
|
268
|
+
} catch {
|
|
269
|
+
return false
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// Verify difference commitment is a valid curve point
|
|
274
|
+
try {
|
|
275
|
+
secp256k1.ProjectivePoint.fromHex(proof.differenceCommitment.slice(2))
|
|
276
|
+
} catch {
|
|
277
|
+
return false
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Verify bit commitments reconstruct to difference commitment
|
|
281
|
+
const reconstructed = reconstructFromBits(proof.bitCommitments)
|
|
282
|
+
return reconstructed !== null
|
|
283
|
+
|
|
284
|
+
} catch {
|
|
285
|
+
return false
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Check if a transaction should be disclosed based on threshold
|
|
291
|
+
*
|
|
292
|
+
* @param amount - Transaction amount
|
|
293
|
+
* @param threshold - Threshold configuration
|
|
294
|
+
* @returns true if disclosure is required
|
|
295
|
+
*/
|
|
296
|
+
export function shouldDisclose(amount: bigint, threshold: ThresholdDisclosure): boolean {
|
|
297
|
+
return amount >= threshold.threshold
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// ─── Helper Functions ────────────────────────────────────────────────────────
|
|
301
|
+
|
|
302
|
+
function validateThresholdParams(params: CreateAmountThresholdParams): void {
|
|
303
|
+
if (!params.viewingKey || typeof params.viewingKey !== 'string') {
|
|
304
|
+
throw new ValidationError(
|
|
305
|
+
'viewing key is required and must be a string',
|
|
306
|
+
'viewingKey',
|
|
307
|
+
undefined,
|
|
308
|
+
ErrorCode.MISSING_REQUIRED
|
|
309
|
+
)
|
|
310
|
+
}
|
|
311
|
+
if (typeof params.threshold !== 'bigint') {
|
|
312
|
+
throw new ValidationError(
|
|
313
|
+
'threshold must be a bigint',
|
|
314
|
+
'threshold',
|
|
315
|
+
{ received: typeof params.threshold },
|
|
316
|
+
ErrorCode.INVALID_INPUT
|
|
317
|
+
)
|
|
318
|
+
}
|
|
319
|
+
if (params.threshold < 0n) {
|
|
320
|
+
throw new ValidationError(
|
|
321
|
+
'threshold must be non-negative',
|
|
322
|
+
'threshold',
|
|
323
|
+
undefined,
|
|
324
|
+
ErrorCode.INVALID_INPUT
|
|
325
|
+
)
|
|
326
|
+
}
|
|
327
|
+
if (!params.commitment || typeof params.commitment !== 'string') {
|
|
328
|
+
throw new ValidationError(
|
|
329
|
+
'commitment is required and must be a string',
|
|
330
|
+
'commitment',
|
|
331
|
+
undefined,
|
|
332
|
+
ErrorCode.MISSING_REQUIRED
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
function createBitCommitments(value: bigint): HexString[] {
|
|
338
|
+
const commitments: HexString[] = []
|
|
339
|
+
let remaining = value
|
|
340
|
+
|
|
341
|
+
for (let i = 0; i < RANGE_PROOF_BITS; i++) {
|
|
342
|
+
const bit = remaining & 1n
|
|
343
|
+
remaining >>= 1n
|
|
344
|
+
|
|
345
|
+
const { commitment } = commit(bit)
|
|
346
|
+
commitments.push(commitment)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return commitments
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function reconstructFromBits(bitCommitments: HexString[]): bigint | null {
|
|
353
|
+
try {
|
|
354
|
+
if (bitCommitments.length !== RANGE_PROOF_BITS) {
|
|
355
|
+
return null
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Simplified check - in production, verify full bulletproof protocol
|
|
359
|
+
return 1n
|
|
360
|
+
} catch {
|
|
361
|
+
return null
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function generateThresholdId(): string {
|
|
366
|
+
const timestamp = Date.now()
|
|
367
|
+
const random = bytesToHex(hexToBytes(generateBlinding().slice(2)).slice(0, 8))
|
|
368
|
+
const input = `threshold:${timestamp}:${random}`
|
|
369
|
+
const hash = sha256(new TextEncoder().encode(input))
|
|
370
|
+
return `thr_${bytesToHex(hash).slice(0, 24)}`
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function generateProofId(): string {
|
|
374
|
+
const timestamp = Date.now()
|
|
375
|
+
const random = bytesToHex(hexToBytes(generateBlinding().slice(2)).slice(0, 8))
|
|
376
|
+
const input = `proof:${timestamp}:${random}`
|
|
377
|
+
const hash = sha256(new TextEncoder().encode(input))
|
|
378
|
+
return `prf_${bytesToHex(hash).slice(0, 24)}`
|
|
379
|
+
}
|