@sip-protocol/sdk 0.4.0 → 0.5.1
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/browser.js
CHANGED
|
@@ -32,11 +32,15 @@ var browser_exports = {};
|
|
|
32
32
|
__export(browser_exports, {
|
|
33
33
|
ATTESTATION_VERSION: () => ATTESTATION_VERSION,
|
|
34
34
|
AptosStealthService: () => AptosStealthService,
|
|
35
|
+
AuditorKeyDerivation: () => AuditorKeyDerivation,
|
|
36
|
+
AuditorType: () => AuditorType,
|
|
35
37
|
BaseWalletAdapter: () => BaseWalletAdapter,
|
|
36
38
|
BrowserNoirProvider: () => BrowserNoirProvider,
|
|
37
39
|
CHAIN_NUMERIC_IDS: () => CHAIN_NUMERIC_IDS,
|
|
38
40
|
COSMOS_CHAIN_PREFIXES: () => CHAIN_PREFIXES,
|
|
39
41
|
ComplianceManager: () => ComplianceManager,
|
|
42
|
+
ComplianceReporter: () => ComplianceReporter,
|
|
43
|
+
ConditionalDisclosure: () => ConditionalDisclosure,
|
|
40
44
|
CosmosStealthService: () => CosmosStealthService,
|
|
41
45
|
CryptoError: () => CryptoError,
|
|
42
46
|
DEFAULT_THRESHOLD: () => DEFAULT_THRESHOLD,
|
|
@@ -89,6 +93,7 @@ __export(browser_exports, {
|
|
|
89
93
|
SmartRouter: () => SmartRouter,
|
|
90
94
|
SolanaWalletAdapter: () => SolanaWalletAdapter,
|
|
91
95
|
SwapStatus: () => SwapStatus,
|
|
96
|
+
ThresholdViewingKey: () => ThresholdViewingKey,
|
|
92
97
|
Treasury: () => Treasury,
|
|
93
98
|
TrezorWalletAdapter: () => TrezorWalletAdapter,
|
|
94
99
|
ValidationError: () => ValidationError,
|
|
@@ -177,6 +182,7 @@ __export(browser_exports, {
|
|
|
177
182
|
generateEd25519StealthAddress: () => generateEd25519StealthAddress,
|
|
178
183
|
generateEd25519StealthMetaAddress: () => generateEd25519StealthMetaAddress,
|
|
179
184
|
generateIntentId: () => generateIntentId,
|
|
185
|
+
generatePdfReport: () => generatePdfReport,
|
|
180
186
|
generateRandomBytes: () => generateRandomBytes,
|
|
181
187
|
generateStealthAddress: () => generateStealthAddress,
|
|
182
188
|
generateStealthMetaAddress: () => generateStealthMetaAddress,
|
|
@@ -316,6 +322,14 @@ var ErrorCode = /* @__PURE__ */ ((ErrorCode2) => {
|
|
|
316
322
|
ErrorCode2["SIGNATURE_FAILED"] = "SIP_3005";
|
|
317
323
|
ErrorCode2["INVALID_CURVE_POINT"] = "SIP_3006";
|
|
318
324
|
ErrorCode2["INVALID_SCALAR"] = "SIP_3007";
|
|
325
|
+
ErrorCode2["INVALID_KEY_SIZE"] = "SIP_3008";
|
|
326
|
+
ErrorCode2["INVALID_ENCRYPTED_DATA"] = "SIP_3009";
|
|
327
|
+
ErrorCode2["INVALID_COMMITMENT"] = "SIP_3010";
|
|
328
|
+
ErrorCode2["INVALID_TIME_LOCK"] = "SIP_3011";
|
|
329
|
+
ErrorCode2["INVALID_SHARE"] = "SIP_3012";
|
|
330
|
+
ErrorCode2["INVALID_THRESHOLD"] = "SIP_3013";
|
|
331
|
+
ErrorCode2["CRYPTO_OPERATION_FAILED"] = "SIP_3014";
|
|
332
|
+
ErrorCode2["INVALID_FORMAT"] = "SIP_3015";
|
|
319
333
|
ErrorCode2["PROOF_FAILED"] = "SIP_4000";
|
|
320
334
|
ErrorCode2["PROOF_GENERATION_FAILED"] = "SIP_4001";
|
|
321
335
|
ErrorCode2["PROOF_VERIFICATION_FAILED"] = "SIP_4002";
|
|
@@ -9239,6 +9253,79 @@ var ComplianceManager = class _ComplianceManager {
|
|
|
9239
9253
|
getAllReports() {
|
|
9240
9254
|
return Array.from(this.reports.values());
|
|
9241
9255
|
}
|
|
9256
|
+
// ─── Dashboard Data API ──────────────────────────────────────────────────────
|
|
9257
|
+
/**
|
|
9258
|
+
* Get list of auditors for dashboard UI
|
|
9259
|
+
*
|
|
9260
|
+
* Returns a simplified view of auditors with essential info.
|
|
9261
|
+
* Alias for getAllAuditors() but returns AuditorRegistration directly.
|
|
9262
|
+
*
|
|
9263
|
+
* @returns Array of auditor registrations
|
|
9264
|
+
*/
|
|
9265
|
+
getAuditorList() {
|
|
9266
|
+
return this.getAllAuditors();
|
|
9267
|
+
}
|
|
9268
|
+
/**
|
|
9269
|
+
* Get pending disclosure requests for dashboard
|
|
9270
|
+
*
|
|
9271
|
+
* Returns disclosure requests waiting for approval/denial.
|
|
9272
|
+
* This is for disclosure REQUESTS, not disclosed transactions.
|
|
9273
|
+
*
|
|
9274
|
+
* @returns Array of pending disclosure requests
|
|
9275
|
+
*/
|
|
9276
|
+
getPendingDisclosures() {
|
|
9277
|
+
return this.getPendingRequests();
|
|
9278
|
+
}
|
|
9279
|
+
/**
|
|
9280
|
+
* Get disclosure history for a specific auditor
|
|
9281
|
+
*
|
|
9282
|
+
* Returns all disclosed transactions that were shared with this auditor,
|
|
9283
|
+
* sorted by disclosure date (most recent first).
|
|
9284
|
+
*
|
|
9285
|
+
* @param auditorId - Auditor ID to get history for
|
|
9286
|
+
* @returns Array of disclosed transactions for this auditor
|
|
9287
|
+
*/
|
|
9288
|
+
getDisclosureHistory(auditorId) {
|
|
9289
|
+
return this.getDisclosedTransactions(auditorId).sort((a, b) => b.disclosedAt - a.disclosedAt);
|
|
9290
|
+
}
|
|
9291
|
+
/**
|
|
9292
|
+
* Get compliance metrics for dashboard
|
|
9293
|
+
*
|
|
9294
|
+
* Calculates key compliance metrics including:
|
|
9295
|
+
* - Total auditors (active + inactive)
|
|
9296
|
+
* - Total disclosures made
|
|
9297
|
+
* - Pending disclosure requests
|
|
9298
|
+
* - Approval rate (approved / total resolved requests)
|
|
9299
|
+
* - Average processing time for disclosure requests
|
|
9300
|
+
*
|
|
9301
|
+
* @returns Compliance metrics object
|
|
9302
|
+
*/
|
|
9303
|
+
getComplianceMetrics() {
|
|
9304
|
+
const allAuditors = this.getAllAuditors();
|
|
9305
|
+
const allDisclosures = this.getDisclosedTransactions();
|
|
9306
|
+
const pendingRequests = this.getPendingRequests();
|
|
9307
|
+
const allRequests = Array.from(this.disclosureRequests.values());
|
|
9308
|
+
const resolvedRequests = allRequests.filter((r) => r.status !== "pending");
|
|
9309
|
+
const approvedRequests = resolvedRequests.filter((r) => r.status === "approved");
|
|
9310
|
+
const approvalRate = resolvedRequests.length > 0 ? approvedRequests.length / resolvedRequests.length : 0;
|
|
9311
|
+
let averageProcessingTime;
|
|
9312
|
+
if (resolvedRequests.length > 0) {
|
|
9313
|
+
const totalProcessingTime = resolvedRequests.reduce((sum, req) => {
|
|
9314
|
+
if (req.resolvedAt) {
|
|
9315
|
+
return sum + (req.resolvedAt - req.requestedAt);
|
|
9316
|
+
}
|
|
9317
|
+
return sum;
|
|
9318
|
+
}, 0);
|
|
9319
|
+
averageProcessingTime = totalProcessingTime / resolvedRequests.length;
|
|
9320
|
+
}
|
|
9321
|
+
return {
|
|
9322
|
+
totalAuditors: allAuditors.length,
|
|
9323
|
+
totalDisclosures: allDisclosures.length,
|
|
9324
|
+
pendingDisclosures: pendingRequests.length,
|
|
9325
|
+
approvalRate,
|
|
9326
|
+
averageProcessingTime
|
|
9327
|
+
};
|
|
9328
|
+
}
|
|
9242
9329
|
// ─── Audit Log ───────────────────────────────────────────────────────────────
|
|
9243
9330
|
/**
|
|
9244
9331
|
* Get audit log entries
|
|
@@ -9437,147 +9524,1767 @@ var ComplianceManager = class _ComplianceManager {
|
|
|
9437
9524
|
highRisk,
|
|
9438
9525
|
flaggedTransactions: flagged
|
|
9439
9526
|
}
|
|
9440
|
-
};
|
|
9441
|
-
if (includeTransactions) {
|
|
9442
|
-
data.transactions = transactions;
|
|
9527
|
+
};
|
|
9528
|
+
if (includeTransactions) {
|
|
9529
|
+
data.transactions = transactions;
|
|
9530
|
+
}
|
|
9531
|
+
return data;
|
|
9532
|
+
}
|
|
9533
|
+
generateCSV(transactions) {
|
|
9534
|
+
const headers = [
|
|
9535
|
+
"Transaction ID",
|
|
9536
|
+
"Disclosure ID",
|
|
9537
|
+
"Type",
|
|
9538
|
+
"Direction",
|
|
9539
|
+
"Token",
|
|
9540
|
+
"Amount",
|
|
9541
|
+
"Sender",
|
|
9542
|
+
"Recipient",
|
|
9543
|
+
"Chain",
|
|
9544
|
+
"Privacy Level",
|
|
9545
|
+
"Timestamp",
|
|
9546
|
+
"TX Hash",
|
|
9547
|
+
"Block",
|
|
9548
|
+
"Risk Score",
|
|
9549
|
+
"Purpose",
|
|
9550
|
+
"Memo"
|
|
9551
|
+
];
|
|
9552
|
+
const rows = transactions.map((tx) => [
|
|
9553
|
+
tx.transactionId,
|
|
9554
|
+
tx.disclosureId,
|
|
9555
|
+
tx.type,
|
|
9556
|
+
tx.direction,
|
|
9557
|
+
tx.token.symbol,
|
|
9558
|
+
tx.amount.toString(),
|
|
9559
|
+
tx.sender,
|
|
9560
|
+
tx.recipient,
|
|
9561
|
+
tx.chain,
|
|
9562
|
+
tx.privacyLevel,
|
|
9563
|
+
new Date(tx.timestamp * 1e3).toISOString(),
|
|
9564
|
+
tx.txHash,
|
|
9565
|
+
tx.blockNumber.toString(),
|
|
9566
|
+
tx.riskScore?.toString() ?? "",
|
|
9567
|
+
tx.purpose ?? "",
|
|
9568
|
+
tx.memo ?? ""
|
|
9569
|
+
]);
|
|
9570
|
+
const escapeForCSV = (val) => {
|
|
9571
|
+
let escaped = val;
|
|
9572
|
+
if (/^[=+\-@|\t]/.test(escaped)) {
|
|
9573
|
+
escaped = `'${escaped}`;
|
|
9574
|
+
}
|
|
9575
|
+
return `"${escaped.replace(/"/g, '""')}"`;
|
|
9576
|
+
};
|
|
9577
|
+
const csvRows = [headers, ...rows].map((row) => row.map(escapeForCSV).join(","));
|
|
9578
|
+
return csvRows.join("\n");
|
|
9579
|
+
}
|
|
9580
|
+
};
|
|
9581
|
+
function generateId(prefix) {
|
|
9582
|
+
return `${prefix}_${(0, import_utils20.bytesToHex)((0, import_utils20.randomBytes)(12))}`;
|
|
9583
|
+
}
|
|
9584
|
+
function validateRegisterAuditorParams(params) {
|
|
9585
|
+
if (!params.organization?.trim()) {
|
|
9586
|
+
throw new ValidationError(
|
|
9587
|
+
"organization is required",
|
|
9588
|
+
"organization",
|
|
9589
|
+
void 0,
|
|
9590
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
9591
|
+
);
|
|
9592
|
+
}
|
|
9593
|
+
if (!params.contactName?.trim()) {
|
|
9594
|
+
throw new ValidationError(
|
|
9595
|
+
"contact name is required",
|
|
9596
|
+
"contactName",
|
|
9597
|
+
void 0,
|
|
9598
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
9599
|
+
);
|
|
9600
|
+
}
|
|
9601
|
+
if (!params.contactEmail?.trim()) {
|
|
9602
|
+
throw new ValidationError(
|
|
9603
|
+
"contact email is required",
|
|
9604
|
+
"contactEmail",
|
|
9605
|
+
void 0,
|
|
9606
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
9607
|
+
);
|
|
9608
|
+
}
|
|
9609
|
+
if (!params.publicKey?.trim()) {
|
|
9610
|
+
throw new ValidationError(
|
|
9611
|
+
"public key is required",
|
|
9612
|
+
"publicKey",
|
|
9613
|
+
void 0,
|
|
9614
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
9615
|
+
);
|
|
9616
|
+
}
|
|
9617
|
+
if (!params.scope) {
|
|
9618
|
+
throw new ValidationError(
|
|
9619
|
+
"audit scope is required",
|
|
9620
|
+
"scope",
|
|
9621
|
+
void 0,
|
|
9622
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
9623
|
+
);
|
|
9624
|
+
}
|
|
9625
|
+
}
|
|
9626
|
+
function validateReportParams(params) {
|
|
9627
|
+
if (!params.title?.trim()) {
|
|
9628
|
+
throw new ValidationError(
|
|
9629
|
+
"report title is required",
|
|
9630
|
+
"title",
|
|
9631
|
+
void 0,
|
|
9632
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
9633
|
+
);
|
|
9634
|
+
}
|
|
9635
|
+
if (!params.type) {
|
|
9636
|
+
throw new ValidationError(
|
|
9637
|
+
"report type is required",
|
|
9638
|
+
"type",
|
|
9639
|
+
void 0,
|
|
9640
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
9641
|
+
);
|
|
9642
|
+
}
|
|
9643
|
+
if (!params.format) {
|
|
9644
|
+
throw new ValidationError(
|
|
9645
|
+
"report format is required",
|
|
9646
|
+
"format",
|
|
9647
|
+
void 0,
|
|
9648
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
9649
|
+
);
|
|
9650
|
+
}
|
|
9651
|
+
if (params.startDate === void 0 || params.startDate === null || params.endDate === void 0 || params.endDate === null) {
|
|
9652
|
+
throw new ValidationError(
|
|
9653
|
+
"date range is required",
|
|
9654
|
+
"dateRange",
|
|
9655
|
+
void 0,
|
|
9656
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
9657
|
+
);
|
|
9658
|
+
}
|
|
9659
|
+
if (params.startDate >= params.endDate) {
|
|
9660
|
+
throw new ValidationError(
|
|
9661
|
+
"start date must be before end date",
|
|
9662
|
+
"dateRange",
|
|
9663
|
+
void 0,
|
|
9664
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
9665
|
+
);
|
|
9666
|
+
}
|
|
9667
|
+
}
|
|
9668
|
+
|
|
9669
|
+
// src/compliance/reports.ts
|
|
9670
|
+
var import_sha25615 = require("@noble/hashes/sha256");
|
|
9671
|
+
var import_utils21 = require("@noble/hashes/utils");
|
|
9672
|
+
|
|
9673
|
+
// src/compliance/pdf.ts
|
|
9674
|
+
function generatePdfReport(report, options = {}) {
|
|
9675
|
+
const {
|
|
9676
|
+
title = "SIP Protocol Audit Report",
|
|
9677
|
+
organization = "",
|
|
9678
|
+
includeTransactions = true,
|
|
9679
|
+
maxTransactions = 100
|
|
9680
|
+
} = options;
|
|
9681
|
+
const content = buildPdfContent(report, {
|
|
9682
|
+
title,
|
|
9683
|
+
organization,
|
|
9684
|
+
includeTransactions,
|
|
9685
|
+
maxTransactions
|
|
9686
|
+
});
|
|
9687
|
+
return generatePdfBinary(content, title);
|
|
9688
|
+
}
|
|
9689
|
+
function buildPdfContent(report, options) {
|
|
9690
|
+
const lines = [];
|
|
9691
|
+
lines.push(options.title);
|
|
9692
|
+
if (options.organization && options.organization.trim() !== "") {
|
|
9693
|
+
lines.push(`Organization: ${options.organization}`);
|
|
9694
|
+
}
|
|
9695
|
+
lines.push(`Report ID: ${report.reportId}`);
|
|
9696
|
+
lines.push(
|
|
9697
|
+
`Generated: ${report.generatedAt.toISOString().replace("T", " ").slice(0, 19)} UTC`
|
|
9698
|
+
);
|
|
9699
|
+
lines.push("");
|
|
9700
|
+
lines.push("Report Period");
|
|
9701
|
+
lines.push(` Start: ${formatDate(report.period.start)}`);
|
|
9702
|
+
lines.push(` End: ${formatDate(report.period.end)}`);
|
|
9703
|
+
lines.push("");
|
|
9704
|
+
lines.push("Summary Statistics");
|
|
9705
|
+
lines.push(` Total Transactions: ${report.summary.transactionCount}`);
|
|
9706
|
+
lines.push(
|
|
9707
|
+
` Total Volume: ${formatBigInt(report.summary.totalVolume)}`
|
|
9708
|
+
);
|
|
9709
|
+
lines.push(
|
|
9710
|
+
` Unique Counterparties: ${report.summary.uniqueCounterparties}`
|
|
9711
|
+
);
|
|
9712
|
+
lines.push("");
|
|
9713
|
+
if (options.includeTransactions && report.transactions.length > 0) {
|
|
9714
|
+
lines.push("Transaction Details");
|
|
9715
|
+
lines.push("");
|
|
9716
|
+
const txToShow = Math.min(
|
|
9717
|
+
report.transactions.length,
|
|
9718
|
+
options.maxTransactions
|
|
9719
|
+
);
|
|
9720
|
+
for (let i = 0; i < txToShow; i++) {
|
|
9721
|
+
const tx = report.transactions[i];
|
|
9722
|
+
lines.push(`Transaction ${i + 1}/${report.transactions.length}`);
|
|
9723
|
+
lines.push(` ID: ${tx.id}`);
|
|
9724
|
+
lines.push(` Sender: ${truncateAddress(tx.sender)}`);
|
|
9725
|
+
lines.push(` Recipient: ${truncateAddress(tx.recipient)}`);
|
|
9726
|
+
lines.push(` Amount: ${formatAmount(tx.amount)}`);
|
|
9727
|
+
lines.push(
|
|
9728
|
+
` Timestamp: ${formatTimestamp(tx.timestamp)}`
|
|
9729
|
+
);
|
|
9730
|
+
if (tx.txHash) {
|
|
9731
|
+
lines.push(` Tx Hash: ${truncateHash(tx.txHash)}`);
|
|
9732
|
+
}
|
|
9733
|
+
if (tx.metadata && Object.keys(tx.metadata).length > 0) {
|
|
9734
|
+
lines.push(` Metadata: ${JSON.stringify(tx.metadata)}`);
|
|
9735
|
+
}
|
|
9736
|
+
lines.push("");
|
|
9737
|
+
}
|
|
9738
|
+
if (report.transactions.length > options.maxTransactions) {
|
|
9739
|
+
lines.push(
|
|
9740
|
+
`... and ${report.transactions.length - options.maxTransactions} more transactions`
|
|
9741
|
+
);
|
|
9742
|
+
lines.push("");
|
|
9743
|
+
}
|
|
9744
|
+
}
|
|
9745
|
+
lines.push("---");
|
|
9746
|
+
lines.push(
|
|
9747
|
+
"This report was generated by SIP Protocol compliance tools."
|
|
9748
|
+
);
|
|
9749
|
+
lines.push("For verification, please contact your compliance officer.");
|
|
9750
|
+
return lines.join("\n");
|
|
9751
|
+
}
|
|
9752
|
+
function generatePdfBinary(content, title) {
|
|
9753
|
+
const objects = [];
|
|
9754
|
+
objects.push(
|
|
9755
|
+
"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj"
|
|
9756
|
+
);
|
|
9757
|
+
objects.push(
|
|
9758
|
+
"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj"
|
|
9759
|
+
);
|
|
9760
|
+
objects.push(
|
|
9761
|
+
"3 0 obj\n<< /Type /Page /Parent 2 0 R /Resources 4 0 R /MediaBox [0 0 612 792] /Contents 5 0 R >>\nendobj"
|
|
9762
|
+
);
|
|
9763
|
+
objects.push(
|
|
9764
|
+
"4 0 obj\n<< /Font << /F1 << /Type /Font /Subtype /Type1 /BaseFont /Courier >> >> >>\nendobj"
|
|
9765
|
+
);
|
|
9766
|
+
const contentStream = buildContentStream(content);
|
|
9767
|
+
objects.push(
|
|
9768
|
+
`5 0 obj
|
|
9769
|
+
<< /Length ${contentStream.length} >>
|
|
9770
|
+
stream
|
|
9771
|
+
${contentStream}
|
|
9772
|
+
endstream
|
|
9773
|
+
endobj`
|
|
9774
|
+
);
|
|
9775
|
+
const now = /* @__PURE__ */ new Date();
|
|
9776
|
+
const pdfDate = formatPdfDate(now);
|
|
9777
|
+
objects.push(
|
|
9778
|
+
`6 0 obj
|
|
9779
|
+
<< /Title (${title}) /Author (SIP Protocol) /Creator (SIP SDK) /CreationDate (${pdfDate}) >>
|
|
9780
|
+
endobj`
|
|
9781
|
+
);
|
|
9782
|
+
const pdfParts = [];
|
|
9783
|
+
pdfParts.push("%PDF-1.4\n%\xE2\xE3\xCF\xD3\n");
|
|
9784
|
+
const xrefOffsets = [0];
|
|
9785
|
+
let currentOffset = pdfParts.join("").length;
|
|
9786
|
+
for (const obj of objects) {
|
|
9787
|
+
xrefOffsets.push(currentOffset);
|
|
9788
|
+
pdfParts.push(obj + "\n");
|
|
9789
|
+
currentOffset = pdfParts.join("").length;
|
|
9790
|
+
}
|
|
9791
|
+
const xrefStart = currentOffset;
|
|
9792
|
+
pdfParts.push("xref\n");
|
|
9793
|
+
pdfParts.push(`0 ${xrefOffsets.length}
|
|
9794
|
+
`);
|
|
9795
|
+
for (let i = 0; i < xrefOffsets.length; i++) {
|
|
9796
|
+
if (i === 0) {
|
|
9797
|
+
pdfParts.push("0000000000 65535 f \n");
|
|
9798
|
+
} else {
|
|
9799
|
+
const offset = String(xrefOffsets[i]).padStart(10, "0");
|
|
9800
|
+
pdfParts.push(`${offset} 00000 n
|
|
9801
|
+
`);
|
|
9802
|
+
}
|
|
9803
|
+
}
|
|
9804
|
+
pdfParts.push("trailer\n");
|
|
9805
|
+
pdfParts.push(
|
|
9806
|
+
`<< /Size ${xrefOffsets.length} /Root 1 0 R /Info 6 0 R >>
|
|
9807
|
+
`
|
|
9808
|
+
);
|
|
9809
|
+
pdfParts.push("startxref\n");
|
|
9810
|
+
pdfParts.push(`${xrefStart}
|
|
9811
|
+
`);
|
|
9812
|
+
pdfParts.push("%%EOF");
|
|
9813
|
+
const pdfString = pdfParts.join("");
|
|
9814
|
+
const encoder = new TextEncoder();
|
|
9815
|
+
return encoder.encode(pdfString);
|
|
9816
|
+
}
|
|
9817
|
+
function buildContentStream(text) {
|
|
9818
|
+
const lines = text.split("\n");
|
|
9819
|
+
const commands = [];
|
|
9820
|
+
commands.push("BT");
|
|
9821
|
+
commands.push("/F1 10 Tf");
|
|
9822
|
+
commands.push("12 TL");
|
|
9823
|
+
let y = 742;
|
|
9824
|
+
for (const line of lines) {
|
|
9825
|
+
commands.push(`50 ${y} Td`);
|
|
9826
|
+
const escaped = escapePdfString(line);
|
|
9827
|
+
commands.push(`(${escaped}) Tj`);
|
|
9828
|
+
y -= 12;
|
|
9829
|
+
if (y < 50) {
|
|
9830
|
+
break;
|
|
9831
|
+
}
|
|
9832
|
+
}
|
|
9833
|
+
commands.push("ET");
|
|
9834
|
+
return commands.join("\n");
|
|
9835
|
+
}
|
|
9836
|
+
function escapePdfString(str) {
|
|
9837
|
+
return str.replace(/\\/g, "\\\\").replace(/\(/g, "\\(").replace(/\)/g, "\\)").replace(/\r/g, "\\r").replace(/\n/g, "\\n");
|
|
9838
|
+
}
|
|
9839
|
+
function formatPdfDate(date) {
|
|
9840
|
+
const year = date.getUTCFullYear();
|
|
9841
|
+
const month = String(date.getUTCMonth() + 1).padStart(2, "0");
|
|
9842
|
+
const day = String(date.getUTCDate()).padStart(2, "0");
|
|
9843
|
+
const hour = String(date.getUTCHours()).padStart(2, "0");
|
|
9844
|
+
const minute = String(date.getUTCMinutes()).padStart(2, "0");
|
|
9845
|
+
const second = String(date.getUTCSeconds()).padStart(2, "0");
|
|
9846
|
+
return `D:${year}${month}${day}${hour}${minute}${second}Z`;
|
|
9847
|
+
}
|
|
9848
|
+
function formatDate(date) {
|
|
9849
|
+
return date.toISOString().split("T")[0];
|
|
9850
|
+
}
|
|
9851
|
+
function formatBigInt(value) {
|
|
9852
|
+
return value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
9853
|
+
}
|
|
9854
|
+
function formatAmount(amount) {
|
|
9855
|
+
try {
|
|
9856
|
+
const num = BigInt(amount);
|
|
9857
|
+
return formatBigInt(num);
|
|
9858
|
+
} catch {
|
|
9859
|
+
return amount;
|
|
9860
|
+
}
|
|
9861
|
+
}
|
|
9862
|
+
function formatTimestamp(timestamp) {
|
|
9863
|
+
const date = new Date(timestamp * 1e3);
|
|
9864
|
+
return date.toISOString().replace("T", " ").slice(0, 19) + " UTC";
|
|
9865
|
+
}
|
|
9866
|
+
function truncateAddress(address) {
|
|
9867
|
+
if (address.length <= 12) {
|
|
9868
|
+
return address;
|
|
9869
|
+
}
|
|
9870
|
+
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
9871
|
+
}
|
|
9872
|
+
function truncateHash(hash2) {
|
|
9873
|
+
if (hash2.length <= 16) {
|
|
9874
|
+
return hash2;
|
|
9875
|
+
}
|
|
9876
|
+
return `${hash2.slice(0, 8)}...${hash2.slice(-8)}`;
|
|
9877
|
+
}
|
|
9878
|
+
|
|
9879
|
+
// src/compliance/reports.ts
|
|
9880
|
+
var ComplianceReporter = class {
|
|
9881
|
+
/**
|
|
9882
|
+
* Generate an audit report from encrypted transactions
|
|
9883
|
+
*
|
|
9884
|
+
* Decrypts transactions using the provided viewing key, filters by date range,
|
|
9885
|
+
* and generates a comprehensive report with summary statistics.
|
|
9886
|
+
*
|
|
9887
|
+
* @param params - Report generation parameters
|
|
9888
|
+
* @returns Audit report with decrypted transactions and statistics
|
|
9889
|
+
* @throws {ValidationError} If parameters are invalid
|
|
9890
|
+
* @throws {CryptoError} If decryption fails
|
|
9891
|
+
*/
|
|
9892
|
+
async generateAuditReport(params) {
|
|
9893
|
+
this.validateParams(params);
|
|
9894
|
+
const viewingKey = this.normalizeViewingKey(params.viewingKey);
|
|
9895
|
+
const decryptedTransactions = this.decryptTransactions(
|
|
9896
|
+
params.transactions,
|
|
9897
|
+
viewingKey,
|
|
9898
|
+
params.startDate,
|
|
9899
|
+
params.endDate
|
|
9900
|
+
);
|
|
9901
|
+
const summary = this.calculateSummary(decryptedTransactions);
|
|
9902
|
+
const period = this.determinePeriod(
|
|
9903
|
+
decryptedTransactions,
|
|
9904
|
+
params.startDate,
|
|
9905
|
+
params.endDate
|
|
9906
|
+
);
|
|
9907
|
+
const reportId = `audit_${generateRandomBytes(16).slice(2)}`;
|
|
9908
|
+
return {
|
|
9909
|
+
reportId,
|
|
9910
|
+
generatedAt: /* @__PURE__ */ new Date(),
|
|
9911
|
+
period,
|
|
9912
|
+
transactions: decryptedTransactions,
|
|
9913
|
+
summary
|
|
9914
|
+
};
|
|
9915
|
+
}
|
|
9916
|
+
/**
|
|
9917
|
+
* Validate report generation parameters
|
|
9918
|
+
*/
|
|
9919
|
+
validateParams(params) {
|
|
9920
|
+
if (!params.viewingKey) {
|
|
9921
|
+
throw new ValidationError(
|
|
9922
|
+
"viewingKey is required",
|
|
9923
|
+
"viewingKey",
|
|
9924
|
+
void 0,
|
|
9925
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
9926
|
+
);
|
|
9927
|
+
}
|
|
9928
|
+
if (!params.transactions) {
|
|
9929
|
+
throw new ValidationError(
|
|
9930
|
+
"transactions array is required",
|
|
9931
|
+
"transactions",
|
|
9932
|
+
void 0,
|
|
9933
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
9934
|
+
);
|
|
9935
|
+
}
|
|
9936
|
+
if (!Array.isArray(params.transactions)) {
|
|
9937
|
+
throw new ValidationError(
|
|
9938
|
+
"transactions must be an array",
|
|
9939
|
+
"transactions",
|
|
9940
|
+
{ received: typeof params.transactions },
|
|
9941
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
9942
|
+
);
|
|
9943
|
+
}
|
|
9944
|
+
if (params.format !== "json" && params.format !== "pdf") {
|
|
9945
|
+
throw new ValidationError(
|
|
9946
|
+
"only JSON and PDF formats are supported",
|
|
9947
|
+
"format",
|
|
9948
|
+
{ received: params.format },
|
|
9949
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
9950
|
+
);
|
|
9951
|
+
}
|
|
9952
|
+
if (params.startDate && params.endDate) {
|
|
9953
|
+
if (params.startDate > params.endDate) {
|
|
9954
|
+
throw new ValidationError(
|
|
9955
|
+
"startDate must be before endDate",
|
|
9956
|
+
"startDate",
|
|
9957
|
+
{
|
|
9958
|
+
startDate: params.startDate.toISOString(),
|
|
9959
|
+
endDate: params.endDate.toISOString()
|
|
9960
|
+
},
|
|
9961
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
9962
|
+
);
|
|
9963
|
+
}
|
|
9964
|
+
}
|
|
9965
|
+
}
|
|
9966
|
+
/**
|
|
9967
|
+
* Normalize viewing key to ViewingKey object
|
|
9968
|
+
*/
|
|
9969
|
+
normalizeViewingKey(viewingKey) {
|
|
9970
|
+
if (typeof viewingKey === "string") {
|
|
9971
|
+
const keyHex = viewingKey.startsWith("0x") ? viewingKey.slice(2) : viewingKey;
|
|
9972
|
+
const keyBytes = (0, import_utils21.hexToBytes)(keyHex);
|
|
9973
|
+
const hashBytes = (0, import_sha25615.sha256)(keyBytes);
|
|
9974
|
+
return {
|
|
9975
|
+
key: `0x${keyHex}`,
|
|
9976
|
+
path: "m/0",
|
|
9977
|
+
hash: `0x${(0, import_utils21.bytesToHex)(hashBytes)}`
|
|
9978
|
+
};
|
|
9979
|
+
}
|
|
9980
|
+
return viewingKey;
|
|
9981
|
+
}
|
|
9982
|
+
/**
|
|
9983
|
+
* Decrypt transactions and filter by date range
|
|
9984
|
+
*/
|
|
9985
|
+
decryptTransactions(encrypted, viewingKey, startDate, endDate) {
|
|
9986
|
+
const decrypted = [];
|
|
9987
|
+
const errors = [];
|
|
9988
|
+
for (let i = 0; i < encrypted.length; i++) {
|
|
9989
|
+
try {
|
|
9990
|
+
const txData = decryptWithViewing(encrypted[i], viewingKey);
|
|
9991
|
+
if (startDate || endDate) {
|
|
9992
|
+
const txDate = new Date(txData.timestamp * 1e3);
|
|
9993
|
+
if (startDate && txDate < startDate) {
|
|
9994
|
+
continue;
|
|
9995
|
+
}
|
|
9996
|
+
if (endDate && txDate > endDate) {
|
|
9997
|
+
continue;
|
|
9998
|
+
}
|
|
9999
|
+
}
|
|
10000
|
+
decrypted.push({
|
|
10001
|
+
id: `tx_${i}`,
|
|
10002
|
+
sender: txData.sender,
|
|
10003
|
+
recipient: txData.recipient,
|
|
10004
|
+
amount: txData.amount,
|
|
10005
|
+
timestamp: txData.timestamp
|
|
10006
|
+
});
|
|
10007
|
+
} catch (error) {
|
|
10008
|
+
errors.push({ index: i, error });
|
|
10009
|
+
}
|
|
10010
|
+
}
|
|
10011
|
+
if (decrypted.length === 0 && encrypted.length > 0) {
|
|
10012
|
+
throw new CryptoError(
|
|
10013
|
+
`Failed to decrypt any transactions. First error: ${errors[0]?.error.message}`,
|
|
10014
|
+
"SIP_3002" /* DECRYPTION_FAILED */,
|
|
10015
|
+
{
|
|
10016
|
+
context: {
|
|
10017
|
+
totalTransactions: encrypted.length,
|
|
10018
|
+
failedCount: errors.length
|
|
10019
|
+
}
|
|
10020
|
+
}
|
|
10021
|
+
);
|
|
10022
|
+
}
|
|
10023
|
+
return decrypted;
|
|
10024
|
+
}
|
|
10025
|
+
/**
|
|
10026
|
+
* Calculate summary statistics
|
|
10027
|
+
*/
|
|
10028
|
+
calculateSummary(transactions) {
|
|
10029
|
+
let totalVolume = 0n;
|
|
10030
|
+
const counterparties = /* @__PURE__ */ new Set();
|
|
10031
|
+
for (const tx of transactions) {
|
|
10032
|
+
try {
|
|
10033
|
+
const amount = BigInt(tx.amount);
|
|
10034
|
+
totalVolume += amount;
|
|
10035
|
+
} catch (error) {
|
|
10036
|
+
console.warn(`Skipping invalid amount in transaction ${tx.id}: ${tx.amount}`);
|
|
10037
|
+
}
|
|
10038
|
+
counterparties.add(tx.sender);
|
|
10039
|
+
counterparties.add(tx.recipient);
|
|
10040
|
+
}
|
|
10041
|
+
return {
|
|
10042
|
+
totalVolume,
|
|
10043
|
+
transactionCount: transactions.length,
|
|
10044
|
+
uniqueCounterparties: counterparties.size
|
|
10045
|
+
};
|
|
10046
|
+
}
|
|
10047
|
+
/**
|
|
10048
|
+
* Determine report period from transactions and params
|
|
10049
|
+
*/
|
|
10050
|
+
determinePeriod(transactions, startDate, endDate) {
|
|
10051
|
+
if (startDate && endDate) {
|
|
10052
|
+
return { start: startDate, end: endDate };
|
|
10053
|
+
}
|
|
10054
|
+
if (transactions.length === 0) {
|
|
10055
|
+
const now = /* @__PURE__ */ new Date();
|
|
10056
|
+
return { start: now, end: now };
|
|
10057
|
+
}
|
|
10058
|
+
let minTimestamp = transactions[0].timestamp;
|
|
10059
|
+
let maxTimestamp = transactions[0].timestamp;
|
|
10060
|
+
for (const tx of transactions) {
|
|
10061
|
+
if (tx.timestamp < minTimestamp) {
|
|
10062
|
+
minTimestamp = tx.timestamp;
|
|
10063
|
+
}
|
|
10064
|
+
if (tx.timestamp > maxTimestamp) {
|
|
10065
|
+
maxTimestamp = tx.timestamp;
|
|
10066
|
+
}
|
|
10067
|
+
}
|
|
10068
|
+
return {
|
|
10069
|
+
start: startDate || new Date(minTimestamp * 1e3),
|
|
10070
|
+
end: endDate || new Date(maxTimestamp * 1e3)
|
|
10071
|
+
};
|
|
10072
|
+
}
|
|
10073
|
+
/**
|
|
10074
|
+
* Export audit report to PDF format
|
|
10075
|
+
*
|
|
10076
|
+
* Generates a professionally formatted PDF document from an audit report.
|
|
10077
|
+
* Works in both Node.js and browser environments.
|
|
10078
|
+
*
|
|
10079
|
+
* @param report - The audit report to export
|
|
10080
|
+
* @param options - PDF export options
|
|
10081
|
+
* @returns PDF document as Uint8Array
|
|
10082
|
+
*
|
|
10083
|
+
* @example
|
|
10084
|
+
* ```typescript
|
|
10085
|
+
* const reporter = new ComplianceReporter()
|
|
10086
|
+
* const report = await reporter.generateAuditReport({...})
|
|
10087
|
+
*
|
|
10088
|
+
* const pdfBytes = reporter.exportToPdf(report, {
|
|
10089
|
+
* title: 'Q1 2025 Audit Report',
|
|
10090
|
+
* organization: 'ACME Corp',
|
|
10091
|
+
* })
|
|
10092
|
+
*
|
|
10093
|
+
* // Save to file (Node.js)
|
|
10094
|
+
* fs.writeFileSync('report.pdf', pdfBytes)
|
|
10095
|
+
* ```
|
|
10096
|
+
*/
|
|
10097
|
+
exportToPdf(report, options) {
|
|
10098
|
+
return generatePdfReport(report, options);
|
|
10099
|
+
}
|
|
10100
|
+
/**
|
|
10101
|
+
* Export transactions to regulatory compliance formats
|
|
10102
|
+
*
|
|
10103
|
+
* Decrypts and exports transactions in formats required by regulators:
|
|
10104
|
+
* - FATF: Financial Action Task Force Travel Rule format
|
|
10105
|
+
* - FINCEN: FinCEN Suspicious Activity Report (SAR) format
|
|
10106
|
+
* - CSV: Generic comma-separated values format
|
|
10107
|
+
*
|
|
10108
|
+
* @param params - Export parameters
|
|
10109
|
+
* @returns Regulatory export in the specified format
|
|
10110
|
+
* @throws {ValidationError} If parameters are invalid
|
|
10111
|
+
* @throws {CryptoError} If decryption fails
|
|
10112
|
+
*
|
|
10113
|
+
* @example
|
|
10114
|
+
* ```typescript
|
|
10115
|
+
* const reporter = new ComplianceReporter()
|
|
10116
|
+
*
|
|
10117
|
+
* // FATF Travel Rule export
|
|
10118
|
+
* const fatfExport = await reporter.exportForRegulator({
|
|
10119
|
+
* viewingKey: myViewingKey,
|
|
10120
|
+
* transactions: encryptedTxs,
|
|
10121
|
+
* jurisdiction: 'EU',
|
|
10122
|
+
* format: 'FATF',
|
|
10123
|
+
* currency: 'EUR',
|
|
10124
|
+
* })
|
|
10125
|
+
*
|
|
10126
|
+
* // FINCEN SAR export (US only)
|
|
10127
|
+
* const fincenExport = await reporter.exportForRegulator({
|
|
10128
|
+
* viewingKey: myViewingKey,
|
|
10129
|
+
* transactions: suspiciousTxs,
|
|
10130
|
+
* jurisdiction: 'US',
|
|
10131
|
+
* format: 'FINCEN',
|
|
10132
|
+
* })
|
|
10133
|
+
*
|
|
10134
|
+
* // CSV export
|
|
10135
|
+
* const csvExport = await reporter.exportForRegulator({
|
|
10136
|
+
* viewingKey: myViewingKey,
|
|
10137
|
+
* transactions: encryptedTxs,
|
|
10138
|
+
* jurisdiction: 'SG',
|
|
10139
|
+
* format: 'CSV',
|
|
10140
|
+
* })
|
|
10141
|
+
* ```
|
|
10142
|
+
*/
|
|
10143
|
+
async exportForRegulator(params) {
|
|
10144
|
+
this.validateRegulatoryParams(params);
|
|
10145
|
+
const viewingKey = this.normalizeViewingKey(params.viewingKey);
|
|
10146
|
+
const decryptedTransactions = this.decryptTransactions(
|
|
10147
|
+
params.transactions,
|
|
10148
|
+
viewingKey,
|
|
10149
|
+
params.startDate,
|
|
10150
|
+
params.endDate
|
|
10151
|
+
);
|
|
10152
|
+
const reportId = `reg_${generateRandomBytes(16).slice(2)}`;
|
|
10153
|
+
switch (params.format) {
|
|
10154
|
+
case "FATF":
|
|
10155
|
+
return this.exportToFATF(
|
|
10156
|
+
reportId,
|
|
10157
|
+
decryptedTransactions,
|
|
10158
|
+
params.jurisdiction,
|
|
10159
|
+
params.currency || "USD"
|
|
10160
|
+
);
|
|
10161
|
+
case "FINCEN":
|
|
10162
|
+
return this.exportToFINCEN(
|
|
10163
|
+
reportId,
|
|
10164
|
+
decryptedTransactions,
|
|
10165
|
+
params.startDate,
|
|
10166
|
+
params.endDate,
|
|
10167
|
+
params.currency || "USD"
|
|
10168
|
+
);
|
|
10169
|
+
case "CSV":
|
|
10170
|
+
return this.exportToCSV(
|
|
10171
|
+
reportId,
|
|
10172
|
+
decryptedTransactions,
|
|
10173
|
+
params.jurisdiction,
|
|
10174
|
+
params.currency || "USD"
|
|
10175
|
+
);
|
|
10176
|
+
default:
|
|
10177
|
+
throw new ValidationError(
|
|
10178
|
+
`unsupported format: ${params.format}`,
|
|
10179
|
+
"format",
|
|
10180
|
+
{ received: params.format },
|
|
10181
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
10182
|
+
);
|
|
10183
|
+
}
|
|
10184
|
+
}
|
|
10185
|
+
/**
|
|
10186
|
+
* Validate regulatory export parameters
|
|
10187
|
+
*/
|
|
10188
|
+
validateRegulatoryParams(params) {
|
|
10189
|
+
if (!params.viewingKey) {
|
|
10190
|
+
throw new ValidationError(
|
|
10191
|
+
"viewingKey is required",
|
|
10192
|
+
"viewingKey",
|
|
10193
|
+
void 0,
|
|
10194
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
10195
|
+
);
|
|
10196
|
+
}
|
|
10197
|
+
if (!params.transactions) {
|
|
10198
|
+
throw new ValidationError(
|
|
10199
|
+
"transactions array is required",
|
|
10200
|
+
"transactions",
|
|
10201
|
+
void 0,
|
|
10202
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
10203
|
+
);
|
|
10204
|
+
}
|
|
10205
|
+
if (!Array.isArray(params.transactions)) {
|
|
10206
|
+
throw new ValidationError(
|
|
10207
|
+
"transactions must be an array",
|
|
10208
|
+
"transactions",
|
|
10209
|
+
{ received: typeof params.transactions },
|
|
10210
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
10211
|
+
);
|
|
10212
|
+
}
|
|
10213
|
+
if (!params.jurisdiction) {
|
|
10214
|
+
throw new ValidationError(
|
|
10215
|
+
"jurisdiction is required",
|
|
10216
|
+
"jurisdiction",
|
|
10217
|
+
void 0,
|
|
10218
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
10219
|
+
);
|
|
10220
|
+
}
|
|
10221
|
+
const validJurisdictions = ["US", "EU", "UK", "SG"];
|
|
10222
|
+
if (!validJurisdictions.includes(params.jurisdiction)) {
|
|
10223
|
+
throw new ValidationError(
|
|
10224
|
+
`invalid jurisdiction. Must be one of: ${validJurisdictions.join(", ")}`,
|
|
10225
|
+
"jurisdiction",
|
|
10226
|
+
{ received: params.jurisdiction },
|
|
10227
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
10228
|
+
);
|
|
10229
|
+
}
|
|
10230
|
+
if (!params.format) {
|
|
10231
|
+
throw new ValidationError(
|
|
10232
|
+
"format is required",
|
|
10233
|
+
"format",
|
|
10234
|
+
void 0,
|
|
10235
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
10236
|
+
);
|
|
10237
|
+
}
|
|
10238
|
+
const validFormats = ["FATF", "FINCEN", "CSV"];
|
|
10239
|
+
if (!validFormats.includes(params.format)) {
|
|
10240
|
+
throw new ValidationError(
|
|
10241
|
+
`invalid format. Must be one of: ${validFormats.join(", ")}`,
|
|
10242
|
+
"format",
|
|
10243
|
+
{ received: params.format },
|
|
10244
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
10245
|
+
);
|
|
10246
|
+
}
|
|
10247
|
+
if (params.format === "FINCEN" && params.jurisdiction !== "US") {
|
|
10248
|
+
throw new ValidationError(
|
|
10249
|
+
"FINCEN format is only available for US jurisdiction",
|
|
10250
|
+
"format",
|
|
10251
|
+
{ jurisdiction: params.jurisdiction, format: params.format },
|
|
10252
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
10253
|
+
);
|
|
10254
|
+
}
|
|
10255
|
+
if (params.startDate && params.endDate) {
|
|
10256
|
+
if (params.startDate > params.endDate) {
|
|
10257
|
+
throw new ValidationError(
|
|
10258
|
+
"startDate must be before endDate",
|
|
10259
|
+
"startDate",
|
|
10260
|
+
{
|
|
10261
|
+
startDate: params.startDate.toISOString(),
|
|
10262
|
+
endDate: params.endDate.toISOString()
|
|
10263
|
+
},
|
|
10264
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
10265
|
+
);
|
|
10266
|
+
}
|
|
10267
|
+
}
|
|
10268
|
+
}
|
|
10269
|
+
/**
|
|
10270
|
+
* Export to FATF Travel Rule format
|
|
10271
|
+
*/
|
|
10272
|
+
exportToFATF(reportId, transactions, jurisdiction, currency) {
|
|
10273
|
+
const fatfTransactions = transactions.map((tx) => ({
|
|
10274
|
+
originatorAccount: tx.sender,
|
|
10275
|
+
beneficiaryAccount: tx.recipient,
|
|
10276
|
+
amount: tx.amount,
|
|
10277
|
+
currency,
|
|
10278
|
+
transactionRef: tx.id,
|
|
10279
|
+
timestamp: new Date(tx.timestamp * 1e3).toISOString()
|
|
10280
|
+
}));
|
|
10281
|
+
return {
|
|
10282
|
+
reportId,
|
|
10283
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10284
|
+
jurisdiction,
|
|
10285
|
+
transactions: fatfTransactions
|
|
10286
|
+
};
|
|
10287
|
+
}
|
|
10288
|
+
/**
|
|
10289
|
+
* Export to FINCEN SAR format
|
|
10290
|
+
*/
|
|
10291
|
+
exportToFINCEN(reportId, transactions, startDate, endDate, currency = "USD") {
|
|
10292
|
+
let totalAmount = 0n;
|
|
10293
|
+
for (const tx of transactions) {
|
|
10294
|
+
try {
|
|
10295
|
+
totalAmount += BigInt(tx.amount);
|
|
10296
|
+
} catch (error) {
|
|
10297
|
+
}
|
|
10298
|
+
}
|
|
10299
|
+
const period = this.determinePeriod(transactions, startDate, endDate);
|
|
10300
|
+
const fincenTransactions = transactions.map((tx) => ({
|
|
10301
|
+
transactionDate: new Date(tx.timestamp * 1e3).toISOString(),
|
|
10302
|
+
amount: tx.amount,
|
|
10303
|
+
currency,
|
|
10304
|
+
narrativeSummary: `Transfer from ${tx.sender} to ${tx.recipient}`,
|
|
10305
|
+
transactionRef: tx.id,
|
|
10306
|
+
parties: {
|
|
10307
|
+
sender: tx.sender,
|
|
10308
|
+
recipient: tx.recipient
|
|
10309
|
+
}
|
|
10310
|
+
}));
|
|
10311
|
+
return {
|
|
10312
|
+
reportId,
|
|
10313
|
+
filingType: "SAR",
|
|
10314
|
+
reportDate: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10315
|
+
jurisdiction: "US",
|
|
10316
|
+
summary: {
|
|
10317
|
+
transactionCount: transactions.length,
|
|
10318
|
+
totalAmount: totalAmount.toString(),
|
|
10319
|
+
period: {
|
|
10320
|
+
start: period.start.toISOString(),
|
|
10321
|
+
end: period.end.toISOString()
|
|
10322
|
+
}
|
|
10323
|
+
},
|
|
10324
|
+
transactions: fincenTransactions
|
|
10325
|
+
};
|
|
10326
|
+
}
|
|
10327
|
+
/**
|
|
10328
|
+
* Export to CSV format
|
|
10329
|
+
*/
|
|
10330
|
+
exportToCSV(reportId, transactions, jurisdiction, currency) {
|
|
10331
|
+
const headers = [
|
|
10332
|
+
"Transaction ID",
|
|
10333
|
+
"Timestamp",
|
|
10334
|
+
"Sender",
|
|
10335
|
+
"Recipient",
|
|
10336
|
+
"Amount",
|
|
10337
|
+
"Currency"
|
|
10338
|
+
];
|
|
10339
|
+
const rows = transactions.map((tx) => [
|
|
10340
|
+
tx.id,
|
|
10341
|
+
new Date(tx.timestamp * 1e3).toISOString(),
|
|
10342
|
+
tx.sender,
|
|
10343
|
+
tx.recipient,
|
|
10344
|
+
tx.amount,
|
|
10345
|
+
currency
|
|
10346
|
+
]);
|
|
10347
|
+
return {
|
|
10348
|
+
reportId,
|
|
10349
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10350
|
+
jurisdiction,
|
|
10351
|
+
headers,
|
|
10352
|
+
rows
|
|
10353
|
+
};
|
|
10354
|
+
}
|
|
10355
|
+
};
|
|
10356
|
+
|
|
10357
|
+
// src/compliance/conditional.ts
|
|
10358
|
+
var import_sha25616 = require("@noble/hashes/sha256");
|
|
10359
|
+
var import_utils22 = require("@noble/hashes/utils");
|
|
10360
|
+
var import_chacha3 = require("@noble/ciphers/chacha.js");
|
|
10361
|
+
var ConditionalDisclosure = class {
|
|
10362
|
+
/**
|
|
10363
|
+
* Create a time-locked disclosure
|
|
10364
|
+
*
|
|
10365
|
+
* Encrypts the viewing key with a deterministic key derived from
|
|
10366
|
+
* the commitment and reveal time. The key can only be reconstructed
|
|
10367
|
+
* after the specified time/block height.
|
|
10368
|
+
*
|
|
10369
|
+
* @param params - Time-lock parameters
|
|
10370
|
+
* @returns Time-lock result with encrypted key
|
|
10371
|
+
* @throws {ValidationError} If parameters are invalid
|
|
10372
|
+
* @throws {CryptoError} If encryption fails
|
|
10373
|
+
*/
|
|
10374
|
+
createTimeLocked(params) {
|
|
10375
|
+
if (!params.viewingKey || !params.viewingKey.startsWith("0x")) {
|
|
10376
|
+
throw new ValidationError(
|
|
10377
|
+
"Invalid viewing key format",
|
|
10378
|
+
"viewingKey",
|
|
10379
|
+
{ viewingKey: params.viewingKey },
|
|
10380
|
+
"SIP_2006" /* INVALID_KEY */
|
|
10381
|
+
);
|
|
10382
|
+
}
|
|
10383
|
+
if (!params.commitment || !params.commitment.startsWith("0x")) {
|
|
10384
|
+
throw new ValidationError(
|
|
10385
|
+
"Invalid commitment format",
|
|
10386
|
+
"commitment",
|
|
10387
|
+
{ commitment: params.commitment },
|
|
10388
|
+
"SIP_3010" /* INVALID_COMMITMENT */
|
|
10389
|
+
);
|
|
10390
|
+
}
|
|
10391
|
+
let revealAfterSeconds;
|
|
10392
|
+
let type;
|
|
10393
|
+
if (params.revealAfter instanceof Date) {
|
|
10394
|
+
revealAfterSeconds = Math.floor(params.revealAfter.getTime() / 1e3);
|
|
10395
|
+
type = "timestamp";
|
|
10396
|
+
} else if (typeof params.revealAfter === "number") {
|
|
10397
|
+
if (params.revealAfter > 1e10) {
|
|
10398
|
+
revealAfterSeconds = Math.floor(params.revealAfter / 1e3);
|
|
10399
|
+
type = "timestamp";
|
|
10400
|
+
} else {
|
|
10401
|
+
revealAfterSeconds = params.revealAfter;
|
|
10402
|
+
type = "blockheight";
|
|
10403
|
+
}
|
|
10404
|
+
if (revealAfterSeconds <= 0) {
|
|
10405
|
+
throw new ValidationError(
|
|
10406
|
+
"Reveal time/block height must be positive",
|
|
10407
|
+
"revealAfter",
|
|
10408
|
+
{ revealAfter: revealAfterSeconds },
|
|
10409
|
+
"SIP_3011" /* INVALID_TIME_LOCK */
|
|
10410
|
+
);
|
|
10411
|
+
}
|
|
10412
|
+
} else {
|
|
10413
|
+
throw new ValidationError(
|
|
10414
|
+
"Invalid revealAfter type (must be Date or number)",
|
|
10415
|
+
"revealAfter",
|
|
10416
|
+
{ revealAfter: params.revealAfter },
|
|
10417
|
+
"SIP_3011" /* INVALID_TIME_LOCK */
|
|
10418
|
+
);
|
|
10419
|
+
}
|
|
10420
|
+
try {
|
|
10421
|
+
const encryptionKey = this._deriveEncryptionKey(
|
|
10422
|
+
params.commitment,
|
|
10423
|
+
revealAfterSeconds
|
|
10424
|
+
);
|
|
10425
|
+
const nonce = (0, import_utils22.randomBytes)(24);
|
|
10426
|
+
const viewingKeyBytes = (0, import_utils22.hexToBytes)(params.viewingKey.slice(2));
|
|
10427
|
+
const cipher = (0, import_chacha3.xchacha20poly1305)(encryptionKey, nonce);
|
|
10428
|
+
const encryptedKey = cipher.encrypt(viewingKeyBytes);
|
|
10429
|
+
const commitmentData = new Uint8Array([
|
|
10430
|
+
...viewingKeyBytes,
|
|
10431
|
+
...this._numberToBytes(revealAfterSeconds)
|
|
10432
|
+
]);
|
|
10433
|
+
const commitmentHash = (0, import_sha25616.sha256)(commitmentData);
|
|
10434
|
+
return {
|
|
10435
|
+
encryptedKey: "0x" + (0, import_utils22.bytesToHex)(encryptedKey),
|
|
10436
|
+
nonce: "0x" + (0, import_utils22.bytesToHex)(nonce),
|
|
10437
|
+
revealAfter: revealAfterSeconds,
|
|
10438
|
+
verificationCommitment: "0x" + (0, import_utils22.bytesToHex)(commitmentHash),
|
|
10439
|
+
encryptionCommitment: params.commitment,
|
|
10440
|
+
type
|
|
10441
|
+
};
|
|
10442
|
+
} catch (error) {
|
|
10443
|
+
if (error instanceof ValidationError) {
|
|
10444
|
+
throw error;
|
|
10445
|
+
}
|
|
10446
|
+
throw new CryptoError(
|
|
10447
|
+
"Failed to create time-locked disclosure",
|
|
10448
|
+
"SIP_3001" /* ENCRYPTION_FAILED */,
|
|
10449
|
+
{
|
|
10450
|
+
cause: error instanceof Error ? error : void 0,
|
|
10451
|
+
operation: "createTimeLocked"
|
|
10452
|
+
}
|
|
10453
|
+
);
|
|
10454
|
+
}
|
|
10455
|
+
}
|
|
10456
|
+
/**
|
|
10457
|
+
* Check if a time-lock is unlocked and retrieve the viewing key
|
|
10458
|
+
*
|
|
10459
|
+
* @param timeLock - Time-lock result to check
|
|
10460
|
+
* @param currentTimeOrBlock - Current time (Date/number) or block height (number)
|
|
10461
|
+
* @returns Unlock result with viewing key if unlocked
|
|
10462
|
+
* @throws {ValidationError} If time-lock format is invalid
|
|
10463
|
+
* @throws {CryptoError} If decryption fails
|
|
10464
|
+
*/
|
|
10465
|
+
checkUnlocked(timeLock, currentTimeOrBlock) {
|
|
10466
|
+
if (!timeLock.encryptedKey || !timeLock.encryptedKey.startsWith("0x")) {
|
|
10467
|
+
throw new ValidationError(
|
|
10468
|
+
"Invalid encrypted key format",
|
|
10469
|
+
"encryptedKey",
|
|
10470
|
+
{ encryptedKey: timeLock.encryptedKey },
|
|
10471
|
+
"SIP_3009" /* INVALID_ENCRYPTED_DATA */
|
|
10472
|
+
);
|
|
10473
|
+
}
|
|
10474
|
+
if (!timeLock.nonce || !timeLock.nonce.startsWith("0x")) {
|
|
10475
|
+
throw new ValidationError(
|
|
10476
|
+
"Invalid nonce format",
|
|
10477
|
+
"nonce",
|
|
10478
|
+
{ nonce: timeLock.nonce },
|
|
10479
|
+
"SIP_3009" /* INVALID_ENCRYPTED_DATA */
|
|
10480
|
+
);
|
|
10481
|
+
}
|
|
10482
|
+
if (!timeLock.verificationCommitment || !timeLock.verificationCommitment.startsWith("0x")) {
|
|
10483
|
+
throw new ValidationError(
|
|
10484
|
+
"Invalid verification commitment format",
|
|
10485
|
+
"verificationCommitment",
|
|
10486
|
+
{ commitment: timeLock.verificationCommitment },
|
|
10487
|
+
"SIP_3010" /* INVALID_COMMITMENT */
|
|
10488
|
+
);
|
|
10489
|
+
}
|
|
10490
|
+
if (!timeLock.encryptionCommitment || !timeLock.encryptionCommitment.startsWith("0x")) {
|
|
10491
|
+
throw new ValidationError(
|
|
10492
|
+
"Invalid encryption commitment format",
|
|
10493
|
+
"encryptionCommitment",
|
|
10494
|
+
{ commitment: timeLock.encryptionCommitment },
|
|
10495
|
+
"SIP_3010" /* INVALID_COMMITMENT */
|
|
10496
|
+
);
|
|
10497
|
+
}
|
|
10498
|
+
let currentValue;
|
|
10499
|
+
if (currentTimeOrBlock instanceof Date) {
|
|
10500
|
+
currentValue = Math.floor(currentTimeOrBlock.getTime() / 1e3);
|
|
10501
|
+
} else if (typeof currentTimeOrBlock === "number") {
|
|
10502
|
+
currentValue = currentTimeOrBlock;
|
|
10503
|
+
} else {
|
|
10504
|
+
currentValue = Math.floor(Date.now() / 1e3);
|
|
10505
|
+
}
|
|
10506
|
+
const unlocked = currentValue >= timeLock.revealAfter;
|
|
10507
|
+
if (!unlocked) {
|
|
10508
|
+
return { unlocked: false };
|
|
10509
|
+
}
|
|
10510
|
+
try {
|
|
10511
|
+
const encryptionKey = this._deriveEncryptionKey(
|
|
10512
|
+
timeLock.encryptionCommitment,
|
|
10513
|
+
timeLock.revealAfter
|
|
10514
|
+
);
|
|
10515
|
+
const nonce = (0, import_utils22.hexToBytes)(timeLock.nonce.slice(2));
|
|
10516
|
+
const encryptedData = (0, import_utils22.hexToBytes)(timeLock.encryptedKey.slice(2));
|
|
10517
|
+
const cipher = (0, import_chacha3.xchacha20poly1305)(encryptionKey, nonce);
|
|
10518
|
+
const decryptedBytes = cipher.decrypt(encryptedData);
|
|
10519
|
+
const viewingKey = "0x" + (0, import_utils22.bytesToHex)(decryptedBytes);
|
|
10520
|
+
return {
|
|
10521
|
+
unlocked: true,
|
|
10522
|
+
viewingKey
|
|
10523
|
+
};
|
|
10524
|
+
} catch (error) {
|
|
10525
|
+
if (error instanceof ValidationError || error instanceof CryptoError) {
|
|
10526
|
+
throw error;
|
|
10527
|
+
}
|
|
10528
|
+
throw new CryptoError(
|
|
10529
|
+
"Failed to decrypt time-locked viewing key",
|
|
10530
|
+
"SIP_3002" /* DECRYPTION_FAILED */,
|
|
10531
|
+
{
|
|
10532
|
+
cause: error instanceof Error ? error : void 0,
|
|
10533
|
+
operation: "checkUnlocked"
|
|
10534
|
+
}
|
|
10535
|
+
);
|
|
10536
|
+
}
|
|
10537
|
+
}
|
|
10538
|
+
/**
|
|
10539
|
+
* Verify a time-lock commitment
|
|
10540
|
+
*
|
|
10541
|
+
* Verifies that the verification commitment in the time-lock matches the hash
|
|
10542
|
+
* of the provided viewing key and reveal time.
|
|
10543
|
+
*
|
|
10544
|
+
* @param timeLock - Time-lock to verify
|
|
10545
|
+
* @param viewingKey - Viewing key to verify against
|
|
10546
|
+
* @returns True if commitment is valid
|
|
10547
|
+
*/
|
|
10548
|
+
verifyCommitment(timeLock, viewingKey) {
|
|
10549
|
+
try {
|
|
10550
|
+
const viewingKeyBytes = (0, import_utils22.hexToBytes)(viewingKey.slice(2));
|
|
10551
|
+
const commitmentData = new Uint8Array([
|
|
10552
|
+
...viewingKeyBytes,
|
|
10553
|
+
...this._numberToBytes(timeLock.revealAfter)
|
|
10554
|
+
]);
|
|
10555
|
+
const expectedCommitment = (0, import_sha25616.sha256)(commitmentData);
|
|
10556
|
+
const actualCommitment = (0, import_utils22.hexToBytes)(timeLock.verificationCommitment.slice(2));
|
|
10557
|
+
if (expectedCommitment.length !== actualCommitment.length) {
|
|
10558
|
+
return false;
|
|
10559
|
+
}
|
|
10560
|
+
let diff = 0;
|
|
10561
|
+
for (let i = 0; i < expectedCommitment.length; i++) {
|
|
10562
|
+
diff |= expectedCommitment[i] ^ actualCommitment[i];
|
|
10563
|
+
}
|
|
10564
|
+
return diff === 0;
|
|
10565
|
+
} catch {
|
|
10566
|
+
return false;
|
|
10567
|
+
}
|
|
10568
|
+
}
|
|
10569
|
+
/**
|
|
10570
|
+
* Derive deterministic encryption key from commitment and reveal time
|
|
10571
|
+
*
|
|
10572
|
+
* @private
|
|
10573
|
+
*/
|
|
10574
|
+
_deriveEncryptionKey(commitment, revealAfter) {
|
|
10575
|
+
const commitmentBytes = (0, import_utils22.hexToBytes)(commitment.slice(2));
|
|
10576
|
+
const timeBytes = this._numberToBytes(revealAfter);
|
|
10577
|
+
const combined = new Uint8Array([...commitmentBytes, ...timeBytes]);
|
|
10578
|
+
const key = (0, import_sha25616.sha256)(combined);
|
|
10579
|
+
if (key.length !== 32) {
|
|
10580
|
+
throw new CryptoError(
|
|
10581
|
+
"Derived key must be 32 bytes",
|
|
10582
|
+
"SIP_3008" /* INVALID_KEY_SIZE */,
|
|
10583
|
+
{
|
|
10584
|
+
context: { actualSize: key.length, expectedSize: 32 },
|
|
10585
|
+
operation: "_deriveEncryptionKey"
|
|
10586
|
+
}
|
|
10587
|
+
);
|
|
10588
|
+
}
|
|
10589
|
+
return key;
|
|
10590
|
+
}
|
|
10591
|
+
/**
|
|
10592
|
+
* Convert number to 8-byte big-endian representation
|
|
10593
|
+
*
|
|
10594
|
+
* @private
|
|
10595
|
+
*/
|
|
10596
|
+
_numberToBytes(num) {
|
|
10597
|
+
const bytes = new Uint8Array(8);
|
|
10598
|
+
const view = new DataView(bytes.buffer);
|
|
10599
|
+
view.setBigUint64(0, BigInt(Math.floor(num)), false);
|
|
10600
|
+
return bytes;
|
|
10601
|
+
}
|
|
10602
|
+
};
|
|
10603
|
+
|
|
10604
|
+
// src/compliance/conditional-threshold.ts
|
|
10605
|
+
var import_secp256k17 = require("@noble/curves/secp256k1");
|
|
10606
|
+
var import_sha25617 = require("@noble/hashes/sha256");
|
|
10607
|
+
var import_utils23 = require("@noble/hashes/utils");
|
|
10608
|
+
var CURVE_ORDER2 = import_secp256k17.secp256k1.CURVE.n;
|
|
10609
|
+
|
|
10610
|
+
// src/compliance/threshold.ts
|
|
10611
|
+
var import_sha25618 = require("@noble/hashes/sha256");
|
|
10612
|
+
var import_utils24 = require("@noble/hashes/utils");
|
|
10613
|
+
var FIELD_PRIME = 2n ** 256n - 189n;
|
|
10614
|
+
var ThresholdViewingKey = class {
|
|
10615
|
+
/**
|
|
10616
|
+
* Create threshold shares from a viewing key
|
|
10617
|
+
*
|
|
10618
|
+
* @param params - Configuration parameters
|
|
10619
|
+
* @returns Threshold shares with commitment
|
|
10620
|
+
* @throws ValidationError if parameters are invalid
|
|
10621
|
+
*
|
|
10622
|
+
* @example
|
|
10623
|
+
* ```typescript
|
|
10624
|
+
* const threshold = ThresholdViewingKey.create({
|
|
10625
|
+
* threshold: 3,
|
|
10626
|
+
* totalShares: 5,
|
|
10627
|
+
* viewingKey: '0xabc123...',
|
|
10628
|
+
* })
|
|
10629
|
+
* ```
|
|
10630
|
+
*/
|
|
10631
|
+
static create(params) {
|
|
10632
|
+
this.validateParams(params.threshold, params.totalShares);
|
|
10633
|
+
this.validateViewingKey(params.viewingKey);
|
|
10634
|
+
const secret = this.viewingKeyToSecret(params.viewingKey);
|
|
10635
|
+
const keyLength = params.viewingKey.slice(2).length;
|
|
10636
|
+
const coefficients = this.generateCoefficients(params.threshold, secret);
|
|
10637
|
+
const commitment = this.createCommitment(secret, coefficients);
|
|
10638
|
+
const shares = [];
|
|
10639
|
+
for (let i = 1; i <= params.totalShares; i++) {
|
|
10640
|
+
const x = BigInt(i);
|
|
10641
|
+
const y = this.evaluatePolynomial(coefficients, x);
|
|
10642
|
+
shares.push(this.encodeShare(x, y, keyLength, commitment));
|
|
10643
|
+
}
|
|
10644
|
+
return {
|
|
10645
|
+
shares,
|
|
10646
|
+
commitment,
|
|
10647
|
+
threshold: params.threshold,
|
|
10648
|
+
totalShares: params.totalShares
|
|
10649
|
+
};
|
|
10650
|
+
}
|
|
10651
|
+
/**
|
|
10652
|
+
* Reconstruct viewing key from threshold shares
|
|
10653
|
+
*
|
|
10654
|
+
* @param shares - Array of encoded shares (must be >= threshold)
|
|
10655
|
+
* @returns Reconstructed viewing key
|
|
10656
|
+
* @throws ValidationError if insufficient or invalid shares
|
|
10657
|
+
*
|
|
10658
|
+
* @example
|
|
10659
|
+
* ```typescript
|
|
10660
|
+
* const viewingKey = ThresholdViewingKey.reconstruct([
|
|
10661
|
+
* 'share1',
|
|
10662
|
+
* 'share2',
|
|
10663
|
+
* 'share3',
|
|
10664
|
+
* ])
|
|
10665
|
+
* ```
|
|
10666
|
+
*/
|
|
10667
|
+
static reconstruct(shares) {
|
|
10668
|
+
if (!shares || shares.length === 0) {
|
|
10669
|
+
throw new ValidationError(
|
|
10670
|
+
"at least one share is required",
|
|
10671
|
+
"shares",
|
|
10672
|
+
{ received: shares },
|
|
10673
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
10674
|
+
);
|
|
10675
|
+
}
|
|
10676
|
+
const decodedShares = shares.map((s) => this.decodeShare(s));
|
|
10677
|
+
const commitment = decodedShares[0].commitment;
|
|
10678
|
+
const keyLength = decodedShares[0].keyLength;
|
|
10679
|
+
for (const share of decodedShares) {
|
|
10680
|
+
if (share.commitment !== commitment) {
|
|
10681
|
+
throw new ValidationError(
|
|
10682
|
+
"shares must all have the same commitment",
|
|
10683
|
+
"shares",
|
|
10684
|
+
{ commitment },
|
|
10685
|
+
"SIP_3012" /* INVALID_SHARE */
|
|
10686
|
+
);
|
|
10687
|
+
}
|
|
10688
|
+
if (share.keyLength !== keyLength) {
|
|
10689
|
+
throw new ValidationError(
|
|
10690
|
+
"shares must all have the same key length",
|
|
10691
|
+
"shares",
|
|
10692
|
+
{ keyLength },
|
|
10693
|
+
"SIP_3012" /* INVALID_SHARE */
|
|
10694
|
+
);
|
|
10695
|
+
}
|
|
10696
|
+
}
|
|
10697
|
+
const xCoords = new Set(decodedShares.map((s) => s.x.toString()));
|
|
10698
|
+
if (xCoords.size !== decodedShares.length) {
|
|
10699
|
+
throw new ValidationError(
|
|
10700
|
+
"shares must have unique x-coordinates",
|
|
10701
|
+
"shares",
|
|
10702
|
+
void 0,
|
|
10703
|
+
"SIP_3012" /* INVALID_SHARE */
|
|
10704
|
+
);
|
|
10705
|
+
}
|
|
10706
|
+
const secret = this.lagrangeInterpolate(decodedShares);
|
|
10707
|
+
return this.secretToViewingKey(secret, keyLength);
|
|
10708
|
+
}
|
|
10709
|
+
/**
|
|
10710
|
+
* Verify a share without revealing the viewing key
|
|
10711
|
+
*
|
|
10712
|
+
* @param share - Encoded share to verify
|
|
10713
|
+
* @param expectedCommitment - Expected commitment hash
|
|
10714
|
+
* @returns True if share is valid
|
|
10715
|
+
*
|
|
10716
|
+
* @example
|
|
10717
|
+
* ```typescript
|
|
10718
|
+
* const isValid = ThresholdViewingKey.verifyShare(
|
|
10719
|
+
* 'share1',
|
|
10720
|
+
* 'commitment_hash'
|
|
10721
|
+
* )
|
|
10722
|
+
* ```
|
|
10723
|
+
*/
|
|
10724
|
+
static verifyShare(share, expectedCommitment) {
|
|
10725
|
+
try {
|
|
10726
|
+
const decoded = this.decodeShare(share);
|
|
10727
|
+
return decoded.commitment === expectedCommitment;
|
|
10728
|
+
} catch {
|
|
10729
|
+
return false;
|
|
10730
|
+
}
|
|
10731
|
+
}
|
|
10732
|
+
// ─── Private Helper Methods ─────────────────────────────────────────────────
|
|
10733
|
+
/**
|
|
10734
|
+
* Validate threshold and total shares parameters
|
|
10735
|
+
*/
|
|
10736
|
+
static validateParams(threshold, totalShares) {
|
|
10737
|
+
if (!Number.isInteger(threshold) || threshold < 2) {
|
|
10738
|
+
throw new ValidationError(
|
|
10739
|
+
"threshold must be an integer >= 2",
|
|
10740
|
+
"threshold",
|
|
10741
|
+
{ received: threshold },
|
|
10742
|
+
"SIP_3013" /* INVALID_THRESHOLD */
|
|
10743
|
+
);
|
|
10744
|
+
}
|
|
10745
|
+
if (!Number.isInteger(totalShares) || totalShares < threshold) {
|
|
10746
|
+
throw new ValidationError(
|
|
10747
|
+
"totalShares must be an integer >= threshold",
|
|
10748
|
+
"totalShares",
|
|
10749
|
+
{ received: totalShares, threshold },
|
|
10750
|
+
"SIP_3013" /* INVALID_THRESHOLD */
|
|
10751
|
+
);
|
|
10752
|
+
}
|
|
10753
|
+
if (totalShares > 255) {
|
|
10754
|
+
throw new ValidationError(
|
|
10755
|
+
"totalShares must be <= 255",
|
|
10756
|
+
"totalShares",
|
|
10757
|
+
{ received: totalShares },
|
|
10758
|
+
"SIP_3013" /* INVALID_THRESHOLD */
|
|
10759
|
+
);
|
|
10760
|
+
}
|
|
10761
|
+
}
|
|
10762
|
+
/**
|
|
10763
|
+
* Validate viewing key format
|
|
10764
|
+
*/
|
|
10765
|
+
static validateViewingKey(viewingKey) {
|
|
10766
|
+
if (!viewingKey || typeof viewingKey !== "string") {
|
|
10767
|
+
throw new ValidationError(
|
|
10768
|
+
"viewingKey is required",
|
|
10769
|
+
"viewingKey",
|
|
10770
|
+
{ received: viewingKey },
|
|
10771
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
10772
|
+
);
|
|
10773
|
+
}
|
|
10774
|
+
if (!viewingKey.startsWith("0x")) {
|
|
10775
|
+
throw new ValidationError(
|
|
10776
|
+
"viewingKey must be hex-encoded (start with 0x)",
|
|
10777
|
+
"viewingKey",
|
|
10778
|
+
{ received: viewingKey },
|
|
10779
|
+
"SIP_3015" /* INVALID_FORMAT */
|
|
10780
|
+
);
|
|
10781
|
+
}
|
|
10782
|
+
if (viewingKey.length < 66) {
|
|
10783
|
+
throw new ValidationError(
|
|
10784
|
+
"viewingKey must be at least 32 bytes",
|
|
10785
|
+
"viewingKey",
|
|
10786
|
+
{ received: viewingKey.length },
|
|
10787
|
+
"SIP_3015" /* INVALID_FORMAT */
|
|
10788
|
+
);
|
|
10789
|
+
}
|
|
10790
|
+
}
|
|
10791
|
+
/**
|
|
10792
|
+
* Convert viewing key to secret (bigint)
|
|
10793
|
+
*/
|
|
10794
|
+
static viewingKeyToSecret(viewingKey) {
|
|
10795
|
+
const bytes = (0, import_utils24.hexToBytes)(viewingKey.slice(2));
|
|
10796
|
+
let secret = 0n;
|
|
10797
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
10798
|
+
secret = secret << 8n | BigInt(bytes[i]);
|
|
10799
|
+
}
|
|
10800
|
+
return this.mod(secret, FIELD_PRIME);
|
|
10801
|
+
}
|
|
10802
|
+
/**
|
|
10803
|
+
* Convert secret (bigint) back to viewing key
|
|
10804
|
+
* @param secret - The secret as bigint
|
|
10805
|
+
* @param hexLength - Length of the hex string (without 0x prefix)
|
|
10806
|
+
*/
|
|
10807
|
+
static secretToViewingKey(secret, hexLength) {
|
|
10808
|
+
let hex = secret.toString(16);
|
|
10809
|
+
hex = hex.padStart(hexLength, "0");
|
|
10810
|
+
return `0x${hex}`;
|
|
10811
|
+
}
|
|
10812
|
+
/**
|
|
10813
|
+
* Generate random polynomial coefficients
|
|
10814
|
+
* Polynomial: f(x) = a₀ + a₁x + a₂x² + ... + aₙ₋₁xⁿ⁻¹
|
|
10815
|
+
* where a₀ = secret
|
|
10816
|
+
*/
|
|
10817
|
+
static generateCoefficients(threshold, secret) {
|
|
10818
|
+
const coefficients = [secret];
|
|
10819
|
+
for (let i = 1; i < threshold; i++) {
|
|
10820
|
+
const randomCoeff = this.randomFieldElement();
|
|
10821
|
+
coefficients.push(randomCoeff);
|
|
10822
|
+
}
|
|
10823
|
+
return coefficients;
|
|
10824
|
+
}
|
|
10825
|
+
/**
|
|
10826
|
+
* Generate a random field element
|
|
10827
|
+
*/
|
|
10828
|
+
static randomFieldElement() {
|
|
10829
|
+
const bytes = (0, import_utils24.randomBytes)(32);
|
|
10830
|
+
let value = 0n;
|
|
10831
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
10832
|
+
value = value << 8n | BigInt(bytes[i]);
|
|
10833
|
+
}
|
|
10834
|
+
return this.mod(value, FIELD_PRIME);
|
|
10835
|
+
}
|
|
10836
|
+
/**
|
|
10837
|
+
* Evaluate polynomial at point x
|
|
10838
|
+
* f(x) = a₀ + a₁x + a₂x² + ... + aₙ₋₁xⁿ⁻¹
|
|
10839
|
+
*/
|
|
10840
|
+
static evaluatePolynomial(coefficients, x) {
|
|
10841
|
+
let result = 0n;
|
|
10842
|
+
let xPower = 1n;
|
|
10843
|
+
for (const coeff of coefficients) {
|
|
10844
|
+
result = this.mod(result + this.mod(coeff * xPower, FIELD_PRIME), FIELD_PRIME);
|
|
10845
|
+
xPower = this.mod(xPower * x, FIELD_PRIME);
|
|
10846
|
+
}
|
|
10847
|
+
return result;
|
|
10848
|
+
}
|
|
10849
|
+
/**
|
|
10850
|
+
* Create commitment hash from secret and coefficients
|
|
10851
|
+
*/
|
|
10852
|
+
static createCommitment(secret, coefficients) {
|
|
10853
|
+
const data = [secret, ...coefficients].map((c) => c.toString(16).padStart(64, "0")).join("");
|
|
10854
|
+
const hash2 = (0, import_sha25618.sha256)((0, import_utils24.hexToBytes)(data));
|
|
10855
|
+
return (0, import_utils24.bytesToHex)(hash2);
|
|
10856
|
+
}
|
|
10857
|
+
/**
|
|
10858
|
+
* Encode share as string: "x:y:len:commitment"
|
|
10859
|
+
*/
|
|
10860
|
+
static encodeShare(x, y, keyLength, commitment) {
|
|
10861
|
+
const xHex = x.toString(16).padStart(2, "0");
|
|
10862
|
+
const yHex = y.toString(16).padStart(64, "0");
|
|
10863
|
+
const lenHex = keyLength.toString(16).padStart(4, "0");
|
|
10864
|
+
return `${xHex}:${yHex}:${lenHex}:${commitment}`;
|
|
10865
|
+
}
|
|
10866
|
+
/**
|
|
10867
|
+
* Decode share from string
|
|
10868
|
+
*/
|
|
10869
|
+
static decodeShare(share) {
|
|
10870
|
+
if (!share || typeof share !== "string") {
|
|
10871
|
+
throw new ValidationError(
|
|
10872
|
+
"share must be a non-empty string",
|
|
10873
|
+
"share",
|
|
10874
|
+
{ received: share },
|
|
10875
|
+
"SIP_3012" /* INVALID_SHARE */
|
|
10876
|
+
);
|
|
10877
|
+
}
|
|
10878
|
+
const parts = share.split(":");
|
|
10879
|
+
if (parts.length !== 4) {
|
|
10880
|
+
throw new ValidationError(
|
|
10881
|
+
'share must have format "x:y:len:commitment"',
|
|
10882
|
+
"share",
|
|
10883
|
+
{ received: share },
|
|
10884
|
+
"SIP_3012" /* INVALID_SHARE */
|
|
10885
|
+
);
|
|
10886
|
+
}
|
|
10887
|
+
const [xHex, yHex, lenHex, commitment] = parts;
|
|
10888
|
+
try {
|
|
10889
|
+
const x = BigInt(`0x${xHex}`);
|
|
10890
|
+
const y = BigInt(`0x${yHex}`);
|
|
10891
|
+
const keyLength = parseInt(lenHex, 16);
|
|
10892
|
+
if (x <= 0n) {
|
|
10893
|
+
throw new ValidationError(
|
|
10894
|
+
"share x-coordinate must be positive",
|
|
10895
|
+
"share",
|
|
10896
|
+
{ x },
|
|
10897
|
+
"SIP_3012" /* INVALID_SHARE */
|
|
10898
|
+
);
|
|
10899
|
+
}
|
|
10900
|
+
if (keyLength < 64) {
|
|
10901
|
+
throw new ValidationError(
|
|
10902
|
+
"key length must be at least 64 (32 bytes)",
|
|
10903
|
+
"share",
|
|
10904
|
+
{ keyLength },
|
|
10905
|
+
"SIP_3012" /* INVALID_SHARE */
|
|
10906
|
+
);
|
|
10907
|
+
}
|
|
10908
|
+
return { x, y, keyLength, commitment };
|
|
10909
|
+
} catch (error) {
|
|
10910
|
+
throw new ValidationError(
|
|
10911
|
+
"failed to decode share",
|
|
10912
|
+
"share",
|
|
10913
|
+
{ error: error.message },
|
|
10914
|
+
"SIP_3012" /* INVALID_SHARE */
|
|
10915
|
+
);
|
|
9443
10916
|
}
|
|
9444
|
-
return data;
|
|
9445
10917
|
}
|
|
9446
|
-
|
|
9447
|
-
|
|
9448
|
-
|
|
9449
|
-
|
|
9450
|
-
|
|
9451
|
-
|
|
9452
|
-
|
|
9453
|
-
|
|
9454
|
-
|
|
9455
|
-
|
|
9456
|
-
|
|
9457
|
-
|
|
9458
|
-
|
|
9459
|
-
|
|
9460
|
-
|
|
9461
|
-
|
|
9462
|
-
"Purpose",
|
|
9463
|
-
"Memo"
|
|
9464
|
-
];
|
|
9465
|
-
const rows = transactions.map((tx) => [
|
|
9466
|
-
tx.transactionId,
|
|
9467
|
-
tx.disclosureId,
|
|
9468
|
-
tx.type,
|
|
9469
|
-
tx.direction,
|
|
9470
|
-
tx.token.symbol,
|
|
9471
|
-
tx.amount.toString(),
|
|
9472
|
-
tx.sender,
|
|
9473
|
-
tx.recipient,
|
|
9474
|
-
tx.chain,
|
|
9475
|
-
tx.privacyLevel,
|
|
9476
|
-
new Date(tx.timestamp * 1e3).toISOString(),
|
|
9477
|
-
tx.txHash,
|
|
9478
|
-
tx.blockNumber.toString(),
|
|
9479
|
-
tx.riskScore?.toString() ?? "",
|
|
9480
|
-
tx.purpose ?? "",
|
|
9481
|
-
tx.memo ?? ""
|
|
9482
|
-
]);
|
|
9483
|
-
const escapeForCSV = (val) => {
|
|
9484
|
-
let escaped = val;
|
|
9485
|
-
if (/^[=+\-@|\t]/.test(escaped)) {
|
|
9486
|
-
escaped = `'${escaped}`;
|
|
10918
|
+
/**
|
|
10919
|
+
* Lagrange interpolation to reconstruct secret
|
|
10920
|
+
* Evaluates polynomial at x=0 to get f(0) = secret
|
|
10921
|
+
*/
|
|
10922
|
+
static lagrangeInterpolate(shares) {
|
|
10923
|
+
let secret = 0n;
|
|
10924
|
+
for (let i = 0; i < shares.length; i++) {
|
|
10925
|
+
const xi = shares[i].x;
|
|
10926
|
+
const yi = shares[i].y;
|
|
10927
|
+
let numerator = 1n;
|
|
10928
|
+
let denominator = 1n;
|
|
10929
|
+
for (let j = 0; j < shares.length; j++) {
|
|
10930
|
+
if (i === j) continue;
|
|
10931
|
+
const xj = shares[j].x;
|
|
10932
|
+
numerator = this.mod(numerator * this.mod(-xj, FIELD_PRIME), FIELD_PRIME);
|
|
10933
|
+
denominator = this.mod(denominator * this.mod(xi - xj, FIELD_PRIME), FIELD_PRIME);
|
|
9487
10934
|
}
|
|
9488
|
-
|
|
9489
|
-
|
|
9490
|
-
|
|
9491
|
-
return
|
|
10935
|
+
const coeff = this.mod(numerator * this.modInverse(denominator, FIELD_PRIME), FIELD_PRIME);
|
|
10936
|
+
secret = this.mod(secret + this.mod(yi * coeff, FIELD_PRIME), FIELD_PRIME);
|
|
10937
|
+
}
|
|
10938
|
+
return secret;
|
|
9492
10939
|
}
|
|
9493
|
-
|
|
9494
|
-
|
|
9495
|
-
|
|
9496
|
-
|
|
9497
|
-
|
|
9498
|
-
if (!params.organization?.trim()) {
|
|
9499
|
-
throw new ValidationError(
|
|
9500
|
-
"organization is required",
|
|
9501
|
-
"organization",
|
|
9502
|
-
void 0,
|
|
9503
|
-
"SIP_2008" /* MISSING_REQUIRED */
|
|
9504
|
-
);
|
|
10940
|
+
/**
|
|
10941
|
+
* Modular arithmetic: a mod m
|
|
10942
|
+
*/
|
|
10943
|
+
static mod(a, m) {
|
|
10944
|
+
return (a % m + m) % m;
|
|
9505
10945
|
}
|
|
9506
|
-
|
|
9507
|
-
|
|
9508
|
-
|
|
9509
|
-
|
|
9510
|
-
|
|
9511
|
-
|
|
9512
|
-
)
|
|
10946
|
+
/**
|
|
10947
|
+
* Modular multiplicative inverse using Extended Euclidean Algorithm
|
|
10948
|
+
* Returns x such that (a * x) mod m = 1
|
|
10949
|
+
*/
|
|
10950
|
+
static modInverse(a, m) {
|
|
10951
|
+
const a0 = this.mod(a, m);
|
|
10952
|
+
if (a0 === 0n) {
|
|
10953
|
+
throw new CryptoError(
|
|
10954
|
+
"modular inverse does not exist (a = 0)",
|
|
10955
|
+
"SIP_3014" /* CRYPTO_OPERATION_FAILED */,
|
|
10956
|
+
{ operation: "modInverse" }
|
|
10957
|
+
);
|
|
10958
|
+
}
|
|
10959
|
+
let [old_r, r] = [a0, m];
|
|
10960
|
+
let [old_s, s] = [1n, 0n];
|
|
10961
|
+
while (r !== 0n) {
|
|
10962
|
+
const quotient = old_r / r;
|
|
10963
|
+
[old_r, r] = [r, old_r - quotient * r];
|
|
10964
|
+
[old_s, s] = [s, old_s - quotient * s];
|
|
10965
|
+
}
|
|
10966
|
+
if (old_r !== 1n) {
|
|
10967
|
+
throw new CryptoError(
|
|
10968
|
+
"modular inverse does not exist (gcd != 1)",
|
|
10969
|
+
"SIP_3014" /* CRYPTO_OPERATION_FAILED */,
|
|
10970
|
+
{ operation: "modInverse" }
|
|
10971
|
+
);
|
|
10972
|
+
}
|
|
10973
|
+
return this.mod(old_s, m);
|
|
9513
10974
|
}
|
|
9514
|
-
|
|
9515
|
-
|
|
9516
|
-
|
|
9517
|
-
|
|
9518
|
-
|
|
9519
|
-
|
|
9520
|
-
|
|
10975
|
+
};
|
|
10976
|
+
|
|
10977
|
+
// src/compliance/derivation.ts
|
|
10978
|
+
var import_sha25619 = require("@noble/hashes/sha256");
|
|
10979
|
+
var import_sha5123 = require("@noble/hashes/sha512");
|
|
10980
|
+
var import_hmac2 = require("@noble/hashes/hmac");
|
|
10981
|
+
var import_utils25 = require("@noble/hashes/utils");
|
|
10982
|
+
var AuditorType = /* @__PURE__ */ ((AuditorType2) => {
|
|
10983
|
+
AuditorType2[AuditorType2["PRIMARY"] = 0] = "PRIMARY";
|
|
10984
|
+
AuditorType2[AuditorType2["REGULATORY"] = 1] = "REGULATORY";
|
|
10985
|
+
AuditorType2[AuditorType2["INTERNAL"] = 2] = "INTERNAL";
|
|
10986
|
+
AuditorType2[AuditorType2["TAX"] = 3] = "TAX";
|
|
10987
|
+
return AuditorType2;
|
|
10988
|
+
})(AuditorType || {});
|
|
10989
|
+
var AuditorKeyDerivation = class {
|
|
10990
|
+
/**
|
|
10991
|
+
* SIP Protocol coin type (BIP-44 registered)
|
|
10992
|
+
*
|
|
10993
|
+
* Note: This is a placeholder. In production, register with SLIP-44:
|
|
10994
|
+
* https://github.com/satoshilabs/slips/blob/master/slip-0044.md
|
|
10995
|
+
*/
|
|
10996
|
+
static COIN_TYPE = 1234;
|
|
10997
|
+
/**
|
|
10998
|
+
* BIP-44 purpose field
|
|
10999
|
+
*/
|
|
11000
|
+
static PURPOSE = 44;
|
|
11001
|
+
/**
|
|
11002
|
+
* Hardened derivation flag (2^31)
|
|
11003
|
+
*/
|
|
11004
|
+
static HARDENED = 2147483648;
|
|
11005
|
+
/**
|
|
11006
|
+
* Generate BIP-44 derivation path
|
|
11007
|
+
*
|
|
11008
|
+
* @param auditorType - Type of auditor key
|
|
11009
|
+
* @param account - Account index (default: 0)
|
|
11010
|
+
* @returns BIP-44 style path string
|
|
11011
|
+
*
|
|
11012
|
+
* @example
|
|
11013
|
+
* ```typescript
|
|
11014
|
+
* AuditorKeyDerivation.derivePath(AuditorType.REGULATORY)
|
|
11015
|
+
* // Returns: "m/44'/1234'/0'/1"
|
|
11016
|
+
*
|
|
11017
|
+
* AuditorKeyDerivation.derivePath(AuditorType.TAX, 5)
|
|
11018
|
+
* // Returns: "m/44'/1234'/5'/3"
|
|
11019
|
+
* ```
|
|
11020
|
+
*/
|
|
11021
|
+
static derivePath(auditorType, account = 0) {
|
|
11022
|
+
this.validateAuditorType(auditorType);
|
|
11023
|
+
this.validateAccount(account);
|
|
11024
|
+
return `m/${this.PURPOSE}'/${this.COIN_TYPE}'/${account}'/${auditorType}`;
|
|
9521
11025
|
}
|
|
9522
|
-
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9526
|
-
|
|
9527
|
-
|
|
9528
|
-
|
|
11026
|
+
/**
|
|
11027
|
+
* Derive a viewing key for an auditor
|
|
11028
|
+
*
|
|
11029
|
+
* Uses BIP-32 style hierarchical deterministic key derivation:
|
|
11030
|
+
* 1. Derive master key from seed
|
|
11031
|
+
* 2. Harden purpose (44')
|
|
11032
|
+
* 3. Harden coin type (1234')
|
|
11033
|
+
* 4. Harden account index
|
|
11034
|
+
* 5. Derive auditor type (non-hardened)
|
|
11035
|
+
*
|
|
11036
|
+
* @param params - Derivation parameters
|
|
11037
|
+
* @returns Derived viewing key with metadata
|
|
11038
|
+
*
|
|
11039
|
+
* @throws {ValidationError} If parameters are invalid
|
|
11040
|
+
*
|
|
11041
|
+
* @example
|
|
11042
|
+
* ```typescript
|
|
11043
|
+
* const regulatoryKey = AuditorKeyDerivation.deriveViewingKey({
|
|
11044
|
+
* masterSeed: randomBytes(32),
|
|
11045
|
+
* auditorType: AuditorType.REGULATORY,
|
|
11046
|
+
* })
|
|
11047
|
+
*
|
|
11048
|
+
* console.log(regulatoryKey.path) // "m/44'/1234'/0'/1"
|
|
11049
|
+
* console.log(regulatoryKey.viewingKey.key) // "0x..."
|
|
11050
|
+
* ```
|
|
11051
|
+
*/
|
|
11052
|
+
static deriveViewingKey(params) {
|
|
11053
|
+
const { masterSeed, auditorType, account = 0 } = params;
|
|
11054
|
+
this.validateMasterSeed(masterSeed);
|
|
11055
|
+
this.validateAuditorType(auditorType);
|
|
11056
|
+
this.validateAccount(account);
|
|
11057
|
+
const path = this.derivePath(auditorType, account);
|
|
11058
|
+
const indices = [
|
|
11059
|
+
this.PURPOSE | this.HARDENED,
|
|
11060
|
+
// 44' (hardened)
|
|
11061
|
+
this.COIN_TYPE | this.HARDENED,
|
|
11062
|
+
// 1234' (hardened)
|
|
11063
|
+
account | this.HARDENED,
|
|
11064
|
+
// account' (hardened)
|
|
11065
|
+
auditorType
|
|
11066
|
+
// auditorType (non-hardened)
|
|
11067
|
+
];
|
|
11068
|
+
const masterData = (0, import_hmac2.hmac)(import_sha5123.sha512, (0, import_utils25.utf8ToBytes)("SIP-MASTER-SEED"), masterSeed);
|
|
11069
|
+
let currentKey = new Uint8Array(masterData.slice(0, 32));
|
|
11070
|
+
let chainCode = new Uint8Array(masterData.slice(32, 64));
|
|
11071
|
+
try {
|
|
11072
|
+
for (let i = 0; i < indices.length; i++) {
|
|
11073
|
+
const index = indices[i];
|
|
11074
|
+
const derived = this.deriveChildKey(currentKey, chainCode, index);
|
|
11075
|
+
if (i > 0) {
|
|
11076
|
+
secureWipe(currentKey);
|
|
11077
|
+
}
|
|
11078
|
+
currentKey = new Uint8Array(derived.key);
|
|
11079
|
+
chainCode = new Uint8Array(derived.chainCode);
|
|
11080
|
+
}
|
|
11081
|
+
const keyHex = `0x${(0, import_utils25.bytesToHex)(currentKey)}`;
|
|
11082
|
+
const hashBytes = (0, import_sha25619.sha256)(currentKey);
|
|
11083
|
+
const hash2 = `0x${(0, import_utils25.bytesToHex)(hashBytes)}`;
|
|
11084
|
+
const viewingKey = {
|
|
11085
|
+
key: keyHex,
|
|
11086
|
+
path,
|
|
11087
|
+
hash: hash2
|
|
11088
|
+
};
|
|
11089
|
+
return {
|
|
11090
|
+
path,
|
|
11091
|
+
viewingKey,
|
|
11092
|
+
auditorType,
|
|
11093
|
+
account
|
|
11094
|
+
};
|
|
11095
|
+
} finally {
|
|
11096
|
+
secureWipe(currentKey);
|
|
11097
|
+
secureWipe(chainCode);
|
|
11098
|
+
}
|
|
9529
11099
|
}
|
|
9530
|
-
|
|
9531
|
-
|
|
9532
|
-
|
|
9533
|
-
|
|
9534
|
-
|
|
9535
|
-
|
|
9536
|
-
|
|
11100
|
+
/**
|
|
11101
|
+
* Derive multiple viewing keys at once
|
|
11102
|
+
*
|
|
11103
|
+
* Efficiently derives keys for multiple auditor types from the same
|
|
11104
|
+
* master seed. This is more efficient than calling deriveViewingKey
|
|
11105
|
+
* multiple times as it reuses intermediate derivations.
|
|
11106
|
+
*
|
|
11107
|
+
* @param params - Derivation parameters
|
|
11108
|
+
* @returns Array of derived viewing keys
|
|
11109
|
+
*
|
|
11110
|
+
* @example
|
|
11111
|
+
* ```typescript
|
|
11112
|
+
* const keys = AuditorKeyDerivation.deriveMultiple({
|
|
11113
|
+
* masterSeed: randomBytes(32),
|
|
11114
|
+
* auditorTypes: [
|
|
11115
|
+
* AuditorType.PRIMARY,
|
|
11116
|
+
* AuditorType.REGULATORY,
|
|
11117
|
+
* AuditorType.INTERNAL,
|
|
11118
|
+
* ],
|
|
11119
|
+
* })
|
|
11120
|
+
*
|
|
11121
|
+
* // keys[0] -> PRIMARY key (m/44'/1234'/0'/0)
|
|
11122
|
+
* // keys[1] -> REGULATORY key (m/44'/1234'/0'/1)
|
|
11123
|
+
* // keys[2] -> INTERNAL key (m/44'/1234'/0'/2)
|
|
11124
|
+
* ```
|
|
11125
|
+
*/
|
|
11126
|
+
static deriveMultiple(params) {
|
|
11127
|
+
const { masterSeed, auditorTypes, account = 0 } = params;
|
|
11128
|
+
this.validateMasterSeed(masterSeed);
|
|
11129
|
+
this.validateAccount(account);
|
|
11130
|
+
if (!auditorTypes || auditorTypes.length === 0) {
|
|
11131
|
+
throw new ValidationError(
|
|
11132
|
+
"at least one auditor type is required",
|
|
11133
|
+
"auditorTypes",
|
|
11134
|
+
{ received: auditorTypes },
|
|
11135
|
+
"SIP_2008" /* MISSING_REQUIRED */
|
|
11136
|
+
);
|
|
11137
|
+
}
|
|
11138
|
+
for (const type of auditorTypes) {
|
|
11139
|
+
this.validateAuditorType(type);
|
|
11140
|
+
}
|
|
11141
|
+
const uniqueTypes = Array.from(new Set(auditorTypes));
|
|
11142
|
+
const commonIndices = [
|
|
11143
|
+
this.PURPOSE | this.HARDENED,
|
|
11144
|
+
// 44' (hardened)
|
|
11145
|
+
this.COIN_TYPE | this.HARDENED,
|
|
11146
|
+
// 1234' (hardened)
|
|
11147
|
+
account | this.HARDENED
|
|
11148
|
+
// account' (hardened)
|
|
11149
|
+
];
|
|
11150
|
+
const masterData = (0, import_hmac2.hmac)(import_sha5123.sha512, (0, import_utils25.utf8ToBytes)("SIP-MASTER-SEED"), masterSeed);
|
|
11151
|
+
let commonKey = new Uint8Array(masterData.slice(0, 32));
|
|
11152
|
+
let commonChainCode = new Uint8Array(masterData.slice(32, 64));
|
|
11153
|
+
try {
|
|
11154
|
+
for (let i = 0; i < commonIndices.length; i++) {
|
|
11155
|
+
const index = commonIndices[i];
|
|
11156
|
+
const derived = this.deriveChildKey(commonKey, commonChainCode, index);
|
|
11157
|
+
if (i > 0) {
|
|
11158
|
+
secureWipe(commonKey);
|
|
11159
|
+
}
|
|
11160
|
+
commonKey = new Uint8Array(derived.key);
|
|
11161
|
+
commonChainCode = new Uint8Array(derived.chainCode);
|
|
11162
|
+
}
|
|
11163
|
+
const results = [];
|
|
11164
|
+
for (const auditorType of uniqueTypes) {
|
|
11165
|
+
const derived = this.deriveChildKey(commonKey, commonChainCode, auditorType);
|
|
11166
|
+
try {
|
|
11167
|
+
const keyHex = `0x${(0, import_utils25.bytesToHex)(derived.key)}`;
|
|
11168
|
+
const hashBytes = (0, import_sha25619.sha256)(derived.key);
|
|
11169
|
+
const hash2 = `0x${(0, import_utils25.bytesToHex)(hashBytes)}`;
|
|
11170
|
+
const path = this.derivePath(auditorType, account);
|
|
11171
|
+
const viewingKey = {
|
|
11172
|
+
key: keyHex,
|
|
11173
|
+
path,
|
|
11174
|
+
hash: hash2
|
|
11175
|
+
};
|
|
11176
|
+
results.push({
|
|
11177
|
+
path,
|
|
11178
|
+
viewingKey,
|
|
11179
|
+
auditorType,
|
|
11180
|
+
account
|
|
11181
|
+
});
|
|
11182
|
+
} finally {
|
|
11183
|
+
secureWipe(derived.key);
|
|
11184
|
+
secureWipe(derived.chainCode);
|
|
11185
|
+
}
|
|
11186
|
+
}
|
|
11187
|
+
return results;
|
|
11188
|
+
} finally {
|
|
11189
|
+
secureWipe(commonKey);
|
|
11190
|
+
secureWipe(commonChainCode);
|
|
11191
|
+
}
|
|
9537
11192
|
}
|
|
9538
|
-
|
|
9539
|
-
|
|
9540
|
-
|
|
9541
|
-
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
|
|
9545
|
-
|
|
9546
|
-
|
|
11193
|
+
/**
|
|
11194
|
+
* Get human-readable name for auditor type
|
|
11195
|
+
*
|
|
11196
|
+
* @param auditorType - Auditor type enum value
|
|
11197
|
+
* @returns Friendly name string
|
|
11198
|
+
*/
|
|
11199
|
+
static getAuditorTypeName(auditorType) {
|
|
11200
|
+
switch (auditorType) {
|
|
11201
|
+
case 0 /* PRIMARY */:
|
|
11202
|
+
return "Primary";
|
|
11203
|
+
case 1 /* REGULATORY */:
|
|
11204
|
+
return "Regulatory";
|
|
11205
|
+
case 2 /* INTERNAL */:
|
|
11206
|
+
return "Internal";
|
|
11207
|
+
case 3 /* TAX */:
|
|
11208
|
+
return "Tax Authority";
|
|
11209
|
+
default:
|
|
11210
|
+
return `Unknown (${auditorType})`;
|
|
11211
|
+
}
|
|
9547
11212
|
}
|
|
9548
|
-
|
|
9549
|
-
|
|
9550
|
-
|
|
9551
|
-
|
|
9552
|
-
|
|
9553
|
-
|
|
9554
|
-
|
|
11213
|
+
// ─── Private Helpers ─────────────────────────────────────────────────────────
|
|
11214
|
+
/**
|
|
11215
|
+
* Derive a child key using BIP-32 HMAC-SHA512 derivation
|
|
11216
|
+
*
|
|
11217
|
+
* @param parentKey - Parent key bytes (32 bytes)
|
|
11218
|
+
* @param chainCode - Parent chain code (32 bytes)
|
|
11219
|
+
* @param index - Child index (use | HARDENED for hardened derivation)
|
|
11220
|
+
* @returns Derived key and chain code
|
|
11221
|
+
*/
|
|
11222
|
+
static deriveChildKey(parentKey, chainCode, index) {
|
|
11223
|
+
const isHardened = (index & this.HARDENED) !== 0;
|
|
11224
|
+
const data = new Uint8Array(37);
|
|
11225
|
+
if (isHardened) {
|
|
11226
|
+
data[0] = 0;
|
|
11227
|
+
data.set(parentKey, 1);
|
|
11228
|
+
} else {
|
|
11229
|
+
data[0] = 1;
|
|
11230
|
+
data.set(parentKey, 1);
|
|
11231
|
+
}
|
|
11232
|
+
const indexView = new DataView(data.buffer, 33, 4);
|
|
11233
|
+
indexView.setUint32(0, index, false);
|
|
11234
|
+
const hmacResult = (0, import_hmac2.hmac)(import_sha5123.sha512, chainCode, data);
|
|
11235
|
+
const childKey = new Uint8Array(hmacResult.slice(0, 32));
|
|
11236
|
+
const childChainCode = new Uint8Array(hmacResult.slice(32, 64));
|
|
11237
|
+
return {
|
|
11238
|
+
key: childKey,
|
|
11239
|
+
chainCode: childChainCode
|
|
11240
|
+
};
|
|
9555
11241
|
}
|
|
9556
|
-
|
|
9557
|
-
|
|
9558
|
-
|
|
9559
|
-
|
|
9560
|
-
|
|
9561
|
-
|
|
9562
|
-
|
|
11242
|
+
/**
|
|
11243
|
+
* Validate master seed
|
|
11244
|
+
*/
|
|
11245
|
+
static validateMasterSeed(seed) {
|
|
11246
|
+
if (!seed || seed.length < 32) {
|
|
11247
|
+
throw new ValidationError(
|
|
11248
|
+
"master seed must be at least 32 bytes",
|
|
11249
|
+
"masterSeed",
|
|
11250
|
+
{ received: seed?.length ?? 0 },
|
|
11251
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
11252
|
+
);
|
|
11253
|
+
}
|
|
9563
11254
|
}
|
|
9564
|
-
|
|
9565
|
-
|
|
9566
|
-
|
|
9567
|
-
|
|
9568
|
-
|
|
9569
|
-
|
|
9570
|
-
|
|
11255
|
+
/**
|
|
11256
|
+
* Validate auditor type
|
|
11257
|
+
*/
|
|
11258
|
+
static validateAuditorType(type) {
|
|
11259
|
+
const validTypes = [
|
|
11260
|
+
0 /* PRIMARY */,
|
|
11261
|
+
1 /* REGULATORY */,
|
|
11262
|
+
2 /* INTERNAL */,
|
|
11263
|
+
3 /* TAX */
|
|
11264
|
+
];
|
|
11265
|
+
if (!validTypes.includes(type)) {
|
|
11266
|
+
throw new ValidationError(
|
|
11267
|
+
`invalid auditor type: ${type}`,
|
|
11268
|
+
"auditorType",
|
|
11269
|
+
{ received: type, valid: validTypes },
|
|
11270
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
11271
|
+
);
|
|
11272
|
+
}
|
|
9571
11273
|
}
|
|
9572
|
-
|
|
9573
|
-
|
|
9574
|
-
|
|
9575
|
-
|
|
9576
|
-
|
|
9577
|
-
|
|
9578
|
-
|
|
11274
|
+
/**
|
|
11275
|
+
* Validate account index
|
|
11276
|
+
*/
|
|
11277
|
+
static validateAccount(account) {
|
|
11278
|
+
if (!Number.isInteger(account) || account < 0 || account >= this.HARDENED) {
|
|
11279
|
+
throw new ValidationError(
|
|
11280
|
+
`account must be a non-negative integer less than ${this.HARDENED}`,
|
|
11281
|
+
"account",
|
|
11282
|
+
{ received: account },
|
|
11283
|
+
"SIP_2001" /* INVALID_INPUT */
|
|
11284
|
+
);
|
|
11285
|
+
}
|
|
9579
11286
|
}
|
|
9580
|
-
}
|
|
11287
|
+
};
|
|
9581
11288
|
|
|
9582
11289
|
// src/wallet/errors.ts
|
|
9583
11290
|
var import_types18 = require("@sip-protocol/types");
|
|
@@ -12726,7 +14433,7 @@ function createTrezorAdapter(config) {
|
|
|
12726
14433
|
|
|
12727
14434
|
// src/wallet/hardware/mock.ts
|
|
12728
14435
|
var import_types51 = require("@sip-protocol/types");
|
|
12729
|
-
var
|
|
14436
|
+
var import_utils26 = require("@noble/hashes/utils");
|
|
12730
14437
|
var MockLedgerAdapter = class extends BaseWalletAdapter {
|
|
12731
14438
|
chain;
|
|
12732
14439
|
name = "mock-ledger";
|
|
@@ -12971,15 +14678,15 @@ var MockLedgerAdapter = class extends BaseWalletAdapter {
|
|
|
12971
14678
|
}
|
|
12972
14679
|
}
|
|
12973
14680
|
generateMockAddress(index) {
|
|
12974
|
-
const bytes = (0,
|
|
14681
|
+
const bytes = (0, import_utils26.randomBytes)(20);
|
|
12975
14682
|
bytes[0] = index;
|
|
12976
|
-
return `0x${(0,
|
|
14683
|
+
return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
|
|
12977
14684
|
}
|
|
12978
14685
|
generateMockPublicKey(index) {
|
|
12979
|
-
const bytes = (0,
|
|
14686
|
+
const bytes = (0, import_utils26.randomBytes)(33);
|
|
12980
14687
|
bytes[0] = 2;
|
|
12981
14688
|
bytes[1] = index;
|
|
12982
|
-
return `0x${(0,
|
|
14689
|
+
return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
|
|
12983
14690
|
}
|
|
12984
14691
|
generateMockSignature(data) {
|
|
12985
14692
|
const sig = new Uint8Array(65);
|
|
@@ -12988,7 +14695,7 @@ var MockLedgerAdapter = class extends BaseWalletAdapter {
|
|
|
12988
14695
|
sig[32 + i] = (data[i % data.length] ?? 0) ^ i * 11;
|
|
12989
14696
|
}
|
|
12990
14697
|
sig[64] = 27;
|
|
12991
|
-
return `0x${(0,
|
|
14698
|
+
return `0x${(0, import_utils26.bytesToHex)(sig)}`;
|
|
12992
14699
|
}
|
|
12993
14700
|
delay(ms) {
|
|
12994
14701
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -13177,15 +14884,15 @@ var MockTrezorAdapter = class extends BaseWalletAdapter {
|
|
|
13177
14884
|
}
|
|
13178
14885
|
}
|
|
13179
14886
|
generateMockAddress(index) {
|
|
13180
|
-
const bytes = (0,
|
|
14887
|
+
const bytes = (0, import_utils26.randomBytes)(20);
|
|
13181
14888
|
bytes[0] = index + 100;
|
|
13182
|
-
return `0x${(0,
|
|
14889
|
+
return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
|
|
13183
14890
|
}
|
|
13184
14891
|
generateMockPublicKey(index) {
|
|
13185
|
-
const bytes = (0,
|
|
14892
|
+
const bytes = (0, import_utils26.randomBytes)(33);
|
|
13186
14893
|
bytes[0] = 3;
|
|
13187
14894
|
bytes[1] = index + 100;
|
|
13188
|
-
return `0x${(0,
|
|
14895
|
+
return `0x${(0, import_utils26.bytesToHex)(bytes)}`;
|
|
13189
14896
|
}
|
|
13190
14897
|
generateMockSignature(data) {
|
|
13191
14898
|
const sig = new Uint8Array(65);
|
|
@@ -13194,7 +14901,7 @@ var MockTrezorAdapter = class extends BaseWalletAdapter {
|
|
|
13194
14901
|
sig[32 + i] = (data[i % data.length] ?? 0) ^ i * 17;
|
|
13195
14902
|
}
|
|
13196
14903
|
sig[64] = 28;
|
|
13197
|
-
return `0x${(0,
|
|
14904
|
+
return `0x${(0, import_utils26.bytesToHex)(sig)}`;
|
|
13198
14905
|
}
|
|
13199
14906
|
delay(ms) {
|
|
13200
14907
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
@@ -13213,7 +14920,7 @@ var import_types54 = require("@sip-protocol/types");
|
|
|
13213
14920
|
// src/proofs/browser.ts
|
|
13214
14921
|
var import_noir_js = require("@noir-lang/noir_js");
|
|
13215
14922
|
var import_bb = require("@aztec/bb.js");
|
|
13216
|
-
var
|
|
14923
|
+
var import_secp256k18 = require("@noble/curves/secp256k1");
|
|
13217
14924
|
|
|
13218
14925
|
// src/proofs/circuits/funding_proof.json
|
|
13219
14926
|
var funding_proof_default = { noir_version: "1.0.0-beta.15+83245db91dcf63420ef4bcbbd85b98f397fee663", hash: "3859649086066977477", abi: { parameters: [{ name: "minimum_required", type: { kind: "integer", sign: "unsigned", width: 64 }, visibility: "public" }, { name: "asset_id", type: { kind: "field" }, visibility: "public" }, { name: "balance", type: { kind: "integer", sign: "unsigned", width: 64 }, visibility: "private" }, { name: "blinding", type: { kind: "field" }, visibility: "private" }], return_type: { abi_type: { kind: "array", length: 32, type: { kind: "integer", sign: "unsigned", width: 8 } }, visibility: "public" }, error_types: { "2900908756532713827": { error_kind: "string", string: "Insufficient balance" }, "15764276373176857197": { error_kind: "string", string: "Stack too deep" }, "15835548349546956319": { error_kind: "string", string: "Field failed to decompose into specified 32 limbs" } } }, bytecode: "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", debug_symbols: "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", file_map: { "17": { source: `use crate::field::field_less_than;
|
|
@@ -14226,7 +15933,7 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
|
|
|
14226
15933
|
* Derive secp256k1 public key coordinates from a private key
|
|
14227
15934
|
*/
|
|
14228
15935
|
static derivePublicKey(privateKey) {
|
|
14229
|
-
const uncompressedPubKey =
|
|
15936
|
+
const uncompressedPubKey = import_secp256k18.secp256k1.getPublicKey(privateKey, false);
|
|
14230
15937
|
const x = Array.from(uncompressedPubKey.slice(1, 33));
|
|
14231
15938
|
const y = Array.from(uncompressedPubKey.slice(33, 65));
|
|
14232
15939
|
return { x, y };
|
|
@@ -14732,14 +16439,14 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
|
|
|
14732
16439
|
}
|
|
14733
16440
|
async computeCommitmentHash(balance, blindingFactor, assetId) {
|
|
14734
16441
|
const blindingField = this.bytesToField(blindingFactor);
|
|
14735
|
-
const { sha256:
|
|
16442
|
+
const { sha256: sha25620 } = await import("@noble/hashes/sha256");
|
|
14736
16443
|
const { bytesToHex: nobleToHex } = await import("@noble/hashes/utils");
|
|
14737
16444
|
const preimage = new Uint8Array([
|
|
14738
16445
|
...this.bigintToBytes(balance, 8),
|
|
14739
16446
|
...blindingFactor.slice(0, 32),
|
|
14740
16447
|
...hexToBytes9(this.assetIdToField(assetId))
|
|
14741
16448
|
]);
|
|
14742
|
-
const hash2 =
|
|
16449
|
+
const hash2 = sha25620(preimage);
|
|
14743
16450
|
const commitmentHash = nobleToHex(hash2);
|
|
14744
16451
|
return { commitmentHash, blindingField };
|
|
14745
16452
|
}
|
|
@@ -14785,46 +16492,46 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
|
|
|
14785
16492
|
return bytes;
|
|
14786
16493
|
}
|
|
14787
16494
|
async computeSenderCommitment(senderAddressField, senderBlindingField) {
|
|
14788
|
-
const { sha256:
|
|
16495
|
+
const { sha256: sha25620 } = await import("@noble/hashes/sha256");
|
|
14789
16496
|
const { bytesToHex: nobleToHex } = await import("@noble/hashes/utils");
|
|
14790
16497
|
const addressBytes = hexToBytes9(senderAddressField);
|
|
14791
16498
|
const blindingBytes = hexToBytes9(senderBlindingField.padStart(64, "0"));
|
|
14792
16499
|
const preimage = new Uint8Array([...addressBytes, ...blindingBytes]);
|
|
14793
|
-
const hash2 =
|
|
16500
|
+
const hash2 = sha25620(preimage);
|
|
14794
16501
|
const commitmentX = nobleToHex(hash2.slice(0, 16)).padStart(64, "0");
|
|
14795
16502
|
const commitmentY = nobleToHex(hash2.slice(16, 32)).padStart(64, "0");
|
|
14796
16503
|
return { commitmentX, commitmentY };
|
|
14797
16504
|
}
|
|
14798
16505
|
async computeNullifier(senderSecretField, intentHashField, nonceField) {
|
|
14799
|
-
const { sha256:
|
|
16506
|
+
const { sha256: sha25620 } = await import("@noble/hashes/sha256");
|
|
14800
16507
|
const { bytesToHex: nobleToHex } = await import("@noble/hashes/utils");
|
|
14801
16508
|
const secretBytes = hexToBytes9(senderSecretField.padStart(64, "0"));
|
|
14802
16509
|
const intentBytes = hexToBytes9(intentHashField);
|
|
14803
16510
|
const nonceBytes = hexToBytes9(nonceField.padStart(64, "0"));
|
|
14804
16511
|
const preimage = new Uint8Array([...secretBytes, ...intentBytes, ...nonceBytes]);
|
|
14805
|
-
const hash2 =
|
|
16512
|
+
const hash2 = sha25620(preimage);
|
|
14806
16513
|
return nobleToHex(hash2);
|
|
14807
16514
|
}
|
|
14808
16515
|
async computeOutputCommitment(outputAmount, outputBlinding) {
|
|
14809
|
-
const { sha256:
|
|
16516
|
+
const { sha256: sha25620 } = await import("@noble/hashes/sha256");
|
|
14810
16517
|
const { bytesToHex: nobleToHex } = await import("@noble/hashes/utils");
|
|
14811
16518
|
const amountBytes = this.bigintToBytes(outputAmount, 8);
|
|
14812
16519
|
const blindingBytes = outputBlinding.slice(0, 32);
|
|
14813
16520
|
const preimage = new Uint8Array([...amountBytes, ...blindingBytes]);
|
|
14814
|
-
const hash2 =
|
|
16521
|
+
const hash2 = sha25620(preimage);
|
|
14815
16522
|
const commitmentX = nobleToHex(hash2.slice(0, 16)).padStart(64, "0");
|
|
14816
16523
|
const commitmentY = nobleToHex(hash2.slice(16, 32)).padStart(64, "0");
|
|
14817
16524
|
return { commitmentX, commitmentY };
|
|
14818
16525
|
}
|
|
14819
16526
|
async computeSolverId(solverSecretField) {
|
|
14820
|
-
const { sha256:
|
|
16527
|
+
const { sha256: sha25620 } = await import("@noble/hashes/sha256");
|
|
14821
16528
|
const { bytesToHex: nobleToHex } = await import("@noble/hashes/utils");
|
|
14822
16529
|
const secretBytes = hexToBytes9(solverSecretField.padStart(64, "0"));
|
|
14823
|
-
const hash2 =
|
|
16530
|
+
const hash2 = sha25620(secretBytes);
|
|
14824
16531
|
return nobleToHex(hash2);
|
|
14825
16532
|
}
|
|
14826
16533
|
async computeOracleMessageHash(recipient, amount, txHash, blockNumber) {
|
|
14827
|
-
const { sha256:
|
|
16534
|
+
const { sha256: sha25620 } = await import("@noble/hashes/sha256");
|
|
14828
16535
|
const recipientBytes = hexToBytes9(this.hexToField(recipient));
|
|
14829
16536
|
const amountBytes = this.bigintToBytes(amount, 8);
|
|
14830
16537
|
const txHashBytes = hexToBytes9(this.hexToField(txHash));
|
|
@@ -14835,11 +16542,11 @@ var BrowserNoirProvider = class _BrowserNoirProvider {
|
|
|
14835
16542
|
...txHashBytes,
|
|
14836
16543
|
...blockBytes
|
|
14837
16544
|
]);
|
|
14838
|
-
const hash2 =
|
|
16545
|
+
const hash2 = sha25620(preimage);
|
|
14839
16546
|
return Array.from(hash2);
|
|
14840
16547
|
}
|
|
14841
16548
|
getPublicKeyCoordinates(privateKey) {
|
|
14842
|
-
const uncompressedPubKey =
|
|
16549
|
+
const uncompressedPubKey = import_secp256k18.secp256k1.getPublicKey(privateKey, false);
|
|
14843
16550
|
const x = Array.from(uncompressedPubKey.slice(1, 33));
|
|
14844
16551
|
const y = Array.from(uncompressedPubKey.slice(33, 65));
|
|
14845
16552
|
return { x, y };
|
|
@@ -15393,11 +17100,15 @@ var ProofWorker = class _ProofWorker {
|
|
|
15393
17100
|
0 && (module.exports = {
|
|
15394
17101
|
ATTESTATION_VERSION,
|
|
15395
17102
|
AptosStealthService,
|
|
17103
|
+
AuditorKeyDerivation,
|
|
17104
|
+
AuditorType,
|
|
15396
17105
|
BaseWalletAdapter,
|
|
15397
17106
|
BrowserNoirProvider,
|
|
15398
17107
|
CHAIN_NUMERIC_IDS,
|
|
15399
17108
|
COSMOS_CHAIN_PREFIXES,
|
|
15400
17109
|
ComplianceManager,
|
|
17110
|
+
ComplianceReporter,
|
|
17111
|
+
ConditionalDisclosure,
|
|
15401
17112
|
CosmosStealthService,
|
|
15402
17113
|
CryptoError,
|
|
15403
17114
|
DEFAULT_THRESHOLD,
|
|
@@ -15450,6 +17161,7 @@ var ProofWorker = class _ProofWorker {
|
|
|
15450
17161
|
SmartRouter,
|
|
15451
17162
|
SolanaWalletAdapter,
|
|
15452
17163
|
SwapStatus,
|
|
17164
|
+
ThresholdViewingKey,
|
|
15453
17165
|
Treasury,
|
|
15454
17166
|
TrezorWalletAdapter,
|
|
15455
17167
|
ValidationError,
|
|
@@ -15538,6 +17250,7 @@ var ProofWorker = class _ProofWorker {
|
|
|
15538
17250
|
generateEd25519StealthAddress,
|
|
15539
17251
|
generateEd25519StealthMetaAddress,
|
|
15540
17252
|
generateIntentId,
|
|
17253
|
+
generatePdfReport,
|
|
15541
17254
|
generateRandomBytes,
|
|
15542
17255
|
generateStealthAddress,
|
|
15543
17256
|
generateStealthMetaAddress,
|